craft-application 2.3.0__tar.gz → 2.5.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.3.0 → craft_application-2.5}/PKG-INFO +4 -4
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/_version.py +2 -2
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/grammar.py +46 -13
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/project.py +2 -1
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/models/grammar.py +31 -4
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/remote/git.py +4 -1
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/remotebuild.py +83 -22
- {craft-application-2.3.0 → craft_application-2.5}/craft_application.egg-info/PKG-INFO +4 -4
- {craft-application-2.3.0 → craft_application-2.5}/craft_application.egg-info/SOURCES.txt +1 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application.egg-info/requires.txt +3 -3
- {craft-application-2.3.0 → craft_application-2.5}/pyproject.toml +7 -4
- {craft-application-2.3.0 → craft_application-2.5}/tests/conftest.py +12 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/conftest.py +11 -1
- craft_application-2.5/tests/integration/data/valid_projects/grammar/testcraft.yaml +21 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/launchpad/test_anonymous_access.py +11 -10
- craft_application-2.5/tests/integration/services/test_remotebuild.py +47 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/conftest.py +10 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_remotebuild.py +127 -3
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/test_application.py +443 -20
- craft-application-2.3.0/tests/integration/data/valid_projects/grammar/testcraft.yaml +0 -11
- {craft-application-2.3.0 → craft_application-2.5}/.editorconfig +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/.jira_sync_config.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/ISSUE_TEMPLATE/bug.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/ISSUE_TEMPLATE/task.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/release-drafter.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/release-drafter.yml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/renovate.json5 +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/workflows/cla-check.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/workflows/docs.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/workflows/release-drafter.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.github/workflows/tests.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.gitignore +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.pre-commit-config.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.readthedocs.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/.yamllint.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/HACKING.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/LICENSE +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/README.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/application.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/commands/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/commands/base.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/commands/lifecycle.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/commands/other.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/errors.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/errors.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/launchpad.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/base.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/build.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/code.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/distro.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/models/recipe.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/launchpad/util.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/misc/instance_bashrc +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/models/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/models/base.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/models/constraints.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/models/metadata.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/models/project.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/py.typed +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/remote/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/remote/errors.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/remote/utils.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/remote/worktree.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/secrets.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/base.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/lifecycle.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/package.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/provider.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/request.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/services/service_factory.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/callbacks.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/error_formatting.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/logging.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/paths.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/platforms.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/repositories.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/snap_config.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/string.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application/util/yaml.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application.egg-info/dependency_links.txt +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/craft_application.egg-info/top_level.txt +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/_static/css/custom.css +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/conf.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/explanation/index.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/howto/index.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/index.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/reference/index.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/docs/tutorials/index.rst +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/setup.cfg +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/build-secrets/secret-source-folder/source-file.txt +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/build-secrets/testcraft.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/invalid_projects/build-error/testcraft.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/adoption/stderr +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/adoption/testcraft.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/basic/stderr +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/basic/testcraft.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/environment/stderr +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/environment/testcraft.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-amd64/hello.txt +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-arm64/hello.txt +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/data/valid_projects/grammar/stderr +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/launchpad/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/launchpad/conftest.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/services/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/services/test_lifecycle.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/services/test_provider.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/services/test_request.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/services/test_service_factory.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/test_application.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/integration/test_version.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/commands/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/commands/test_base.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/commands/test_lifecycle.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/commands/test_other.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/conftest.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/conftest.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/models/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/models/test_base.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/models/test_code.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/test_launchpad.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/launchpad/test_util.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/models/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/models/project_models/basic_project.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/models/project_models/full_project.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/models/project_models/invalid_project.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/models/test_constraints.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/models/test_project.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/remote/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/remote/conftest.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/remote/test_errors.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/remote/test_git.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/remote/test_utils.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/remote/test_worktree.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_lifecycle.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_package.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_provider.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_repositories.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_request.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/services/test_service_factory.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/test_errors.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/test_grammar.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/test_nothing.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/test_secrets.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/__init__.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/invalid_yaml/_README +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/invalid_yaml/unhashable.yaml-invalid +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/test_error_formatting.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/test_logging.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/test_paths.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/test_snap_config.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/test_string.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/test_yaml.py +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/tests/unit/util/valid_yaml/empty.yaml +0 -0
- {craft-application-2.3.0 → craft_application-2.5}/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.5.0
|
|
4
4
|
Summary: A framework for *craft applications.
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/x-rst
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: craft-archives>=1.1.3
|
|
19
19
|
Requires-Dist: craft-cli>=2.4.0
|
|
20
|
-
Requires-Dist: craft-grammar>=1.
|
|
20
|
+
Requires-Dist: craft-grammar>=1.2.0
|
|
21
21
|
Requires-Dist: craft-parts>=1.21.1
|
|
22
22
|
Requires-Dist: craft-providers<2.0,>=1.20.0
|
|
23
23
|
Requires-Dist: snap-helpers>=0.4.2
|
|
@@ -37,7 +37,7 @@ 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
39
|
Requires-Dist: pytest-mock==3.14.0; extra == "dev"
|
|
40
|
-
Requires-Dist: pytest-rerunfailures==
|
|
40
|
+
Requires-Dist: pytest-rerunfailures==14.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pytest-time>=0.3.1; extra == "dev"
|
|
42
42
|
Requires-Dist: responses~=0.25.0; extra == "dev"
|
|
43
43
|
Requires-Dist: craft-application[remote]; 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.358; extra == "types"
|
|
51
51
|
Requires-Dist: types-requests; extra == "types"
|
|
52
52
|
Requires-Dist: types-urllib3; extra == "types"
|
|
53
53
|
Provides-Extra: docs
|
|
@@ -15,38 +15,71 @@
|
|
|
15
15
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
"""Grammar processor."""
|
|
17
17
|
|
|
18
|
-
from typing import Any
|
|
18
|
+
from typing import Any, cast
|
|
19
19
|
|
|
20
|
+
import craft_cli
|
|
20
21
|
from craft_grammar import GrammarProcessor # type: ignore[import-untyped]
|
|
22
|
+
from craft_grammar.errors import GrammarSyntaxError # type: ignore[import-untyped]
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
from craft_application.errors import CraftValidationError
|
|
25
|
+
|
|
26
|
+
# Values that should return as a single object / list / dict.
|
|
27
|
+
_NON_SCALAR_VALUES = [
|
|
28
|
+
"source-submodules",
|
|
29
|
+
"after",
|
|
30
|
+
"overlay-packages",
|
|
31
|
+
"build-attributes",
|
|
32
|
+
"permissions",
|
|
24
33
|
"build-environment",
|
|
25
34
|
"build-packages",
|
|
26
35
|
"stage-packages",
|
|
27
36
|
"build-snaps",
|
|
28
37
|
"stage-snaps",
|
|
38
|
+
"overlay",
|
|
39
|
+
"organize",
|
|
40
|
+
"stage",
|
|
41
|
+
"prime",
|
|
29
42
|
]
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
# Values that should return a dict, not in a list.
|
|
45
|
+
_DICT_ONLY_VALUES = [
|
|
46
|
+
"organize",
|
|
47
|
+
]
|
|
32
48
|
|
|
33
49
|
|
|
34
50
|
def process_part(
|
|
35
51
|
*, part_yaml_data: dict[str, Any], processor: GrammarProcessor
|
|
36
52
|
) -> dict[str, Any]:
|
|
37
53
|
"""Process grammar for a given part."""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for key in existing_keys:
|
|
54
|
+
for key in part_yaml_data:
|
|
41
55
|
unprocessed_grammar = part_yaml_data[key]
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
craft_cli.emit.debug(f"Processing grammar for {key}: {unprocessed_grammar}")
|
|
57
|
+
|
|
58
|
+
# grammar aware models can only be a string or list of dict, skip any other type.
|
|
59
|
+
if isinstance(unprocessed_grammar, list):
|
|
60
|
+
if any(not isinstance(d, dict) for d in unprocessed_grammar): # type: ignore[reportUnknownVariableType]
|
|
61
|
+
continue
|
|
62
|
+
if any(not isinstance(k, str) for d in unprocessed_grammar for k in d): # type: ignore[reportUnknownVariableType]
|
|
63
|
+
continue
|
|
64
|
+
unprocessed_grammar = cast(list[dict[str, Any]], unprocessed_grammar)
|
|
65
|
+
elif isinstance(unprocessed_grammar, str):
|
|
44
66
|
unprocessed_grammar = [unprocessed_grammar]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
else:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
processed_grammar = processor.process(grammar=unprocessed_grammar)
|
|
72
|
+
except GrammarSyntaxError as e:
|
|
73
|
+
raise CraftValidationError(
|
|
74
|
+
f"Invalid grammar syntax while processing '{key}' in '{part_yaml_data}': {e}"
|
|
75
|
+
) from e
|
|
76
|
+
|
|
77
|
+
# special cases
|
|
78
|
+
# scalar values should return as a single object, not in a list.
|
|
79
|
+
# dict values should return as a dict, not in a list.
|
|
80
|
+
if key not in _NON_SCALAR_VALUES or key in _DICT_ONLY_VALUES:
|
|
49
81
|
processed_grammar = processed_grammar[0] if processed_grammar else None
|
|
82
|
+
|
|
50
83
|
part_yaml_data[key] = processed_grammar
|
|
51
84
|
|
|
52
85
|
return part_yaml_data
|
{craft-application-2.3.0 → craft_application-2.5}/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]
|
|
@@ -19,6 +19,10 @@ from typing import Any
|
|
|
19
19
|
|
|
20
20
|
import pydantic
|
|
21
21
|
from craft_grammar.models import ( # type: ignore[import-untyped]
|
|
22
|
+
GrammarBool,
|
|
23
|
+
GrammarDict,
|
|
24
|
+
GrammarDictList,
|
|
25
|
+
GrammarInt,
|
|
22
26
|
GrammarSingleEntryDictList,
|
|
23
27
|
GrammarStr,
|
|
24
28
|
GrammarStrList,
|
|
@@ -38,13 +42,36 @@ class _GrammarAwareModel(pydantic.BaseModel):
|
|
|
38
42
|
|
|
39
43
|
|
|
40
44
|
class _GrammarAwarePart(_GrammarAwareModel):
|
|
45
|
+
plugin: GrammarStr | None
|
|
41
46
|
source: GrammarStr | None
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
source_checksum: GrammarStr | None
|
|
48
|
+
source_branch: GrammarStr | None
|
|
49
|
+
source_commit: GrammarStr | None
|
|
50
|
+
source_depth: GrammarInt | None
|
|
51
|
+
source_subdir: GrammarStr | None
|
|
52
|
+
source_submodules: GrammarStrList | None
|
|
53
|
+
source_tag: GrammarStr | None
|
|
54
|
+
source_type: GrammarStr | None
|
|
55
|
+
disable_parallel: GrammarBool | None
|
|
56
|
+
after: GrammarStrList | None
|
|
57
|
+
overlay_packages: GrammarStrList | None
|
|
58
|
+
stage_snaps: GrammarStrList | None
|
|
44
59
|
stage_packages: GrammarStrList | None
|
|
45
60
|
build_snaps: GrammarStrList | None
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
build_packages: GrammarStrList | None
|
|
62
|
+
build_environment: GrammarSingleEntryDictList | None
|
|
63
|
+
build_attributes: GrammarStrList | None
|
|
64
|
+
organize_files: GrammarDict | None
|
|
65
|
+
overlay_files: GrammarStrList | None
|
|
66
|
+
stage_files: GrammarStrList | None
|
|
67
|
+
prime_files: GrammarStrList | None
|
|
68
|
+
override_pull: GrammarStr | None
|
|
69
|
+
overlay_script: GrammarStr | None
|
|
70
|
+
override_build: GrammarStr | None
|
|
71
|
+
override_stage: GrammarStr | None
|
|
72
|
+
override_prime: GrammarStr | None
|
|
73
|
+
permissions: GrammarDictList | None
|
|
74
|
+
parse_info: GrammarStrList | None
|
|
48
75
|
|
|
49
76
|
|
|
50
77
|
class GrammarAwareProject(_GrammarAwareModel):
|
|
@@ -253,7 +253,10 @@ class GitRepo:
|
|
|
253
253
|
# temporarily call git directly due to libgit2 bug that unable to push
|
|
254
254
|
# large repos using https. See https://github.com/libgit2/libgit2/issues/6385
|
|
255
255
|
# and https://github.com/snapcore/snapcraft/issues/4478
|
|
256
|
-
|
|
256
|
+
# Force push in case this repository already exists. The repository is always
|
|
257
|
+
# going to exist solely for remote builds, so the only potential issue here is a
|
|
258
|
+
# race condition with multiple remote builds on the same machine.
|
|
259
|
+
cmd: list[str] = ["git", "push", "--force", remote_url, refspec, "--progress"]
|
|
257
260
|
if push_tags:
|
|
258
261
|
cmd.append("--tags")
|
|
259
262
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"""Service class for remote build commands."""
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
import contextlib
|
|
19
20
|
import datetime
|
|
20
21
|
import itertools
|
|
21
22
|
import os
|
|
@@ -27,10 +28,16 @@ from typing import TYPE_CHECKING, Any, cast
|
|
|
27
28
|
from urllib import parse
|
|
28
29
|
|
|
29
30
|
import craft_cli
|
|
31
|
+
import launchpadlib.errors # type: ignore[import-untyped]
|
|
30
32
|
import platformdirs
|
|
31
33
|
|
|
32
34
|
from craft_application import errors, launchpad, models
|
|
33
|
-
from craft_application.remote import
|
|
35
|
+
from craft_application.remote import (
|
|
36
|
+
GitRepo,
|
|
37
|
+
WorkTree,
|
|
38
|
+
check_git_repo_for_remote_build,
|
|
39
|
+
utils,
|
|
40
|
+
)
|
|
34
41
|
from craft_application.services import base
|
|
35
42
|
|
|
36
43
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -62,6 +69,7 @@ class RemoteBuildService(base.AppService):
|
|
|
62
69
|
self._repository: launchpad.models.GitRepository = None # type: ignore[assignment]
|
|
63
70
|
self._recipe: launchpad.models.recipe.BaseRecipe = None # type: ignore[assignment]
|
|
64
71
|
self._builds: Collection[launchpad.models.Build] = []
|
|
72
|
+
self._project_name: str | None = None
|
|
65
73
|
|
|
66
74
|
def setup(self) -> None:
|
|
67
75
|
"""Set up the remote builder."""
|
|
@@ -69,6 +77,35 @@ class RemoteBuildService(base.AppService):
|
|
|
69
77
|
|
|
70
78
|
# region Public API
|
|
71
79
|
# Commands will call a subset of these methods, generally in order.
|
|
80
|
+
def set_project(self, name: str) -> None:
|
|
81
|
+
"""Set the name of the project to use."""
|
|
82
|
+
if self._is_setup:
|
|
83
|
+
raise RuntimeError(
|
|
84
|
+
"Project name may only be set before starting or resuming builds."
|
|
85
|
+
)
|
|
86
|
+
try:
|
|
87
|
+
self._lp_project = self.lp.get_project(name)
|
|
88
|
+
except launchpad.errors.NotFoundError:
|
|
89
|
+
raise errors.CraftError(
|
|
90
|
+
message=f"Could not find project on Launchpad: {name}",
|
|
91
|
+
resolution="Ensure the project exists and that you have access to it.",
|
|
92
|
+
retcode=77, # EX_NOPERM from sysexits.h
|
|
93
|
+
reportable=False,
|
|
94
|
+
logpath_report=False,
|
|
95
|
+
)
|
|
96
|
+
self._project_name = name
|
|
97
|
+
|
|
98
|
+
def is_project_private(self) -> bool:
|
|
99
|
+
"""Check whether the named project is private."""
|
|
100
|
+
if not self._lp_project:
|
|
101
|
+
raise RuntimeError(
|
|
102
|
+
"Cannot check if the project is private before setting its name."
|
|
103
|
+
)
|
|
104
|
+
return self._lp_project.information_type not in (
|
|
105
|
+
launchpad.models.InformationType.PUBLIC,
|
|
106
|
+
launchpad.models.InformationType.PUBLIC_SECURITY,
|
|
107
|
+
)
|
|
108
|
+
|
|
72
109
|
def set_timeout(self, seconds_in_future: int) -> None:
|
|
73
110
|
"""Set the deadline to a certain number of seconds in the future."""
|
|
74
111
|
self._deadline = time.monotonic_ns() + (seconds_in_future * 10**9)
|
|
@@ -85,10 +122,12 @@ class RemoteBuildService(base.AppService):
|
|
|
85
122
|
|
|
86
123
|
project = cast(models.Project, self._services.project)
|
|
87
124
|
|
|
125
|
+
check_git_repo_for_remote_build(project_dir)
|
|
126
|
+
|
|
88
127
|
self._name = utils.get_build_id(self._app.name, project.name, project_dir)
|
|
89
128
|
self._lp_project = self._ensure_project()
|
|
90
|
-
_, self._repository = self.
|
|
91
|
-
self._recipe = self.
|
|
129
|
+
_, self._repository = self._ensure_repository(project_dir)
|
|
130
|
+
self._recipe = self._ensure_recipe(
|
|
92
131
|
self._name, self._repository, architectures=architectures
|
|
93
132
|
)
|
|
94
133
|
self._check_timeout()
|
|
@@ -207,28 +246,32 @@ class RemoteBuildService(base.AppService):
|
|
|
207
246
|
|
|
208
247
|
def _ensure_project(self) -> launchpad.models.Project:
|
|
209
248
|
"""Ensure that the user's remote build project exists."""
|
|
249
|
+
if self._lp_project:
|
|
250
|
+
return self._lp_project
|
|
210
251
|
try:
|
|
211
|
-
return
|
|
212
|
-
self.lp, f"{self.lp.username}-craft-remote-build"
|
|
213
|
-
)
|
|
252
|
+
return self.lp.get_project(f"{self.lp.username}-craft-remote-build")
|
|
214
253
|
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.",
|
|
254
|
+
return self.lp.new_project(
|
|
255
|
+
name=f"{self.lp.username}-craft-remote-build",
|
|
256
|
+
title=f"*craft remote builds for {self.lp.username}",
|
|
257
|
+
display_name=f"{self.lp.username} remote builds",
|
|
258
|
+
summary=f"Automatically-generated project for housing {self.lp.username}'s remote builds in snapcraft, charmcraft, etc.",
|
|
221
259
|
)
|
|
222
260
|
|
|
223
|
-
def
|
|
261
|
+
def _ensure_repository(
|
|
224
262
|
self, project_dir: pathlib.Path
|
|
225
263
|
) -> tuple[WorkTree, launchpad.models.GitRepository]:
|
|
226
|
-
"""Create a repository on the local machine and on Launchpad."""
|
|
264
|
+
"""Create a repository on the local machine and ensure it's on Launchpad."""
|
|
227
265
|
work_tree = WorkTree(self._app.name, self._name, project_dir)
|
|
228
266
|
work_tree.init_repo()
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
267
|
+
try:
|
|
268
|
+
lp_repository = self.lp.new_repository(
|
|
269
|
+
self._name, project=self._lp_project.name
|
|
270
|
+
)
|
|
271
|
+
except launchpadlib.errors.HTTPError:
|
|
272
|
+
lp_repository = self.lp.get_repository(
|
|
273
|
+
name=self._name, project=self._lp_project.name
|
|
274
|
+
)
|
|
232
275
|
|
|
233
276
|
token = lp_repository.get_access_token(
|
|
234
277
|
f"{self._app.name} {self._app.version} remote build",
|
|
@@ -247,9 +290,20 @@ class RemoteBuildService(base.AppService):
|
|
|
247
290
|
|
|
248
291
|
def _get_repository(self) -> launchpad.models.GitRepository:
|
|
249
292
|
"""Get an existing repository on Launchpad."""
|
|
250
|
-
return
|
|
251
|
-
|
|
252
|
-
|
|
293
|
+
return self.lp.get_repository(name=self._name, owner=self.lp.username)
|
|
294
|
+
|
|
295
|
+
def _ensure_recipe(
|
|
296
|
+
self,
|
|
297
|
+
name: str,
|
|
298
|
+
repository: launchpad.models.GitRepository,
|
|
299
|
+
**kwargs: Any, # noqa: ANN401
|
|
300
|
+
) -> launchpad.models.Recipe:
|
|
301
|
+
"""Get a recipe or create it if it doesn't exist."""
|
|
302
|
+
with contextlib.suppress(ValueError): # Recipe doesn't exist
|
|
303
|
+
recipe = self._get_recipe()
|
|
304
|
+
recipe.delete()
|
|
305
|
+
|
|
306
|
+
return self._new_recipe(name, repository, **kwargs)
|
|
253
307
|
|
|
254
308
|
def _new_recipe(
|
|
255
309
|
self,
|
|
@@ -257,9 +311,16 @@ class RemoteBuildService(base.AppService):
|
|
|
257
311
|
repository: launchpad.models.GitRepository,
|
|
258
312
|
**kwargs: Any, # noqa: ANN401
|
|
259
313
|
) -> launchpad.models.Recipe:
|
|
260
|
-
"""Create a new recipe."""
|
|
314
|
+
"""Create a new recipe for the given repository."""
|
|
315
|
+
repository.lp_refresh() # Prevents a race condition on new repositories.
|
|
316
|
+
git_ref = parse.urlparse(str(repository.git_https_url)).path + "/+ref/main"
|
|
261
317
|
return self.RecipeClass.new(
|
|
262
|
-
self.lp,
|
|
318
|
+
self.lp,
|
|
319
|
+
name,
|
|
320
|
+
self.lp.username,
|
|
321
|
+
git_ref=git_ref,
|
|
322
|
+
project=self._lp_project.name,
|
|
323
|
+
**kwargs,
|
|
263
324
|
)
|
|
264
325
|
|
|
265
326
|
def _get_recipe(self) -> launchpad.models.Recipe:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: craft-application
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: A framework for *craft applications.
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/x-rst
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: craft-archives>=1.1.3
|
|
19
19
|
Requires-Dist: craft-cli>=2.4.0
|
|
20
|
-
Requires-Dist: craft-grammar>=1.
|
|
20
|
+
Requires-Dist: craft-grammar>=1.2.0
|
|
21
21
|
Requires-Dist: craft-parts>=1.21.1
|
|
22
22
|
Requires-Dist: craft-providers<2.0,>=1.20.0
|
|
23
23
|
Requires-Dist: snap-helpers>=0.4.2
|
|
@@ -37,7 +37,7 @@ 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
39
|
Requires-Dist: pytest-mock==3.14.0; extra == "dev"
|
|
40
|
-
Requires-Dist: pytest-rerunfailures==
|
|
40
|
+
Requires-Dist: pytest-rerunfailures==14.0; extra == "dev"
|
|
41
41
|
Requires-Dist: pytest-time>=0.3.1; extra == "dev"
|
|
42
42
|
Requires-Dist: responses~=0.25.0; extra == "dev"
|
|
43
43
|
Requires-Dist: craft-application[remote]; 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.358; 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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
craft-archives>=1.1.3
|
|
2
2
|
craft-cli>=2.4.0
|
|
3
|
-
craft-grammar>=1.
|
|
3
|
+
craft-grammar>=1.2.0
|
|
4
4
|
craft-parts>=1.21.1
|
|
5
5
|
craft-providers<2.0,>=1.20.0
|
|
6
6
|
snap-helpers>=0.4.2
|
|
@@ -24,7 +24,7 @@ pytest==8.1.1
|
|
|
24
24
|
pytest-check==2.3.1
|
|
25
25
|
pytest-cov==5.0.0
|
|
26
26
|
pytest-mock==3.14.0
|
|
27
|
-
pytest-rerunfailures==
|
|
27
|
+
pytest-rerunfailures==14.0
|
|
28
28
|
pytest-time>=0.3.1
|
|
29
29
|
responses~=0.25.0
|
|
30
30
|
craft-application[remote]
|
|
@@ -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.358
|
|
53
53
|
types-requests
|
|
54
54
|
types-urllib3
|
|
@@ -5,7 +5,7 @@ dynamic = ["version", "readme"]
|
|
|
5
5
|
dependencies = [
|
|
6
6
|
"craft-archives>=1.1.3",
|
|
7
7
|
"craft-cli>=2.4.0",
|
|
8
|
-
"craft-grammar>=1.
|
|
8
|
+
"craft-grammar>=1.2.0",
|
|
9
9
|
"craft-parts>=1.21.1",
|
|
10
10
|
"craft-providers>=1.20.0,<2.0",
|
|
11
11
|
"snap-helpers>=0.4.2",
|
|
@@ -52,7 +52,7 @@ dev = [
|
|
|
52
52
|
"pytest-check==2.3.1",
|
|
53
53
|
"pytest-cov==5.0.0",
|
|
54
54
|
"pytest-mock==3.14.0",
|
|
55
|
-
"pytest-rerunfailures==
|
|
55
|
+
"pytest-rerunfailures==14.0",
|
|
56
56
|
"pytest-time>=0.3.1",
|
|
57
57
|
"responses~=0.25.0",
|
|
58
58
|
"craft-application[remote]"
|
|
@@ -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.358",
|
|
68
68
|
"types-requests",
|
|
69
69
|
"types-urllib3",
|
|
70
70
|
]
|
|
@@ -84,7 +84,7 @@ apt = [
|
|
|
84
84
|
|
|
85
85
|
[build-system]
|
|
86
86
|
requires = [
|
|
87
|
-
"setuptools==69.
|
|
87
|
+
"setuptools==69.4.0",
|
|
88
88
|
"setuptools_scm[toml]>=7.1"
|
|
89
89
|
]
|
|
90
90
|
build-backend = "setuptools.build_meta"
|
|
@@ -286,6 +286,9 @@ lint.ignore = [
|
|
|
286
286
|
]
|
|
287
287
|
# isort leaves init files alone by default, this makes ruff ignore them too.
|
|
288
288
|
"__init__.py" = ["I001"]
|
|
289
|
+
# TODO: Revert this
|
|
290
|
+
# https://github.com/canonical/craft-application/issues/306
|
|
291
|
+
"tests/integration/launchpad/test_anonymous_access.py" = ["ERA001"]
|
|
289
292
|
|
|
290
293
|
[tool.ruff.lint.pep8-naming]
|
|
291
294
|
classmethod-decorators = [
|
|
@@ -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:
|
|
@@ -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,21 @@
|
|
|
1
|
+
name: empty
|
|
2
|
+
title: A most basic project
|
|
3
|
+
version: 1.0
|
|
4
|
+
base: ["ubuntu", "22.04"]
|
|
5
|
+
|
|
6
|
+
parts:
|
|
7
|
+
hello-world:
|
|
8
|
+
plugin: dump
|
|
9
|
+
source:
|
|
10
|
+
- on amd64 to amd64: src/on-amd64-to-amd64
|
|
11
|
+
- on amd64 to arm64: src/on-amd64-to-arm64
|
|
12
|
+
organize:
|
|
13
|
+
- on amd64 to amd64:
|
|
14
|
+
hello.txt: hello-amd64.txt
|
|
15
|
+
- on amd64 to arm64:
|
|
16
|
+
hello.txt: hello-arm64.txt
|
|
17
|
+
build-environment:
|
|
18
|
+
- on amd64 to amd64:
|
|
19
|
+
- MY_VAR: amd64
|
|
20
|
+
- on amd64 to arm64:
|
|
21
|
+
- MY_VAR: arm64
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
from craft_application import launchpad
|
|
5
|
-
from craft_application.launchpad import models
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
def test_anonymous_login(tmp_path):
|
|
@@ -16,15 +15,17 @@ def test_anonymous_login(tmp_path):
|
|
|
16
15
|
assert cache_dir.is_dir()
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
# TODO: Re-enable this
|
|
19
|
+
# https://github.com/canonical/craft-application/issues/306
|
|
20
|
+
# def test_get_basic_items(anonymous_lp):
|
|
21
|
+
# snapstore_server = anonymous_lp.get_project("snapstore-server")
|
|
22
|
+
# assert snapstore_server.name == "snapstore-server"
|
|
23
|
+
# assert snapstore_server.title == "Snap Store Server"
|
|
24
|
+
#
|
|
25
|
+
# snap_recipes = list(models.SnapRecipe.find(anonymous_lp, owner="lengau"))
|
|
26
|
+
# assert len(snap_recipes) > 0
|
|
27
|
+
# for recipe in snap_recipes:
|
|
28
|
+
# assert recipe.owner_name == "lengau"
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@pytest.mark.parametrize(
|
|
@@ -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()
|