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.
Files changed (305) hide show
  1. build/lib/build/lib/build/lib/build/lib/craft_application/__init__.py +51 -0
  2. build/lib/build/lib/build/lib/build/lib/craft_application/_version.py +16 -0
  3. build/lib/build/lib/build/lib/build/lib/craft_application/application.py +477 -0
  4. build/lib/build/lib/build/lib/build/lib/craft_application/commands/__init__.py +28 -0
  5. build/lib/build/lib/build/lib/build/lib/craft_application/commands/base.py +76 -0
  6. build/lib/build/lib/build/lib/build/lib/craft_application/commands/lifecycle.py +359 -0
  7. build/lib/build/lib/build/lib/build/lib/craft_application/commands/other.py +51 -0
  8. build/lib/build/lib/build/lib/build/lib/craft_application/errors.py +105 -0
  9. build/lib/build/lib/build/lib/build/lib/craft_application/models/__init__.py +41 -0
  10. build/lib/build/lib/build/lib/build/lib/craft_application/models/base.py +112 -0
  11. build/lib/build/lib/build/lib/build/lib/craft_application/models/constraints.py +70 -0
  12. build/lib/build/lib/build/lib/build/lib/craft_application/models/metadata.py +34 -0
  13. build/lib/build/lib/build/lib/build/lib/craft_application/models/project.py +94 -0
  14. build/lib/build/lib/build/lib/build/lib/craft_application/secrets.py +190 -0
  15. build/lib/build/lib/build/lib/build/lib/craft_application/services/__init__.py +31 -0
  16. build/lib/build/lib/build/lib/build/lib/craft_application/services/base.py +59 -0
  17. build/lib/build/lib/build/lib/build/lib/craft_application/services/lifecycle.py +226 -0
  18. build/lib/build/lib/build/lib/build/lib/craft_application/services/package.py +53 -0
  19. build/lib/build/lib/build/lib/build/lib/craft_application/services/provider.py +234 -0
  20. build/lib/build/lib/build/lib/build/lib/craft_application/services/service_factory.py +93 -0
  21. build/lib/build/lib/build/lib/build/lib/craft_application/util/__init__.py +30 -0
  22. build/lib/build/lib/build/lib/build/lib/craft_application/util/error_formatting.py +110 -0
  23. build/lib/build/lib/build/lib/build/lib/craft_application/util/paths.py +34 -0
  24. build/lib/build/lib/build/lib/build/lib/craft_application/util/platforms.py +64 -0
  25. build/lib/build/lib/build/lib/build/lib/craft_application/util/yaml.py +94 -0
  26. build/lib/build/lib/build/lib/build/lib/tests/integration/__init__.py +0 -0
  27. build/lib/build/lib/build/lib/build/lib/tests/integration/conftest.py +76 -0
  28. build/lib/build/lib/build/lib/build/lib/tests/integration/services/__init__.py +0 -0
  29. build/lib/build/lib/build/lib/build/lib/tests/integration/services/test_lifecycle.py +99 -0
  30. build/lib/build/lib/build/lib/build/lib/tests/integration/services/test_provider.py +74 -0
  31. build/lib/build/lib/build/lib/build/lib/tests/integration/services/test_service_factory.py +54 -0
  32. build/lib/build/lib/build/lib/build/lib/tests/integration/test_application.py +346 -0
  33. build/lib/build/lib/build/lib/build/lib/tests/integration/test_version.py +65 -0
  34. build/lib/build/lib/build/lib/build/lib/tests/unit/__init__.py +0 -0
  35. build/lib/build/lib/build/lib/build/lib/tests/unit/commands/__init__.py +0 -0
  36. build/lib/build/lib/build/lib/build/lib/tests/unit/commands/test_base.py +75 -0
  37. build/lib/build/lib/build/lib/build/lib/tests/unit/commands/test_lifecycle.py +513 -0
  38. build/lib/build/lib/build/lib/build/lib/tests/unit/commands/test_other.py +39 -0
  39. build/lib/build/lib/build/lib/build/lib/tests/unit/conftest.py +40 -0
  40. build/lib/build/lib/build/lib/build/lib/tests/unit/models/__init__.py +0 -0
  41. build/lib/build/lib/build/lib/build/lib/tests/unit/models/test_constraints.py +158 -0
  42. build/lib/build/lib/build/lib/build/lib/tests/unit/models/test_project.py +218 -0
  43. build/lib/build/lib/build/lib/build/lib/tests/unit/services/__init__.py +0 -0
  44. build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_lifecycle.py +358 -0
  45. build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_package.py +47 -0
  46. build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_provider.py +397 -0
  47. build/lib/build/lib/build/lib/build/lib/tests/unit/services/test_service_factory.py +140 -0
  48. build/lib/build/lib/build/lib/build/lib/tests/unit/test_application.py +597 -0
  49. build/lib/build/lib/build/lib/build/lib/tests/unit/test_errors.py +63 -0
  50. build/lib/build/lib/build/lib/build/lib/tests/unit/test_nothing.py +2 -0
  51. build/lib/build/lib/build/lib/build/lib/tests/unit/test_secrets.py +154 -0
  52. build/lib/build/lib/build/lib/build/lib/tests/unit/util/__init__.py +0 -0
  53. build/lib/build/lib/build/lib/build/lib/tests/unit/util/test_error_formatting.py +98 -0
  54. build/lib/build/lib/build/lib/build/lib/tests/unit/util/test_paths.py +26 -0
  55. build/lib/build/lib/build/lib/build/lib/tests/unit/util/test_yaml.py +42 -0
  56. build/lib/build/lib/build/lib/build/lib/venv/bin/activate_this.py +36 -0
  57. build/lib/build/lib/build/lib/craft_application/__init__.py +51 -0
  58. build/lib/build/lib/build/lib/craft_application/_version.py +16 -0
  59. build/lib/build/lib/build/lib/craft_application/application.py +477 -0
  60. build/lib/build/lib/build/lib/craft_application/commands/__init__.py +28 -0
  61. build/lib/build/lib/build/lib/craft_application/commands/base.py +76 -0
  62. build/lib/build/lib/build/lib/craft_application/commands/lifecycle.py +359 -0
  63. build/lib/build/lib/build/lib/craft_application/commands/other.py +51 -0
  64. build/lib/build/lib/build/lib/craft_application/errors.py +105 -0
  65. build/lib/build/lib/build/lib/craft_application/models/__init__.py +41 -0
  66. build/lib/build/lib/build/lib/craft_application/models/base.py +112 -0
  67. build/lib/build/lib/build/lib/craft_application/models/constraints.py +70 -0
  68. build/lib/build/lib/build/lib/craft_application/models/metadata.py +34 -0
  69. build/lib/build/lib/build/lib/craft_application/models/project.py +94 -0
  70. build/lib/build/lib/build/lib/craft_application/secrets.py +190 -0
  71. build/lib/build/lib/build/lib/craft_application/services/__init__.py +31 -0
  72. build/lib/build/lib/build/lib/craft_application/services/base.py +59 -0
  73. build/lib/build/lib/build/lib/craft_application/services/lifecycle.py +226 -0
  74. build/lib/build/lib/build/lib/craft_application/services/package.py +53 -0
  75. build/lib/build/lib/build/lib/craft_application/services/provider.py +234 -0
  76. build/lib/build/lib/build/lib/craft_application/services/service_factory.py +93 -0
  77. build/lib/build/lib/build/lib/craft_application/util/__init__.py +30 -0
  78. build/lib/build/lib/build/lib/craft_application/util/error_formatting.py +110 -0
  79. build/lib/build/lib/build/lib/craft_application/util/paths.py +34 -0
  80. build/lib/build/lib/build/lib/craft_application/util/platforms.py +64 -0
  81. build/lib/build/lib/build/lib/craft_application/util/yaml.py +94 -0
  82. build/lib/build/lib/build/lib/tests/integration/__init__.py +0 -0
  83. build/lib/build/lib/build/lib/tests/integration/conftest.py +76 -0
  84. build/lib/build/lib/build/lib/tests/integration/services/__init__.py +0 -0
  85. build/lib/build/lib/build/lib/tests/integration/services/test_lifecycle.py +99 -0
  86. build/lib/build/lib/build/lib/tests/integration/services/test_provider.py +74 -0
  87. build/lib/build/lib/build/lib/tests/integration/services/test_service_factory.py +54 -0
  88. build/lib/build/lib/build/lib/tests/integration/test_application.py +346 -0
  89. build/lib/build/lib/build/lib/tests/integration/test_version.py +65 -0
  90. build/lib/build/lib/build/lib/tests/unit/__init__.py +0 -0
  91. build/lib/build/lib/build/lib/tests/unit/commands/__init__.py +0 -0
  92. build/lib/build/lib/build/lib/tests/unit/commands/test_base.py +75 -0
  93. build/lib/build/lib/build/lib/tests/unit/commands/test_lifecycle.py +513 -0
  94. build/lib/build/lib/build/lib/tests/unit/commands/test_other.py +39 -0
  95. build/lib/build/lib/build/lib/tests/unit/conftest.py +40 -0
  96. build/lib/build/lib/build/lib/tests/unit/models/__init__.py +0 -0
  97. build/lib/build/lib/build/lib/tests/unit/models/test_constraints.py +158 -0
  98. build/lib/build/lib/build/lib/tests/unit/models/test_project.py +218 -0
  99. build/lib/build/lib/build/lib/tests/unit/services/__init__.py +0 -0
  100. build/lib/build/lib/build/lib/tests/unit/services/test_lifecycle.py +358 -0
  101. build/lib/build/lib/build/lib/tests/unit/services/test_package.py +47 -0
  102. build/lib/build/lib/build/lib/tests/unit/services/test_provider.py +397 -0
  103. build/lib/build/lib/build/lib/tests/unit/services/test_service_factory.py +140 -0
  104. build/lib/build/lib/build/lib/tests/unit/test_application.py +597 -0
  105. build/lib/build/lib/build/lib/tests/unit/test_errors.py +63 -0
  106. build/lib/build/lib/build/lib/tests/unit/test_nothing.py +2 -0
  107. build/lib/build/lib/build/lib/tests/unit/test_secrets.py +154 -0
  108. build/lib/build/lib/build/lib/tests/unit/util/__init__.py +0 -0
  109. build/lib/build/lib/build/lib/tests/unit/util/test_error_formatting.py +98 -0
  110. build/lib/build/lib/build/lib/tests/unit/util/test_paths.py +26 -0
  111. build/lib/build/lib/build/lib/tests/unit/util/test_yaml.py +42 -0
  112. build/lib/build/lib/build/lib/venv/bin/activate_this.py +36 -0
  113. build/lib/build/lib/craft_application/__init__.py +51 -0
  114. build/lib/build/lib/craft_application/_version.py +16 -0
  115. build/lib/build/lib/craft_application/application.py +477 -0
  116. build/lib/build/lib/craft_application/commands/__init__.py +28 -0
  117. build/lib/build/lib/craft_application/commands/base.py +76 -0
  118. build/lib/build/lib/craft_application/commands/lifecycle.py +359 -0
  119. build/lib/build/lib/craft_application/commands/other.py +51 -0
  120. build/lib/build/lib/craft_application/errors.py +105 -0
  121. build/lib/build/lib/craft_application/models/__init__.py +41 -0
  122. build/lib/build/lib/craft_application/models/base.py +112 -0
  123. build/lib/build/lib/craft_application/models/constraints.py +70 -0
  124. build/lib/build/lib/craft_application/models/metadata.py +34 -0
  125. build/lib/build/lib/craft_application/models/project.py +94 -0
  126. build/lib/build/lib/craft_application/secrets.py +190 -0
  127. build/lib/build/lib/craft_application/services/__init__.py +31 -0
  128. build/lib/build/lib/craft_application/services/base.py +59 -0
  129. build/lib/build/lib/craft_application/services/lifecycle.py +226 -0
  130. build/lib/build/lib/craft_application/services/package.py +53 -0
  131. build/lib/build/lib/craft_application/services/provider.py +234 -0
  132. build/lib/build/lib/craft_application/services/service_factory.py +93 -0
  133. build/lib/build/lib/craft_application/util/__init__.py +30 -0
  134. build/lib/build/lib/craft_application/util/error_formatting.py +110 -0
  135. build/lib/build/lib/craft_application/util/paths.py +34 -0
  136. build/lib/build/lib/craft_application/util/platforms.py +64 -0
  137. build/lib/build/lib/craft_application/util/yaml.py +94 -0
  138. build/lib/build/lib/tests/integration/__init__.py +0 -0
  139. build/lib/build/lib/tests/integration/conftest.py +76 -0
  140. build/lib/build/lib/tests/integration/services/__init__.py +0 -0
  141. build/lib/build/lib/tests/integration/services/test_lifecycle.py +99 -0
  142. build/lib/build/lib/tests/integration/services/test_provider.py +74 -0
  143. build/lib/build/lib/tests/integration/services/test_service_factory.py +54 -0
  144. build/lib/build/lib/tests/integration/test_application.py +346 -0
  145. build/lib/build/lib/tests/integration/test_version.py +65 -0
  146. build/lib/build/lib/tests/unit/__init__.py +0 -0
  147. build/lib/build/lib/tests/unit/commands/__init__.py +0 -0
  148. build/lib/build/lib/tests/unit/commands/test_base.py +75 -0
  149. build/lib/build/lib/tests/unit/commands/test_lifecycle.py +513 -0
  150. build/lib/build/lib/tests/unit/commands/test_other.py +39 -0
  151. build/lib/build/lib/tests/unit/conftest.py +40 -0
  152. build/lib/build/lib/tests/unit/models/__init__.py +0 -0
  153. build/lib/build/lib/tests/unit/models/test_constraints.py +158 -0
  154. build/lib/build/lib/tests/unit/models/test_project.py +218 -0
  155. build/lib/build/lib/tests/unit/services/__init__.py +0 -0
  156. build/lib/build/lib/tests/unit/services/test_lifecycle.py +358 -0
  157. build/lib/build/lib/tests/unit/services/test_package.py +47 -0
  158. build/lib/build/lib/tests/unit/services/test_provider.py +397 -0
  159. build/lib/build/lib/tests/unit/services/test_service_factory.py +140 -0
  160. build/lib/build/lib/tests/unit/test_application.py +597 -0
  161. build/lib/build/lib/tests/unit/test_errors.py +63 -0
  162. build/lib/build/lib/tests/unit/test_nothing.py +2 -0
  163. build/lib/build/lib/tests/unit/test_secrets.py +154 -0
  164. build/lib/build/lib/tests/unit/util/__init__.py +0 -0
  165. build/lib/build/lib/tests/unit/util/test_error_formatting.py +98 -0
  166. build/lib/build/lib/tests/unit/util/test_paths.py +26 -0
  167. build/lib/build/lib/tests/unit/util/test_yaml.py +42 -0
  168. build/lib/build/lib/venv/bin/activate_this.py +36 -0
  169. build/lib/craft_application/__init__.py +51 -0
  170. build/lib/craft_application/_version.py +16 -0
  171. build/lib/craft_application/application.py +477 -0
  172. build/lib/craft_application/commands/__init__.py +28 -0
  173. build/lib/craft_application/commands/base.py +76 -0
  174. build/lib/craft_application/commands/lifecycle.py +359 -0
  175. build/lib/craft_application/commands/other.py +51 -0
  176. build/lib/craft_application/errors.py +105 -0
  177. build/lib/craft_application/models/__init__.py +41 -0
  178. build/lib/craft_application/models/base.py +112 -0
  179. build/lib/craft_application/models/constraints.py +70 -0
  180. build/lib/craft_application/models/metadata.py +34 -0
  181. build/lib/craft_application/models/project.py +94 -0
  182. build/lib/craft_application/secrets.py +190 -0
  183. build/lib/craft_application/services/__init__.py +31 -0
  184. build/lib/craft_application/services/base.py +59 -0
  185. build/lib/craft_application/services/lifecycle.py +226 -0
  186. build/lib/craft_application/services/package.py +53 -0
  187. build/lib/craft_application/services/provider.py +234 -0
  188. build/lib/craft_application/services/service_factory.py +93 -0
  189. build/lib/craft_application/util/__init__.py +30 -0
  190. build/lib/craft_application/util/error_formatting.py +110 -0
  191. build/lib/craft_application/util/paths.py +34 -0
  192. build/lib/craft_application/util/platforms.py +64 -0
  193. build/lib/craft_application/util/yaml.py +94 -0
  194. build/lib/tests/integration/__init__.py +0 -0
  195. build/lib/tests/integration/conftest.py +76 -0
  196. build/lib/tests/integration/services/__init__.py +0 -0
  197. build/lib/tests/integration/services/test_lifecycle.py +99 -0
  198. build/lib/tests/integration/services/test_provider.py +74 -0
  199. build/lib/tests/integration/services/test_service_factory.py +54 -0
  200. build/lib/tests/integration/test_application.py +346 -0
  201. build/lib/tests/integration/test_version.py +65 -0
  202. build/lib/tests/unit/__init__.py +0 -0
  203. build/lib/tests/unit/commands/__init__.py +0 -0
  204. build/lib/tests/unit/commands/test_base.py +75 -0
  205. build/lib/tests/unit/commands/test_lifecycle.py +513 -0
  206. build/lib/tests/unit/commands/test_other.py +39 -0
  207. build/lib/tests/unit/conftest.py +40 -0
  208. build/lib/tests/unit/models/__init__.py +0 -0
  209. build/lib/tests/unit/models/test_constraints.py +158 -0
  210. build/lib/tests/unit/models/test_project.py +218 -0
  211. build/lib/tests/unit/services/__init__.py +0 -0
  212. build/lib/tests/unit/services/test_lifecycle.py +358 -0
  213. build/lib/tests/unit/services/test_package.py +47 -0
  214. build/lib/tests/unit/services/test_provider.py +397 -0
  215. build/lib/tests/unit/services/test_service_factory.py +140 -0
  216. build/lib/tests/unit/test_application.py +597 -0
  217. build/lib/tests/unit/test_errors.py +63 -0
  218. build/lib/tests/unit/test_nothing.py +2 -0
  219. build/lib/tests/unit/test_secrets.py +154 -0
  220. build/lib/tests/unit/util/__init__.py +0 -0
  221. build/lib/tests/unit/util/test_error_formatting.py +98 -0
  222. build/lib/tests/unit/util/test_paths.py +26 -0
  223. build/lib/tests/unit/util/test_yaml.py +42 -0
  224. build/lib/venv/bin/activate_this.py +36 -0
  225. craft_application/__init__.py +51 -0
  226. craft_application/_version.py +16 -0
  227. craft_application/application.py +477 -0
  228. craft_application/commands/__init__.py +28 -0
  229. craft_application/commands/base.py +76 -0
  230. craft_application/commands/lifecycle.py +359 -0
  231. craft_application/commands/other.py +51 -0
  232. craft_application/errors.py +105 -0
  233. craft_application/models/__init__.py +41 -0
  234. craft_application/models/base.py +112 -0
  235. craft_application/models/constraints.py +70 -0
  236. craft_application/models/metadata.py +34 -0
  237. craft_application/models/project.py +94 -0
  238. craft_application/py.typed +0 -0
  239. craft_application/secrets.py +190 -0
  240. craft_application/services/__init__.py +31 -0
  241. craft_application/services/base.py +59 -0
  242. craft_application/services/lifecycle.py +226 -0
  243. craft_application/services/package.py +53 -0
  244. craft_application/services/provider.py +234 -0
  245. craft_application/services/service_factory.py +93 -0
  246. craft_application/util/__init__.py +30 -0
  247. craft_application/util/error_formatting.py +110 -0
  248. craft_application/util/paths.py +34 -0
  249. craft_application/util/platforms.py +64 -0
  250. craft_application/util/yaml.py +94 -0
  251. craft_application-0.1.0.dist-info/LICENSE +165 -0
  252. craft_application-0.1.0.dist-info/METADATA +76 -0
  253. craft_application-0.1.0.dist-info/RECORD +305 -0
  254. craft_application-0.1.0.dist-info/WHEEL +5 -0
  255. craft_application-0.1.0.dist-info/top_level.txt +5 -0
  256. docs/_static/css/custom.css +28 -0
  257. docs/explanation/index.rst +7 -0
  258. docs/howto/index.rst +7 -0
  259. docs/reference/index.rst +13 -0
  260. docs/tutorials/index.rst +11 -0
  261. tests/integration/__init__.py +0 -0
  262. tests/integration/conftest.py +76 -0
  263. tests/integration/data/build-secrets/secret-source-folder/source-file.txt +1 -0
  264. tests/integration/data/build-secrets/testcraft.yaml +15 -0
  265. tests/integration/data/valid_projects/basic/stderr +1 -0
  266. tests/integration/data/valid_projects/basic/testcraft.yaml +8 -0
  267. tests/integration/data/valid_projects/environment/stderr +1 -0
  268. tests/integration/data/valid_projects/environment/testcraft.yaml +14 -0
  269. tests/integration/services/__init__.py +0 -0
  270. tests/integration/services/test_lifecycle.py +99 -0
  271. tests/integration/services/test_provider.py +74 -0
  272. tests/integration/services/test_service_factory.py +54 -0
  273. tests/integration/test_application.py +346 -0
  274. tests/integration/test_version.py +65 -0
  275. tests/unit/__init__.py +0 -0
  276. tests/unit/commands/__init__.py +0 -0
  277. tests/unit/commands/test_base.py +75 -0
  278. tests/unit/commands/test_lifecycle.py +513 -0
  279. tests/unit/commands/test_other.py +39 -0
  280. tests/unit/conftest.py +40 -0
  281. tests/unit/models/__init__.py +0 -0
  282. tests/unit/models/project_models/basic_project.yaml +5 -0
  283. tests/unit/models/project_models/full_project.yaml +15 -0
  284. tests/unit/models/project_models/invalid_project.yaml +1 -0
  285. tests/unit/models/test_constraints.py +158 -0
  286. tests/unit/models/test_project.py +218 -0
  287. tests/unit/services/__init__.py +0 -0
  288. tests/unit/services/test_lifecycle.py +358 -0
  289. tests/unit/services/test_package.py +47 -0
  290. tests/unit/services/test_provider.py +397 -0
  291. tests/unit/services/test_service_factory.py +140 -0
  292. tests/unit/test_application.py +597 -0
  293. tests/unit/test_errors.py +63 -0
  294. tests/unit/test_nothing.py +2 -0
  295. tests/unit/test_secrets.py +154 -0
  296. tests/unit/util/__init__.py +0 -0
  297. tests/unit/util/invalid_yaml/_README +4 -0
  298. tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +3 -0
  299. tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +2 -0
  300. tests/unit/util/invalid_yaml/unhashable.yaml-invalid +2 -0
  301. tests/unit/util/test_error_formatting.py +98 -0
  302. tests/unit/util/test_paths.py +26 -0
  303. tests/unit/util/test_yaml.py +42 -0
  304. tests/unit/util/valid_yaml/empty.yaml +0 -0
  305. 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]