craft-application 4.2.2__tar.gz → 4.2.4__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 (193) hide show
  1. {craft_application-4.2.2/craft_application.egg-info → craft_application-4.2.4}/PKG-INFO +1 -1
  2. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/_version.py +2 -2
  3. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/application.py +4 -1
  4. craft_application-4.2.4/craft_application/remote/errors.py +54 -0
  5. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/remote/git.py +4 -2
  6. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/provider.py +10 -6
  7. {craft_application-4.2.2 → craft_application-4.2.4/craft_application.egg-info}/PKG-INFO +1 -1
  8. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application.egg-info/SOURCES.txt +1 -0
  9. {craft_application-4.2.2 → craft_application-4.2.4}/docs/reference/changelog.rst +23 -0
  10. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/git/test_git.py +1 -1
  11. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/remote/test_errors.py +11 -15
  12. craft_application-4.2.4/tests/unit/remote/test_git.py +57 -0
  13. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/remote/test_utils.py +2 -1
  14. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_provider.py +48 -16
  15. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/test_application.py +32 -0
  16. craft_application-4.2.2/craft_application/remote/errors.py +0 -76
  17. {craft_application-4.2.2 → craft_application-4.2.4}/.editorconfig +0 -0
  18. {craft_application-4.2.2 → craft_application-4.2.4}/.github/.jira_sync_config.yaml +0 -0
  19. {craft_application-4.2.2 → craft_application-4.2.4}/.github/ISSUE_TEMPLATE/bug.yaml +0 -0
  20. {craft_application-4.2.2 → craft_application-4.2.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  21. {craft_application-4.2.2 → craft_application-4.2.4}/.github/ISSUE_TEMPLATE/task.yaml +0 -0
  22. {craft_application-4.2.2 → craft_application-4.2.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  23. {craft_application-4.2.2 → craft_application-4.2.4}/.github/release-drafter.yaml +0 -0
  24. {craft_application-4.2.2 → craft_application-4.2.4}/.github/release-drafter.yml +0 -0
  25. {craft_application-4.2.2 → craft_application-4.2.4}/.github/renovate.json5 +0 -0
  26. {craft_application-4.2.2 → craft_application-4.2.4}/.github/workflows/check-renovate.yaml +0 -0
  27. {craft_application-4.2.2 → craft_application-4.2.4}/.github/workflows/cla-check.yaml +0 -0
  28. {craft_application-4.2.2 → craft_application-4.2.4}/.github/workflows/docs.yaml +0 -0
  29. {craft_application-4.2.2 → craft_application-4.2.4}/.github/workflows/release-drafter.yaml +0 -0
  30. {craft_application-4.2.2 → craft_application-4.2.4}/.github/workflows/release-publish.yaml +0 -0
  31. {craft_application-4.2.2 → craft_application-4.2.4}/.github/workflows/tests.yaml +0 -0
  32. {craft_application-4.2.2 → craft_application-4.2.4}/.gitignore +0 -0
  33. {craft_application-4.2.2 → craft_application-4.2.4}/.pre-commit-config.yaml +0 -0
  34. {craft_application-4.2.2 → craft_application-4.2.4}/.readthedocs.yaml +0 -0
  35. {craft_application-4.2.2 → craft_application-4.2.4}/.yamllint.yaml +0 -0
  36. {craft_application-4.2.2 → craft_application-4.2.4}/HACKING.rst +0 -0
  37. {craft_application-4.2.2 → craft_application-4.2.4}/LICENSE +0 -0
  38. {craft_application-4.2.2 → craft_application-4.2.4}/README.rst +0 -0
  39. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/__init__.py +0 -0
  40. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/_config.py +0 -0
  41. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/commands/__init__.py +0 -0
  42. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/commands/base.py +0 -0
  43. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/commands/lifecycle.py +0 -0
  44. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/commands/other.py +0 -0
  45. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/errors.py +0 -0
  46. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/git/__init__.py +0 -0
  47. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/git/_errors.py +0 -0
  48. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/git/_git_repo.py +0 -0
  49. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/git/_models.py +0 -0
  50. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/grammar.py +0 -0
  51. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/__init__.py +0 -0
  52. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/errors.py +0 -0
  53. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/launchpad.py +0 -0
  54. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/__init__.py +0 -0
  55. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/base.py +0 -0
  56. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/build.py +0 -0
  57. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/code.py +0 -0
  58. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/distro.py +0 -0
  59. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/project.py +0 -0
  60. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/models/recipe.py +0 -0
  61. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/launchpad/util.py +0 -0
  62. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/misc/instance_bashrc +0 -0
  63. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/models/__init__.py +0 -0
  64. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/models/base.py +0 -0
  65. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/models/constraints.py +0 -0
  66. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/models/grammar.py +0 -0
  67. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/models/metadata.py +0 -0
  68. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/models/project.py +0 -0
  69. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/py.typed +0 -0
  70. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/remote/__init__.py +0 -0
  71. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/remote/utils.py +0 -0
  72. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/remote/worktree.py +0 -0
  73. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/secrets.py +0 -0
  74. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/__init__.py +0 -0
  75. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/base.py +0 -0
  76. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/config.py +0 -0
  77. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/lifecycle.py +0 -0
  78. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/package.py +0 -0
  79. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/remotebuild.py +0 -0
  80. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/request.py +0 -0
  81. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/services/service_factory.py +0 -0
  82. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/__init__.py +0 -0
  83. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/callbacks.py +0 -0
  84. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/docs.py +0 -0
  85. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/error_formatting.py +0 -0
  86. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/logging.py +0 -0
  87. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/paths.py +0 -0
  88. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/platforms.py +0 -0
  89. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/repositories.py +0 -0
  90. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/retry.py +0 -0
  91. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/snap_config.py +0 -0
  92. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/string.py +0 -0
  93. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application/util/yaml.py +0 -0
  94. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application.egg-info/dependency_links.txt +0 -0
  95. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application.egg-info/requires.txt +0 -0
  96. {craft_application-4.2.2 → craft_application-4.2.4}/craft_application.egg-info/top_level.txt +0 -0
  97. {craft_application-4.2.2 → craft_application-4.2.4}/docs/conf.py +0 -0
  98. {craft_application-4.2.2 → craft_application-4.2.4}/docs/explanation/build-plans.rst +0 -0
  99. {craft_application-4.2.2 → craft_application-4.2.4}/docs/explanation/index.rst +0 -0
  100. {craft_application-4.2.2 → craft_application-4.2.4}/docs/howto/index.rst +0 -0
  101. {craft_application-4.2.2 → craft_application-4.2.4}/docs/howto/partitions.rst +0 -0
  102. {craft_application-4.2.2 → craft_application-4.2.4}/docs/index.rst +0 -0
  103. {craft_application-4.2.2 → craft_application-4.2.4}/docs/reference/environment-variables.rst +0 -0
  104. {craft_application-4.2.2 → craft_application-4.2.4}/docs/reference/index.rst +0 -0
  105. {craft_application-4.2.2 → craft_application-4.2.4}/docs/reference/platforms.rst +0 -0
  106. {craft_application-4.2.2 → craft_application-4.2.4}/docs/tutorials/index.rst +0 -0
  107. {craft_application-4.2.2 → craft_application-4.2.4}/pyproject.toml +0 -0
  108. {craft_application-4.2.2 → craft_application-4.2.4}/setup.cfg +0 -0
  109. {craft_application-4.2.2 → craft_application-4.2.4}/tests/__init__.py +0 -0
  110. {craft_application-4.2.2 → craft_application-4.2.4}/tests/conftest.py +0 -0
  111. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/__init__.py +0 -0
  112. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/conftest.py +0 -0
  113. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/build-secrets/secret-source-folder/source-file.txt +0 -0
  114. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/build-secrets/testcraft.yaml +0 -0
  115. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/invalid_projects/build-error/testcraft.yaml +0 -0
  116. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/adoption/stderr +0 -0
  117. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/adoption/testcraft.yaml +0 -0
  118. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/basic/stderr +0 -0
  119. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/basic/testcraft.yaml +0 -0
  120. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/build-for-all/stderr +0 -0
  121. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/build-for-all/testcraft.yaml +0 -0
  122. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/environment/stderr +0 -0
  123. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/environment/testcraft.yaml +0 -0
  124. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-amd64/hello.txt +0 -0
  125. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-arm64/hello.txt +0 -0
  126. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/grammar/stderr +0 -0
  127. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/data/valid_projects/grammar/testcraft.yaml +0 -0
  128. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/launchpad/__init__.py +0 -0
  129. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/launchpad/conftest.py +0 -0
  130. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/launchpad/test_anonymous_access.py +0 -0
  131. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/services/__init__.py +0 -0
  132. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/services/test_lifecycle.py +0 -0
  133. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/services/test_provider.py +0 -0
  134. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/services/test_remotebuild.py +0 -0
  135. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/services/test_request.py +0 -0
  136. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/services/test_service_factory.py +0 -0
  137. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/test_application.py +0 -0
  138. {craft_application-4.2.2 → craft_application-4.2.4}/tests/integration/test_version.py +0 -0
  139. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/__init__.py +0 -0
  140. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/commands/__init__.py +0 -0
  141. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/commands/test_base.py +0 -0
  142. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/commands/test_lifecycle.py +0 -0
  143. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/commands/test_other.py +0 -0
  144. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/conftest.py +0 -0
  145. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/git/__init__.py +0 -0
  146. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/git/test_errors.py +0 -0
  147. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/__init__.py +0 -0
  148. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/conftest.py +0 -0
  149. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/models/__init__.py +0 -0
  150. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/models/test_base.py +0 -0
  151. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/models/test_code.py +0 -0
  152. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/test_launchpad.py +0 -0
  153. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/launchpad/test_util.py +0 -0
  154. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/__init__.py +0 -0
  155. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/project_models/basic_project.yaml +0 -0
  156. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/project_models/full_project.yaml +0 -0
  157. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/project_models/invalid_project.yaml +0 -0
  158. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/test_base.py +0 -0
  159. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/test_constraints.py +0 -0
  160. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/test_grammar.py +0 -0
  161. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/models/test_project.py +0 -0
  162. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/remote/__init__.py +0 -0
  163. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/remote/conftest.py +0 -0
  164. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/remote/test_worktree.py +0 -0
  165. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/__init__.py +0 -0
  166. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/conftest.py +0 -0
  167. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_config.py +0 -0
  168. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_lifecycle.py +0 -0
  169. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_package.py +0 -0
  170. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_remotebuild.py +0 -0
  171. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_repositories.py +0 -0
  172. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_request.py +0 -0
  173. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/services/test_service_factory.py +0 -0
  174. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/test_errors.py +0 -0
  175. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/test_grammar.py +0 -0
  176. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/test_nothing.py +0 -0
  177. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/test_secrets.py +0 -0
  178. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/__init__.py +0 -0
  179. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/invalid_yaml/_README +0 -0
  180. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +0 -0
  181. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +0 -0
  182. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/invalid_yaml/unhashable.yaml-invalid +0 -0
  183. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_docs.py +0 -0
  184. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_error_formatting.py +0 -0
  185. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_logging.py +0 -0
  186. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_paths.py +0 -0
  187. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_platforms.py +0 -0
  188. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_retry.py +0 -0
  189. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_snap_config.py +0 -0
  190. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_string.py +0 -0
  191. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/test_yaml.py +0 -0
  192. {craft_application-4.2.2 → craft_application-4.2.4}/tests/unit/util/valid_yaml/empty.yaml +0 -0
  193. {craft_application-4.2.2 → craft_application-4.2.4}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: craft-application
3
- Version: 4.2.2
3
+ Version: 4.2.4
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 = '4.2.2'
16
- __version_tuple__ = version_tuple = (4, 2, 2)
15
+ __version__ = version = '4.2.4'
16
+ __version_tuple__ = version_tuple = (4, 2, 4)
@@ -505,7 +505,10 @@ class Application:
505
505
  :param item: the name of the namespace or config item.
506
506
  :returns: the requested value.
507
507
  """
508
- return getattr(parsed_args, item, self.services.config.get(item))
508
+ arg_value = getattr(parsed_args, item, None)
509
+ if arg_value is not None:
510
+ return arg_value
511
+ return self.services.config.get(item)
509
512
 
510
513
  def _run_inner(self) -> int:
511
514
  """Actual run implementation."""
@@ -0,0 +1,54 @@
1
+ # Copyright 2023-2024 Canonical Ltd.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License version 3 as
5
+ # published by the Free Software Foundation.
6
+ #
7
+ # This program is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
+ # GNU Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
+
15
+ """Remote build errors."""
16
+
17
+ import craft_cli.errors
18
+
19
+ from craft_application.util import humanize_list
20
+
21
+
22
+ class RemoteBuildError(craft_cli.errors.CraftError):
23
+ """Error for remote builds."""
24
+
25
+
26
+ class RemoteBuildGitError(RemoteBuildError):
27
+ """Git repository cannot be prepared correctly.
28
+
29
+ :param message: Git error message.
30
+ """
31
+
32
+ def __init__(self, message: str) -> None:
33
+ message = f"Git operation failed with: {message}"
34
+ super().__init__(message=message)
35
+
36
+
37
+ class UnsupportedArchitectureError(RemoteBuildError):
38
+ """Unsupported architecture error.
39
+
40
+ :param architectures: List of unsupported architectures.
41
+ """
42
+
43
+ def __init__(self, architectures: list[str]) -> None:
44
+ message = (
45
+ "The following architectures are not supported by the remote builder: "
46
+ f"{humanize_list(architectures, 'and')}."
47
+ )
48
+ resolution = "Remove them from the architecture list and try again."
49
+
50
+ super().__init__(message=message, resolution=resolution)
51
+
52
+
53
+ class RemoteBuildInvalidGitRepoError(RemoteBuildError):
54
+ """The Git repository is invalid for remote build."""
@@ -32,10 +32,12 @@ def check_git_repo_for_remote_build(path: Path) -> None:
32
32
 
33
33
  if git_type == GitType.INVALID:
34
34
  raise RemoteBuildInvalidGitRepoError(
35
- f"Could not find a git repository in {str(path)!r}"
35
+ message=f"Could not find a git repository in {str(path)!r}",
36
+ resolution="Initialize a git repository in the project directory",
36
37
  )
37
38
 
38
39
  if git_type == GitType.SHALLOW:
39
40
  raise RemoteBuildInvalidGitRepoError(
40
- "Remote build for shallow cloned git repos are no longer supported"
41
+ message="Remote builds for shallow cloned git repos are not supported",
42
+ resolution="Make a non-shallow clone of the repository",
41
43
  )
@@ -98,12 +98,16 @@ class ProviderService(base.ProjectService):
98
98
  self.environment[f"{scheme.upper()}_PROXY"] = value
99
99
 
100
100
  if self._install_snap:
101
- channel = (
102
- None
103
- if util.is_running_from_snap(self._app.name)
104
- else os.getenv("CRAFT_SNAP_CHANNEL", "latest/stable")
105
- )
106
- self.snaps.append(Snap(name=self._app.name, channel=channel, classic=True))
101
+ if util.is_running_from_snap(self._app.name):
102
+ # use the aliased name of the snap when injecting
103
+ name = os.getenv("SNAP_INSTANCE_NAME", self._app.name)
104
+ channel = None
105
+ else:
106
+ # use the snap name when installing from the store
107
+ name = self._app.name
108
+ channel = os.getenv("CRAFT_SNAP_CHANNEL", "latest/stable")
109
+
110
+ self.snaps.append(Snap(name=name, channel=channel, classic=True))
107
111
 
108
112
  @contextlib.contextmanager
109
113
  def instance(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: craft-application
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: A framework for *craft applications.
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Environment :: Console
@@ -160,6 +160,7 @@ tests/unit/models/project_models/invalid_project.yaml
160
160
  tests/unit/remote/__init__.py
161
161
  tests/unit/remote/conftest.py
162
162
  tests/unit/remote/test_errors.py
163
+ tests/unit/remote/test_git.py
163
164
  tests/unit/remote/test_utils.py
164
165
  tests/unit/remote/test_worktree.py
165
166
  tests/unit/services/__init__.py
@@ -2,6 +2,27 @@
2
2
  Changelog
3
3
  *********
4
4
 
5
+ 4.2.4 (2024-Sep-19)
6
+ -------------------
7
+
8
+ Remote build
9
+ ============
10
+
11
+ - Remote build errors are now a subclass of ``CraftError``.
12
+
13
+ For a complete list of commits, check out the `4.2.4`_ release on GitHub.
14
+
15
+ 4.2.3 (2024-Sep-18)
16
+ -------------------
17
+
18
+ Application
19
+ ===========
20
+
21
+ - ``get_arg_or_config`` now correctly checks the config service if the passed
22
+ namespace has ``None`` as the value of the requested item.
23
+
24
+ For a complete list of commits, check out the `4.2.3`_ release on GitHub.
25
+
5
26
  4.2.2 (2024-Sep-13)
6
27
  -------------------
7
28
 
@@ -266,3 +287,5 @@ For a complete list of commits, check out the `2.7.0`_ release on GitHub.
266
287
  .. _4.2.0: https://github.com/canonical/craft-application/releases/tag/4.2.0
267
288
  .. _4.2.1: https://github.com/canonical/craft-application/releases/tag/4.2.1
268
289
  .. _4.2.2: https://github.com/canonical/craft-application/releases/tag/4.2.2
290
+ .. _4.2.3: https://github.com/canonical/craft-application/releases/tag/4.2.3
291
+ .. _4.2.4: https://github.com/canonical/craft-application/releases/tag/4.2.4
@@ -894,6 +894,6 @@ def test_check_git_repo_for_remote_build_shallow(empty_working_directory):
894
894
 
895
895
  with pytest.raises(
896
896
  RemoteBuildInvalidGitRepoError,
897
- match="Remote build for shallow cloned git repos are no longer supported",
897
+ match="Remote builds for shallow cloned git repos are not supported",
898
898
  ):
899
899
  check_git_repo_for_remote_build(git_shallow_path)
@@ -15,25 +15,21 @@
15
15
  from craft_application.remote import errors
16
16
 
17
17
 
18
+ def test_git_error():
19
+ """Test RemoteBuildGitError."""
20
+ error = errors.RemoteBuildGitError(message="failed to push some refs to 'unknown'")
21
+
22
+ assert (
23
+ str(error) == "Git operation failed with: failed to push some refs to 'unknown'"
24
+ )
25
+
26
+
18
27
  def test_unsupported_architecture_error():
19
28
  """Test UnsupportedArchitectureError."""
20
29
  error = errors.UnsupportedArchitectureError(architectures=["amd64", "arm64"])
21
30
 
22
31
  assert str(error) == (
23
- "Architecture not supported by the remote builder.\nThe following "
24
- "architectures are not supported by the remote builder: ['amd64', 'arm64'].\n"
25
- "Please remove them from the architecture list and try again."
26
- )
27
- assert repr(error) == (
28
- "UnsupportedArchitectureError(brief='Architecture not supported by the remote "
29
- "builder.', details=\"The following architectures are not supported by the "
30
- "remote builder: ['amd64', 'arm64'].\\nPlease remove them from the "
31
- 'architecture list and try again.")'
32
- )
33
-
34
- assert error.brief == "Architecture not supported by the remote builder."
35
- assert error.details == (
36
32
  "The following architectures are not supported by the remote builder: "
37
- "['amd64', 'arm64'].\nPlease remove them from the architecture list and "
38
- "try again."
33
+ "'amd64' and 'arm64'."
39
34
  )
35
+ assert error.resolution == "Remove them from the architecture list and try again."
@@ -0,0 +1,57 @@
1
+ # Copyright 2024 Canonical Ltd.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License version 3 as
5
+ # published by the Free Software Foundation.
6
+ #
7
+ # This program is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
+ # GNU Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
+
15
+ """Remote-build git tests."""
16
+
17
+ import pytest
18
+ from craft_application.git import GitType
19
+ from craft_application.remote import errors, git
20
+
21
+
22
+ @pytest.fixture
23
+ def mock_get_git_repo_type(mocker):
24
+ return mocker.patch("craft_application.remote.git.get_git_repo_type")
25
+
26
+
27
+ def test_git_normal(tmp_path, mock_get_git_repo_type):
28
+ """No-op for a normal git repo."""
29
+ mock_get_git_repo_type.return_value = GitType.NORMAL
30
+
31
+ assert git.check_git_repo_for_remote_build(tmp_path) is None
32
+
33
+
34
+ def test_git_invalid_error(tmp_path, mock_get_git_repo_type):
35
+ """Raise an error for invalid git repos."""
36
+ mock_get_git_repo_type.return_value = GitType.INVALID
37
+
38
+ with pytest.raises(errors.RemoteBuildInvalidGitRepoError) as err:
39
+ git.check_git_repo_for_remote_build(tmp_path)
40
+
41
+ assert str(err.value) == f"Could not find a git repository in {str(tmp_path)!r}"
42
+ assert (
43
+ err.value.resolution == "Initialize a git repository in the project directory"
44
+ )
45
+
46
+
47
+ def test_git_shallow_clone_error(tmp_path, mock_get_git_repo_type):
48
+ """Raise an error for shallowly cloned repos."""
49
+ mock_get_git_repo_type.return_value = GitType.SHALLOW
50
+
51
+ with pytest.raises(errors.RemoteBuildInvalidGitRepoError) as err:
52
+ git.check_git_repo_for_remote_build(tmp_path)
53
+
54
+ assert (
55
+ str(err.value) == "Remote builds for shallow cloned git repos are not supported"
56
+ )
57
+ assert err.value.resolution == "Make a non-shallow clone of the repository"
@@ -25,6 +25,7 @@ from craft_application.remote import (
25
25
  validate_architectures,
26
26
  )
27
27
  from craft_application.remote.utils import _SUPPORTED_ARCHS
28
+ from craft_application.util import humanize_list
28
29
 
29
30
  ###############################
30
31
  # validate architecture tests #
@@ -57,7 +58,7 @@ def test_validate_architectures_error(archs, expected_archs):
57
58
 
58
59
  assert (
59
60
  "The following architectures are not supported by the remote builder: "
60
- f"{expected_archs}"
61
+ f"{humanize_list(expected_archs, 'and')}"
61
62
  ) in str(raised.value)
62
63
 
63
64
 
@@ -84,41 +84,70 @@ def test_setup_proxy_environment(
84
84
 
85
85
 
86
86
  @pytest.mark.parametrize(
87
- ("install_snap", "environment", "snaps"),
87
+ ("environment", "snaps"),
88
88
  [
89
- (True, {}, [Snap(name="testcraft", channel="latest/stable", classic=True)]),
90
- (
91
- True,
89
+ pytest.param(
90
+ {},
91
+ [Snap(name="testcraft", channel="latest/stable", classic=True)],
92
+ id="install-from-store-default-channel",
93
+ ),
94
+ pytest.param(
92
95
  {"CRAFT_SNAP_CHANNEL": "something"},
93
96
  [Snap(name="testcraft", channel="something", classic=True)],
97
+ id="install-from-store-with-channel",
94
98
  ),
95
- (
96
- True,
97
- {"SNAP_NAME": "testcraft", "SNAP": "/snap/testcraft/x1"},
98
- [Snap(name="testcraft", channel=None, classic=True)],
99
+ pytest.param(
100
+ {
101
+ "SNAP_NAME": "testcraft",
102
+ "SNAP_INSTANCE_NAME": "testcraft_1",
103
+ "SNAP": "/snap/testcraft/x1",
104
+ },
105
+ [Snap(name="testcraft_1", channel=None, classic=True)],
106
+ id="inject-from-host",
99
107
  ),
100
- (
101
- True,
108
+ pytest.param(
102
109
  {
103
110
  "SNAP_NAME": "testcraft",
111
+ "SNAP_INSTANCE_NAME": "testcraft_1",
104
112
  "SNAP": "/snap/testcraft/x1",
105
113
  "CRAFT_SNAP_CHANNEL": "something",
106
114
  },
115
+ [Snap(name="testcraft_1", channel=None, classic=True)],
116
+ id="inject-from-host-ignore-channel",
117
+ ),
118
+ pytest.param(
119
+ # SNAP_INSTANCE_NAME may not exist if snapd < 2.43 or feature is disabled
120
+ {
121
+ "SNAP_NAME": "testcraft",
122
+ "SNAP": "/snap/testcraft/x1",
123
+ },
107
124
  [Snap(name="testcraft", channel=None, classic=True)],
125
+ id="missing-snap-instance-name",
108
126
  ),
109
- (False, {}, []),
110
- (False, {"CRAFT_SNAP_CHANNEL": "something"}, []),
111
- (
112
- False,
127
+ pytest.param(
128
+ # SNAP_INSTANCE_NAME may not exist if snapd < 2.43 or feature is disabled
113
129
  {
114
130
  "SNAP_NAME": "testcraft",
115
131
  "SNAP": "/snap/testcraft/x1",
132
+ # CRAFT_SNAP_CHANNEL should be ignored
116
133
  "CRAFT_SNAP_CHANNEL": "something",
117
134
  },
118
- [],
135
+ [Snap(name="testcraft", channel=None, classic=True)],
136
+ id="missing-snap-instance-name-ignore-snap-channel",
137
+ ),
138
+ pytest.param(
139
+ # this can happen when running testcraft from a venv in a snapped terminal
140
+ {
141
+ "SNAP_NAME": "kitty",
142
+ "SNAP_INSTANCE_NAME": "kitty",
143
+ "SNAP": "/snap/kitty/x1",
144
+ },
145
+ [Snap(name="testcraft", channel="latest/stable", classic=True)],
146
+ id="running-inside-another-snap",
119
147
  ),
120
148
  ],
121
149
  )
150
+ @pytest.mark.parametrize("install_snap", [True, False])
122
151
  def test_install_snap(
123
152
  monkeypatch,
124
153
  app_metadata,
@@ -143,7 +172,10 @@ def test_install_snap(
143
172
  )
144
173
  service.setup()
145
174
 
146
- assert service.snaps == snaps
175
+ if install_snap:
176
+ assert service.snaps == snaps
177
+ else:
178
+ assert service.snaps == []
147
179
 
148
180
 
149
181
  @pytest.mark.parametrize(
@@ -614,6 +614,38 @@ def test_run_managed_empty_plan(app, fake_project):
614
614
  app.run_managed(None, None)
615
615
 
616
616
 
617
+ @pytest.mark.parametrize(
618
+ ("parsed_args", "environ", "item", "expected"),
619
+ [
620
+ (argparse.Namespace(), {}, "build_for", None),
621
+ (argparse.Namespace(build_for=None), {}, "build_for", None),
622
+ (
623
+ argparse.Namespace(build_for=None),
624
+ {"CRAFT_BUILD_FOR": "arm64"},
625
+ "build_for",
626
+ "arm64",
627
+ ),
628
+ (
629
+ argparse.Namespace(build_for=None),
630
+ {"TESTCRAFT_BUILD_FOR": "arm64"},
631
+ "build_for",
632
+ "arm64",
633
+ ),
634
+ (
635
+ argparse.Namespace(build_for="riscv64"),
636
+ {"TESTCRAFT_BUILD_FOR": "arm64"},
637
+ "build_for",
638
+ "riscv64",
639
+ ),
640
+ ],
641
+ )
642
+ def test_get_arg_or_config(monkeypatch, app, parsed_args, environ, item, expected):
643
+ for var, content in environ.items():
644
+ monkeypatch.setenv(var, content)
645
+
646
+ assert app.get_arg_or_config(parsed_args, item) == expected
647
+
648
+
617
649
  @pytest.mark.parametrize(
618
650
  ("managed", "error", "exit_code", "message"),
619
651
  [
@@ -1,76 +0,0 @@
1
- # Copyright 2023-2024 Canonical Ltd.
2
- #
3
- # This program is free software: you can redistribute it and/or modify
4
- # it under the terms of the GNU Lesser General Public License version 3 as
5
- # published by the Free Software Foundation.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU Lesser General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU Lesser General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- """Remote build errors."""
16
-
17
- from dataclasses import dataclass
18
-
19
-
20
- @dataclass(repr=True)
21
- class RemoteBuildError(Exception):
22
- """Unexpected remote build error.
23
-
24
- :param brief: Brief description of error.
25
- :param details: Detailed information.
26
- """
27
-
28
- brief: str
29
- details: str | None = None
30
-
31
- def __str__(self) -> str:
32
- """Return the string representation of the error."""
33
- components = [self.brief]
34
-
35
- if self.details:
36
- components.append(self.details)
37
-
38
- return "\n".join(components)
39
-
40
-
41
- class RemoteBuildGitError(RemoteBuildError):
42
- """Git repository cannot be prepared correctly."""
43
-
44
- def __init__(self, message: str) -> None:
45
- self.message = message
46
- brief = "Git operation failed"
47
- details = message
48
-
49
- super().__init__(brief=brief, details=details)
50
-
51
-
52
- class UnsupportedArchitectureError(RemoteBuildError):
53
- """Unsupported architecture error."""
54
-
55
- def __init__(self, architectures: list[str]) -> None:
56
- brief = "Architecture not supported by the remote builder."
57
- details = (
58
- "The following architectures are not supported by the remote builder: "
59
- f"{architectures}.\nPlease remove them from the "
60
- "architecture list and try again."
61
- )
62
-
63
- super().__init__(brief=brief, details=details)
64
-
65
-
66
- class RemoteBuildInvalidGitRepoError(RemoteBuildError):
67
- """The Git repository is invalid for remote build.
68
-
69
- :param brief: Brief description of error.
70
- :param details: Detailed information.
71
- """
72
-
73
- def __init__(self, details: str) -> None:
74
- brief = "The Git repository is invalid for remote build."
75
-
76
- super().__init__(brief=brief, details=details)