Skip to content

Commit

Permalink
Merge pull request #1653 from pazz/gh-flake
Browse files Browse the repository at this point in the history
Add github action to check the nix build and deactivate broken CI jobs until the gpg dependency issue is resolved.
  • Loading branch information
lucc authored May 7, 2024
2 parents 7c2e43c + 395a698 commit c6f3c6b
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 61 deletions.
27 changes: 25 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
name: Run code checks
name: Run code checks with different build tools

on:
- push
- pull_request

jobs:

check:
poetry:

# TODO remove this line after the gpg dependency problem from #1630 and
# c1137ea9 is fixed. Until then these checks are deactivated.
if: false

runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -28,3 +33,21 @@ jobs:
- name: Run check "${{ matrix.check }}"
run: "${{ matrix.check }}"

nix-flake:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
derivation:
- default
- docs

steps:
- name: Install Nix
uses: cachix/install-nix-action@v22

- uses: actions/checkout@v4

- name: Build the ${{ matrix.derivation }} derivation
run: 'nix build --print-build-logs .\#${{ matrix.derivation }}'
12 changes: 8 additions & 4 deletions .github/workflows/test.yml → .github/workflows/test.yml-broken
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# WARNING: This workflow is deactivated until the gpg dependency problem from
# issue 1630 (see
# https://github.com/pazz/alot/issues/1630#issuecomment-1938174029 and
# onwards) is fixed. The problem was introduced in c1137ea9: the gpg
# dependency is required with version > 1.10.0 and such a version is not
# currently available on PyPI but must be build from hand.

name: Run tests

on:
Expand Down Expand Up @@ -59,11 +66,8 @@ jobs:
- name: disable some tests that don't work in CI
run: >
sed -Ei
-e '1iimport unittest'
-e 's/^(\s*)(async )?def test_(no_spawn_no_stdin_attached|save_named_query|parsing_notmuch_config_with_non_bool_synchronize_flag_fails)/\[email protected]("broken in ci")\n&/'
-e 's/^(\s*)async def test_no_spawn_no_stdin_attached/\[email protected]("broken in ci")\n&/'
tests/commands/test_global.py
tests/db/test_manager.py
tests/settings/test_manager.py

- name: Run tests
run: python3 -m unittest --verbose
6 changes: 2 additions & 4 deletions alot/db/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ def exclude_tags(self):
if exclude_tags is not None:
return exclude_tags

exclude_tags = settings.get_notmuch_setting('search', 'exclude_tags')
if exclude_tags:
return [t for t in exclude_tags.split(';') if t]
return settings.get_notmuch_setting('search', 'exclude_tags')

def flush(self):
"""
Expand All @@ -72,7 +70,7 @@ def flush(self):
raise DatabaseROError()
if self.writequeue:
# read notmuch's config regarding imap flag synchronization
sync = settings.get_notmuch_setting('maildir', 'synchronize_flags') == 'true'
sync = settings.get_notmuch_setting('maildir', 'synchronize_flags')

# go through writequeue entries
while self.writequeue:
Expand Down
7 changes: 1 addition & 6 deletions alot/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def call_cmd(cmdlist, stdin=None):
return out, err, ret


async def call_cmd_async(cmdlist, stdin=None, env=None):
async def call_cmd_async(cmdlist, stdin=None):
"""Given a command, call that command asynchronously and return the output.
This function only handles `OSError` when creating the subprocess, any
Expand All @@ -305,15 +305,10 @@ async def call_cmd_async(cmdlist, stdin=None, env=None):
termenc = urwid.util.detected_encoding
cmdlist = [s.encode(termenc) for s in cmdlist]

environment = os.environ.copy()
if env is not None:
environment.update(env)
logging.debug('ENV = %s', environment)
logging.debug('CMD = %s', cmdlist)
try:
proc = await asyncio.create_subprocess_exec(
*cmdlist,
env=environment,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
stdin=asyncio.subprocess.PIPE if stdin else None)
Expand Down
24 changes: 18 additions & 6 deletions alot/settings/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,15 @@ def read_notmuch_config(path):
The configuration value for a key under a section can be accessed with
``config[section][key]``.
The returned value is a dict ``config`` with
The returned value is a dict ``config`` with some values converted to
special python types. These are the values that alot is known to use. All
other values in the returned dict are just strings.
:param path: path to the configuration file, which is passed as
argument to the --config option of notmuch.
:type path: str
:raises: :class:`~alot.settings.errors.ConfigError`
:rtype: `dict`
:rtype: dict
"""
cmd = ['notmuch', '--config', path, 'config', 'list']
out, err, code = call_cmd(cmd)
Expand All @@ -108,11 +110,21 @@ def read_notmuch_config(path):
logging.error(msg)
raise ConfigError(msg)

parsed = ConfigObj(infile=out.splitlines(), interpolation=False,
list_values=False)

config = {}
for line in out.splitlines():
left_hand, right_hand = line.split("=", maxsplit=1)
section, key = left_hand.split(".", maxsplit=1)
config.setdefault(section, {})[key] = right_hand
try:
for dotted_key, value in parsed.items():
section, key = dotted_key.split(".", maxsplit=1)
if section == "maildir" and key == "synchronize_flags":
value = parsed.as_bool(dotted_key)
if section == "search" and key == "exclude_tags":
if value:
value = [t for t in value.split(';') if t]
config.setdefault(section, {})[key] = value
except ValueError as e:
raise ConfigError(f"Bad value in notmuch config file: {e}")

return config

Expand Down
33 changes: 25 additions & 8 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@
let
# see https://github.com/nix-community/poetry2nix/tree/master#api for more functions and examples.
pkgs = nixpkgs.legacyPackages.${system};
inherit (poetry2nix.lib.mkPoetry2Nix { inherit pkgs; }) mkPoetryApplication overrides;
inherit (poetry2nix.lib.mkPoetry2Nix { inherit pkgs; }) mkPoetryApplication mkPoetryEnv overrides;
defaultArgs = {
projectDir = self;
overrides = overrides.withDefaults (final: prev: {
gpg = prev.gpgme;
notmuch2 = pkgs.python3.pkgs.notmuch2;
});
};
in
{
packages = {
alot = mkPoetryApplication {
projectDir = self;
alot = mkPoetryApplication (defaultArgs // {
nativeBuildInputs = [
pkgs.python3.pkgs.cffi
];
Expand All @@ -27,12 +33,23 @@
pkgs.gpgme.dev
pkgs.python3.pkgs.cffi
];
overrides = overrides.withDefaults (final: prev: {
gpg = prev.gpgme;
notmuch2 = pkgs.python3.pkgs.notmuch2;
});

};
nativeCheckInputs = with pkgs; [ gnupg notmuch procps ];
checkPhase = ''
# In the nix sandbox stdin is not a terminal but /dev/null so we
# change the shell command only in this specific test.
sed -i '/test_no_spawn_no_stdin_attached/,/^$/s/test -t 0/sh -c "[ $(wc -l) -eq 0 ]"/' tests/commands/test_global.py
python3 -m unittest -v
'';
});
docs = pkgs.runCommand "alot-docs" {
src = self;
nativeBuildInputs = [
(mkPoetryEnv (defaultArgs // { groups = ["doc"]; }))
pkgs.gnumake
];
} ''make -C $src/docs html man BUILDDIR=$out'';
default = self.packages.${system}.alot;
};
});
Expand Down
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ pycodestyle = "*"
pytest = "*"
sphinx = "*"

[tool.poetry.group.doc]
optional = true

[tool.poetry.group.doc.dependencies]
sphinx = "*"

[tool.poetry.scripts]
alot = "alot.__main__:main"

Expand Down
25 changes: 17 additions & 8 deletions tests/db/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

"""Test suite for alot.db.manager module."""

import tempfile
import textwrap
import os
import shutil
import tempfile
import textwrap
from unittest import mock

from alot.db.manager import DBManager
from alot.settings.const import settings
Expand Down Expand Up @@ -43,10 +44,18 @@ def setUpClass(cls):
settings.read_notmuch_config(cls.notmuch_config_path)

def test_save_named_query(self):
alias = 'key'
querystring = 'query string'
self.manager.save_named_query(alias, querystring)
self.manager.flush()
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as f:
f.write(textwrap.dedent("""\
[maildir]
synchronize_flags = true
"""))
self.addCleanup(os.unlink, f.name)

with mock.patch.dict('os.environ', NOTMUCH_CONFIG=f.name):
alias = 'key'
querystring = 'query string'
self.manager.save_named_query(alias, querystring)
self.manager.flush()

named_queries_dict = self.manager.get_named_queries()
self.assertDictEqual(named_queries_dict, {alias: querystring})
named_queries_dict = self.manager.get_named_queries()
self.assertDictEqual(named_queries_dict, {alias: querystring})
12 changes: 6 additions & 6 deletions tests/db/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,9 +739,9 @@ def test_text_plain_with_attachment_text(self):
@mock.patch('alot.db.utils.settings.mailcap_find_match',
mock.Mock(return_value=(None, None)))
def test_simple_utf8_file(self):
mail = email.message_from_binary_file(
open('tests/static/mail/utf8.eml', 'rb'),
_class=email.message.EmailMessage)
with open('tests/static/mail/utf8.eml', 'rb') as f:
mail = email.message_from_binary_file(
f, _class=email.message.EmailMessage)
body_part = utils.get_body_part(mail)
actual = utils.extract_body_part(body_part)
expected = "Liebe Grüße!\n"
Expand All @@ -757,9 +757,9 @@ def test_utf8_plaintext_mailcap(self):
https://github.com/pazz/alot/issues/1522
"""
mail = email.message_from_binary_file(
open('tests/static/mail/utf8.eml', 'rb'),
_class=email.message.EmailMessage)
with open('tests/static/mail/utf8.eml', 'rb') as f:
mail = email.message_from_binary_file(
f, _class=email.message.EmailMessage)
body_part = utils.get_body_part(mail)
actual = utils.extract_body_part(body_part)
expected = "Liebe Grüße?\n"
Expand Down
17 changes: 0 additions & 17 deletions tests/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,23 +422,6 @@ async def test_stdin(self):
ret = await helper.call_cmd_async(['cat', '-'], stdin='foo')
self.assertEqual(ret[0], 'foo')

@utilities.async_test
async def test_env_set(self):
with mock.patch.dict(os.environ, {}, clear=True):
ret = await helper.call_cmd_async(
['python3', '-c', 'import os; '
'print(os.environ.get("foo", "fail"), end="")'
],
env={'foo': 'bar'})
self.assertEqual(ret[0], 'bar')

@utilities.async_test
async def test_env_doesnt_pollute(self):
with mock.patch.dict(os.environ, {}, clear=True):
await helper.call_cmd_async(['echo', '-n', 'foo'],
env={'foo': 'bar'})
self.assertEqual(os.environ, {})

@utilities.async_test
async def test_command_fails(self):
_, err, ret = await helper.call_cmd_async(['_____better_not_exist'])
Expand Down

0 comments on commit c6f3c6b

Please sign in to comment.