craft-application 0.1.0__py3-none-any.whl
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.
- build/lib/build/lib/build/lib/build/lib/craft_application/__init__.py +51 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/_version.py +16 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/application.py +477 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/commands/__init__.py +28 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/commands/base.py +76 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/commands/lifecycle.py +359 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/commands/other.py +51 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/errors.py +105 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/models/__init__.py +41 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/models/base.py +112 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/models/constraints.py +70 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/models/metadata.py +34 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/models/project.py +94 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/secrets.py +190 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/services/__init__.py +31 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/services/base.py +59 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/services/lifecycle.py +226 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/services/package.py +53 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/services/provider.py +234 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/services/service_factory.py +93 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/util/__init__.py +30 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/util/error_formatting.py +110 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/util/paths.py +34 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/util/platforms.py +64 -0
- build/lib/build/lib/build/lib/build/lib/craft_application/util/yaml.py +94 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/conftest.py +76 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/services/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/services/test_lifecycle.py +99 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/services/test_provider.py +74 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/services/test_service_factory.py +54 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/test_application.py +346 -0
- build/lib/build/lib/build/lib/build/lib/tests/integration/test_version.py +65 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/commands/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/commands/test_base.py +75 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/commands/test_lifecycle.py +513 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/commands/test_other.py +39 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/conftest.py +40 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/models/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/models/test_constraints.py +158 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/models/test_project.py +218 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/services/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_lifecycle.py +358 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_package.py +47 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_provider.py +397 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_service_factory.py +140 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/test_application.py +597 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/test_errors.py +63 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/test_nothing.py +2 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/test_secrets.py +154 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/util/__init__.py +0 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/util/test_error_formatting.py +98 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/util/test_paths.py +26 -0
- build/lib/build/lib/build/lib/build/lib/tests/unit/util/test_yaml.py +42 -0
- build/lib/build/lib/build/lib/build/lib/venv/bin/activate_this.py +36 -0
- build/lib/build/lib/build/lib/craft_application/__init__.py +51 -0
- build/lib/build/lib/build/lib/craft_application/_version.py +16 -0
- build/lib/build/lib/build/lib/craft_application/application.py +477 -0
- build/lib/build/lib/build/lib/craft_application/commands/__init__.py +28 -0
- build/lib/build/lib/build/lib/craft_application/commands/base.py +76 -0
- build/lib/build/lib/build/lib/craft_application/commands/lifecycle.py +359 -0
- build/lib/build/lib/build/lib/craft_application/commands/other.py +51 -0
- build/lib/build/lib/build/lib/craft_application/errors.py +105 -0
- build/lib/build/lib/build/lib/craft_application/models/__init__.py +41 -0
- build/lib/build/lib/build/lib/craft_application/models/base.py +112 -0
- build/lib/build/lib/build/lib/craft_application/models/constraints.py +70 -0
- build/lib/build/lib/build/lib/craft_application/models/metadata.py +34 -0
- build/lib/build/lib/build/lib/craft_application/models/project.py +94 -0
- build/lib/build/lib/build/lib/craft_application/secrets.py +190 -0
- build/lib/build/lib/build/lib/craft_application/services/__init__.py +31 -0
- build/lib/build/lib/build/lib/craft_application/services/base.py +59 -0
- build/lib/build/lib/build/lib/craft_application/services/lifecycle.py +226 -0
- build/lib/build/lib/build/lib/craft_application/services/package.py +53 -0
- build/lib/build/lib/build/lib/craft_application/services/provider.py +234 -0
- build/lib/build/lib/build/lib/craft_application/services/service_factory.py +93 -0
- build/lib/build/lib/build/lib/craft_application/util/__init__.py +30 -0
- build/lib/build/lib/build/lib/craft_application/util/error_formatting.py +110 -0
- build/lib/build/lib/build/lib/craft_application/util/paths.py +34 -0
- build/lib/build/lib/build/lib/craft_application/util/platforms.py +64 -0
- build/lib/build/lib/build/lib/craft_application/util/yaml.py +94 -0
- build/lib/build/lib/build/lib/tests/integration/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/integration/conftest.py +76 -0
- build/lib/build/lib/build/lib/tests/integration/services/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/integration/services/test_lifecycle.py +99 -0
- build/lib/build/lib/build/lib/tests/integration/services/test_provider.py +74 -0
- build/lib/build/lib/build/lib/tests/integration/services/test_service_factory.py +54 -0
- build/lib/build/lib/build/lib/tests/integration/test_application.py +346 -0
- build/lib/build/lib/build/lib/tests/integration/test_version.py +65 -0
- build/lib/build/lib/build/lib/tests/unit/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/unit/commands/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/unit/commands/test_base.py +75 -0
- build/lib/build/lib/build/lib/tests/unit/commands/test_lifecycle.py +513 -0
- build/lib/build/lib/build/lib/tests/unit/commands/test_other.py +39 -0
- build/lib/build/lib/build/lib/tests/unit/conftest.py +40 -0
- build/lib/build/lib/build/lib/tests/unit/models/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/unit/models/test_constraints.py +158 -0
- build/lib/build/lib/build/lib/tests/unit/models/test_project.py +218 -0
- build/lib/build/lib/build/lib/tests/unit/services/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/unit/services/test_lifecycle.py +358 -0
- build/lib/build/lib/build/lib/tests/unit/services/test_package.py +47 -0
- build/lib/build/lib/build/lib/tests/unit/services/test_provider.py +397 -0
- build/lib/build/lib/build/lib/tests/unit/services/test_service_factory.py +140 -0
- build/lib/build/lib/build/lib/tests/unit/test_application.py +597 -0
- build/lib/build/lib/build/lib/tests/unit/test_errors.py +63 -0
- build/lib/build/lib/build/lib/tests/unit/test_nothing.py +2 -0
- build/lib/build/lib/build/lib/tests/unit/test_secrets.py +154 -0
- build/lib/build/lib/build/lib/tests/unit/util/__init__.py +0 -0
- build/lib/build/lib/build/lib/tests/unit/util/test_error_formatting.py +98 -0
- build/lib/build/lib/build/lib/tests/unit/util/test_paths.py +26 -0
- build/lib/build/lib/build/lib/tests/unit/util/test_yaml.py +42 -0
- build/lib/build/lib/build/lib/venv/bin/activate_this.py +36 -0
- build/lib/build/lib/craft_application/__init__.py +51 -0
- build/lib/build/lib/craft_application/_version.py +16 -0
- build/lib/build/lib/craft_application/application.py +477 -0
- build/lib/build/lib/craft_application/commands/__init__.py +28 -0
- build/lib/build/lib/craft_application/commands/base.py +76 -0
- build/lib/build/lib/craft_application/commands/lifecycle.py +359 -0
- build/lib/build/lib/craft_application/commands/other.py +51 -0
- build/lib/build/lib/craft_application/errors.py +105 -0
- build/lib/build/lib/craft_application/models/__init__.py +41 -0
- build/lib/build/lib/craft_application/models/base.py +112 -0
- build/lib/build/lib/craft_application/models/constraints.py +70 -0
- build/lib/build/lib/craft_application/models/metadata.py +34 -0
- build/lib/build/lib/craft_application/models/project.py +94 -0
- build/lib/build/lib/craft_application/secrets.py +190 -0
- build/lib/build/lib/craft_application/services/__init__.py +31 -0
- build/lib/build/lib/craft_application/services/base.py +59 -0
- build/lib/build/lib/craft_application/services/lifecycle.py +226 -0
- build/lib/build/lib/craft_application/services/package.py +53 -0
- build/lib/build/lib/craft_application/services/provider.py +234 -0
- build/lib/build/lib/craft_application/services/service_factory.py +93 -0
- build/lib/build/lib/craft_application/util/__init__.py +30 -0
- build/lib/build/lib/craft_application/util/error_formatting.py +110 -0
- build/lib/build/lib/craft_application/util/paths.py +34 -0
- build/lib/build/lib/craft_application/util/platforms.py +64 -0
- build/lib/build/lib/craft_application/util/yaml.py +94 -0
- build/lib/build/lib/tests/integration/__init__.py +0 -0
- build/lib/build/lib/tests/integration/conftest.py +76 -0
- build/lib/build/lib/tests/integration/services/__init__.py +0 -0
- build/lib/build/lib/tests/integration/services/test_lifecycle.py +99 -0
- build/lib/build/lib/tests/integration/services/test_provider.py +74 -0
- build/lib/build/lib/tests/integration/services/test_service_factory.py +54 -0
- build/lib/build/lib/tests/integration/test_application.py +346 -0
- build/lib/build/lib/tests/integration/test_version.py +65 -0
- build/lib/build/lib/tests/unit/__init__.py +0 -0
- build/lib/build/lib/tests/unit/commands/__init__.py +0 -0
- build/lib/build/lib/tests/unit/commands/test_base.py +75 -0
- build/lib/build/lib/tests/unit/commands/test_lifecycle.py +513 -0
- build/lib/build/lib/tests/unit/commands/test_other.py +39 -0
- build/lib/build/lib/tests/unit/conftest.py +40 -0
- build/lib/build/lib/tests/unit/models/__init__.py +0 -0
- build/lib/build/lib/tests/unit/models/test_constraints.py +158 -0
- build/lib/build/lib/tests/unit/models/test_project.py +218 -0
- build/lib/build/lib/tests/unit/services/__init__.py +0 -0
- build/lib/build/lib/tests/unit/services/test_lifecycle.py +358 -0
- build/lib/build/lib/tests/unit/services/test_package.py +47 -0
- build/lib/build/lib/tests/unit/services/test_provider.py +397 -0
- build/lib/build/lib/tests/unit/services/test_service_factory.py +140 -0
- build/lib/build/lib/tests/unit/test_application.py +597 -0
- build/lib/build/lib/tests/unit/test_errors.py +63 -0
- build/lib/build/lib/tests/unit/test_nothing.py +2 -0
- build/lib/build/lib/tests/unit/test_secrets.py +154 -0
- build/lib/build/lib/tests/unit/util/__init__.py +0 -0
- build/lib/build/lib/tests/unit/util/test_error_formatting.py +98 -0
- build/lib/build/lib/tests/unit/util/test_paths.py +26 -0
- build/lib/build/lib/tests/unit/util/test_yaml.py +42 -0
- build/lib/build/lib/venv/bin/activate_this.py +36 -0
- build/lib/craft_application/__init__.py +51 -0
- build/lib/craft_application/_version.py +16 -0
- build/lib/craft_application/application.py +477 -0
- build/lib/craft_application/commands/__init__.py +28 -0
- build/lib/craft_application/commands/base.py +76 -0
- build/lib/craft_application/commands/lifecycle.py +359 -0
- build/lib/craft_application/commands/other.py +51 -0
- build/lib/craft_application/errors.py +105 -0
- build/lib/craft_application/models/__init__.py +41 -0
- build/lib/craft_application/models/base.py +112 -0
- build/lib/craft_application/models/constraints.py +70 -0
- build/lib/craft_application/models/metadata.py +34 -0
- build/lib/craft_application/models/project.py +94 -0
- build/lib/craft_application/secrets.py +190 -0
- build/lib/craft_application/services/__init__.py +31 -0
- build/lib/craft_application/services/base.py +59 -0
- build/lib/craft_application/services/lifecycle.py +226 -0
- build/lib/craft_application/services/package.py +53 -0
- build/lib/craft_application/services/provider.py +234 -0
- build/lib/craft_application/services/service_factory.py +93 -0
- build/lib/craft_application/util/__init__.py +30 -0
- build/lib/craft_application/util/error_formatting.py +110 -0
- build/lib/craft_application/util/paths.py +34 -0
- build/lib/craft_application/util/platforms.py +64 -0
- build/lib/craft_application/util/yaml.py +94 -0
- build/lib/tests/integration/__init__.py +0 -0
- build/lib/tests/integration/conftest.py +76 -0
- build/lib/tests/integration/services/__init__.py +0 -0
- build/lib/tests/integration/services/test_lifecycle.py +99 -0
- build/lib/tests/integration/services/test_provider.py +74 -0
- build/lib/tests/integration/services/test_service_factory.py +54 -0
- build/lib/tests/integration/test_application.py +346 -0
- build/lib/tests/integration/test_version.py +65 -0
- build/lib/tests/unit/__init__.py +0 -0
- build/lib/tests/unit/commands/__init__.py +0 -0
- build/lib/tests/unit/commands/test_base.py +75 -0
- build/lib/tests/unit/commands/test_lifecycle.py +513 -0
- build/lib/tests/unit/commands/test_other.py +39 -0
- build/lib/tests/unit/conftest.py +40 -0
- build/lib/tests/unit/models/__init__.py +0 -0
- build/lib/tests/unit/models/test_constraints.py +158 -0
- build/lib/tests/unit/models/test_project.py +218 -0
- build/lib/tests/unit/services/__init__.py +0 -0
- build/lib/tests/unit/services/test_lifecycle.py +358 -0
- build/lib/tests/unit/services/test_package.py +47 -0
- build/lib/tests/unit/services/test_provider.py +397 -0
- build/lib/tests/unit/services/test_service_factory.py +140 -0
- build/lib/tests/unit/test_application.py +597 -0
- build/lib/tests/unit/test_errors.py +63 -0
- build/lib/tests/unit/test_nothing.py +2 -0
- build/lib/tests/unit/test_secrets.py +154 -0
- build/lib/tests/unit/util/__init__.py +0 -0
- build/lib/tests/unit/util/test_error_formatting.py +98 -0
- build/lib/tests/unit/util/test_paths.py +26 -0
- build/lib/tests/unit/util/test_yaml.py +42 -0
- build/lib/venv/bin/activate_this.py +36 -0
- craft_application/__init__.py +51 -0
- craft_application/_version.py +16 -0
- craft_application/application.py +477 -0
- craft_application/commands/__init__.py +28 -0
- craft_application/commands/base.py +76 -0
- craft_application/commands/lifecycle.py +359 -0
- craft_application/commands/other.py +51 -0
- craft_application/errors.py +105 -0
- craft_application/models/__init__.py +41 -0
- craft_application/models/base.py +112 -0
- craft_application/models/constraints.py +70 -0
- craft_application/models/metadata.py +34 -0
- craft_application/models/project.py +94 -0
- craft_application/py.typed +0 -0
- craft_application/secrets.py +190 -0
- craft_application/services/__init__.py +31 -0
- craft_application/services/base.py +59 -0
- craft_application/services/lifecycle.py +226 -0
- craft_application/services/package.py +53 -0
- craft_application/services/provider.py +234 -0
- craft_application/services/service_factory.py +93 -0
- craft_application/util/__init__.py +30 -0
- craft_application/util/error_formatting.py +110 -0
- craft_application/util/paths.py +34 -0
- craft_application/util/platforms.py +64 -0
- craft_application/util/yaml.py +94 -0
- craft_application-0.1.0.dist-info/LICENSE +165 -0
- craft_application-0.1.0.dist-info/METADATA +76 -0
- craft_application-0.1.0.dist-info/RECORD +305 -0
- craft_application-0.1.0.dist-info/WHEEL +5 -0
- craft_application-0.1.0.dist-info/top_level.txt +5 -0
- docs/_static/css/custom.css +28 -0
- docs/explanation/index.rst +7 -0
- docs/howto/index.rst +7 -0
- docs/reference/index.rst +13 -0
- docs/tutorials/index.rst +11 -0
- tests/integration/__init__.py +0 -0
- tests/integration/conftest.py +76 -0
- tests/integration/data/build-secrets/secret-source-folder/source-file.txt +1 -0
- tests/integration/data/build-secrets/testcraft.yaml +15 -0
- tests/integration/data/valid_projects/basic/stderr +1 -0
- tests/integration/data/valid_projects/basic/testcraft.yaml +8 -0
- tests/integration/data/valid_projects/environment/stderr +1 -0
- tests/integration/data/valid_projects/environment/testcraft.yaml +14 -0
- tests/integration/services/__init__.py +0 -0
- tests/integration/services/test_lifecycle.py +99 -0
- tests/integration/services/test_provider.py +74 -0
- tests/integration/services/test_service_factory.py +54 -0
- tests/integration/test_application.py +346 -0
- tests/integration/test_version.py +65 -0
- tests/unit/__init__.py +0 -0
- tests/unit/commands/__init__.py +0 -0
- tests/unit/commands/test_base.py +75 -0
- tests/unit/commands/test_lifecycle.py +513 -0
- tests/unit/commands/test_other.py +39 -0
- tests/unit/conftest.py +40 -0
- tests/unit/models/__init__.py +0 -0
- tests/unit/models/project_models/basic_project.yaml +5 -0
- tests/unit/models/project_models/full_project.yaml +15 -0
- tests/unit/models/project_models/invalid_project.yaml +1 -0
- tests/unit/models/test_constraints.py +158 -0
- tests/unit/models/test_project.py +218 -0
- tests/unit/services/__init__.py +0 -0
- tests/unit/services/test_lifecycle.py +358 -0
- tests/unit/services/test_package.py +47 -0
- tests/unit/services/test_provider.py +397 -0
- tests/unit/services/test_service_factory.py +140 -0
- tests/unit/test_application.py +597 -0
- tests/unit/test_errors.py +63 -0
- tests/unit/test_nothing.py +2 -0
- tests/unit/test_secrets.py +154 -0
- tests/unit/util/__init__.py +0 -0
- tests/unit/util/invalid_yaml/_README +4 -0
- tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +3 -0
- tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +2 -0
- tests/unit/util/invalid_yaml/unhashable.yaml-invalid +2 -0
- tests/unit/util/test_error_formatting.py +98 -0
- tests/unit/util/test_paths.py +26 -0
- tests/unit/util/test_yaml.py +42 -0
- tests/unit/util/valid_yaml/empty.yaml +0 -0
- venv/bin/activate_this.py +36 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# This file is part of craft-application.
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2023 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 along
|
|
15
|
+
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
"""Framework for *craft applications."""
|
|
17
|
+
|
|
18
|
+
from craft_application.application import Application, AppFeatures, AppMetadata
|
|
19
|
+
from craft_application import models
|
|
20
|
+
from craft_application.services import (
|
|
21
|
+
AppService,
|
|
22
|
+
ProjectService,
|
|
23
|
+
LifecycleService,
|
|
24
|
+
PackageService,
|
|
25
|
+
ProviderService,
|
|
26
|
+
ServiceFactory,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from ._version import __version__
|
|
31
|
+
except ImportError: # pragma: no cover
|
|
32
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
__version__ = version("craft-archives")
|
|
36
|
+
except PackageNotFoundError:
|
|
37
|
+
__version__ = "dev"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"__version__",
|
|
41
|
+
"Application",
|
|
42
|
+
"AppFeatures",
|
|
43
|
+
"AppMetadata",
|
|
44
|
+
"AppService",
|
|
45
|
+
"models",
|
|
46
|
+
"ProjectService",
|
|
47
|
+
"LifecycleService",
|
|
48
|
+
"PackageService",
|
|
49
|
+
"ProviderService",
|
|
50
|
+
"ServiceFactory",
|
|
51
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.1.0'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
# This file is part of craft_application.
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2023 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 along
|
|
15
|
+
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
"""Main application classes for a craft-application."""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import functools
|
|
20
|
+
import importlib
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import pathlib
|
|
24
|
+
import signal
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
from importlib import metadata
|
|
29
|
+
from typing import TYPE_CHECKING, Any, cast, final
|
|
30
|
+
|
|
31
|
+
import craft_cli
|
|
32
|
+
import craft_parts
|
|
33
|
+
import craft_providers
|
|
34
|
+
from xdg.BaseDirectory import save_cache_path # type: ignore[import]
|
|
35
|
+
|
|
36
|
+
from craft_application import commands, models, secrets, util
|
|
37
|
+
from craft_application.models import BuildInfo
|
|
38
|
+
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from craft_application.services import service_factory
|
|
41
|
+
|
|
42
|
+
GLOBAL_VERSION = craft_cli.GlobalArgument(
|
|
43
|
+
"version", "flag", "-V", "--version", "Show the application version and exit"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class AppFeatures:
|
|
49
|
+
"""Specific features that can be enabled/disabled per-application."""
|
|
50
|
+
|
|
51
|
+
build_secrets: bool = False
|
|
52
|
+
"""Support for build-time secrets."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@final
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class AppMetadata:
|
|
58
|
+
"""Metadata about a *craft application."""
|
|
59
|
+
|
|
60
|
+
name: str
|
|
61
|
+
summary: str | None = None
|
|
62
|
+
version: str = field(init=False)
|
|
63
|
+
source_ignore_patterns: list[str] = field(default_factory=lambda: [])
|
|
64
|
+
managed_instance_project_path = pathlib.PurePosixPath("/root/project")
|
|
65
|
+
features: AppFeatures = AppFeatures()
|
|
66
|
+
|
|
67
|
+
ProjectClass: type[models.Project] = models.Project
|
|
68
|
+
|
|
69
|
+
def __post_init__(self) -> None:
|
|
70
|
+
setter = super().__setattr__
|
|
71
|
+
|
|
72
|
+
# Try to determine the app version.
|
|
73
|
+
try:
|
|
74
|
+
# First, via the __version__ attribute on the app's main package.
|
|
75
|
+
version = importlib.import_module(self.name).__version__
|
|
76
|
+
except (AttributeError, ModuleNotFoundError):
|
|
77
|
+
try:
|
|
78
|
+
# If that fails, try via the installed metadata.
|
|
79
|
+
version = metadata.version(self.name)
|
|
80
|
+
except metadata.PackageNotFoundError:
|
|
81
|
+
# If that fails too, default to "dev".
|
|
82
|
+
version = "dev"
|
|
83
|
+
|
|
84
|
+
setter("version", version)
|
|
85
|
+
if self.summary is None:
|
|
86
|
+
md = metadata.metadata(self.name)
|
|
87
|
+
setter("summary", md["summary"])
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Application:
|
|
91
|
+
"""Craft Application Builder.
|
|
92
|
+
|
|
93
|
+
:ivar app: Metadata about this application
|
|
94
|
+
:ivar services: A ServiceFactory for this application
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
app: AppMetadata,
|
|
100
|
+
services: service_factory.ServiceFactory,
|
|
101
|
+
) -> None:
|
|
102
|
+
self.app = app
|
|
103
|
+
self.services = services
|
|
104
|
+
self._command_groups: list[craft_cli.CommandGroup] = []
|
|
105
|
+
self._global_arguments: list[craft_cli.GlobalArgument] = [GLOBAL_VERSION]
|
|
106
|
+
|
|
107
|
+
# When build_secrets are enabled, this contains the secret info to pass to
|
|
108
|
+
# managed instances.
|
|
109
|
+
self._secrets: secrets.BuildSecrets | None = None
|
|
110
|
+
|
|
111
|
+
if self.is_managed():
|
|
112
|
+
self._work_dir = pathlib.Path("/root")
|
|
113
|
+
else:
|
|
114
|
+
self._work_dir = pathlib.Path.cwd()
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def command_groups(self) -> list[craft_cli.CommandGroup]:
|
|
118
|
+
"""Return command groups."""
|
|
119
|
+
lifecycle_commands = commands.get_lifecycle_command_group()
|
|
120
|
+
other_commands = commands.get_other_command_group()
|
|
121
|
+
|
|
122
|
+
merged: dict[str, list[type[craft_cli.BaseCommand]]] = {}
|
|
123
|
+
all_groups = [lifecycle_commands, other_commands, *self._command_groups]
|
|
124
|
+
|
|
125
|
+
# Merge the default command groups with those provided by the application,
|
|
126
|
+
# so that we don't get multiple groups with the same name.
|
|
127
|
+
for group in all_groups:
|
|
128
|
+
merged.setdefault(group.name, []).extend(group.commands)
|
|
129
|
+
|
|
130
|
+
return [
|
|
131
|
+
craft_cli.CommandGroup(name, commands_)
|
|
132
|
+
for name, commands_ in merged.items()
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def log_path(self) -> pathlib.Path | None:
|
|
137
|
+
"""Get the path to this process's log file, if any."""
|
|
138
|
+
if self.is_managed():
|
|
139
|
+
return util.get_managed_logpath(self.app)
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
def add_global_argument(self, argument: craft_cli.GlobalArgument) -> None:
|
|
143
|
+
"""Add a global argument to the Application."""
|
|
144
|
+
self._global_arguments.append(argument)
|
|
145
|
+
|
|
146
|
+
def add_command_group(
|
|
147
|
+
self, name: str, commands: list[type[craft_cli.BaseCommand]]
|
|
148
|
+
) -> None:
|
|
149
|
+
"""Add a CommandGroup to the Application."""
|
|
150
|
+
self._command_groups.append(craft_cli.CommandGroup(name, commands))
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def cache_dir(self) -> str:
|
|
154
|
+
"""Get the directory for caching any data."""
|
|
155
|
+
# Mypy doesn't know what type save_cache_path returns, but pyright figures
|
|
156
|
+
# it out correctly. We can remove this ignore comment once the typeshed has
|
|
157
|
+
# xdg types: https://github.com/python/typeshed/pull/10163
|
|
158
|
+
return save_cache_path(self.app.name) # type: ignore[no-any-return]
|
|
159
|
+
|
|
160
|
+
def _configure_services(
|
|
161
|
+
self,
|
|
162
|
+
platform: str | None, # noqa: ARG002 (Unused method argument)
|
|
163
|
+
build_for: str | None,
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Configure additional keyword arguments for any service classes.
|
|
166
|
+
|
|
167
|
+
Any child classes that override this must either call this directly or must
|
|
168
|
+
provide a valid ``project`` to ``self.services``.
|
|
169
|
+
"""
|
|
170
|
+
self.services.set_kwargs(
|
|
171
|
+
"lifecycle",
|
|
172
|
+
cache_dir=self.cache_dir,
|
|
173
|
+
work_dir=self._work_dir,
|
|
174
|
+
build_for=build_for,
|
|
175
|
+
)
|
|
176
|
+
self.services.set_kwargs(
|
|
177
|
+
"provider",
|
|
178
|
+
work_dir=self._work_dir,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@functools.cached_property
|
|
182
|
+
def project(self) -> models.Project:
|
|
183
|
+
"""Get this application's Project metadata."""
|
|
184
|
+
# Current working directory contains the project file
|
|
185
|
+
project_file = pathlib.Path(f"{self.app.name}.yaml").resolve()
|
|
186
|
+
craft_cli.emit.debug(f"Loading project file '{project_file!s}'")
|
|
187
|
+
|
|
188
|
+
with project_file.open() as file:
|
|
189
|
+
yaml_data = util.safe_yaml_load(file)
|
|
190
|
+
|
|
191
|
+
yaml_data = self._transform_project_yaml(yaml_data)
|
|
192
|
+
|
|
193
|
+
return self.app.ProjectClass.from_yaml_data(yaml_data, project_file)
|
|
194
|
+
|
|
195
|
+
def is_managed(self) -> bool:
|
|
196
|
+
"""Shortcut to tell whether we're running in managed mode."""
|
|
197
|
+
return self.services.ProviderClass.is_managed()
|
|
198
|
+
|
|
199
|
+
def run_managed(self, platform: str | None, build_for: str | None) -> None:
|
|
200
|
+
"""Run the application in a managed instance."""
|
|
201
|
+
extra_args: dict[str, Any] = {}
|
|
202
|
+
|
|
203
|
+
build_plan = self.project.get_build_plan()
|
|
204
|
+
build_plan = _filter_plan(build_plan, platform, build_for)
|
|
205
|
+
|
|
206
|
+
for build_info in build_plan:
|
|
207
|
+
env = {"CRAFT_PLATFORM": build_info.platform}
|
|
208
|
+
|
|
209
|
+
if self.app.features.build_secrets:
|
|
210
|
+
# If using build secrets, put them in the environment of the managed
|
|
211
|
+
# instance.
|
|
212
|
+
secret_values = cast(secrets.BuildSecrets, self._secrets)
|
|
213
|
+
env.update(secret_values.environment)
|
|
214
|
+
|
|
215
|
+
extra_args["env"] = env
|
|
216
|
+
|
|
217
|
+
craft_cli.emit.debug(
|
|
218
|
+
f"Running {self.app.name}:{build_info.platform} in {build_info.build_for} instance..."
|
|
219
|
+
)
|
|
220
|
+
instance_path = pathlib.PosixPath("/root/project")
|
|
221
|
+
|
|
222
|
+
with self.services.provider.instance(
|
|
223
|
+
build_info, work_dir=self._work_dir
|
|
224
|
+
) as instance:
|
|
225
|
+
try:
|
|
226
|
+
# Pyright doesn't fully understand craft_providers's CompletedProcess.
|
|
227
|
+
instance.execute_run( # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
|
|
228
|
+
[self.app.name, *sys.argv[1:]],
|
|
229
|
+
cwd=instance_path,
|
|
230
|
+
check=True,
|
|
231
|
+
**extra_args,
|
|
232
|
+
)
|
|
233
|
+
except subprocess.CalledProcessError as exc:
|
|
234
|
+
raise craft_providers.ProviderError(
|
|
235
|
+
f"Failed to execute {self.app.name} in instance."
|
|
236
|
+
) from exc
|
|
237
|
+
|
|
238
|
+
def configure(self, global_args: dict[str, Any]) -> None:
|
|
239
|
+
"""Configure the application using any global arguments."""
|
|
240
|
+
|
|
241
|
+
def _get_dispatcher(self) -> craft_cli.Dispatcher:
|
|
242
|
+
"""Configure this application. Should be called by the run method.
|
|
243
|
+
|
|
244
|
+
Side-effect: This method may exit the process.
|
|
245
|
+
|
|
246
|
+
:returns: A ready-to-run Dispatcher object
|
|
247
|
+
"""
|
|
248
|
+
# Set the logging level to DEBUG for all craft-libraries. This is OK even if
|
|
249
|
+
# the specific application doesn't use a specific library, the call does not
|
|
250
|
+
# import the package.
|
|
251
|
+
craft_libs = ["craft_archives", "craft_parts", "craft_providers", "craft_store"]
|
|
252
|
+
for craft_lib in craft_libs:
|
|
253
|
+
logger = logging.getLogger(craft_lib)
|
|
254
|
+
logger.setLevel(logging.DEBUG)
|
|
255
|
+
|
|
256
|
+
craft_cli.emit.init(
|
|
257
|
+
mode=craft_cli.EmitterMode.BRIEF,
|
|
258
|
+
appname=self.app.name,
|
|
259
|
+
greeting=f"Starting {self.app.name}",
|
|
260
|
+
log_filepath=self.log_path,
|
|
261
|
+
streaming_brief=True,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
dispatcher = craft_cli.Dispatcher(
|
|
265
|
+
self.app.name,
|
|
266
|
+
self.command_groups,
|
|
267
|
+
summary=str(self.app.summary),
|
|
268
|
+
extra_global_args=self._global_arguments,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
craft_cli.emit.trace("pre-parsing arguments...")
|
|
273
|
+
# Workaround for the fact that craft_cli requires a command.
|
|
274
|
+
# https://github.com/canonical/craft-cli/issues/141
|
|
275
|
+
if "--version" in sys.argv or "-V" in sys.argv:
|
|
276
|
+
try:
|
|
277
|
+
global_args = dispatcher.pre_parse_args(["pull", *sys.argv[1:]])
|
|
278
|
+
except craft_cli.ArgumentParsingError:
|
|
279
|
+
global_args = dispatcher.pre_parse_args(sys.argv[1:])
|
|
280
|
+
else:
|
|
281
|
+
global_args = dispatcher.pre_parse_args(sys.argv[1:])
|
|
282
|
+
|
|
283
|
+
if global_args.get("version"):
|
|
284
|
+
craft_cli.emit.ended_ok()
|
|
285
|
+
print(f"{self.app.name} {self.app.version}")
|
|
286
|
+
sys.exit(0)
|
|
287
|
+
except craft_cli.ProvideHelpException as err:
|
|
288
|
+
print(err, file=sys.stderr) # to stderr, as argparse normally does
|
|
289
|
+
craft_cli.emit.ended_ok()
|
|
290
|
+
sys.exit(0)
|
|
291
|
+
except craft_cli.ArgumentParsingError as err:
|
|
292
|
+
print(err, file=sys.stderr) # to stderr, as argparse normally does
|
|
293
|
+
craft_cli.emit.ended_ok()
|
|
294
|
+
sys.exit(64) # Command line usage error from sysexits.h
|
|
295
|
+
except KeyboardInterrupt as err:
|
|
296
|
+
self._emit_error(craft_cli.CraftError("Interrupted."), cause=err)
|
|
297
|
+
sys.exit(128 + signal.SIGINT)
|
|
298
|
+
except Exception as err: # noqa: BLE001
|
|
299
|
+
self._emit_error(
|
|
300
|
+
craft_cli.CraftError(
|
|
301
|
+
f"Internal error while loading {self.app.name}: {err!r}"
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
if os.getenv("CRAFT_DEBUG") == "1":
|
|
305
|
+
raise
|
|
306
|
+
sys.exit(70) # EX_SOFTWARE from sysexits.h
|
|
307
|
+
|
|
308
|
+
craft_cli.emit.trace("Preparing application...")
|
|
309
|
+
self.configure(global_args)
|
|
310
|
+
|
|
311
|
+
return dispatcher
|
|
312
|
+
|
|
313
|
+
def run(self) -> int: # noqa: PLR0912 (too many branches due to error handling)
|
|
314
|
+
"""Bootstrap and run the application."""
|
|
315
|
+
dispatcher = self._get_dispatcher()
|
|
316
|
+
craft_cli.emit.trace("Preparing application...")
|
|
317
|
+
|
|
318
|
+
return_code = 1 # General error
|
|
319
|
+
try:
|
|
320
|
+
command = cast(
|
|
321
|
+
commands.AppCommand,
|
|
322
|
+
dispatcher.load_command(
|
|
323
|
+
{
|
|
324
|
+
"app": self.app,
|
|
325
|
+
"services": self.services,
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
)
|
|
329
|
+
platform = getattr(dispatcher.parsed_args(), "platform", None)
|
|
330
|
+
build_for = getattr(dispatcher.parsed_args(), "build_for", None)
|
|
331
|
+
self._configure_services(platform, build_for)
|
|
332
|
+
|
|
333
|
+
if not command.run_managed(dispatcher.parsed_args()):
|
|
334
|
+
# command runs in the outer instance
|
|
335
|
+
craft_cli.emit.debug(f"Running {self.app.name} {command.name} on host")
|
|
336
|
+
if command.always_load_project:
|
|
337
|
+
self.services.project = self.project
|
|
338
|
+
return_code = dispatcher.run() or 0
|
|
339
|
+
elif not self.is_managed():
|
|
340
|
+
# command runs in inner instance, but this is the outer instance
|
|
341
|
+
self.services.project = self.project
|
|
342
|
+
self.run_managed(platform, build_for)
|
|
343
|
+
return_code = 0
|
|
344
|
+
else:
|
|
345
|
+
# command runs in inner instance
|
|
346
|
+
self.services.project = self.project
|
|
347
|
+
return_code = dispatcher.run() or 0
|
|
348
|
+
except craft_cli.ArgumentParsingError as err:
|
|
349
|
+
print(err, file=sys.stderr) # to stderr, as argparse normally does
|
|
350
|
+
craft_cli.emit.ended_ok()
|
|
351
|
+
return_code = 64 # Command line usage error from sysexits.h
|
|
352
|
+
except KeyboardInterrupt as err:
|
|
353
|
+
self._emit_error(craft_cli.CraftError("Interrupted."), cause=err)
|
|
354
|
+
return_code = 128 + signal.SIGINT
|
|
355
|
+
except craft_cli.CraftError as err:
|
|
356
|
+
self._emit_error(err)
|
|
357
|
+
except craft_parts.PartsError as err:
|
|
358
|
+
self._emit_error(
|
|
359
|
+
craft_cli.CraftError(
|
|
360
|
+
err.brief, details=err.details, resolution=err.resolution
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
return_code = 1
|
|
364
|
+
except craft_providers.ProviderError as err:
|
|
365
|
+
self._emit_error(
|
|
366
|
+
craft_cli.CraftError(
|
|
367
|
+
err.brief, details=err.details, resolution=err.resolution
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
return_code = 1
|
|
371
|
+
except Exception as err: # noqa: BLE001 pylint: disable=broad-except
|
|
372
|
+
self._emit_error(
|
|
373
|
+
craft_cli.CraftError(f"{self.app.name} internal error: {err!r}")
|
|
374
|
+
)
|
|
375
|
+
if os.getenv("CRAFT_DEBUG") == "1":
|
|
376
|
+
raise
|
|
377
|
+
return_code = 70 # EX_SOFTWARE from sysexits.h
|
|
378
|
+
else:
|
|
379
|
+
craft_cli.emit.ended_ok()
|
|
380
|
+
|
|
381
|
+
return return_code
|
|
382
|
+
|
|
383
|
+
def _emit_error(
|
|
384
|
+
self, error: craft_cli.CraftError, *, cause: BaseException | None = None
|
|
385
|
+
) -> None:
|
|
386
|
+
"""Emit the error in a centralized way so we can alter it consistently."""
|
|
387
|
+
# set the cause, if any
|
|
388
|
+
if cause is not None:
|
|
389
|
+
error.__cause__ = cause
|
|
390
|
+
|
|
391
|
+
# Do not report the internal logpath if running inside an instance
|
|
392
|
+
if self.is_managed():
|
|
393
|
+
error.logpath_report = False
|
|
394
|
+
|
|
395
|
+
craft_cli.emit.error(error)
|
|
396
|
+
|
|
397
|
+
def _transform_project_yaml(self, yaml_data: dict[str, Any]) -> dict[str, Any]:
|
|
398
|
+
"""Update the project's yaml data with runtime properties.
|
|
399
|
+
|
|
400
|
+
Performs task such as environment expansion. Note that this transforms
|
|
401
|
+
``yaml_data`` in-place.
|
|
402
|
+
"""
|
|
403
|
+
# Perform variable expansion.
|
|
404
|
+
self._expand_environment(yaml_data)
|
|
405
|
+
|
|
406
|
+
# Handle build secrets.
|
|
407
|
+
if self.app.features.build_secrets:
|
|
408
|
+
self._render_secrets(yaml_data)
|
|
409
|
+
|
|
410
|
+
# Perform extra, application-specific transformations.
|
|
411
|
+
return self._extra_yaml_transform(yaml_data)
|
|
412
|
+
|
|
413
|
+
def _expand_environment(self, yaml_data: dict[str, Any]) -> None:
|
|
414
|
+
"""Perform expansion of project environment variables."""
|
|
415
|
+
project_vars = self._project_vars(yaml_data)
|
|
416
|
+
|
|
417
|
+
info = craft_parts.ProjectInfo(
|
|
418
|
+
application_name=self.app.name, # not used in environment expansion
|
|
419
|
+
cache_dir=pathlib.Path(), # not used in environment expansion
|
|
420
|
+
project_name=yaml_data.get("name", ""),
|
|
421
|
+
project_dirs=craft_parts.ProjectDirs(work_dir=self._work_dir),
|
|
422
|
+
project_vars=project_vars,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
self._set_global_environment(info)
|
|
426
|
+
|
|
427
|
+
craft_parts.expand_environment(yaml_data, info=info)
|
|
428
|
+
|
|
429
|
+
def _project_vars(self, yaml_data: dict[str, Any]) -> dict[str, str]:
|
|
430
|
+
"""Return a dict with project-specific variables, for a craft_part.ProjectInfo."""
|
|
431
|
+
return {"version": cast(str, yaml_data["version"])}
|
|
432
|
+
|
|
433
|
+
def _set_global_environment(self, info: craft_parts.ProjectInfo) -> None:
|
|
434
|
+
"""Populate the ProjectInfo's global environment."""
|
|
435
|
+
info.global_environment.update(
|
|
436
|
+
{
|
|
437
|
+
"CRAFT_PROJECT_VERSION": info.get_project_var("version", raw_read=True),
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
def _render_secrets(self, yaml_data: dict[str, Any]) -> None:
|
|
442
|
+
"""Render build-secrets, in-place."""
|
|
443
|
+
secret_values = secrets.render_secrets(
|
|
444
|
+
yaml_data, managed_mode=self.is_managed()
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
num_secrets = len(secret_values.secret_strings)
|
|
448
|
+
craft_cli.emit.debug(f"Project has {num_secrets} build-secret(s).")
|
|
449
|
+
|
|
450
|
+
craft_cli.emit.set_secrets(list(secret_values.secret_strings))
|
|
451
|
+
|
|
452
|
+
self._secrets = secret_values
|
|
453
|
+
|
|
454
|
+
def _extra_yaml_transform(self, yaml_data: dict[str, Any]) -> dict[str, Any]:
|
|
455
|
+
"""Perform additional transformations on a project's yaml data.
|
|
456
|
+
|
|
457
|
+
Note: subclasses should return a new dict and keep the parameter unmodified.
|
|
458
|
+
"""
|
|
459
|
+
return yaml_data
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _filter_plan(
|
|
463
|
+
build_plan: list[BuildInfo], platform: str | None, build_for: str | None
|
|
464
|
+
) -> list[BuildInfo]:
|
|
465
|
+
"""Filter out builds not matching build-on and build-for."""
|
|
466
|
+
host_arch = util.get_host_architecture()
|
|
467
|
+
|
|
468
|
+
plan: list[BuildInfo] = []
|
|
469
|
+
for build_info in build_plan:
|
|
470
|
+
platform_matches = not platform or build_info.platform == platform
|
|
471
|
+
build_on_matches = build_info.build_on == host_arch
|
|
472
|
+
build_for_matches = not build_for or build_info.build_for == build_for
|
|
473
|
+
|
|
474
|
+
if platform_matches and build_on_matches and build_for_matches:
|
|
475
|
+
plan.append(build_info)
|
|
476
|
+
|
|
477
|
+
return plan
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# This file is part of craft-application.
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2023 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
|
+
"""Command classes for a craft application."""
|
|
17
|
+
|
|
18
|
+
from craft_application.commands.base import AppCommand
|
|
19
|
+
from craft_application.commands import lifecycle
|
|
20
|
+
from craft_application.commands.lifecycle import get_lifecycle_command_group
|
|
21
|
+
from craft_application.commands.other import get_other_command_group
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"AppCommand",
|
|
25
|
+
"lifecycle",
|
|
26
|
+
"get_lifecycle_command_group",
|
|
27
|
+
"get_other_command_group",
|
|
28
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# This file is part of craft-application.
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2023 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
|
+
"""Base command for craft-application commands."""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
from craft_cli import BaseCommand, emit
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
24
|
+
import argparse
|
|
25
|
+
|
|
26
|
+
from craft_application import application
|
|
27
|
+
from craft_application.services import service_factory
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AppCommand(BaseCommand):
|
|
31
|
+
"""Command for use with craft-application."""
|
|
32
|
+
|
|
33
|
+
always_load_project: bool = False
|
|
34
|
+
"""The project is also loaded in non-managed mode."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, config: dict[str, Any] | None) -> None:
|
|
37
|
+
if config is None:
|
|
38
|
+
# This should only be the case when the command is not going to be run.
|
|
39
|
+
# For example, when requesting help on the command.
|
|
40
|
+
emit.trace("Not completing command configuration")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
super().__init__(config)
|
|
44
|
+
|
|
45
|
+
self._app: application.AppMetadata = config["app"]
|
|
46
|
+
self._services: service_factory.ServiceFactory = config["services"]
|
|
47
|
+
|
|
48
|
+
def run_managed(
|
|
49
|
+
self,
|
|
50
|
+
parsed_args: argparse.Namespace, # noqa: ARG002 (the unused argument is for subclasses)
|
|
51
|
+
) -> bool:
|
|
52
|
+
"""Whether this command should run in managed mode.
|
|
53
|
+
|
|
54
|
+
By default returns `False`. Subclasses can override this method to change this,
|
|
55
|
+
including by inspecting the arguments in `parsed_args`.
|
|
56
|
+
"""
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
def get_managed_cmd(
|
|
60
|
+
self,
|
|
61
|
+
parsed_args: argparse.Namespace, # - Used by subclasses
|
|
62
|
+
) -> list[str]:
|
|
63
|
+
"""Get the command to run in managed mode.
|
|
64
|
+
|
|
65
|
+
:param parsed_args: The parsed arguments used.
|
|
66
|
+
:returns: A list of strings ready to be passed into a craft-providers executor.
|
|
67
|
+
:raises: RuntimeError if this command is not supposed to run managed.
|
|
68
|
+
|
|
69
|
+
Commands that have additional parameters to pass in managed mode should
|
|
70
|
+
override this method to include those parameters.
|
|
71
|
+
"""
|
|
72
|
+
if not self.run_managed(parsed_args):
|
|
73
|
+
raise RuntimeError("Unmanaged commands should not be run in managed mode.")
|
|
74
|
+
cmd_name = self._app.name
|
|
75
|
+
verbosity = emit.get_mode().name.lower()
|
|
76
|
+
return [cmd_name, f"--verbosity={verbosity}", self.name]
|