craft-application 2.3.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.
Files changed (167) hide show
  1. {craft-application-2.3.0 → craft-application-2.4.0}/PKG-INFO +1 -1
  2. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/_version.py +2 -2
  3. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/project.py +2 -1
  4. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/remotebuild.py +38 -9
  5. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application.egg-info/PKG-INFO +1 -1
  6. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application.egg-info/SOURCES.txt +1 -0
  7. {craft-application-2.3.0 → craft-application-2.4.0}/tests/conftest.py +12 -0
  8. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/conftest.py +11 -1
  9. craft-application-2.4.0/tests/integration/services/test_remotebuild.py +47 -0
  10. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_remotebuild.py +59 -1
  11. {craft-application-2.3.0 → craft-application-2.4.0}/.editorconfig +0 -0
  12. {craft-application-2.3.0 → craft-application-2.4.0}/.github/.jira_sync_config.yaml +0 -0
  13. {craft-application-2.3.0 → craft-application-2.4.0}/.github/ISSUE_TEMPLATE/bug.yaml +0 -0
  14. {craft-application-2.3.0 → craft-application-2.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  15. {craft-application-2.3.0 → craft-application-2.4.0}/.github/ISSUE_TEMPLATE/task.yaml +0 -0
  16. {craft-application-2.3.0 → craft-application-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  17. {craft-application-2.3.0 → craft-application-2.4.0}/.github/release-drafter.yaml +0 -0
  18. {craft-application-2.3.0 → craft-application-2.4.0}/.github/release-drafter.yml +0 -0
  19. {craft-application-2.3.0 → craft-application-2.4.0}/.github/renovate.json5 +0 -0
  20. {craft-application-2.3.0 → craft-application-2.4.0}/.github/workflows/cla-check.yaml +0 -0
  21. {craft-application-2.3.0 → craft-application-2.4.0}/.github/workflows/docs.yaml +0 -0
  22. {craft-application-2.3.0 → craft-application-2.4.0}/.github/workflows/release-drafter.yaml +0 -0
  23. {craft-application-2.3.0 → craft-application-2.4.0}/.github/workflows/tests.yaml +0 -0
  24. {craft-application-2.3.0 → craft-application-2.4.0}/.gitignore +0 -0
  25. {craft-application-2.3.0 → craft-application-2.4.0}/.pre-commit-config.yaml +0 -0
  26. {craft-application-2.3.0 → craft-application-2.4.0}/.readthedocs.yaml +0 -0
  27. {craft-application-2.3.0 → craft-application-2.4.0}/.yamllint.yaml +0 -0
  28. {craft-application-2.3.0 → craft-application-2.4.0}/HACKING.rst +0 -0
  29. {craft-application-2.3.0 → craft-application-2.4.0}/LICENSE +0 -0
  30. {craft-application-2.3.0 → craft-application-2.4.0}/README.rst +0 -0
  31. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/__init__.py +0 -0
  32. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/application.py +0 -0
  33. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/commands/__init__.py +0 -0
  34. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/commands/base.py +0 -0
  35. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/commands/lifecycle.py +0 -0
  36. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/commands/other.py +0 -0
  37. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/errors.py +0 -0
  38. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/grammar.py +0 -0
  39. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/__init__.py +0 -0
  40. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/errors.py +0 -0
  41. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/launchpad.py +0 -0
  42. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/__init__.py +0 -0
  43. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/base.py +0 -0
  44. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/build.py +0 -0
  45. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/code.py +0 -0
  46. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/distro.py +0 -0
  47. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/models/recipe.py +0 -0
  48. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/launchpad/util.py +0 -0
  49. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/misc/instance_bashrc +0 -0
  50. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/models/__init__.py +0 -0
  51. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/models/base.py +0 -0
  52. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/models/constraints.py +0 -0
  53. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/models/grammar.py +0 -0
  54. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/models/metadata.py +0 -0
  55. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/models/project.py +0 -0
  56. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/py.typed +0 -0
  57. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/remote/__init__.py +0 -0
  58. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/remote/errors.py +0 -0
  59. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/remote/git.py +0 -0
  60. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/remote/utils.py +0 -0
  61. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/remote/worktree.py +0 -0
  62. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/secrets.py +0 -0
  63. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/__init__.py +0 -0
  64. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/base.py +0 -0
  65. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/lifecycle.py +0 -0
  66. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/package.py +0 -0
  67. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/provider.py +0 -0
  68. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/request.py +0 -0
  69. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/services/service_factory.py +0 -0
  70. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/__init__.py +0 -0
  71. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/callbacks.py +0 -0
  72. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/error_formatting.py +0 -0
  73. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/logging.py +0 -0
  74. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/paths.py +0 -0
  75. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/platforms.py +0 -0
  76. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/repositories.py +0 -0
  77. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/snap_config.py +0 -0
  78. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/string.py +0 -0
  79. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application/util/yaml.py +0 -0
  80. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application.egg-info/dependency_links.txt +0 -0
  81. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application.egg-info/requires.txt +0 -0
  82. {craft-application-2.3.0 → craft-application-2.4.0}/craft_application.egg-info/top_level.txt +0 -0
  83. {craft-application-2.3.0 → craft-application-2.4.0}/docs/_static/css/custom.css +0 -0
  84. {craft-application-2.3.0 → craft-application-2.4.0}/docs/conf.py +0 -0
  85. {craft-application-2.3.0 → craft-application-2.4.0}/docs/explanation/index.rst +0 -0
  86. {craft-application-2.3.0 → craft-application-2.4.0}/docs/howto/index.rst +0 -0
  87. {craft-application-2.3.0 → craft-application-2.4.0}/docs/index.rst +0 -0
  88. {craft-application-2.3.0 → craft-application-2.4.0}/docs/reference/index.rst +0 -0
  89. {craft-application-2.3.0 → craft-application-2.4.0}/docs/tutorials/index.rst +0 -0
  90. {craft-application-2.3.0 → craft-application-2.4.0}/pyproject.toml +0 -0
  91. {craft-application-2.3.0 → craft-application-2.4.0}/setup.cfg +0 -0
  92. {craft-application-2.3.0 → craft-application-2.4.0}/tests/__init__.py +0 -0
  93. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/__init__.py +0 -0
  94. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/build-secrets/secret-source-folder/source-file.txt +0 -0
  95. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/build-secrets/testcraft.yaml +0 -0
  96. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/invalid_projects/build-error/testcraft.yaml +0 -0
  97. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/adoption/stderr +0 -0
  98. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/adoption/testcraft.yaml +0 -0
  99. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/basic/stderr +0 -0
  100. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/basic/testcraft.yaml +0 -0
  101. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/environment/stderr +0 -0
  102. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/environment/testcraft.yaml +0 -0
  103. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-amd64/hello.txt +0 -0
  104. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-arm64/hello.txt +0 -0
  105. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/stderr +0 -0
  106. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/data/valid_projects/grammar/testcraft.yaml +0 -0
  107. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/launchpad/__init__.py +0 -0
  108. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/launchpad/conftest.py +0 -0
  109. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/launchpad/test_anonymous_access.py +0 -0
  110. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/services/__init__.py +0 -0
  111. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/services/test_lifecycle.py +0 -0
  112. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/services/test_provider.py +0 -0
  113. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/services/test_request.py +0 -0
  114. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/services/test_service_factory.py +0 -0
  115. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/test_application.py +0 -0
  116. {craft-application-2.3.0 → craft-application-2.4.0}/tests/integration/test_version.py +0 -0
  117. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/__init__.py +0 -0
  118. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/commands/__init__.py +0 -0
  119. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/commands/test_base.py +0 -0
  120. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/commands/test_lifecycle.py +0 -0
  121. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/commands/test_other.py +0 -0
  122. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/conftest.py +0 -0
  123. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/__init__.py +0 -0
  124. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/conftest.py +0 -0
  125. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/models/__init__.py +0 -0
  126. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/models/test_base.py +0 -0
  127. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/models/test_code.py +0 -0
  128. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/test_launchpad.py +0 -0
  129. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/launchpad/test_util.py +0 -0
  130. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/models/__init__.py +0 -0
  131. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/models/project_models/basic_project.yaml +0 -0
  132. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/models/project_models/full_project.yaml +0 -0
  133. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/models/project_models/invalid_project.yaml +0 -0
  134. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/models/test_constraints.py +0 -0
  135. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/models/test_project.py +0 -0
  136. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/remote/__init__.py +0 -0
  137. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/remote/conftest.py +0 -0
  138. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/remote/test_errors.py +0 -0
  139. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/remote/test_git.py +0 -0
  140. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/remote/test_utils.py +0 -0
  141. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/remote/test_worktree.py +0 -0
  142. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/__init__.py +0 -0
  143. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/conftest.py +0 -0
  144. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_lifecycle.py +0 -0
  145. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_package.py +0 -0
  146. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_provider.py +0 -0
  147. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_repositories.py +0 -0
  148. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_request.py +0 -0
  149. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/services/test_service_factory.py +0 -0
  150. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/test_application.py +0 -0
  151. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/test_errors.py +0 -0
  152. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/test_grammar.py +0 -0
  153. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/test_nothing.py +0 -0
  154. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/test_secrets.py +0 -0
  155. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/__init__.py +0 -0
  156. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/_README +0 -0
  157. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +0 -0
  158. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +0 -0
  159. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/invalid_yaml/unhashable.yaml-invalid +0 -0
  160. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/test_error_formatting.py +0 -0
  161. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/test_logging.py +0 -0
  162. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/test_paths.py +0 -0
  163. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/test_snap_config.py +0 -0
  164. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/test_string.py +0 -0
  165. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/test_yaml.py +0 -0
  166. {craft-application-2.3.0 → craft-application-2.4.0}/tests/unit/util/valid_yaml/empty.yaml +0 -0
  167. {craft-application-2.3.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.0
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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.3.0'
16
- __version_tuple__ = version_tuple = (2, 3, 0)
15
+ __version__ = version = '2.4.0'
16
+ __version_tuple__ = version_tuple = (2, 4, 0)
@@ -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]
@@ -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 launchpad.models.Project.get(
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 launchpad.models.Project.new(
216
- self.lp,
217
- f"*craft remote builds for {self.lp.username}",
218
- f"{self.lp.username}-craft-remote-build",
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.0
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
@@ -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
@@ -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.services import provider
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()
@@ -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
  ):