craft-application 2.2.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {craft-application-2.2.0 → craft-application-2.4.0}/PKG-INFO +4 -4
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/_version.py +2 -2
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/application.py +32 -8
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/project.py +2 -1
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/lifecycle.py +3 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/provider.py +1 -2
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/remotebuild.py +38 -9
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application.egg-info/PKG-INFO +4 -4
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application.egg-info/SOURCES.txt +1 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application.egg-info/requires.txt +3 -3
- {craft-application-2.2.0 → craft-application-2.4.0}/pyproject.toml +3 -3
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/conftest.py +25 -1
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/conftest.py +11 -1
- craft-application-2.4.0/tests/integration/services/test_remotebuild.py +47 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_lifecycle.py +20 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_provider.py +0 -4
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_remotebuild.py +59 -1
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/test_application.py +64 -1
- {craft-application-2.2.0 → craft-application-2.4.0}/.editorconfig +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/.jira_sync_config.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/ISSUE_TEMPLATE/bug.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/ISSUE_TEMPLATE/task.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/release-drafter.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/release-drafter.yml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/renovate.json5 +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/workflows/cla-check.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/workflows/docs.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/workflows/release-drafter.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.github/workflows/tests.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.gitignore +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.pre-commit-config.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.readthedocs.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/.yamllint.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/HACKING.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/LICENSE +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/README.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/commands/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/commands/base.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/commands/lifecycle.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/commands/other.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/errors.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/grammar.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/errors.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/launchpad.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/base.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/build.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/code.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/distro.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/recipe.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/util.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/misc/instance_bashrc +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/models/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/models/base.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/models/constraints.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/models/grammar.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/models/metadata.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/models/project.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/py.typed +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/remote/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/remote/errors.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/remote/git.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/remote/utils.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/remote/worktree.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/secrets.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/base.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/package.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/request.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/service_factory.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/callbacks.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/error_formatting.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/logging.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/paths.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/platforms.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/repositories.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/snap_config.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/string.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application/util/yaml.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application.egg-info/dependency_links.txt +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/craft_application.egg-info/top_level.txt +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/_static/css/custom.css +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/conf.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/explanation/index.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/howto/index.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/index.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/reference/index.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/docs/tutorials/index.rst +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/setup.cfg +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/build-secrets/secret-source-folder/source-file.txt +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/build-secrets/testcraft.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/invalid_projects/build-error/testcraft.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/adoption/stderr +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/adoption/testcraft.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/basic/stderr +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/basic/testcraft.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/environment/stderr +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/environment/testcraft.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-amd64/hello.txt +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-arm64/hello.txt +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/stderr +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/testcraft.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/launchpad/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/launchpad/conftest.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/launchpad/test_anonymous_access.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/services/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/services/test_lifecycle.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/services/test_provider.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/services/test_request.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/services/test_service_factory.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/test_application.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/integration/test_version.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/commands/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/commands/test_base.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/commands/test_lifecycle.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/commands/test_other.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/conftest.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/conftest.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/models/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/models/test_base.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/models/test_code.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/test_launchpad.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/launchpad/test_util.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/models/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/models/project_models/basic_project.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/models/project_models/full_project.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/models/project_models/invalid_project.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/models/test_constraints.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/models/test_project.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/remote/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/remote/conftest.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/remote/test_errors.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/remote/test_git.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/remote/test_utils.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/remote/test_worktree.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/conftest.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_package.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_repositories.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_request.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/services/test_service_factory.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/test_errors.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/test_grammar.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/test_nothing.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/test_secrets.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/__init__.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/_README +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/unhashable.yaml-invalid +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/test_error_formatting.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/test_logging.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/test_paths.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/test_snap_config.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/test_string.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/test_yaml.py +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tests/unit/util/valid_yaml/empty.yaml +0 -0
- {craft-application-2.2.0 → craft-application-2.4.0}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: craft-application
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: A framework for *craft applications.
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -30,13 +30,13 @@ Requires-Dist: typing_extensions>=4.4.0
|
|
|
30
30
|
Provides-Extra: remote
|
|
31
31
|
Requires-Dist: launchpadlib>=1.10.16; extra == "remote"
|
|
32
32
|
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: coverage[toml]==7.4.
|
|
33
|
+
Requires-Dist: coverage[toml]==7.4.4; extra == "dev"
|
|
34
34
|
Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
35
35
|
Requires-Dist: pyfakefs~=5.3; extra == "dev"
|
|
36
36
|
Requires-Dist: pytest==8.1.1; extra == "dev"
|
|
37
37
|
Requires-Dist: pytest-check==2.3.1; extra == "dev"
|
|
38
38
|
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
39
|
-
Requires-Dist: pytest-mock==3.
|
|
39
|
+
Requires-Dist: pytest-mock==3.14.0; extra == "dev"
|
|
40
40
|
Requires-Dist: pytest-rerunfailures==13.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pytest-time>=0.3.1; extra == "dev"
|
|
42
42
|
Requires-Dist: responses~=0.25.0; extra == "dev"
|
|
@@ -47,7 +47,7 @@ Requires-Dist: codespell[toml]==2.2.6; extra == "lint"
|
|
|
47
47
|
Requires-Dist: yamllint==1.35.1; extra == "lint"
|
|
48
48
|
Provides-Extra: types
|
|
49
49
|
Requires-Dist: mypy[reports]==1.9.0; extra == "types"
|
|
50
|
-
Requires-Dist: pyright==1.1.
|
|
50
|
+
Requires-Dist: pyright==1.1.356; extra == "types"
|
|
51
51
|
Requires-Dist: types-requests; extra == "types"
|
|
52
52
|
Requires-Dist: types-urllib3; extra == "types"
|
|
53
53
|
Provides-Extra: docs
|
|
@@ -130,6 +130,7 @@ class Application:
|
|
|
130
130
|
# When build_secrets are enabled, this contains the secret info to pass to
|
|
131
131
|
# managed instances.
|
|
132
132
|
self._secrets: secrets.BuildSecrets | None = None
|
|
133
|
+
self._partitions: list[str] | None = None
|
|
133
134
|
# Cached project object, allows only the first time we load the project
|
|
134
135
|
# to specify things like the project directory.
|
|
135
136
|
# This is set as a private attribute in order to discourage real application
|
|
@@ -219,6 +220,7 @@ class Application:
|
|
|
219
220
|
cache_dir=self.cache_dir,
|
|
220
221
|
work_dir=self._work_dir,
|
|
221
222
|
build_plan=self._build_plan,
|
|
223
|
+
partitions=self._partitions,
|
|
222
224
|
)
|
|
223
225
|
self.services.set_kwargs(
|
|
224
226
|
"provider",
|
|
@@ -288,6 +290,9 @@ class Application:
|
|
|
288
290
|
GrammarAwareProject.validate_grammar(yaml_data)
|
|
289
291
|
|
|
290
292
|
build_on = host_arch
|
|
293
|
+
|
|
294
|
+
# Setup partitions, some projects require the yaml data, most will not
|
|
295
|
+
self._partitions = self._setup_partitions(yaml_data)
|
|
291
296
|
yaml_data = self._transform_project_yaml(yaml_data, build_on, build_for)
|
|
292
297
|
self.__project = self.app.ProjectClass.from_yaml_data(yaml_data, project_path)
|
|
293
298
|
|
|
@@ -344,14 +349,19 @@ class Application:
|
|
|
344
349
|
with self.services.provider.instance(
|
|
345
350
|
build_info, work_dir=self._work_dir
|
|
346
351
|
) as instance:
|
|
352
|
+
cmd = [self.app.name, *sys.argv[1:]]
|
|
353
|
+
craft_cli.emit.debug(
|
|
354
|
+
f"Executing {cmd} in instance location {instance_path} with {extra_args}."
|
|
355
|
+
)
|
|
347
356
|
try:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
357
|
+
with craft_cli.emit.pause():
|
|
358
|
+
# Pyright doesn't fully understand craft_providers's CompletedProcess.
|
|
359
|
+
instance.execute_run( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
|
|
360
|
+
cmd,
|
|
361
|
+
cwd=instance_path,
|
|
362
|
+
check=True,
|
|
363
|
+
**extra_args,
|
|
364
|
+
)
|
|
355
365
|
except subprocess.CalledProcessError as exc:
|
|
356
366
|
raise craft_providers.ProviderError(
|
|
357
367
|
f"Failed to execute {self.app.name} in instance."
|
|
@@ -603,18 +613,32 @@ class Application:
|
|
|
603
613
|
def _expand_environment(self, yaml_data: dict[str, Any]) -> None:
|
|
604
614
|
"""Perform expansion of project environment variables."""
|
|
605
615
|
environment_vars = self._get_project_vars(yaml_data)
|
|
616
|
+
project_dirs = craft_parts.ProjectDirs(
|
|
617
|
+
work_dir=self._work_dir, partitions=self._partitions
|
|
618
|
+
)
|
|
619
|
+
|
|
606
620
|
info = craft_parts.ProjectInfo(
|
|
607
621
|
application_name=self.app.name, # not used in environment expansion
|
|
608
622
|
cache_dir=pathlib.Path(), # not used in environment expansion
|
|
609
623
|
project_name=yaml_data.get("name", ""),
|
|
610
|
-
project_dirs=
|
|
624
|
+
project_dirs=project_dirs,
|
|
611
625
|
project_vars=environment_vars,
|
|
626
|
+
partitions=self._partitions,
|
|
612
627
|
)
|
|
613
628
|
|
|
614
629
|
self._set_global_environment(info)
|
|
615
630
|
|
|
616
631
|
craft_parts.expand_environment(yaml_data, info=info)
|
|
617
632
|
|
|
633
|
+
def _setup_partitions(self, yaml_data: dict[str, Any]) -> list[str] | None:
|
|
634
|
+
"""Return partitions to be used.
|
|
635
|
+
|
|
636
|
+
When returning you will also need to ensure that the feature is enabled
|
|
637
|
+
on Application instantiation craft_parts.Features(partitions_enabled=True)
|
|
638
|
+
"""
|
|
639
|
+
_ = yaml_data
|
|
640
|
+
return None
|
|
641
|
+
|
|
618
642
|
def _get_project_vars(self, yaml_data: dict[str, Any]) -> dict[str, str]:
|
|
619
643
|
"""Return a dict with project variables to be expanded."""
|
|
620
644
|
pvars: dict[str, str] = {}
|
{craft-application-2.2.0 → craft-application-2.4.0}/craft_application/launchpad/models/project.py
RENAMED
|
@@ -37,7 +37,7 @@ import launchpadlib.errors # type: ignore[import-untyped]
|
|
|
37
37
|
from typing_extensions import Self, Any
|
|
38
38
|
from typing import TYPE_CHECKING
|
|
39
39
|
|
|
40
|
-
from ..models.base import LaunchpadObject
|
|
40
|
+
from ..models.base import InformationType, LaunchpadObject
|
|
41
41
|
from .. import errors
|
|
42
42
|
|
|
43
43
|
if TYPE_CHECKING:
|
|
@@ -64,6 +64,7 @@ class Project(LaunchpadObject):
|
|
|
64
64
|
display_name: str
|
|
65
65
|
summary: str
|
|
66
66
|
description: str
|
|
67
|
+
information_type: InformationType
|
|
67
68
|
|
|
68
69
|
@classmethod
|
|
69
70
|
def new( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
@@ -134,12 +134,14 @@ class LifecycleService(base.ProjectService):
|
|
|
134
134
|
work_dir: Path | str,
|
|
135
135
|
cache_dir: Path | str,
|
|
136
136
|
build_plan: list[models.BuildInfo],
|
|
137
|
+
partitions: list[str] | None = None,
|
|
137
138
|
**lifecycle_kwargs: Any, # noqa: ANN401 - eventually used in an Any
|
|
138
139
|
) -> None:
|
|
139
140
|
super().__init__(app, services, project=project)
|
|
140
141
|
self._work_dir = work_dir
|
|
141
142
|
self._cache_dir = cache_dir
|
|
142
143
|
self._build_plan = build_plan
|
|
144
|
+
self._partitions = partitions
|
|
143
145
|
self._manager_kwargs = lifecycle_kwargs
|
|
144
146
|
self._lcm: LifecycleManager = None # type: ignore[assignment]
|
|
145
147
|
|
|
@@ -190,6 +192,7 @@ class LifecycleService(base.ProjectService):
|
|
|
190
192
|
project_vars_part_name=self._project.adopt_info,
|
|
191
193
|
project_vars=self._project_vars,
|
|
192
194
|
track_stage_packages=True,
|
|
195
|
+
partitions=self._partitions,
|
|
193
196
|
**self._manager_kwargs,
|
|
194
197
|
)
|
|
195
198
|
except PartsError as err:
|
|
@@ -133,8 +133,7 @@ class ProviderService(base.ProjectService):
|
|
|
133
133
|
emit.debug("Instance launched and working directory mounted")
|
|
134
134
|
self._setup_instance_bashrc(instance)
|
|
135
135
|
try:
|
|
136
|
-
|
|
137
|
-
yield instance
|
|
136
|
+
yield instance
|
|
138
137
|
finally:
|
|
139
138
|
self._capture_logs_from_instance(instance)
|
|
140
139
|
|
{craft-application-2.2.0 → craft-application-2.4.0}/craft_application/services/remotebuild.py
RENAMED
|
@@ -62,6 +62,7 @@ class RemoteBuildService(base.AppService):
|
|
|
62
62
|
self._repository: launchpad.models.GitRepository = None # type: ignore[assignment]
|
|
63
63
|
self._recipe: launchpad.models.recipe.BaseRecipe = None # type: ignore[assignment]
|
|
64
64
|
self._builds: Collection[launchpad.models.Build] = []
|
|
65
|
+
self._project_name: str | None = None
|
|
65
66
|
|
|
66
67
|
def setup(self) -> None:
|
|
67
68
|
"""Set up the remote builder."""
|
|
@@ -69,6 +70,35 @@ class RemoteBuildService(base.AppService):
|
|
|
69
70
|
|
|
70
71
|
# region Public API
|
|
71
72
|
# Commands will call a subset of these methods, generally in order.
|
|
73
|
+
def set_project(self, name: str) -> None:
|
|
74
|
+
"""Set the name of the project to use."""
|
|
75
|
+
if self._is_setup:
|
|
76
|
+
raise RuntimeError(
|
|
77
|
+
"Project name may only be set before starting or resuming builds."
|
|
78
|
+
)
|
|
79
|
+
try:
|
|
80
|
+
self._lp_project = self.lp.get_project(name)
|
|
81
|
+
except launchpad.errors.NotFoundError:
|
|
82
|
+
raise errors.CraftError(
|
|
83
|
+
message=f"Could not find project on Launchpad: {name}",
|
|
84
|
+
resolution="Ensure the project exists and that you have access to it.",
|
|
85
|
+
retcode=77, # EX_NOPERM from sysexits.h
|
|
86
|
+
reportable=False,
|
|
87
|
+
logpath_report=False,
|
|
88
|
+
)
|
|
89
|
+
self._project_name = name
|
|
90
|
+
|
|
91
|
+
def is_project_private(self) -> bool:
|
|
92
|
+
"""Check whether the named project is private."""
|
|
93
|
+
if not self._lp_project:
|
|
94
|
+
raise RuntimeError(
|
|
95
|
+
"Cannot check if the project is private before setting its name."
|
|
96
|
+
)
|
|
97
|
+
return self._lp_project.information_type not in (
|
|
98
|
+
launchpad.models.InformationType.PUBLIC,
|
|
99
|
+
launchpad.models.InformationType.PUBLIC_SECURITY,
|
|
100
|
+
)
|
|
101
|
+
|
|
72
102
|
def set_timeout(self, seconds_in_future: int) -> None:
|
|
73
103
|
"""Set the deadline to a certain number of seconds in the future."""
|
|
74
104
|
self._deadline = time.monotonic_ns() + (seconds_in_future * 10**9)
|
|
@@ -207,17 +237,16 @@ class RemoteBuildService(base.AppService):
|
|
|
207
237
|
|
|
208
238
|
def _ensure_project(self) -> launchpad.models.Project:
|
|
209
239
|
"""Ensure that the user's remote build project exists."""
|
|
240
|
+
if self._lp_project:
|
|
241
|
+
return self._lp_project
|
|
210
242
|
try:
|
|
211
|
-
return
|
|
212
|
-
self.lp, f"{self.lp.username}-craft-remote-build"
|
|
213
|
-
)
|
|
243
|
+
return self.lp.get_project(f"{self.lp.username}-craft-remote-build")
|
|
214
244
|
except launchpad.errors.NotFoundError:
|
|
215
|
-
return
|
|
216
|
-
self.lp,
|
|
217
|
-
f"*craft remote builds for {self.lp.username}",
|
|
218
|
-
f"{self.lp.username}
|
|
219
|
-
f"{self.lp.username} remote builds",
|
|
220
|
-
f"Automatically-generated project for housing {self.lp.username}'s remote builds in snapcraft, charmcraft, etc.",
|
|
245
|
+
return self.lp.new_project(
|
|
246
|
+
name=f"{self.lp.username}-craft-remote-build",
|
|
247
|
+
title=f"*craft remote builds for {self.lp.username}",
|
|
248
|
+
display_name=f"{self.lp.username} remote builds",
|
|
249
|
+
summary=f"Automatically-generated project for housing {self.lp.username}'s remote builds in snapcraft, charmcraft, etc.",
|
|
221
250
|
)
|
|
222
251
|
|
|
223
252
|
def _new_repository(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: craft-application
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: A framework for *craft applications.
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -30,13 +30,13 @@ Requires-Dist: typing_extensions>=4.4.0
|
|
|
30
30
|
Provides-Extra: remote
|
|
31
31
|
Requires-Dist: launchpadlib>=1.10.16; extra == "remote"
|
|
32
32
|
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: coverage[toml]==7.4.
|
|
33
|
+
Requires-Dist: coverage[toml]==7.4.4; extra == "dev"
|
|
34
34
|
Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
35
35
|
Requires-Dist: pyfakefs~=5.3; extra == "dev"
|
|
36
36
|
Requires-Dist: pytest==8.1.1; extra == "dev"
|
|
37
37
|
Requires-Dist: pytest-check==2.3.1; extra == "dev"
|
|
38
38
|
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
39
|
-
Requires-Dist: pytest-mock==3.
|
|
39
|
+
Requires-Dist: pytest-mock==3.14.0; extra == "dev"
|
|
40
40
|
Requires-Dist: pytest-rerunfailures==13.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pytest-time>=0.3.1; extra == "dev"
|
|
42
42
|
Requires-Dist: responses~=0.25.0; extra == "dev"
|
|
@@ -47,7 +47,7 @@ Requires-Dist: codespell[toml]==2.2.6; extra == "lint"
|
|
|
47
47
|
Requires-Dist: yamllint==1.35.1; extra == "lint"
|
|
48
48
|
Provides-Extra: types
|
|
49
49
|
Requires-Dist: mypy[reports]==1.9.0; extra == "types"
|
|
50
|
-
Requires-Dist: pyright==1.1.
|
|
50
|
+
Requires-Dist: pyright==1.1.356; extra == "types"
|
|
51
51
|
Requires-Dist: types-requests; extra == "types"
|
|
52
52
|
Requires-Dist: types-urllib3; extra == "types"
|
|
53
53
|
Provides-Extra: docs
|
|
@@ -109,6 +109,7 @@ tests/integration/launchpad/test_anonymous_access.py
|
|
|
109
109
|
tests/integration/services/__init__.py
|
|
110
110
|
tests/integration/services/test_lifecycle.py
|
|
111
111
|
tests/integration/services/test_provider.py
|
|
112
|
+
tests/integration/services/test_remotebuild.py
|
|
112
113
|
tests/integration/services/test_request.py
|
|
113
114
|
tests/integration/services/test_service_factory.py
|
|
114
115
|
tests/unit/__init__.py
|
|
@@ -17,13 +17,13 @@ typing_extensions>=4.4.0
|
|
|
17
17
|
python-apt>=2.4.0
|
|
18
18
|
|
|
19
19
|
[dev]
|
|
20
|
-
coverage[toml]==7.4.
|
|
20
|
+
coverage[toml]==7.4.4
|
|
21
21
|
hypothesis>=6.0
|
|
22
22
|
pyfakefs~=5.3
|
|
23
23
|
pytest==8.1.1
|
|
24
24
|
pytest-check==2.3.1
|
|
25
25
|
pytest-cov==5.0.0
|
|
26
|
-
pytest-mock==3.
|
|
26
|
+
pytest-mock==3.14.0
|
|
27
27
|
pytest-rerunfailures==13.0
|
|
28
28
|
pytest-time>=0.3.1
|
|
29
29
|
responses~=0.25.0
|
|
@@ -49,6 +49,6 @@ launchpadlib>=1.10.16
|
|
|
49
49
|
|
|
50
50
|
[types]
|
|
51
51
|
mypy[reports]==1.9.0
|
|
52
|
-
pyright==1.1.
|
|
52
|
+
pyright==1.1.356
|
|
53
53
|
types-requests
|
|
54
54
|
types-urllib3
|
|
@@ -45,13 +45,13 @@ remote = [
|
|
|
45
45
|
"launchpadlib>=1.10.16",
|
|
46
46
|
]
|
|
47
47
|
dev = [
|
|
48
|
-
"coverage[toml]==7.4.
|
|
48
|
+
"coverage[toml]==7.4.4",
|
|
49
49
|
"hypothesis>=6.0",
|
|
50
50
|
"pyfakefs~=5.3",
|
|
51
51
|
"pytest==8.1.1",
|
|
52
52
|
"pytest-check==2.3.1",
|
|
53
53
|
"pytest-cov==5.0.0",
|
|
54
|
-
"pytest-mock==3.
|
|
54
|
+
"pytest-mock==3.14.0",
|
|
55
55
|
"pytest-rerunfailures==13.0",
|
|
56
56
|
"pytest-time>=0.3.1",
|
|
57
57
|
"responses~=0.25.0",
|
|
@@ -64,7 +64,7 @@ lint = [
|
|
|
64
64
|
]
|
|
65
65
|
types = [
|
|
66
66
|
"mypy[reports]==1.9.0",
|
|
67
|
-
"pyright==1.1.
|
|
67
|
+
"pyright==1.1.356",
|
|
68
68
|
"types-requests",
|
|
69
69
|
"types-urllib3",
|
|
70
70
|
]
|
|
@@ -72,6 +72,18 @@ def features(request) -> dict[str, bool]:
|
|
|
72
72
|
return features
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
@pytest.fixture(scope="session")
|
|
76
|
+
def default_app_metadata() -> craft_application.AppMetadata:
|
|
77
|
+
with pytest.MonkeyPatch.context() as m:
|
|
78
|
+
m.setattr(metadata, "version", lambda _: "3.14159")
|
|
79
|
+
return craft_application.AppMetadata(
|
|
80
|
+
"testcraft",
|
|
81
|
+
"A fake app for testing craft-application",
|
|
82
|
+
BuildPlannerClass=MyBuildPlanner,
|
|
83
|
+
source_ignore_patterns=["*.snap", "*.charm", "*.starcraft"],
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
75
87
|
@pytest.fixture()
|
|
76
88
|
def app_metadata(features) -> craft_application.AppMetadata:
|
|
77
89
|
with pytest.MonkeyPatch.context() as m:
|
|
@@ -132,13 +144,25 @@ def full_build_plan(mocker) -> list[models.BuildInfo]:
|
|
|
132
144
|
return build_plan
|
|
133
145
|
|
|
134
146
|
|
|
147
|
+
@pytest.fixture()
|
|
148
|
+
def enable_partitions() -> Iterator[craft_parts.Features]:
|
|
149
|
+
"""Enable the partitions feature in craft_parts for the relevant test."""
|
|
150
|
+
enable_overlay = craft_parts.Features().enable_overlay
|
|
151
|
+
|
|
152
|
+
craft_parts.Features.reset()
|
|
153
|
+
yield craft_parts.Features(enable_overlay=enable_overlay, enable_partitions=True)
|
|
154
|
+
craft_parts.Features.reset()
|
|
155
|
+
|
|
156
|
+
|
|
135
157
|
@pytest.fixture()
|
|
136
158
|
def enable_overlay() -> Iterator[craft_parts.Features]:
|
|
137
159
|
"""Enable the overlay feature in craft_parts for the relevant test."""
|
|
138
160
|
if not os.getenv("CI") and not shutil.which("fuse-overlayfs"):
|
|
139
161
|
pytest.skip("fuse-overlayfs not installed, skipping overlay tests.")
|
|
162
|
+
|
|
163
|
+
enable_partitions = craft_parts.Features().enable_partitions
|
|
140
164
|
craft_parts.Features.reset()
|
|
141
|
-
yield craft_parts.Features(enable_overlay=True)
|
|
165
|
+
yield craft_parts.Features(enable_overlay=True, enable_partitions=enable_partitions)
|
|
142
166
|
craft_parts.Features.reset()
|
|
143
167
|
|
|
144
168
|
|
|
@@ -18,9 +18,11 @@ import os
|
|
|
18
18
|
import pathlib
|
|
19
19
|
import sys
|
|
20
20
|
import tempfile
|
|
21
|
+
from unittest import mock
|
|
21
22
|
|
|
22
23
|
import pytest
|
|
23
|
-
from craft_application
|
|
24
|
+
from craft_application import launchpad
|
|
25
|
+
from craft_application.services import provider, remotebuild
|
|
24
26
|
from craft_providers import lxd, multipass
|
|
25
27
|
|
|
26
28
|
|
|
@@ -53,6 +55,14 @@ def provider_service(app_metadata, fake_project, fake_build_plan, fake_services)
|
|
|
53
55
|
)
|
|
54
56
|
|
|
55
57
|
|
|
58
|
+
@pytest.fixture(scope="session")
|
|
59
|
+
def anonymous_remote_build_service(default_app_metadata):
|
|
60
|
+
"""Provider service with install snap disabled for integration tests"""
|
|
61
|
+
service = remotebuild.RemoteBuildService(default_app_metadata, services=mock.Mock())
|
|
62
|
+
service.lp = launchpad.Launchpad.anonymous("testcraft")
|
|
63
|
+
return service
|
|
64
|
+
|
|
65
|
+
|
|
56
66
|
@pytest.fixture()
|
|
57
67
|
def snap_safe_tmp_path():
|
|
58
68
|
"""A temporary path accessible to snap-confined craft providers.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# This file is part of craft-application.
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2024 Canonical Ltd.
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License version 3, as
|
|
7
|
+
# published by the Free Software Foundation.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
10
|
+
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
|
|
11
|
+
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the GNU Lesser General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
"""Tests for the remote build service."""
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
from craft_application import errors
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture(scope="module", params=["charmcraft", "coreutils", "dpkg", "sudo"])
|
|
23
|
+
def public_project_name(request):
|
|
24
|
+
return request.param
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_use_public_project(anonymous_remote_build_service, public_project_name):
|
|
28
|
+
"""Test that we can get a real (public) project using an anonymous client."""
|
|
29
|
+
anonymous_remote_build_service.set_project(public_project_name)
|
|
30
|
+
|
|
31
|
+
assert anonymous_remote_build_service._lp_project.name == public_project_name
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_error_with_nonexistent_project(anonymous_remote_build_service):
|
|
35
|
+
"""Test failing gracefully with a nonexistent project."""
|
|
36
|
+
name = "this launchpad project does not exist!"
|
|
37
|
+
|
|
38
|
+
with pytest.raises(
|
|
39
|
+
errors.CraftError, match=f"Could not find project on Launchpad: {name}"
|
|
40
|
+
):
|
|
41
|
+
anonymous_remote_build_service.set_project(name)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_project_is_public(anonymous_remote_build_service, public_project_name):
|
|
45
|
+
"""Test that the given project is public."""
|
|
46
|
+
anonymous_remote_build_service.set_project(public_project_name)
|
|
47
|
+
assert not anonymous_remote_build_service.is_project_private()
|
|
@@ -306,6 +306,26 @@ def test_init_with_feature_package_repositories(
|
|
|
306
306
|
assert service._lcm._project_info.package_repositories == package_repositories
|
|
307
307
|
|
|
308
308
|
|
|
309
|
+
@pytest.mark.usefixtures("enable_partitions")
|
|
310
|
+
def test_init_with_partitions(
|
|
311
|
+
app_metadata, fake_project, fake_services, tmp_path, fake_build_plan
|
|
312
|
+
):
|
|
313
|
+
service = lifecycle.LifecycleService(
|
|
314
|
+
app_metadata,
|
|
315
|
+
fake_services,
|
|
316
|
+
project=fake_project,
|
|
317
|
+
work_dir=tmp_path,
|
|
318
|
+
cache_dir=tmp_path,
|
|
319
|
+
platform=None,
|
|
320
|
+
build_plan=fake_build_plan,
|
|
321
|
+
partitions=["default", "mypartition"],
|
|
322
|
+
)
|
|
323
|
+
assert service._lcm is None
|
|
324
|
+
service.setup()
|
|
325
|
+
assert service._lcm is not None
|
|
326
|
+
assert service._lcm._project_info.partitions == ["default", "mypartition"]
|
|
327
|
+
|
|
328
|
+
|
|
309
329
|
def test_prime_dir(lifecycle_service, tmp_path):
|
|
310
330
|
prime_dir = lifecycle_service.prime_dir
|
|
311
331
|
|
|
@@ -290,7 +290,6 @@ def test_instance(
|
|
|
290
290
|
provider_service,
|
|
291
291
|
base_name,
|
|
292
292
|
allow_unstable,
|
|
293
|
-
mocker,
|
|
294
293
|
):
|
|
295
294
|
mock_provider = mock.MagicMock(spec=craft_providers.Provider)
|
|
296
295
|
monkeypatch.setattr(
|
|
@@ -298,7 +297,6 @@ def test_instance(
|
|
|
298
297
|
"get_provider",
|
|
299
298
|
lambda name: mock_provider, # noqa: ARG005 (unused argument)
|
|
300
299
|
)
|
|
301
|
-
spy_pause = mocker.spy(provider.emit, "pause")
|
|
302
300
|
arch = util.get_host_architecture()
|
|
303
301
|
build_info = models.BuildInfo("foo", arch, arch, base_name)
|
|
304
302
|
|
|
@@ -326,8 +324,6 @@ def test_instance(
|
|
|
326
324
|
)
|
|
327
325
|
with check:
|
|
328
326
|
emitter.assert_progress("Launching managed .+ instance...", regex=True)
|
|
329
|
-
with check:
|
|
330
|
-
assert spy_pause.call_count == 1
|
|
331
327
|
|
|
332
328
|
|
|
333
329
|
def test_load_bashrc(emitter):
|
|
@@ -21,7 +21,7 @@ from unittest import mock
|
|
|
21
21
|
import lazr.restfulclient.errors
|
|
22
22
|
import lazr.restfulclient.resource
|
|
23
23
|
import pytest
|
|
24
|
-
from craft_application import launchpad
|
|
24
|
+
from craft_application import errors, launchpad
|
|
25
25
|
from craft_application.remote import git
|
|
26
26
|
|
|
27
27
|
from tests.unit.services.conftest import (
|
|
@@ -41,6 +41,35 @@ def mock_lp_project(fake_launchpad, mock_project_entry):
|
|
|
41
41
|
return launchpad.models.Project(fake_launchpad, mock_project_entry)
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
@pytest.mark.parametrize("name", ["some-project", "another-project"])
|
|
45
|
+
def test_set_project_success(remote_build_service, mock_project_entry, name):
|
|
46
|
+
mock_project_entry.name = name
|
|
47
|
+
remote_build_service.lp.lp.projects = {name: mock_project_entry}
|
|
48
|
+
|
|
49
|
+
remote_build_service.set_project(name)
|
|
50
|
+
project = remote_build_service._ensure_project()
|
|
51
|
+
|
|
52
|
+
assert project.name == name
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.mark.parametrize("name", ["some-project", "another-project"])
|
|
56
|
+
def test_set_project_name_error(remote_build_service, mock_project_entry, name):
|
|
57
|
+
mock_project_entry.name = name
|
|
58
|
+
remote_build_service.lp.lp.projects.__getitem__.side_effect = (
|
|
59
|
+
lazr.restfulclient.errors.NotFound("yo", "dawg")
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
with pytest.raises(
|
|
63
|
+
errors.CraftError, match=f"Could not find project on Launchpad: {name}"
|
|
64
|
+
) as exc_info:
|
|
65
|
+
remote_build_service.set_project(name)
|
|
66
|
+
|
|
67
|
+
assert (
|
|
68
|
+
exc_info.value.resolution
|
|
69
|
+
== "Ensure the project exists and that you have access to it."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
44
73
|
def test_ensure_project_existing(remote_build_service, mock_project_entry):
|
|
45
74
|
remote_build_service.lp.lp.projects = {
|
|
46
75
|
"craft_test_user-craft-remote-build": mock_project_entry
|
|
@@ -57,6 +86,35 @@ def test_ensure_project_new(remote_build_service):
|
|
|
57
86
|
remote_build_service._ensure_project()
|
|
58
87
|
|
|
59
88
|
|
|
89
|
+
@pytest.mark.parametrize(
|
|
90
|
+
("information_type", "expected"),
|
|
91
|
+
[
|
|
92
|
+
(launchpad.models.InformationType.PUBLIC, False),
|
|
93
|
+
(launchpad.models.InformationType.PUBLIC_SECURITY, False),
|
|
94
|
+
(launchpad.models.InformationType.PRIVATE, True),
|
|
95
|
+
(launchpad.models.InformationType.PRIVATE_SECURITY, True),
|
|
96
|
+
(launchpad.models.InformationType.PROPRIETARY, True),
|
|
97
|
+
(launchpad.models.InformationType.EMBARGOED, True),
|
|
98
|
+
],
|
|
99
|
+
)
|
|
100
|
+
def test_is_project_private(
|
|
101
|
+
remote_build_service, mock_project_entry, information_type, expected
|
|
102
|
+
):
|
|
103
|
+
mock_project_entry.information_type = str(information_type.value)
|
|
104
|
+
remote_build_service.lp.lp.projects = {"test-project": mock_project_entry}
|
|
105
|
+
|
|
106
|
+
remote_build_service.set_project("test-project")
|
|
107
|
+
assert remote_build_service.is_project_private() == expected
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_is_project_private_error(remote_build_service):
|
|
111
|
+
with pytest.raises(
|
|
112
|
+
RuntimeError,
|
|
113
|
+
match="Cannot check if the project is private before setting its name.",
|
|
114
|
+
):
|
|
115
|
+
remote_build_service.is_project_private()
|
|
116
|
+
|
|
117
|
+
|
|
60
118
|
def test_new_repository(
|
|
61
119
|
monkeypatch, tmp_path, remote_build_service, mock_lp_project, mock_push_url
|
|
62
120
|
):
|