From fb8441f18e613145f3eee8ed59e25e093ddc5c14 Mon Sep 17 00:00:00 2001 From: jsuchan-reef <135112713+jsuchan-reef@users.noreply.github.com> Date: Fri, 1 Dec 2023 20:23:54 +0700 Subject: [PATCH 1/5] use xdg compliant path for config (#447) * change: config lookup path is xdg-compliant on such OSes * add backward-compatible lookup for pre-existing config file * remove obsolete monkeypatch * CR feedback * fix tests --- b2sdk/account_info/sqlite_account_info.py | 42 +++++++++++++------ changelog.d/b2cli958.changed.md | 1 + test/unit/account_info/test_account_info.py | 41 +++++++++++------- .../account_info/test_sqlite_account_info.py | 18 ++++++-- 4 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 changelog.d/b2cli958.changed.md diff --git a/b2sdk/account_info/sqlite_account_info.py b/b2sdk/account_info/sqlite_account_info.py index c1d858a78..fffd663cc 100644 --- a/b2sdk/account_info/sqlite_account_info.py +++ b/b2sdk/account_info/sqlite_account_info.py @@ -15,6 +15,7 @@ import re import sqlite3 import stat +import sys import threading from .exception import CorruptAccountInfo, MissingAccountData @@ -53,18 +54,19 @@ def __init__(self, file_name=None, last_upgrade_to_run=None, profile: str | None If ``profile`` arg is provided: - * ``{XDG_CONFIG_HOME_ENV_VAR}/b2/db-.sqlite``, if ``{XDG_CONFIG_HOME_ENV_VAR}`` env var is set + * ``{B2_ACCOUNT_INFO_PROFILE_FILE}`` if it already exists + * ``${XDG_CONFIG_HOME_ENV_VAR}/b2/db-.sqlite`` on XDG-compatible OSes (Linux, BSD) * ``{B2_ACCOUNT_INFO_PROFILE_FILE}`` Otherwise: * ``file_name``, if truthy * ``{B2_ACCOUNT_INFO_ENV_VAR}`` env var's value, if set - * ``{B2_ACCOUNT_INFO_DEFAULT_FILE}``, if it exists - * ``{XDG_CONFIG_HOME_ENV_VAR}/b2/account_info``, if ``{XDG_CONFIG_HOME_ENV_VAR}`` env var is set + * ``{B2_ACCOUNT_INFO_DEFAULT_FILE}``, if it already exists + * ``${XDG_CONFIG_HOME_ENV_VAR}/b2/account_info`` on XDG-compatible OSes (Linux, BSD) * ``{B2_ACCOUNT_INFO_DEFAULT_FILE}``, as default - If the directory ``{XDG_CONFIG_HOME_ENV_VAR}/b2`` does not exist (and is needed), it is created. + If the directory ``${XDG_CONFIG_HOME_ENV_VAR}/b2`` does not exist (and is needed), it is created. :param str file_name: The sqlite file to use; overrides the default. :param int last_upgrade_to_run: For testing only, override the auto-update on the db. @@ -91,10 +93,25 @@ def __init__(self, file_name=None, last_upgrade_to_run=None, profile: str | None ) @classmethod - def _get_user_account_info_path(cls, file_name=None, profile=None): + def _get_xdg_config_path(cls) -> str | None: + """ + Return XDG config path if the OS is XDG-compatible (Linux, BSD), None otherwise. + + If $XDG_CONFIG_HOME is empty but the OS is XDG compliant, fallback to ~/.config as expected by XDG standard. + """ + xdg_config_home = os.getenv(XDG_CONFIG_HOME_ENV_VAR) + if xdg_config_home or sys.platform not in ('win32', 'darwin'): + return xdg_config_home or os.path.join(os.path.expanduser('~/.config')) + return None + + @classmethod + def _get_user_account_info_path(cls, file_name: str | None = None, profile: str | None = None): if profile and not B2_ACCOUNT_INFO_PROFILE_NAME_REGEXP.match(profile): raise ValueError(f'Invalid profile name: {profile}') + profile_file = B2_ACCOUNT_INFO_PROFILE_FILE.format(profile=profile) if profile else None + xdg_config_path = cls._get_xdg_config_path() + if file_name: if profile: raise ValueError('Provide either file_name or profile, not both') @@ -106,16 +123,17 @@ def _get_user_account_info_path(cls, file_name=None, profile=None): format(B2_ACCOUNT_INFO_ENV_VAR) ) user_account_info_path = os.environ[B2_ACCOUNT_INFO_ENV_VAR] - elif os.path.exists(os.path.expanduser(B2_ACCOUNT_INFO_DEFAULT_FILE)) and not profile: + elif not profile and os.path.exists(os.path.expanduser(B2_ACCOUNT_INFO_DEFAULT_FILE)): user_account_info_path = B2_ACCOUNT_INFO_DEFAULT_FILE - elif XDG_CONFIG_HOME_ENV_VAR in os.environ: - config_home = os.environ[XDG_CONFIG_HOME_ENV_VAR] + elif profile and os.path.exists(profile_file): + user_account_info_path = profile_file + elif xdg_config_path: + os.makedirs(os.path.join(xdg_config_path, 'b2'), mode=0o755, exist_ok=True) + file_name = f'db-{profile}.sqlite' if profile else 'account_info' - user_account_info_path = os.path.join(config_home, 'b2', file_name) - if not os.path.exists(os.path.join(config_home, 'b2')): - os.makedirs(os.path.join(config_home, 'b2'), mode=0o755) + user_account_info_path = os.path.join(xdg_config_path, 'b2', file_name) elif profile: - user_account_info_path = B2_ACCOUNT_INFO_PROFILE_FILE.format(profile=profile) + user_account_info_path = profile_file else: user_account_info_path = B2_ACCOUNT_INFO_DEFAULT_FILE diff --git a/changelog.d/b2cli958.changed.md b/changelog.d/b2cli958.changed.md new file mode 100644 index 000000000..804d8a35a --- /dev/null +++ b/changelog.d/b2cli958.changed.md @@ -0,0 +1 @@ +On XDG compatible OSes (Linux, BSD), the profile file is now created in `$XDG_CONFIG_HOME` (with a fallback to `~/.config/` in absence of given env. variable). diff --git a/test/unit/account_info/test_account_info.py b/test/unit/account_info/test_account_info.py index e67893344..b332ffd71 100644 --- a/test/unit/account_info/test_account_info.py +++ b/test/unit/account_info/test_account_info.py @@ -14,6 +14,7 @@ import platform import shutil import stat +import sys import tempfile import unittest.mock as mock from abc import ABCMeta, abstractmethod @@ -345,6 +346,7 @@ def setUp(self, request): self.test_home = tempfile.mkdtemp() yield + for cleanup_method in [ lambda: os.unlink(self.db_path), lambda: shutil.rmtree(self.test_home) ]: @@ -414,30 +416,26 @@ def _make_sqlite_account_info(self, env=None, last_upgrade_to_run=None): last_upgrade_to_run=last_upgrade_to_run, ) - def test_uses_default(self): - account_info = self._make_sqlite_account_info( - env={ + def test_uses_xdg_config_home(self, apiver): + is_xdg_os = bool(SqliteAccountInfo._get_xdg_config_path()) + with WindowsSafeTempDir() as d: + env = { 'HOME': self.test_home, 'USERPROFILE': self.test_home, } - ) - actual_path = os.path.abspath(account_info.filename) - assert os.path.join(self.test_home, '.b2_account_info') == actual_path - def test_uses_xdg_config_home(self, apiver): - with WindowsSafeTempDir() as d: - account_info = self._make_sqlite_account_info( - env={ - 'HOME': self.test_home, - 'USERPROFILE': self.test_home, - XDG_CONFIG_HOME_ENV_VAR: d, - } - ) + if is_xdg_os: + # pass the env. variable on XDG-like OS only + env[XDG_CONFIG_HOME_ENV_VAR] = d + + account_info = self._make_sqlite_account_info(env=env) if apiver in ['v0', 'v1']: expected_path = os.path.abspath(os.path.join(self.test_home, '.b2_account_info')) - else: + elif is_xdg_os: assert os.path.exists(os.path.join(d, 'b2')) expected_path = os.path.abspath(os.path.join(d, 'b2', 'account_info')) + else: + expected_path = os.path.abspath(os.path.join(self.test_home, '.b2_account_info')) actual_path = os.path.abspath(account_info.filename) assert expected_path == actual_path @@ -469,3 +467,14 @@ def test_account_info_env_var_overrides_xdg_config_home(self): expected_path = os.path.abspath(os.path.join(d, 'b2_account_info')) actual_path = os.path.abspath(account_info.filename) assert expected_path == actual_path + + def test_resolve_xdg_os_default(self): + is_xdg_os = bool(SqliteAccountInfo._get_xdg_config_path()) + assert is_xdg_os == (sys.platform not in ('win32', 'darwin')) + + def test_resolve_xdg_os_default_no_env_var(self, monkeypatch): + # ensure that XDG_CONFIG_HOME_ENV_VAR doesn't to resolve XDG-like OS + monkeypatch.delenv(XDG_CONFIG_HOME_ENV_VAR, raising=False) + + is_xdg_os = bool(SqliteAccountInfo._get_xdg_config_path()) + assert is_xdg_os == (sys.platform not in ('win32', 'darwin')) diff --git a/test/unit/account_info/test_sqlite_account_info.py b/test/unit/account_info/test_sqlite_account_info.py index 75f7b9ca1..1bcd28c50 100644 --- a/test/unit/account_info/test_sqlite_account_info.py +++ b/test/unit/account_info/test_sqlite_account_info.py @@ -95,9 +95,15 @@ def test_profile_and_xdg_config_env_var(self, monkeypatch): os.path.join('~', 'custom', 'b2', 'db-secondary.sqlite') ) - def test_profile(self): + def test_profile(self, monkeypatch): + xdg_config_path = SqliteAccountInfo._get_xdg_config_path() + if xdg_config_path: + expected_path = (xdg_config_path, 'b2', 'db-foo.sqlite') + else: + expected_path = ('~', '.b2db-foo.sqlite') + account_info_path = SqliteAccountInfo._get_user_account_info_path(profile='foo') - assert account_info_path == os.path.expanduser(os.path.join('~', '.b2db-foo.sqlite')) + assert account_info_path == os.path.expanduser(os.path.join(*expected_path)) def test_file_name(self): account_info_path = SqliteAccountInfo._get_user_account_info_path( @@ -129,5 +135,11 @@ def test_xdg_config_env_var(self, monkeypatch): ) def test_default_file(self): + xdg_config_path = SqliteAccountInfo._get_xdg_config_path() + if xdg_config_path: + expected_path = os.path.join(xdg_config_path, 'b2', 'account_info') + else: + expected_path = B2_ACCOUNT_INFO_DEFAULT_FILE + account_info_path = SqliteAccountInfo._get_user_account_info_path() - assert account_info_path == os.path.expanduser(B2_ACCOUNT_INFO_DEFAULT_FILE) + assert account_info_path == os.path.expanduser(expected_path) From d8b78823ca5291349f4b7141b2ba28a4850285b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Nowacki?= Date: Sat, 2 Dec 2023 12:01:19 +0100 Subject: [PATCH 2/5] make `realm` optional in B2Api.authorize_account --- b2sdk/api.py | 4 +- b2sdk/v2/api.py | 10 +++++ changelog.d/+authorize_account.changed.md | 1 + test/unit/api/test_api.py | 48 +++++++++++++++++---- test/unit/bucket/test_bucket.py | 24 +++++++++-- test/unit/file_version/test_file_version.py | 6 ++- test/unit/replication/conftest.py | 6 ++- test/unit/v_all/test_api.py | 6 ++- test/unit/v_all/test_replication.py | 6 ++- 9 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 changelog.d/+authorize_account.changed.md diff --git a/b2sdk/api.py b/b2sdk/api.py index bee11c0de..e76c177cd 100644 --- a/b2sdk/api.py +++ b/b2sdk/api.py @@ -209,13 +209,13 @@ def authorize_automatically(self): return self.session.authorize_automatically() @limit_trace_arguments(only=('self', 'realm')) - def authorize_account(self, realm, application_key_id, application_key): + def authorize_account(self, application_key_id, application_key, realm='production'): """ Perform account authorization. - :param str realm: a realm to authorize account in (usually just "production") :param str application_key_id: :term:`application key ID` :param str application_key: user's :term:`application key` + :param str realm: a realm to authorize account in (usually just "production") """ self.session.authorize_account(realm, application_key_id, application_key) self._populate_bucket_cache_from_key() diff --git a/b2sdk/v2/api.py b/b2sdk/v2/api.py index 40690c0f9..efc33ff90 100644 --- a/b2sdk/v2/api.py +++ b/b2sdk/v2/api.py @@ -28,6 +28,7 @@ class Services(v3.Services): # override to use legacy B2Session with legacy B2Http # and to raise old style BucketIdNotFound exception # and to use old style Bucket +# and to use legacy authorize_account signature class B2Api(v3.B2Api): SESSION_CLASS = staticmethod(B2Session) BUCKET_CLASS = staticmethod(Bucket) @@ -46,3 +47,12 @@ def get_bucket_by_id(self, bucket_id: str) -> v3.Bucket: return super().get_bucket_by_id(bucket_id) except v3BucketIdNotFound as e: raise BucketIdNotFound(e.bucket_id) + + # one should put "@limit_trace_arguments(only=('self', 'realm'))" here but logfury meta magic copies the appropriate + # attributes from base classes + def authorize_account(self, realm, application_key_id, application_key): + return super().authorize_account( + application_key_id=application_key_id, + application_key=application_key, + realm=realm, + ) \ No newline at end of file diff --git a/changelog.d/+authorize_account.changed.md b/changelog.d/+authorize_account.changed.md new file mode 100644 index 000000000..49397c6c0 --- /dev/null +++ b/changelog.d/+authorize_account.changed.md @@ -0,0 +1 @@ +Change v3.B2Api.authorize_account signature to make `realm` optional and `"production"` by default \ No newline at end of file diff --git a/test/unit/api/test_api.py b/test/unit/api/test_api.py index 08151f7bb..46889a88d 100644 --- a/test/unit/api/test_api.py +++ b/test/unit/api/test_api.py @@ -312,7 +312,11 @@ def test_buckets_with_encryption(self): # now check it with no readBucketEncryption permission to see that it's unknown key = create_key(self.api, ['listBuckets'], 'key1') - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) buckets = { b.name: b for b in self.api.list_buckets() # scan again with new key @@ -352,7 +356,11 @@ def test_reauthorize_with_app_key(self): key = create_key(self.api, ['listBuckets'], 'key1') # authorize with the key - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) # expire the auth token we just got self.raw_api.expire_auth_token(self.account_info.get_account_auth_token()) @@ -365,14 +373,22 @@ def test_list_buckets_with_restriction(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) assert [b.name for b in self.api.list_buckets(bucket_name=bucket1.name)] == ['bucket1'] def test_get_bucket_by_name_with_bucket_restriction(self): self._authorize_account() bucket1 = self.api.create_bucket('bucket1', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) assert self.api.get_bucket_by_name('bucket1').id_ == bucket1.id_ def test_list_buckets_with_restriction_and_wrong_name(self): @@ -380,7 +396,11 @@ def test_list_buckets_with_restriction_and_wrong_name(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') bucket2 = self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) with pytest.raises(RestrictedBucket) as excinfo: self.api.list_buckets(bucket_name=bucket2.name) assert str(excinfo.value) == 'Application key is restricted to bucket: bucket1' @@ -390,7 +410,11 @@ def test_list_buckets_with_restriction_and_no_name(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) with pytest.raises(RestrictedBucket) as excinfo: self.api.list_buckets() assert str(excinfo.value) == 'Application key is restricted to bucket: bucket1' @@ -400,13 +424,21 @@ def test_list_buckets_with_restriction_and_wrong_id(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) with pytest.raises(RestrictedBucket) as excinfo: self.api.list_buckets(bucket_id='not the one bound to the key') assert str(excinfo.value) == f'Application key is restricted to bucket: {bucket1.id_}' def _authorize_account(self): - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + application_key_id=self.application_key_id, + application_key=self.master_key, + realm='production', + ) def test_update_file_retention(self): self._authorize_account() diff --git a/test/unit/bucket/test_bucket.py b/test/unit/bucket/test_bucket.py index e77cec4d7..6bce189b4 100644 --- a/test/unit/bucket/test_bucket.py +++ b/test/unit/bucket/test_bucket.py @@ -250,7 +250,11 @@ def setUp(self): self.api = self.get_api() self.simulator = self.api.session.raw_api (self.account_id, self.master_key) = self.simulator.create_account() - self.api.authorize_account('production', self.account_id, self.master_key) + self.api.authorize_account( + application_key_id=self.account_id, + application_key=self.master_key, + realm='production', + ) self.api_url = self.account_info.get_api_url() self.account_auth_token = self.account_info.get_account_auth_token() self.bucket = self.api.create_bucket(self.bucket_name, 'allPublic') @@ -463,7 +467,11 @@ def test_version_by_name_file_lock(self): ] ) - low_perm_api.authorize_account('production', low_perm_key.id_, low_perm_key.application_key) + low_perm_api.authorize_account( + application_key_id=low_perm_key.id_, + application_key=low_perm_key.application_key, + realm='production', + ) low_perm_bucket = low_perm_api.get_bucket_by_name('my-bucket-with-file-lock') file_version = low_perm_bucket.get_file_info_by_name('a') @@ -2680,7 +2688,11 @@ def test_authorize_for_bucket_ensures_cache(self): bucket_id=self.bucket_id, ) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) # Check whether the bucket fetching performs an API call. with mock.patch.object(self.api, 'list_buckets') as mock_list_buckets: @@ -2702,7 +2714,11 @@ def test_authorize_for_non_existing_bucket(self): ) with self.assertRaises(RestrictedBucketMissing): - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) class TestDownloadLocalDirectoryIssues(TestCaseWithBucket): diff --git a/test/unit/file_version/test_file_version.py b/test/unit/file_version/test_file_version.py index aad21dd9d..f7c4bfca7 100644 --- a/test/unit/file_version/test_file_version.py +++ b/test/unit/file_version/test_file_version.py @@ -47,7 +47,11 @@ def setUp(self): ) self.raw_api = self.api.session.raw_api (self.application_key_id, self.master_key) = self.raw_api.create_account() - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + application_key_id=self.application_key_id, + application_key=self.master_key, + realm='production', + ) self.bucket = self.api.create_bucket('testbucket', 'allPrivate', is_file_lock_enabled=True) self.file_version = self.bucket.upload_bytes( b'nothing', 'test_file', cache_control='private, max-age=3600' diff --git a/test/unit/replication/conftest.py b/test/unit/replication/conftest.py index 38a1718d2..c91542644 100644 --- a/test/unit/replication/conftest.py +++ b/test/unit/replication/conftest.py @@ -32,7 +32,11 @@ def api() -> B2Api: simulator = api.session.raw_api account_id, master_key = simulator.create_account() - api.authorize_account('production', account_id, master_key) + api.authorize_account( + application_key_id=account_id, + application_key=master_key, + realm='production', + ) # api_url = account_info.get_api_url() # account_auth_token = account_info.get_account_auth_token()1 return api diff --git a/test/unit/v_all/test_api.py b/test/unit/v_all/test_api.py index 782cd9b00..efb2552d3 100644 --- a/test/unit/v_all/test_api.py +++ b/test/unit/v_all/test_api.py @@ -116,7 +116,11 @@ def setUp(self): (self.application_key_id, self.master_key) = self.raw_api.create_account() def _authorize_account(self): - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + realm='production', + application_key_id=self.application_key_id, + application_key=self.master_key, + ) @pytest.mark.apiver(to_ver=1) def test_get_bucket_by_id_up_to_v1(self): diff --git a/test/unit/v_all/test_replication.py b/test/unit/v_all/test_replication.py index ad499f382..8d82721a9 100644 --- a/test/unit/v_all/test_replication.py +++ b/test/unit/v_all/test_replication.py @@ -39,7 +39,11 @@ def setUp(self): self.application_key_id, self.master_key = self.raw_api.create_account() def _authorize_account(self): - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + realm='production', + application_key_id=self.application_key_id, + application_key=self.master_key, + ) @pytest.mark.apiver(from_ver=2) def test_setup_both(self): From 21a5f45ac26b33887d3f78d983411d925bc79dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Nowacki?= Date: Sat, 2 Dec 2023 12:01:42 +0100 Subject: [PATCH 3/5] rename malformed news item file --- changelog.d/{b2cli958.changed.md => +b2cli958.changed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{b2cli958.changed.md => +b2cli958.changed.md} (100%) diff --git a/changelog.d/b2cli958.changed.md b/changelog.d/+b2cli958.changed.md similarity index 100% rename from changelog.d/b2cli958.changed.md rename to changelog.d/+b2cli958.changed.md From f5d50d7682a3da061b44691978f04ee0fd1baaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Nowacki?= Date: Sat, 2 Dec 2023 21:33:12 +0100 Subject: [PATCH 4/5] Add `-v` to pytest in CI --- .github/workflows/ci.yml | 4 ++-- changelog.d/+make_tests_in_ci_verbose.infrastructure.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog.d/+make_tests_in_ci_verbose.infrastructure.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22723bdc6..2e3a9e430 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,12 +109,12 @@ jobs: - name: Install dependencies run: python -m pip install --upgrade nox pip setuptools - name: Run unit tests - run: nox -vs unit + run: nox -vs unit -- -v env: SKIP_COVERAGE: ${{ startsWith(matrix.python-version, env.SKIP_COVERAGE_PYTHON_VERSION_PREFIX) }} - name: Run integration tests if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} - run: nox -vs integration -- --dont-cleanup-old-buckets + run: nox -vs integration -- --dont-cleanup-old-buckets -v doc: needs: build runs-on: ubuntu-latest diff --git a/changelog.d/+make_tests_in_ci_verbose.infrastructure.md b/changelog.d/+make_tests_in_ci_verbose.infrastructure.md new file mode 100644 index 000000000..3b56d7038 --- /dev/null +++ b/changelog.d/+make_tests_in_ci_verbose.infrastructure.md @@ -0,0 +1 @@ +Add `-v` to pytest in CI \ No newline at end of file From 8c203aeba9a27d6b80c2c9a2b4fd474809ff6e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Nowacki?= Date: Sat, 2 Dec 2023 21:51:05 +0100 Subject: [PATCH 5/5] Run windows pypy3.9 tests on nightly builds --- .github/workflows/ci.yml | 9 ++++++++- .../+run_windows_pypy_tests_on_nightly.infrastructure.md | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e3a9e430..632ff6c72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.9-nightly", "pypy-3.10"] exclude: - os: "macos-latest" python-version: "pypy-3.9" @@ -97,6 +97,13 @@ jobs: python-version: "3.12" - os: "windows-latest" python-version: "3.12" + # TODO: pypy-3.9 started breaking on windows, due to some prints leftover in pypy release + - os: "ubuntu-latest" + python-version: "pypy-3.9-nightly" + - os: "macos-latest" + python-version: "pypy-3.9-nightly" + - os: "windows-latest" + python-version: "pypy-3.9" steps: - uses: actions/checkout@v3 with: diff --git a/changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md b/changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md new file mode 100644 index 000000000..fe7d8f12c --- /dev/null +++ b/changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md @@ -0,0 +1 @@ +Run windows pypy3.9 tests on nightly builds \ No newline at end of file