@codyswann/lisa 1.0.0

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 (322) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +867 -0
  3. package/all/copy-overwrite/.claude/README.md +205 -0
  4. package/all/copy-overwrite/.claude/agents/agent-architect.md +311 -0
  5. package/all/copy-overwrite/.claude/agents/codebase-analyzer.md +146 -0
  6. package/all/copy-overwrite/.claude/agents/codebase-locator.md +125 -0
  7. package/all/copy-overwrite/.claude/agents/codebase-pattern-finder.md +237 -0
  8. package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +183 -0
  9. package/all/copy-overwrite/.claude/agents/hooks-expert.md +74 -0
  10. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +246 -0
  11. package/all/copy-overwrite/.claude/agents/slash-command-architect.md +87 -0
  12. package/all/copy-overwrite/.claude/agents/web-search-researcher.md +112 -0
  13. package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +8 -0
  14. package/all/copy-overwrite/.claude/commands/git/commit.md +44 -0
  15. package/all/copy-overwrite/.claude/commands/git/prune.md +34 -0
  16. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +50 -0
  17. package/all/copy-overwrite/.claude/commands/jira/create.md +50 -0
  18. package/all/copy-overwrite/.claude/commands/jira/verify.md +34 -0
  19. package/all/copy-overwrite/.claude/commands/project/archive.md +8 -0
  20. package/all/copy-overwrite/.claude/commands/project/bootstrap.md +49 -0
  21. package/all/copy-overwrite/.claude/commands/project/complete-task.md +7 -0
  22. package/all/copy-overwrite/.claude/commands/project/debrief.md +65 -0
  23. package/all/copy-overwrite/.claude/commands/project/execute.md +94 -0
  24. package/all/copy-overwrite/.claude/commands/project/implement.md +42 -0
  25. package/all/copy-overwrite/.claude/commands/project/local-code-review.md +88 -0
  26. package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +74 -0
  27. package/all/copy-overwrite/.claude/commands/project/plan.md +314 -0
  28. package/all/copy-overwrite/.claude/commands/project/research.md +248 -0
  29. package/all/copy-overwrite/.claude/commands/project/review.md +63 -0
  30. package/all/copy-overwrite/.claude/commands/project/setup.md +19 -0
  31. package/all/copy-overwrite/.claude/commands/project/verify.md +38 -0
  32. package/all/copy-overwrite/.claude/commands/pull-request/review.md +12 -0
  33. package/all/copy-overwrite/.claude/commands/rules/format-md.md +72 -0
  34. package/all/copy-overwrite/.claude/commands/sonarqube/check.md +6 -0
  35. package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +3 -0
  36. package/all/copy-overwrite/.claude/hooks/README.md +301 -0
  37. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +181 -0
  38. package/all/copy-overwrite/.claude/settings.json +41 -0
  39. package/all/copy-overwrite/.claude/settings.local.json.example +14 -0
  40. package/all/copy-overwrite/.claude/skills/coding-philosophy/SKILL.md +405 -0
  41. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +416 -0
  42. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +316 -0
  43. package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +118 -0
  44. package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +202 -0
  45. package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +210 -0
  46. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  47. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  48. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  49. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
  50. package/all/copy-overwrite/CLAUDE.md +77 -0
  51. package/all/copy-overwrite/HUMAN.md +17 -0
  52. package/all/copy-overwrite/specs/.keep +0 -0
  53. package/all/create-only/PROJECT_RULES.md +0 -0
  54. package/cdk/merge/package.json +20 -0
  55. package/dist/cli/index.d.ts +7 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +107 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/prompts.d.ts +45 -0
  60. package/dist/cli/prompts.d.ts.map +1 -0
  61. package/dist/cli/prompts.js +58 -0
  62. package/dist/cli/prompts.js.map +1 -0
  63. package/dist/core/config.d.ts +73 -0
  64. package/dist/core/config.d.ts.map +1 -0
  65. package/dist/core/config.js +36 -0
  66. package/dist/core/config.js.map +1 -0
  67. package/dist/core/index.d.ts +4 -0
  68. package/dist/core/index.d.ts.map +1 -0
  69. package/dist/core/index.js +4 -0
  70. package/dist/core/index.js.map +1 -0
  71. package/dist/core/lisa.d.ts +81 -0
  72. package/dist/core/lisa.d.ts.map +1 -0
  73. package/dist/core/lisa.js +459 -0
  74. package/dist/core/lisa.js.map +1 -0
  75. package/dist/core/manifest.d.ts +58 -0
  76. package/dist/core/manifest.d.ts.map +1 -0
  77. package/dist/core/manifest.js +104 -0
  78. package/dist/core/manifest.js.map +1 -0
  79. package/dist/detection/detector.interface.d.ts +15 -0
  80. package/dist/detection/detector.interface.d.ts.map +1 -0
  81. package/dist/detection/detector.interface.js +2 -0
  82. package/dist/detection/detector.interface.js.map +1 -0
  83. package/dist/detection/detectors/cdk.d.ts +10 -0
  84. package/dist/detection/detectors/cdk.d.ts.map +1 -0
  85. package/dist/detection/detectors/cdk.js +34 -0
  86. package/dist/detection/detectors/cdk.js.map +1 -0
  87. package/dist/detection/detectors/expo.d.ts +10 -0
  88. package/dist/detection/detectors/expo.d.ts.map +1 -0
  89. package/dist/detection/detectors/expo.js +30 -0
  90. package/dist/detection/detectors/expo.js.map +1 -0
  91. package/dist/detection/detectors/nestjs.d.ts +10 -0
  92. package/dist/detection/detectors/nestjs.d.ts.map +1 -0
  93. package/dist/detection/detectors/nestjs.js +34 -0
  94. package/dist/detection/detectors/nestjs.js.map +1 -0
  95. package/dist/detection/detectors/npm-package.d.ts +13 -0
  96. package/dist/detection/detectors/npm-package.d.ts.map +1 -0
  97. package/dist/detection/detectors/npm-package.js +30 -0
  98. package/dist/detection/detectors/npm-package.js.map +1 -0
  99. package/dist/detection/detectors/typescript.d.ts +10 -0
  100. package/dist/detection/detectors/typescript.d.ts.map +1 -0
  101. package/dist/detection/detectors/typescript.js +25 -0
  102. package/dist/detection/detectors/typescript.js.map +1 -0
  103. package/dist/detection/index.d.ts +24 -0
  104. package/dist/detection/index.d.ts.map +1 -0
  105. package/dist/detection/index.js +57 -0
  106. package/dist/detection/index.js.map +1 -0
  107. package/dist/errors/index.d.ts +69 -0
  108. package/dist/errors/index.d.ts.map +1 -0
  109. package/dist/errors/index.js +110 -0
  110. package/dist/errors/index.js.map +1 -0
  111. package/dist/index.d.ts +3 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +8 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/logging/console-logger.d.ts +12 -0
  116. package/dist/logging/console-logger.d.ts.map +1 -0
  117. package/dist/logging/console-logger.js +22 -0
  118. package/dist/logging/console-logger.js.map +1 -0
  119. package/dist/logging/index.d.ts +4 -0
  120. package/dist/logging/index.d.ts.map +1 -0
  121. package/dist/logging/index.js +3 -0
  122. package/dist/logging/index.js.map +1 -0
  123. package/dist/logging/logger.interface.d.ts +20 -0
  124. package/dist/logging/logger.interface.d.ts.map +1 -0
  125. package/dist/logging/logger.interface.js +2 -0
  126. package/dist/logging/logger.interface.js.map +1 -0
  127. package/dist/logging/silent-logger.d.ts +12 -0
  128. package/dist/logging/silent-logger.d.ts.map +1 -0
  129. package/dist/logging/silent-logger.js +21 -0
  130. package/dist/logging/silent-logger.js.map +1 -0
  131. package/dist/strategies/copy-contents.d.ts +14 -0
  132. package/dist/strategies/copy-contents.d.ts.map +1 -0
  133. package/dist/strategies/copy-contents.js +69 -0
  134. package/dist/strategies/copy-contents.js.map +1 -0
  135. package/dist/strategies/copy-overwrite.d.ts +14 -0
  136. package/dist/strategies/copy-overwrite.d.ts.map +1 -0
  137. package/dist/strategies/copy-overwrite.js +47 -0
  138. package/dist/strategies/copy-overwrite.js.map +1 -0
  139. package/dist/strategies/create-only.d.ts +13 -0
  140. package/dist/strategies/create-only.d.ts.map +1 -0
  141. package/dist/strategies/create-only.js +30 -0
  142. package/dist/strategies/create-only.js.map +1 -0
  143. package/dist/strategies/index.d.ts +31 -0
  144. package/dist/strategies/index.d.ts.map +1 -0
  145. package/dist/strategies/index.js +52 -0
  146. package/dist/strategies/index.js.map +1 -0
  147. package/dist/strategies/merge.d.ts +13 -0
  148. package/dist/strategies/merge.d.ts.map +1 -0
  149. package/dist/strategies/merge.js +60 -0
  150. package/dist/strategies/merge.js.map +1 -0
  151. package/dist/strategies/strategy.interface.d.ts +31 -0
  152. package/dist/strategies/strategy.interface.d.ts.map +1 -0
  153. package/dist/strategies/strategy.interface.js +2 -0
  154. package/dist/strategies/strategy.interface.js.map +1 -0
  155. package/dist/transaction/backup.d.ts +38 -0
  156. package/dist/transaction/backup.d.ts.map +1 -0
  157. package/dist/transaction/backup.js +97 -0
  158. package/dist/transaction/backup.js.map +1 -0
  159. package/dist/transaction/index.d.ts +4 -0
  160. package/dist/transaction/index.d.ts.map +1 -0
  161. package/dist/transaction/index.js +3 -0
  162. package/dist/transaction/index.js.map +1 -0
  163. package/dist/transaction/transaction.d.ts +34 -0
  164. package/dist/transaction/transaction.d.ts.map +1 -0
  165. package/dist/transaction/transaction.js +68 -0
  166. package/dist/transaction/transaction.js.map +1 -0
  167. package/dist/utils/file-operations.d.ts +29 -0
  168. package/dist/utils/file-operations.d.ts.map +1 -0
  169. package/dist/utils/file-operations.js +84 -0
  170. package/dist/utils/file-operations.js.map +1 -0
  171. package/dist/utils/index.d.ts +4 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +4 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/json-utils.d.ts +22 -0
  176. package/dist/utils/json-utils.d.ts.map +1 -0
  177. package/dist/utils/json-utils.js +57 -0
  178. package/dist/utils/json-utils.js.map +1 -0
  179. package/dist/utils/path-utils.d.ts +21 -0
  180. package/dist/utils/path-utils.d.ts.map +1 -0
  181. package/dist/utils/path-utils.js +35 -0
  182. package/dist/utils/path-utils.js.map +1 -0
  183. package/eslint-plugin-code-organization/README.md +149 -0
  184. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
  185. package/eslint-plugin-code-organization/index.js +23 -0
  186. package/eslint-plugin-code-organization/package.json +10 -0
  187. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
  188. package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +238 -0
  189. package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +360 -0
  190. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +360 -0
  191. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
  192. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
  193. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
  194. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +327 -0
  195. package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +299 -0
  196. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +749 -0
  197. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +318 -0
  198. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +198 -0
  199. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +207 -0
  200. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +268 -0
  201. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +619 -0
  202. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
  203. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +276 -0
  204. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +414 -0
  205. package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +202 -0
  206. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +443 -0
  207. package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +309 -0
  208. package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +417 -0
  209. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +431 -0
  210. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +290 -0
  211. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +169 -0
  212. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +411 -0
  213. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +343 -0
  214. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +307 -0
  215. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
  216. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +354 -0
  217. package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +362 -0
  218. package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +505 -0
  219. package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +711 -0
  220. package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +446 -0
  221. package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +223 -0
  222. package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +319 -0
  223. package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +420 -0
  224. package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +556 -0
  225. package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +590 -0
  226. package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +291 -0
  227. package/expo/copy-overwrite/.easignore.extra +2 -0
  228. package/expo/copy-overwrite/.mcp.json +33 -0
  229. package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +234 -0
  230. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +84 -0
  231. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +196 -0
  232. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +289 -0
  233. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +32 -0
  234. package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +10 -0
  235. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +230 -0
  236. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +91 -0
  237. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +178 -0
  238. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +238 -0
  239. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +260 -0
  240. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +29 -0
  241. package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +10 -0
  242. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +51 -0
  243. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +55 -0
  244. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +73 -0
  245. package/expo/copy-overwrite/eslint.config.mjs +560 -0
  246. package/expo/copy-overwrite/lighthouserc.js +194 -0
  247. package/expo/create-only/lighthouserc-config.json +28 -0
  248. package/expo/merge/package.json +132 -0
  249. package/lisa.sh +35 -0
  250. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +176 -0
  251. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +527 -0
  252. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +483 -0
  253. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +257 -0
  254. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
  255. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +513 -0
  256. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +536 -0
  257. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +275 -0
  258. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
  259. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +450 -0
  260. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +536 -0
  261. package/nestjs/merge/package.json +75 -0
  262. package/package.json +124 -0
  263. package/typescript/copy-contents/.husky/commit-msg +91 -0
  264. package/typescript/copy-contents/.husky/pre-commit +96 -0
  265. package/typescript/copy-contents/.husky/pre-push +211 -0
  266. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +74 -0
  267. package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +59 -0
  268. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +103 -0
  269. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +388 -0
  270. package/typescript/copy-overwrite/.github/README.md +455 -0
  271. package/typescript/copy-overwrite/.github/dependabot.yml +40 -0
  272. package/typescript/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +129 -0
  273. package/typescript/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +354 -0
  274. package/typescript/copy-overwrite/.github/k6/README.md +386 -0
  275. package/typescript/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +264 -0
  276. package/typescript/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +115 -0
  277. package/typescript/copy-overwrite/.github/k6/examples/data-driven-test.js +268 -0
  278. package/typescript/copy-overwrite/.github/k6/scenarios/load.js +142 -0
  279. package/typescript/copy-overwrite/.github/k6/scenarios/load.json +27 -0
  280. package/typescript/copy-overwrite/.github/k6/scenarios/smoke.js +26 -0
  281. package/typescript/copy-overwrite/.github/k6/scenarios/smoke.json +20 -0
  282. package/typescript/copy-overwrite/.github/k6/scenarios/soak.js +244 -0
  283. package/typescript/copy-overwrite/.github/k6/scenarios/soak.json +29 -0
  284. package/typescript/copy-overwrite/.github/k6/scenarios/spike.js +180 -0
  285. package/typescript/copy-overwrite/.github/k6/scenarios/spike.json +32 -0
  286. package/typescript/copy-overwrite/.github/k6/scenarios/stress.js +206 -0
  287. package/typescript/copy-overwrite/.github/k6/scenarios/stress.json +38 -0
  288. package/typescript/copy-overwrite/.github/k6/scripts/api-test.js +452 -0
  289. package/typescript/copy-overwrite/.github/k6/scripts/default-test.js +185 -0
  290. package/typescript/copy-overwrite/.github/k6/thresholds/normal.json +30 -0
  291. package/typescript/copy-overwrite/.github/k6/thresholds/relaxed.json +21 -0
  292. package/typescript/copy-overwrite/.github/k6/thresholds/strict.json +29 -0
  293. package/typescript/copy-overwrite/.github/workflows/build.yml +72 -0
  294. package/typescript/copy-overwrite/.github/workflows/ci.yml +49 -0
  295. package/typescript/copy-overwrite/.github/workflows/claude.yml +51 -0
  296. package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +113 -0
  297. package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +195 -0
  298. package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +267 -0
  299. package/typescript/copy-overwrite/.github/workflows/deploy.yml +228 -0
  300. package/typescript/copy-overwrite/.github/workflows/k6-load-test-README.md +230 -0
  301. package/typescript/copy-overwrite/.github/workflows/lighthouse.yml +68 -0
  302. package/typescript/copy-overwrite/.github/workflows/load-test.yml +282 -0
  303. package/typescript/copy-overwrite/.github/workflows/quality.yml +1737 -0
  304. package/typescript/copy-overwrite/.github/workflows/release.yml +1599 -0
  305. package/typescript/copy-overwrite/.gitleaksignore +28 -0
  306. package/typescript/copy-overwrite/.nvmrc +1 -0
  307. package/typescript/copy-overwrite/.prettierignore +23 -0
  308. package/typescript/copy-overwrite/.prettierrc.json +22 -0
  309. package/typescript/copy-overwrite/.versionrc +42 -0
  310. package/typescript/copy-overwrite/.yamllint +20 -0
  311. package/typescript/copy-overwrite/commitlint.config.js +11 -0
  312. package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +149 -0
  313. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
  314. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +23 -0
  315. package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +10 -0
  316. package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
  317. package/typescript/copy-overwrite/eslint.config.mjs +390 -0
  318. package/typescript/copy-overwrite/eslint.ignore.config.json +57 -0
  319. package/typescript/copy-overwrite/eslint.thresholds.config.json +5 -0
  320. package/typescript/github-rulesets/base.json +106 -0
  321. package/typescript/merge/.claude/settings.json +28 -0
  322. package/typescript/merge/package.json +71 -0
@@ -0,0 +1,1599 @@
1
+ name: Enhanced Release Workflow v4 (with Enterprise Features)
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ environment:
7
+ description: 'Target environment for the release'
8
+ required: true
9
+ type: string
10
+ skip_jobs:
11
+ description: 'Comma-separated list of jobs to skip (test,type-check,lint,format,audit,cdk-synth)'
12
+ required: false
13
+ type: string
14
+ default: ''
15
+ force_release:
16
+ description: 'Force create a release even if no changes detected'
17
+ required: false
18
+ type: boolean
19
+ default: false
20
+ release_strategy:
21
+ description: 'Version strategy: standard-version, semantic, calendar, custom'
22
+ required: false
23
+ type: string
24
+ default: 'standard-version'
25
+ prerelease:
26
+ description: 'Create pre-release (alpha, beta, rc)'
27
+ required: false
28
+ type: string
29
+ default: ''
30
+ custom_version:
31
+ description: 'Custom version number (only for custom strategy)'
32
+ required: false
33
+ type: string
34
+ default: ''
35
+ jira_project_key:
36
+ description: 'JIRA project key for release creation'
37
+ required: false
38
+ type: string
39
+ default: ''
40
+ release_notes_template:
41
+ description: 'Template for release notes (default, detailed, security)'
42
+ required: false
43
+ type: string
44
+ default: 'default'
45
+ debug:
46
+ description: 'Enable debug logging'
47
+ required: false
48
+ type: boolean
49
+ default: false
50
+ require_approval:
51
+ description: 'Require approval before creating release'
52
+ required: false
53
+ type: boolean
54
+ default: false
55
+ approval_environment:
56
+ description: 'GitHub environment for approval (if require_approval is true)'
57
+ required: false
58
+ type: string
59
+ default: ''
60
+ require_signatures:
61
+ description: 'Require signed releases (needs RELEASE_SIGNING_KEY secret)'
62
+ required: false
63
+ type: boolean
64
+ default: false
65
+ check_dependencies:
66
+ description: 'Check service dependencies before release'
67
+ required: false
68
+ type: boolean
69
+ default: false
70
+ override_blackout:
71
+ description: 'Override blackout period restrictions'
72
+ required: false
73
+ type: boolean
74
+ default: false
75
+ emergency_release:
76
+ description: 'Mark as emergency release'
77
+ required: false
78
+ type: boolean
79
+ default: false
80
+ generate_sbom:
81
+ description: 'Generate Software Bill of Materials'
82
+ required: false
83
+ type: boolean
84
+ default: false
85
+ node_version:
86
+ description: 'Node.js version to use'
87
+ required: true
88
+ type: string
89
+ package_manager:
90
+ description: 'Package manager to use (npm, yarn, pnpm, bun)'
91
+ required: false
92
+ type: string
93
+ default: 'npm'
94
+ sourcemaps:
95
+ description: 'Path to source maps for Sentry release (optional)'
96
+ required: false
97
+ type: string
98
+ default: ''
99
+ outputs:
100
+ version:
101
+ description: 'The new version number'
102
+ value: ${{ jobs.version.outputs.version }}
103
+ tag:
104
+ description: 'The new version tag'
105
+ value: ${{ jobs.version.outputs.tag }}
106
+ release_url:
107
+ description: 'URL of the created GitHub release'
108
+ value: ${{ jobs.github_release.outputs.release_url }}
109
+ release_id:
110
+ description: 'Release correlation ID for tracking'
111
+ value: ${{ jobs.release_init.outputs.correlation_id }}
112
+ release_metrics_url:
113
+ description: 'URL to release metrics dashboard'
114
+ value: ${{ jobs.release_analytics.outputs.dashboard_url }}
115
+ signed:
116
+ description: 'Whether the release was signed'
117
+ value: ${{ jobs.release_signing.outputs.signed }}
118
+ attestation_url:
119
+ description: 'URL to release attestation'
120
+ value: ${{ jobs.release_attestation.outputs.attestation_url }}
121
+ compliance_status:
122
+ description: 'Compliance validation status'
123
+ value: ${{ jobs.release_compliance.outputs.status }}
124
+ sentry_release_url:
125
+ description: 'URL to Sentry release'
126
+ value: ${{ jobs.sentry_release.outputs.sentry_release_url }}
127
+ jira_release_created:
128
+ description: 'Whether the Jira release was created'
129
+ value: ${{ jobs.jira_release.outputs.jira_release_created }}
130
+ jira_release_url:
131
+ description: 'URL to Jira release'
132
+ value: ${{ jobs.jira_release.outputs.jira_release_url }}
133
+ secrets:
134
+ DEPLOY_KEY:
135
+ description: 'SSH deploy key with write access to bypass branch protection (add to ruleset bypass list)'
136
+ required: false
137
+ RELEASE_SIGNING_KEY:
138
+ required: false
139
+ SIGNING_KEY_ID:
140
+ required: false
141
+ SIGNING_KEY_PASSPHRASE:
142
+ required: false
143
+ SENTRY_AUTH_TOKEN:
144
+ required: false
145
+ SENTRY_ORG:
146
+ required: false
147
+ SENTRY_PROJECT:
148
+ required: false
149
+
150
+ jobs:
151
+ # Initialize release with correlation ID and logging
152
+ release_init:
153
+ name: 🚀 Initialize Release
154
+ runs-on: ubuntu-latest
155
+ outputs:
156
+ correlation_id: ${{ steps.init.outputs.correlation_id }}
157
+ start_time: ${{ steps.init.outputs.start_time }}
158
+ release_logger: ${{ steps.init.outputs.logger_created }}
159
+ steps:
160
+ - name: Generate Release Correlation ID
161
+ id: init
162
+ run: |
163
+ # Generate unique correlation ID
164
+ CORRELATION_ID="rel-$(date +%Y%m%d%H%M%S)-${{ github.run_id }}"
165
+ echo "correlation_id=$CORRELATION_ID" >> $GITHUB_OUTPUT
166
+ echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
167
+ echo "logger_created=true" >> $GITHUB_OUTPUT
168
+
169
+ # Initialize release context
170
+ echo "## 🚀 Release Workflow Started" >> $GITHUB_STEP_SUMMARY
171
+ echo "- **Correlation ID**: $CORRELATION_ID" >> $GITHUB_STEP_SUMMARY
172
+ echo "- **Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY
173
+ echo "- **Strategy**: ${{ inputs.release_strategy }}" >> $GITHUB_STEP_SUMMARY
174
+ echo "- **Approval Required**: ${{ inputs.require_approval }}" >> $GITHUB_STEP_SUMMARY
175
+ echo "- **Signatures Required**: ${{ inputs.require_signatures }}" >> $GITHUB_STEP_SUMMARY
176
+ echo "- **Emergency Release**: ${{ inputs.emergency_release }}" >> $GITHUB_STEP_SUMMARY
177
+
178
+ # Check if this is a promotion merge (env branch to env branch)
179
+ check_promotion:
180
+ name: 🔍 Check Promotion
181
+ runs-on: ubuntu-latest
182
+ outputs:
183
+ is_promotion: ${{ steps.check.outputs.is_promotion }}
184
+ skip_reason: ${{ steps.check.outputs.skip_reason }}
185
+ steps:
186
+ - uses: actions/checkout@v4
187
+ with:
188
+ fetch-depth: 10 # Need some history to analyze merge commits
189
+
190
+ - name: Check if this is a promotion merge
191
+ id: check
192
+ run: |
193
+ COMMIT_MSG=$(git log -1 --pretty=%B)
194
+ IS_PROMOTION=false
195
+ SKIP_REASON=""
196
+
197
+ # Environment branches that we track
198
+ ENV_BRANCHES="dev|staging|main|master|production"
199
+
200
+ # Check for merge commits from environment branches to environment branches
201
+ # Pattern 1: "Merge branch 'staging' into main"
202
+ if echo "$COMMIT_MSG" | grep -qiE "^Merge branch ['\"]?($ENV_BRANCHES)['\"]? into ($ENV_BRANCHES)"; then
203
+ IS_PROMOTION=true
204
+ SKIP_REASON="Merge from environment branch detected"
205
+ fi
206
+
207
+ # Pattern 2: "Merge pull request #123 from org/staging" or similar
208
+ if echo "$COMMIT_MSG" | grep -qiE "^Merge pull request.*from .*/($ENV_BRANCHES)$"; then
209
+ IS_PROMOTION=true
210
+ SKIP_REASON="PR merge from environment branch detected"
211
+ fi
212
+
213
+ # Pattern 3: Check parent commits for promotion patterns
214
+ # If both parents are from environment branches, it's likely a promotion
215
+ PARENT_COUNT=$(git rev-list --parents -n 1 HEAD | wc -w)
216
+ if [ "$PARENT_COUNT" -gt 2 ]; then
217
+ # This is a merge commit, check if parent branches are environment branches
218
+ PARENT_BRANCHES=$(git log -1 --pretty=%P | xargs -n1 git branch -a --contains 2>/dev/null | grep -E "($ENV_BRANCHES)" | head -2)
219
+ PARENT_ENV_COUNT=$(echo "$PARENT_BRANCHES" | grep -cE "($ENV_BRANCHES)" || true)
220
+ if [ "$PARENT_ENV_COUNT" -ge 2 ]; then
221
+ IS_PROMOTION=true
222
+ SKIP_REASON="Merge between environment branches detected"
223
+ fi
224
+ fi
225
+
226
+ echo "is_promotion=$IS_PROMOTION" >> $GITHUB_OUTPUT
227
+ echo "skip_reason=$SKIP_REASON" >> $GITHUB_OUTPUT
228
+
229
+ if [ "$IS_PROMOTION" = "true" ]; then
230
+ echo "## ⏭️ Promotion Detected" >> $GITHUB_STEP_SUMMARY
231
+ echo "" >> $GITHUB_STEP_SUMMARY
232
+ echo "**Reason**: $SKIP_REASON" >> $GITHUB_STEP_SUMMARY
233
+ echo "" >> $GITHUB_STEP_SUMMARY
234
+ echo "Version bump will be skipped to avoid duplicate versioning." >> $GITHUB_STEP_SUMMARY
235
+ echo "" >> $GITHUB_STEP_SUMMARY
236
+ echo "**Commit message**:" >> $GITHUB_STEP_SUMMARY
237
+ echo '```' >> $GITHUB_STEP_SUMMARY
238
+ echo "$COMMIT_MSG" | head -5 >> $GITHUB_STEP_SUMMARY
239
+ echo '```' >> $GITHUB_STEP_SUMMARY
240
+ else
241
+ echo "✅ Not a promotion merge - version bump will proceed" >> $GITHUB_STEP_SUMMARY
242
+ fi
243
+
244
+ # Check release schedule and blackout periods
245
+ release_schedule:
246
+ name: 📅 Release Schedule Check
247
+ needs: [release_init]
248
+ runs-on: ubuntu-latest
249
+ outputs:
250
+ allowed: ${{ steps.schedule.outputs.allowed }}
251
+ blackout_reason: ${{ steps.schedule.outputs.reason }}
252
+ steps:
253
+ - name: Check Release Schedule
254
+ id: schedule
255
+ run: |
256
+ # Check if we're in a blackout period
257
+ CURRENT_DATE=$(date +%Y-%m-%d)
258
+ CURRENT_TIME=$(date +%H:%M)
259
+ CURRENT_DAY=$(date +%A)
260
+ CURRENT_HOUR=$(date +%H)
261
+
262
+ IS_BLACKOUT=false
263
+ BLACKOUT_REASON=""
264
+
265
+ # Check environment-specific blackout rules
266
+ case "${{ inputs.environment }}" in
267
+ production|prod|main)
268
+ # No weekend releases for production
269
+ if [[ "$CURRENT_DAY" == "Saturday" || "$CURRENT_DAY" == "Sunday" ]]; then
270
+ IS_BLACKOUT=true
271
+ BLACKOUT_REASON="Weekend release restriction for production"
272
+ fi
273
+
274
+ # No late night releases (10 PM - 6 AM)
275
+ if [[ $CURRENT_HOUR -ge 22 || $CURRENT_HOUR -lt 6 ]]; then
276
+ IS_BLACKOUT=true
277
+ BLACKOUT_REASON="Late night release restriction (10 PM - 6 AM)"
278
+ fi
279
+
280
+ # Holiday blackouts
281
+ HOLIDAYS=(
282
+ "2024-12-24:2025-01-02:Holiday Season"
283
+ "2025-07-03:2025-07-05:Independence Day"
284
+ "2025-11-27:2025-11-29:Thanksgiving"
285
+ )
286
+
287
+ for holiday in "${HOLIDAYS[@]}"; do
288
+ IFS=':' read -r start end name <<< "$holiday"
289
+ if [[ "$CURRENT_DATE" > "$start" && "$CURRENT_DATE" < "$end" ]]; then
290
+ IS_BLACKOUT=true
291
+ BLACKOUT_REASON="$name blackout period"
292
+ break
293
+ fi
294
+ done
295
+ ;;
296
+
297
+ staging|stage)
298
+ # Staging has fewer restrictions
299
+ if [[ "$CURRENT_DAY" == "Sunday" && $CURRENT_HOUR -ge 20 ]]; then
300
+ IS_BLACKOUT=true
301
+ BLACKOUT_REASON="Sunday evening restriction for staging"
302
+ fi
303
+ ;;
304
+ esac
305
+
306
+ # Handle blackout
307
+ if [ "$IS_BLACKOUT" == "true" ]; then
308
+ if [ "${{ inputs.override_blackout }}" == "true" ]; then
309
+ echo "⚠️ Blackout override enabled: $BLACKOUT_REASON"
310
+ echo "allowed=true" >> $GITHUB_OUTPUT
311
+ echo "reason=Override: $BLACKOUT_REASON" >> $GITHUB_OUTPUT
312
+ else
313
+ echo "❌ Release blocked: $BLACKOUT_REASON" >> $GITHUB_STEP_SUMMARY
314
+ echo "Use override_blackout=true to force release" >> $GITHUB_STEP_SUMMARY
315
+ echo "allowed=false" >> $GITHUB_OUTPUT
316
+ echo "reason=$BLACKOUT_REASON" >> $GITHUB_OUTPUT
317
+ fi
318
+ else
319
+ echo "✅ Release schedule check passed" >> $GITHUB_STEP_SUMMARY
320
+ echo "allowed=true" >> $GITHUB_OUTPUT
321
+ echo "reason=No blackout restrictions" >> $GITHUB_OUTPUT
322
+ fi
323
+
324
+ - name: Enforce Schedule
325
+ if: steps.schedule.outputs.allowed != 'true'
326
+ run: |
327
+ echo "::error::Release blocked due to blackout period: ${{ steps.schedule.outputs.reason }}"
328
+ exit 1
329
+
330
+ # Optional approval gate
331
+ release_approval:
332
+ name: 🚦 Release Approval
333
+ needs: [release_init, release_schedule]
334
+ if: ${{ inputs.require_approval == true }}
335
+ runs-on: ubuntu-latest
336
+ environment:
337
+ name: ${{ inputs.approval_environment || inputs.environment }}
338
+ outputs:
339
+ approved: ${{ steps.approval.outputs.approved }}
340
+ approver: ${{ steps.approval.outputs.approver }}
341
+ approval_time: ${{ steps.approval.outputs.time }}
342
+ steps:
343
+ - name: Request Approval
344
+ id: approval
345
+ run: |
346
+ echo "## 🚦 Release Approval Required" >> $GITHUB_STEP_SUMMARY
347
+ echo "" >> $GITHUB_STEP_SUMMARY
348
+ echo "**Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY
349
+ echo "**Requester**: @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
350
+ echo "**Correlation ID**: ${{ needs.release_init.outputs.correlation_id }}" >> $GITHUB_STEP_SUMMARY
351
+ echo "" >> $GITHUB_STEP_SUMMARY
352
+ echo "This release requires manual approval to proceed." >> $GITHUB_STEP_SUMMARY
353
+
354
+ # If we reach here, approval was granted (environment protection rules)
355
+ echo "approved=true" >> $GITHUB_OUTPUT
356
+ echo "approver=${{ github.actor }}" >> $GITHUB_OUTPUT
357
+ echo "time=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
358
+
359
+ echo "✅ Release approved by environment protection rules" >> $GITHUB_STEP_SUMMARY
360
+
361
+ # Run quality checks
362
+ quality:
363
+ name: 🔍 Quality Checks
364
+ needs: [release_init, release_schedule]
365
+ uses: ./.github/workflows/quality.yml
366
+ with:
367
+ skip_jobs: ${{ inputs.skip_jobs }}
368
+ node_version: ${{ inputs.node_version }}
369
+ package_manager: ${{ inputs.package_manager }}
370
+ secrets: inherit
371
+
372
+ # Version management with observability
373
+ version:
374
+ name: 📦 Version Management
375
+ needs: [release_init, quality, release_approval, check_promotion]
376
+ if: |
377
+ always() && !cancelled() &&
378
+ needs.quality.result == 'success' &&
379
+ (needs.release_approval.result == 'success' || needs.release_approval.result == 'skipped') &&
380
+ needs.check_promotion.outputs.is_promotion != 'true'
381
+ runs-on: ubuntu-latest
382
+ permissions:
383
+ contents: write
384
+ env:
385
+ HUSKY: '0'
386
+ outputs:
387
+ version: ${{ steps.version.outputs.version }}
388
+ tag: ${{ steps.version.outputs.tag }}
389
+ prerelease: ${{ steps.version.outputs.prerelease }}
390
+ bump_type: ${{ steps.version.outputs.bump_type }}
391
+ strategy: ${{ inputs.release_strategy }}
392
+ changelog_generated: ${{ steps.changelog.outputs.generated }}
393
+ steps:
394
+ # Use SSH deploy key for pushing to protected branches
395
+ # Setup: Add DEPLOY_KEY secret and add the deploy key to ruleset bypass list
396
+ - uses: actions/checkout@v4
397
+ with:
398
+ fetch-depth: 0
399
+ ssh-key: ${{ secrets.DEPLOY_KEY }}
400
+
401
+ - name: Setup Release Logger
402
+ id: logger
403
+ run: |
404
+ # Create logging utilities
405
+ cat > release-logger.sh << 'EOF'
406
+ #!/bin/bash
407
+
408
+ # Correlation ID from init job
409
+ RELEASE_CORRELATION_ID="${{ needs.release_init.outputs.correlation_id }}"
410
+ export RELEASE_CORRELATION_ID
411
+
412
+ # Structured log function
413
+ log_release_event() {
414
+ local event_type="$1"
415
+ local event_status="$2"
416
+ local event_message="$3"
417
+ shift 3
418
+
419
+ # Additional key-value pairs
420
+ local additional_fields=""
421
+ while [ $# -gt 0 ] && [ $# -ge 2 ]; do
422
+ additional_fields="$additional_fields,\"$1\":\"$2\""
423
+ shift 2
424
+ done
425
+
426
+ # Create structured log entry
427
+ local log_entry=$(jq -n \
428
+ --arg correlation_id "$RELEASE_CORRELATION_ID" \
429
+ --arg workflow_id "${{ github.run_id }}" \
430
+ --arg event_type "$event_type" \
431
+ --arg event_status "$event_status" \
432
+ --arg event_message "$event_message" \
433
+ --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)" \
434
+ --arg environment "${{ inputs.environment }}" \
435
+ --arg version "${VERSION:-unknown}" \
436
+ --arg tag "${TAG:-unknown}" \
437
+ --arg actor "${{ github.actor }}" \
438
+ --arg repository "${{ github.repository }}" \
439
+ --arg ref "${{ github.ref }}" \
440
+ --arg sha "${{ github.sha }}" \
441
+ "{
442
+ \"correlation_id\": \$correlation_id,
443
+ \"workflow_id\": \$workflow_id,
444
+ \"event_type\": \$event_type,
445
+ \"event_status\": \$event_status,
446
+ \"event_message\": \$event_message,
447
+ \"timestamp\": \$timestamp,
448
+ \"release\": {
449
+ \"environment\": \$environment,
450
+ \"version\": \$version,
451
+ \"tag\": \$tag
452
+ },
453
+ \"context\": {
454
+ \"actor\": \$actor,
455
+ \"repository\": \$repository,
456
+ \"ref\": \$ref,
457
+ \"sha\": \$sha
458
+ }${additional_fields}
459
+ }")
460
+
461
+ # Output to stdout and file
462
+ echo "$log_entry" | tee -a release-events.jsonl
463
+
464
+ # Also log to GitHub Actions if debug enabled
465
+ if [ "${{ inputs.debug }}" == "true" ]; then
466
+ echo "::notice title=Release Event::$event_type - $event_status: $event_message"
467
+ fi
468
+ }
469
+
470
+ export -f log_release_event
471
+ EOF
472
+
473
+ chmod +x release-logger.sh
474
+ source ./release-logger.sh
475
+
476
+ # Log version process start
477
+ log_release_event "version_management" "started" "Version determination process started" \
478
+ "strategy" "${{ inputs.release_strategy }}" \
479
+ "approval_required" "${{ inputs.require_approval }}" \
480
+ "signatures_required" "${{ inputs.require_signatures }}"
481
+
482
+ - name: Setup Node.js
483
+ uses: actions/setup-node@v4
484
+ with:
485
+ node-version: ${{ inputs.node_version }}
486
+ cache: ${{ inputs.package_manager != 'bun' && inputs.package_manager || '' }}
487
+
488
+ - name: Setup Bun
489
+ if: inputs.package_manager == 'bun'
490
+ uses: oven-sh/setup-bun@v2
491
+ with:
492
+ bun-version: latest
493
+
494
+ - name: Install dependencies
495
+ run: |
496
+ if [ "${{ inputs.package_manager }}" = "npm" ]; then
497
+ npm ci
498
+ elif [ "${{ inputs.package_manager }}" = "yarn" ]; then
499
+ yarn install --frozen-lockfile
500
+ elif [ "${{ inputs.package_manager }}" = "pnpm" ]; then
501
+ pnpm install --frozen-lockfile
502
+ elif [ "${{ inputs.package_manager }}" = "bun" ]; then
503
+ bun install --frozen-lockfile
504
+ else
505
+ npm ci
506
+ fi
507
+
508
+ - name: Determine Version
509
+ id: version
510
+ run: |
511
+ source ./release-logger.sh
512
+
513
+ # Version determination logic (same as v3)
514
+ case "${{ inputs.release_strategy }}" in
515
+ "standard-version")
516
+ VERSION=$(npx standard-version --dry-run | grep "tagging release" | awk '{print $4}')
517
+ ;;
518
+ "semantic")
519
+ if git log --format=%B -n 50 --no-merges | grep -qE "^(BREAKING CHANGE:|.*!:)"; then
520
+ BUMP_TYPE="major"
521
+ elif git log --format=%B -n 50 --no-merges | grep -qE "^feat(\(.+\))?:"; then
522
+ BUMP_TYPE="minor"
523
+ else
524
+ BUMP_TYPE="patch"
525
+ fi
526
+
527
+ CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
528
+ VERSION=$(npx semver -i $BUMP_TYPE ${CURRENT_VERSION#v})
529
+ ;;
530
+ "calendar")
531
+ VERSION=$(date +%Y.%m.%d)
532
+ if [ -n "${{ inputs.prerelease }}" ]; then
533
+ VERSION="${VERSION}-${{ inputs.prerelease }}.$(date +%H%M)"
534
+ fi
535
+ ;;
536
+ "custom")
537
+ VERSION="${{ inputs.custom_version }}"
538
+ if [ -z "$VERSION" ]; then
539
+ log_release_event "version_error" "failed" "Custom version not provided"
540
+ exit 1
541
+ fi
542
+ ;;
543
+ esac
544
+
545
+ # Add prerelease suffix if specified
546
+ if [ -n "${{ inputs.prerelease }}" ] && [ "${{ inputs.release_strategy }}" != "calendar" ]; then
547
+ VERSION="${VERSION}-${{ inputs.prerelease }}.$(date +%s)"
548
+ echo "prerelease=true" >> $GITHUB_OUTPUT
549
+ fi
550
+
551
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
552
+ echo "tag=v$VERSION" >> $GITHUB_OUTPUT
553
+ echo "bump_type=${BUMP_TYPE:-custom}" >> $GITHUB_OUTPUT
554
+
555
+ export VERSION TAG="v$VERSION"
556
+
557
+ log_release_event "version_determination" "completed" "Version determined successfully" \
558
+ "version" "$VERSION" \
559
+ "bump_type" "${BUMP_TYPE:-custom}"
560
+
561
+ - name: Configure Git
562
+ if: inputs.release_strategy == 'standard-version'
563
+ run: |
564
+ git config user.name "github-actions[bot]"
565
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
566
+
567
+ - name: Generate Changelog
568
+ id: changelog
569
+ run: |
570
+ source ./release-logger.sh
571
+
572
+ log_release_event "changelog_generation" "started" "Generating changelog and release notes"
573
+
574
+ # Generate changelog based on strategy
575
+ if [ "${{ inputs.release_strategy }}" == "standard-version" ]; then
576
+ npx standard-version --release-as ${{ steps.version.outputs.version }} --skip.tag --releaseCommitMessageFormat "chore(release): {{currentTag}} [skip ci]"
577
+ else
578
+ # Custom changelog generation
579
+ LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
580
+ if [ -n "$LAST_TAG" ]; then
581
+ COMMIT_RANGE="$LAST_TAG..HEAD"
582
+ else
583
+ COMMIT_RANGE="HEAD"
584
+ fi
585
+
586
+ # Generate changelog with categories
587
+ cat > CHANGELOG_ENTRY.md << EOF
588
+ ## [${{ steps.version.outputs.version }}] - $(date +%Y-%m-%d)
589
+
590
+ ### Release Information
591
+ - **Environment**: ${{ inputs.environment }}
592
+ - **Release Type**: ${{ inputs.release_strategy }}
593
+ - **Correlation ID**: ${{ needs.release_init.outputs.correlation_id }}
594
+ - **Emergency Release**: ${{ inputs.emergency_release }}
595
+ - **Approved By**: ${{ needs.release_approval.outputs.approver || 'N/A' }}
596
+
597
+ EOF
598
+
599
+ # Add commit categories
600
+ if git log $COMMIT_RANGE --format=%B | grep -qE "^(feat|feature)(\(.+\))?:"; then
601
+ echo "### ✨ Features" >> CHANGELOG_ENTRY.md
602
+ git log $COMMIT_RANGE --format="- %s (%h)" --grep="^feat" --grep="^feature" >> CHANGELOG_ENTRY.md
603
+ echo "" >> CHANGELOG_ENTRY.md
604
+ fi
605
+
606
+ if git log $COMMIT_RANGE --format=%B | grep -qE "^fix(\(.+\))?:"; then
607
+ echo "### 🐛 Bug Fixes" >> CHANGELOG_ENTRY.md
608
+ git log $COMMIT_RANGE --format="- %s (%h)" --grep="^fix" >> CHANGELOG_ENTRY.md
609
+ echo "" >> CHANGELOG_ENTRY.md
610
+ fi
611
+
612
+ # Update CHANGELOG.md
613
+ if [ -f CHANGELOG.md ]; then
614
+ cp CHANGELOG.md CHANGELOG.md.bak
615
+ echo -e "# Changelog\n" > CHANGELOG.md
616
+ cat CHANGELOG_ENTRY.md >> CHANGELOG.md
617
+ echo "" >> CHANGELOG.md
618
+ tail -n +2 CHANGELOG.md.bak >> CHANGELOG.md
619
+ else
620
+ echo -e "# Changelog\n" > CHANGELOG.md
621
+ cat CHANGELOG_ENTRY.md >> CHANGELOG.md
622
+ fi
623
+ fi
624
+
625
+ echo "generated=true" >> $GITHUB_OUTPUT
626
+
627
+ log_release_event "changelog_generation" "completed" "Changelog generated successfully"
628
+
629
+ - name: Push Changelog Changes
630
+ if: inputs.release_strategy == 'standard-version'
631
+ run: |
632
+ git push origin HEAD:${{ github.ref_name }}
633
+
634
+ - name: Generate SBOM
635
+ if: ${{ inputs.generate_sbom == true }}
636
+ continue-on-error: true
637
+ run: |
638
+ source ./release-logger.sh
639
+
640
+ log_release_event "sbom_generation" "started" "Generating Software Bill of Materials"
641
+
642
+ # Install SBOM generator
643
+ npm install -g @cyclonedx/cyclonedx-npm
644
+
645
+ # Generate SBOM in CycloneDX format
646
+ # Note: Using || true to prevent failure on warnings
647
+ cyclonedx-npm --output-format json --output-file sbom.json || {
648
+ echo "::warning::SBOM generation encountered issues but continuing"
649
+ echo '{"components":[],"metadata":{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}}' > sbom.json
650
+ }
651
+
652
+ # Add release metadata to SBOM
653
+ jq --arg version "${{ steps.version.outputs.version }}" \
654
+ --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
655
+ --arg sha "${{ github.sha }}" \
656
+ '.metadata.component.version = $version |
657
+ .metadata.timestamp = $timestamp |
658
+ .metadata.component["bom-ref"] = $sha' \
659
+ sbom.json > sbom-release.json
660
+
661
+ mv sbom-release.json sbom.json
662
+
663
+ log_release_event "sbom_generation" "completed" "SBOM generated successfully" \
664
+ "format" "CycloneDX" \
665
+ "components" "$(jq '.components | length' sbom.json)"
666
+
667
+ - name: Upload Version Artifacts
668
+ uses: actions/upload-artifact@v4
669
+ with:
670
+ name: version-artifacts-${{ github.run_id }}
671
+ path: |
672
+ release-events.jsonl
673
+ CHANGELOG.md
674
+ CHANGELOG_ENTRY.md
675
+ sbom.json
676
+ retention-days: 90
677
+
678
+ # Release signing
679
+ release_signing:
680
+ name: 🔏 Release Signing
681
+ needs: [release_init, version]
682
+ if: inputs.require_signatures == true
683
+ runs-on: ubuntu-latest
684
+ outputs:
685
+ signed: ${{ steps.sign.outputs.signed }}
686
+ signature_files: ${{ steps.sign.outputs.files }}
687
+ steps:
688
+ - uses: actions/checkout@v4
689
+ with:
690
+ fetch-depth: 0
691
+ ref: ${{ github.sha }}
692
+
693
+ - name: Download Version Artifacts
694
+ uses: actions/download-artifact@v4
695
+ with:
696
+ name: version-artifacts-${{ github.run_id }}
697
+
698
+ - name: Setup Signing Environment
699
+ id: setup
700
+ run: |
701
+ # Install signing tools
702
+ if [ "${{ runner.os }}" == "Linux" ]; then
703
+ sudo apt-get update
704
+ sudo apt-get install -y gnupg2
705
+ fi
706
+
707
+ # Check if signing key is available
708
+ if [ -n "${{ secrets.RELEASE_SIGNING_KEY }}" ]; then
709
+ echo "✅ Signing key available"
710
+ echo "signing_available=true" >> $GITHUB_OUTPUT
711
+ else
712
+ echo "⚠️ No signing key available"
713
+ if [ "${{ inputs.require_signatures }}" == "true" ]; then
714
+ echo "::error::Signatures required but RELEASE_SIGNING_KEY not configured"
715
+ exit 1
716
+ fi
717
+ echo "signing_available=false" >> $GITHUB_OUTPUT
718
+ fi
719
+
720
+ - name: Import Signing Key
721
+ if: steps.setup.outputs.signing_available == 'true'
722
+ run: |
723
+ # Import GPG key
724
+ echo "${{ secrets.RELEASE_SIGNING_KEY }}" | base64 -d > signing.key
725
+
726
+ if [ -n "${{ secrets.SIGNING_KEY_PASSPHRASE }}" ]; then
727
+ echo "${{ secrets.SIGNING_KEY_PASSPHRASE }}" | \
728
+ gpg --batch --yes --passphrase-fd 0 --import signing.key
729
+ else
730
+ gpg --batch --yes --import signing.key
731
+ fi
732
+
733
+ rm -f signing.key
734
+
735
+ # List imported keys
736
+ gpg --list-secret-keys
737
+
738
+ # Configure git
739
+ git config --global user.signingkey "${{ secrets.SIGNING_KEY_ID || secrets.RELEASE_SIGNING_KEY_ID }}"
740
+ git config --global commit.gpgsign true
741
+ git config --global tag.gpgsign true
742
+
743
+ - name: Sign Release Artifacts
744
+ id: sign
745
+ if: steps.setup.outputs.signing_available == 'true'
746
+ run: |
747
+ # Function to sign files
748
+ sign_artifact() {
749
+ local file="$1"
750
+ local sig_file="${file}.sig"
751
+ local sha_file="${file}.sha256"
752
+
753
+ if [ -f "$file" ]; then
754
+ # Create detached signature
755
+ gpg --armor --detach-sign \
756
+ --local-user "${{ secrets.SIGNING_KEY_ID || secrets.RELEASE_SIGNING_KEY_ID }}" \
757
+ --output "$sig_file" \
758
+ "$file"
759
+
760
+ # Generate checksum
761
+ sha256sum "$file" | cut -d' ' -f1 > "$sha_file"
762
+
763
+ echo "✅ Signed: $file"
764
+ return 0
765
+ else
766
+ echo "⚠️ File not found: $file"
767
+ return 1
768
+ fi
769
+ }
770
+
771
+ # Sign release artifacts
772
+ SIGNED_FILES=()
773
+
774
+ # Sign changelog
775
+ if sign_artifact "CHANGELOG.md"; then
776
+ SIGNED_FILES+=("CHANGELOG.md")
777
+ fi
778
+
779
+ # Sign SBOM if generated
780
+ if [ -f "sbom.json" ] && sign_artifact "sbom.json"; then
781
+ SIGNED_FILES+=("sbom.json")
782
+ fi
783
+
784
+ # Sign package.json
785
+ if sign_artifact "package.json"; then
786
+ SIGNED_FILES+=("package.json")
787
+ fi
788
+
789
+ # Create release manifest
790
+ cat > release-manifest.json << EOF
791
+ {
792
+ "version": "${{ needs.version.outputs.version }}",
793
+ "tag": "${{ needs.version.outputs.tag }}",
794
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
795
+ "sha": "${{ github.sha }}",
796
+ "signed_by": "${{ secrets.SIGNING_KEY_ID || 'release-bot' }}",
797
+ "artifacts": [
798
+ EOF
799
+
800
+ # Add artifact entries
801
+ first=true
802
+ for file in "${SIGNED_FILES[@]}"; do
803
+ if [ "$first" != "true" ]; then
804
+ echo "," >> release-manifest.json
805
+ fi
806
+ first=false
807
+
808
+ cat >> release-manifest.json << EOF
809
+ {
810
+ "name": "$file",
811
+ "sha256": "$(cat ${file}.sha256)",
812
+ "signature": "$(cat ${file}.sig | base64 -w0)"
813
+ }
814
+ EOF
815
+ done
816
+
817
+ cat >> release-manifest.json << EOF
818
+ ]
819
+ }
820
+ EOF
821
+
822
+ # Sign the manifest
823
+ sign_artifact "release-manifest.json"
824
+
825
+ echo "signed=true" >> $GITHUB_OUTPUT
826
+ echo "files=${SIGNED_FILES[*]}" >> $GITHUB_OUTPUT
827
+
828
+ - name: Create Signed Git Tag
829
+ if: steps.setup.outputs.signing_available == 'true'
830
+ run: |
831
+ # Create signed tag
832
+ if [ -n "${{ secrets.SIGNING_KEY_PASSPHRASE }}" ]; then
833
+ echo "${{ secrets.SIGNING_KEY_PASSPHRASE }}" | \
834
+ git tag -s "${{ needs.version.outputs.tag }}" \
835
+ -m "Release ${{ needs.version.outputs.version }}" \
836
+ -m "Signed-off-by: ${{ github.actor }}" \
837
+ -m "Correlation ID: ${{ needs.release_init.outputs.correlation_id }}"
838
+ else
839
+ git tag -s "${{ needs.version.outputs.tag }}" \
840
+ -m "Release ${{ needs.version.outputs.version }}" \
841
+ -m "Signed-off-by: ${{ github.actor }}" \
842
+ -m "Correlation ID: ${{ needs.release_init.outputs.correlation_id }}"
843
+ fi
844
+
845
+ echo "✅ Created signed tag: ${{ needs.version.outputs.tag }}"
846
+
847
+ - name: Upload Signed Artifacts
848
+ if: steps.sign.outputs.signed == 'true'
849
+ uses: actions/upload-artifact@v4
850
+ with:
851
+ name: signed-artifacts-${{ github.run_id }}
852
+ path: |
853
+ *.sig
854
+ *.sha256
855
+ release-manifest.json
856
+ retention-days: 365
857
+
858
+ # Release attestation
859
+ release_attestation:
860
+ name: 📜 Release Attestation
861
+ needs: [release_init, version, quality, release_signing]
862
+ if: |
863
+ always() &&
864
+ !cancelled() &&
865
+ needs.version.result == 'success' &&
866
+ (needs.release_signing.result == 'success' || needs.release_signing.result == 'skipped')
867
+ runs-on: ubuntu-latest
868
+ outputs:
869
+ attestation_created: ${{ steps.attestation.outputs.created }}
870
+ attestation_url: ${{ steps.attestation.outputs.url }}
871
+ steps:
872
+ - name: Generate SLSA Provenance
873
+ id: provenance
874
+ run: |
875
+ # Create SLSA provenance attestation
876
+ cat > provenance.json << EOF
877
+ {
878
+ "_type": "https://in-toto.io/Statement/v0.1",
879
+ "predicateType": "https://slsa.dev/provenance/v0.2",
880
+ "subject": [{
881
+ "name": "${{ github.repository }}",
882
+ "digest": {
883
+ "sha256": "${{ github.sha }}"
884
+ }
885
+ }],
886
+ "predicate": {
887
+ "builder": {
888
+ "id": "https://github.com/actions/runner"
889
+ },
890
+ "buildType": "https://github.com/slsa-framework/slsa-github-generator/generic@v1",
891
+ "invocation": {
892
+ "configSource": {
893
+ "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
894
+ "digest": {
895
+ "sha1": "${{ github.sha }}"
896
+ },
897
+ "entryPoint": ".github/workflows/release.yml"
898
+ },
899
+ "parameters": {
900
+ "environment": "${{ inputs.environment }}",
901
+ "version": "${{ needs.version.outputs.version }}",
902
+ "release_strategy": "${{ inputs.release_strategy }}",
903
+ "emergency_release": ${{ inputs.emergency_release }},
904
+ "approval_required": ${{ inputs.require_approval }},
905
+ "signatures_required": ${{ inputs.require_signatures }}
906
+ },
907
+ "environment": {
908
+ "github_run_id": "${{ github.run_id }}",
909
+ "github_run_number": "${{ github.run_number }}",
910
+ "github_actor": "${{ github.actor }}",
911
+ "github_event_name": "${{ github.event_name }}"
912
+ }
913
+ },
914
+ "buildConfig": {
915
+ "version": 1,
916
+ "steps": [
917
+ {
918
+ "command": ["quality-checks"],
919
+ "env": null
920
+ },
921
+ {
922
+ "command": ["version-determination"],
923
+ "env": null
924
+ },
925
+ {
926
+ "command": ["release-creation"],
927
+ "env": null
928
+ }
929
+ ]
930
+ },
931
+ "metadata": {
932
+ "completeness": {
933
+ "parameters": true,
934
+ "environment": true,
935
+ "materials": false
936
+ },
937
+ "reproducible": false,
938
+ "buildStartedOn": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
939
+ "buildFinishedOn": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
940
+ }
941
+ }
942
+ }
943
+ EOF
944
+
945
+ - name: Create Release Attestation
946
+ id: attestation
947
+ run: |
948
+ # Create comprehensive release attestation
949
+ cat > release-attestation.json << EOF
950
+ {
951
+ "version": "${{ needs.version.outputs.version }}",
952
+ "tag": "${{ needs.version.outputs.tag }}",
953
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
954
+ "correlation_id": "${{ needs.release_init.outputs.correlation_id }}",
955
+ "environment": "${{ inputs.environment }}",
956
+ "attestations": {
957
+ "quality": {
958
+ "passed": ${{ needs.quality.result == 'success' }},
959
+ "skipped_checks": "${{ inputs.skip_jobs }}",
960
+ "workflow_result": "${{ needs.quality.result }}"
961
+ },
962
+ "approvals": {
963
+ "required": ${{ inputs.require_approval }},
964
+ "received": ${{ needs.release_approval.outputs.approved == 'true' || inputs.require_approval == false }},
965
+ "approver": "${{ needs.release_approval.outputs.approver || 'N/A' }}",
966
+ "approval_time": "${{ needs.release_approval.outputs.approval_time || 'N/A' }}"
967
+ },
968
+ "signatures": {
969
+ "required": ${{ inputs.require_signatures }},
970
+ "artifacts_signed": ${{ needs.release_signing.outputs.signed == 'true' }},
971
+ "signed_files": "${{ needs.release_signing.outputs.signature_files || 'none' }}"
972
+ },
973
+ "compliance": {
974
+ "frameworks": ["SOC2", "ISO27001"],
975
+ "emergency_release": ${{ inputs.emergency_release }},
976
+ "blackout_override": ${{ inputs.override_blackout }},
977
+ "dependency_check": ${{ inputs.check_dependencies }}
978
+ },
979
+ "integrity": {
980
+ "source_sha": "${{ github.sha }}",
981
+ "repository": "${{ github.repository }}",
982
+ "ref": "${{ github.ref }}",
983
+ "actor": "${{ github.actor }}"
984
+ },
985
+ "sbom": {
986
+ "generated": ${{ inputs.generate_sbom }},
987
+ "format": "CycloneDX",
988
+ "version": "1.4"
989
+ }
990
+ },
991
+ "metadata": {
992
+ "workflow_id": "${{ github.run_id }}",
993
+ "workflow_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
994
+ "initiated_by": "${{ github.actor }}",
995
+ "event_type": "${{ github.event_name }}"
996
+ }
997
+ }
998
+ EOF
999
+
1000
+ echo "created=true" >> $GITHUB_OUTPUT
1001
+ echo "url=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" >> $GITHUB_OUTPUT
1002
+
1003
+ - name: Store Attestations
1004
+ uses: actions/upload-artifact@v4
1005
+ with:
1006
+ name: release-attestations-${{ needs.version.outputs.version }}
1007
+ path: |
1008
+ provenance.json
1009
+ release-attestation.json
1010
+ retention-days: 365
1011
+
1012
+ # Create GitHub Release with enterprise features
1013
+ github_release:
1014
+ name: 🎉 Create GitHub Release
1015
+ needs: [release_init, version, release_signing, release_attestation]
1016
+ runs-on: ubuntu-latest
1017
+ if: |
1018
+ always() &&
1019
+ !cancelled() &&
1020
+ needs.version.result == 'success' &&
1021
+ (needs.release_signing.result == 'success' || needs.release_signing.result == 'skipped') &&
1022
+ (needs.release_attestation.result == 'success' || needs.release_attestation.result == 'skipped')
1023
+ outputs:
1024
+ release_url: ${{ steps.create_release.outputs.html_url }}
1025
+ release_id: ${{ steps.create_release.outputs.id }}
1026
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
1027
+ steps:
1028
+ - uses: actions/checkout@v4
1029
+ with:
1030
+ fetch-depth: 0
1031
+
1032
+ - name: Download All Artifacts
1033
+ uses: actions/download-artifact@v4
1034
+ with:
1035
+ pattern: '*-${{ github.run_id }}'
1036
+
1037
+ - name: Generate Release Notes
1038
+ id: release_notes
1039
+ run: |
1040
+ # Create release notes
1041
+ cat > RELEASE_NOTES.md << 'EOF'
1042
+ # Release ${{ needs.version.outputs.version }}
1043
+
1044
+ ## 📊 Release Information
1045
+ - **Version**: ${{ needs.version.outputs.version }}
1046
+ - **Environment**: ${{ inputs.environment }}
1047
+ - **Release Date**: $(date +"%Y-%m-%d %H:%M:%S UTC")
1048
+ - **Release ID**: ${{ needs.release_init.outputs.correlation_id }}
1049
+ - **Emergency Release**: ${{ inputs.emergency_release && '⚠️ Yes' || 'No' }}
1050
+
1051
+ ## 🔐 Security & Compliance
1052
+ - **Signed Release**: ${{ needs.release_signing.outputs.signed == 'true' && '✅ Yes' || '❌ No' }}
1053
+ - **Approval Required**: ${{ inputs.require_approval && '✅ Yes' || 'No' }}
1054
+ - **SBOM Generated**: ${{ inputs.generate_sbom && '✅ Yes' || 'No' }}
1055
+ - **Attestation**: ✅ [View Attestation](${{ needs.release_attestation.outputs.attestation_url }})
1056
+
1057
+ ## 📝 Changelog
1058
+ EOF
1059
+
1060
+ # Include changelog content
1061
+ if [ -f "version-artifacts-${{ github.run_id }}/CHANGELOG_ENTRY.md" ]; then
1062
+ cat "version-artifacts-${{ github.run_id }}/CHANGELOG_ENTRY.md" >> RELEASE_NOTES.md
1063
+ fi
1064
+
1065
+ # Add verification instructions if signed
1066
+ if [ "${{ needs.release_signing.outputs.signed }}" == "true" ]; then
1067
+ cat >> RELEASE_NOTES.md << 'EOF'
1068
+
1069
+ ## 🔒 Verification
1070
+
1071
+ This release is cryptographically signed. To verify:
1072
+
1073
+ ```bash
1074
+ # Download signature files
1075
+ curl -L -o release-manifest.json.sig <signature-url>
1076
+ curl -L -o release-manifest.json <manifest-url>
1077
+
1078
+ # Import public key
1079
+ gpg --import release-key.pub
1080
+
1081
+ # Verify signature
1082
+ gpg --verify release-manifest.json.sig release-manifest.json
1083
+ ```
1084
+ EOF
1085
+ fi
1086
+
1087
+ cat >> RELEASE_NOTES.md << 'EOF'
1088
+
1089
+ ## 🔗 Links
1090
+ - [Workflow Run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
1091
+ - [Compare Changes](https://github.com/${{ github.repository }}/compare/${{ github.event.before }}...${{ needs.version.outputs.tag }})
1092
+ EOF
1093
+
1094
+ - name: Create GitHub Release
1095
+ id: create_release
1096
+ env:
1097
+ GITHUB_TOKEN: ${{ github.token }}
1098
+ run: |
1099
+ # Prepare release data
1100
+ RELEASE_DATA=$(jq -n \
1101
+ --arg tag "${{ needs.version.outputs.tag }}" \
1102
+ --arg name "Release ${{ needs.version.outputs.version }}" \
1103
+ --arg body "$(cat RELEASE_NOTES.md)" \
1104
+ --arg target "${{ github.sha }}" \
1105
+ --argjson prerelease "${{ needs.version.outputs.prerelease == 'true' }}" \
1106
+ --argjson draft false \
1107
+ '{
1108
+ tag_name: $tag,
1109
+ name: $name,
1110
+ body: $body,
1111
+ target_commitish: $target,
1112
+ prerelease: $prerelease,
1113
+ draft: $draft
1114
+ }')
1115
+
1116
+ # Create release
1117
+ RESPONSE=$(curl -X POST \
1118
+ -H "Authorization: token $GITHUB_TOKEN" \
1119
+ -H "Accept: application/vnd.github.v3+json" \
1120
+ -d "$RELEASE_DATA" \
1121
+ "https://api.github.com/repos/${{ github.repository }}/releases")
1122
+
1123
+ # Extract release information
1124
+ echo "$RESPONSE" | jq -r '.html_url' > .release_url
1125
+ echo "$RESPONSE" | jq -r '.id' > .release_id
1126
+ echo "$RESPONSE" | jq -r '.upload_url' > .upload_url
1127
+
1128
+ echo "html_url=$(cat .release_url)" >> $GITHUB_OUTPUT
1129
+ echo "id=$(cat .release_id)" >> $GITHUB_OUTPUT
1130
+ echo "upload_url=$(cat .upload_url)" >> $GITHUB_OUTPUT
1131
+
1132
+ - name: Upload Release Assets
1133
+ if: needs.release_signing.outputs.signed == 'true'
1134
+ env:
1135
+ GITHUB_TOKEN: ${{ github.token }}
1136
+ run: |
1137
+ UPLOAD_URL=$(cat .upload_url | sed 's/{?name,label}//')
1138
+
1139
+ # Upload signature files
1140
+ for sig_file in signed-artifacts-${{ github.run_id }}/*.sig; do
1141
+ if [ -f "$sig_file" ]; then
1142
+ filename=$(basename "$sig_file")
1143
+ curl -X POST \
1144
+ -H "Authorization: token $GITHUB_TOKEN" \
1145
+ -H "Content-Type: application/pgp-signature" \
1146
+ --data-binary @"$sig_file" \
1147
+ "${UPLOAD_URL}?name=${filename}"
1148
+ fi
1149
+ done
1150
+
1151
+ # Upload checksums
1152
+ for sha_file in signed-artifacts-${{ github.run_id }}/*.sha256; do
1153
+ if [ -f "$sha_file" ]; then
1154
+ filename=$(basename "$sha_file")
1155
+ curl -X POST \
1156
+ -H "Authorization: token $GITHUB_TOKEN" \
1157
+ -H "Content-Type: text/plain" \
1158
+ --data-binary @"$sha_file" \
1159
+ "${UPLOAD_URL}?name=${filename}"
1160
+ fi
1161
+ done
1162
+
1163
+ # Upload release manifest
1164
+ if [ -f "signed-artifacts-${{ github.run_id }}/release-manifest.json" ]; then
1165
+ curl -X POST \
1166
+ -H "Authorization: token $GITHUB_TOKEN" \
1167
+ -H "Content-Type: application/json" \
1168
+ --data-binary @"signed-artifacts-${{ github.run_id }}/release-manifest.json" \
1169
+ "${UPLOAD_URL}?name=release-manifest.json"
1170
+ fi
1171
+
1172
+ # Upload SBOM if generated
1173
+ if [ -f "version-artifacts-${{ github.run_id }}/sbom.json" ]; then
1174
+ curl -X POST \
1175
+ -H "Authorization: token $GITHUB_TOKEN" \
1176
+ -H "Content-Type: application/json" \
1177
+ --data-binary @"version-artifacts-${{ github.run_id }}/sbom.json" \
1178
+ "${UPLOAD_URL}?name=sbom.json"
1179
+ fi
1180
+
1181
+ # Sentry Release Creation
1182
+ sentry_release:
1183
+ name: 🚨 Create Sentry Release
1184
+ needs: [release_init, version]
1185
+ runs-on: ubuntu-latest
1186
+ if: |
1187
+ always() &&
1188
+ !cancelled() &&
1189
+ needs.version.result == 'success'
1190
+ outputs:
1191
+ sentry_release_created: ${{ steps.set_outputs.outputs.created }}
1192
+ sentry_release_url: ${{ steps.set_outputs.outputs.url }}
1193
+ steps:
1194
+ - uses: actions/checkout@v4
1195
+ with:
1196
+ fetch-depth: 0
1197
+
1198
+ - name: Check Sentry Configuration
1199
+ id: check_config
1200
+ env:
1201
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
1202
+ SENTRY_ORG: ${{ vars.SENTRY_ORG }}
1203
+ SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }}
1204
+ run: |
1205
+ # Check if all required Sentry secrets are available
1206
+ if [ -z "$SENTRY_AUTH_TOKEN" ] || [ -z "$SENTRY_ORG" ] || [ -z "$SENTRY_PROJECT" ]; then
1207
+ echo "⚠️ Sentry configuration incomplete - skipping Sentry release" >> $GITHUB_STEP_SUMMARY
1208
+ echo "configured=false" >> $GITHUB_OUTPUT
1209
+ # Set default outputs for skipped job
1210
+ echo "created=false" >> $GITHUB_OUTPUT
1211
+ echo "url=" >> $GITHUB_OUTPUT
1212
+ else
1213
+ echo "✅ Sentry configuration verified" >> $GITHUB_STEP_SUMMARY
1214
+ echo "configured=true" >> $GITHUB_OUTPUT
1215
+ fi
1216
+
1217
+ - name: Create Sentry Release
1218
+ id: sentry
1219
+ if: steps.check_config.outputs.configured == 'true'
1220
+ uses: getsentry/action-release@v1
1221
+ env:
1222
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
1223
+ SENTRY_ORG: ${{ vars.SENTRY_ORG }}
1224
+ SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }}
1225
+ with:
1226
+ environment: ${{ inputs.environment }}
1227
+ version: ${{ needs.version.outputs.version }}
1228
+ sourcemaps: ${{ inputs.sourcemaps }}
1229
+
1230
+ - name: Update Release Summary
1231
+ if: steps.check_config.outputs.configured == 'true' && steps.sentry.outcome == 'success'
1232
+ run: |
1233
+ echo "## 🚨 Sentry Release" >> $GITHUB_STEP_SUMMARY
1234
+ echo "" >> $GITHUB_STEP_SUMMARY
1235
+ echo "✅ Sentry release created successfully" >> $GITHUB_STEP_SUMMARY
1236
+ echo "- **Version**: ${{ needs.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
1237
+ echo "- **Environment**: ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY
1238
+ if [ -n "${{ inputs.sourcemaps }}" ]; then
1239
+ echo "- **Source Maps**: Uploaded from \`${{ inputs.sourcemaps }}\`" >> $GITHUB_STEP_SUMMARY
1240
+ fi
1241
+ echo "- **Organization**: ${{ vars.SENTRY_ORG }}" >> $GITHUB_STEP_SUMMARY
1242
+ echo "- **Project**: ${{ vars.SENTRY_PROJECT }}" >> $GITHUB_STEP_SUMMARY
1243
+
1244
+ - name: Set Outputs
1245
+ if: always()
1246
+ id: set_outputs
1247
+ run: |
1248
+ # Set outputs based on whether Sentry was configured and successful
1249
+ if [ "${{ steps.check_config.outputs.configured }}" == "true" ] && [ "${{ steps.sentry.outcome }}" == "success" ]; then
1250
+ echo "created=true" >> $GITHUB_OUTPUT
1251
+ echo "url=https://sentry.io/organizations/${{ vars.SENTRY_ORG }}/releases/${{ needs.version.outputs.version }}/" >> $GITHUB_OUTPUT
1252
+ else
1253
+ echo "created=false" >> $GITHUB_OUTPUT
1254
+ echo "url=" >> $GITHUB_OUTPUT
1255
+ fi
1256
+
1257
+ # Check Jira Setup
1258
+ checks_jira_setup:
1259
+ name: 📋 Check Jira Setup
1260
+ needs: [version]
1261
+ runs-on: ubuntu-latest
1262
+ outputs:
1263
+ has_jira_setup: ${{ steps.check.outputs.has_jira_setup }}
1264
+ steps:
1265
+ - name: Checkout
1266
+ uses: actions/checkout@v4
1267
+ - uses: noliran/branch-based-secrets@v1
1268
+ with:
1269
+ secrets: JIRA_AUTOMATION_WEBHOOK
1270
+ - name: Check Jira Configuration
1271
+ id: check
1272
+ run: |
1273
+ if [[ -z "${JIRA_AUTOMATION_WEBHOOK}" ]] || [[ -z "${{ vars.JIRA_PROJECT_KEY }}" ]]; then
1274
+ echo "⚠️ Jira configuration incomplete - skipping Jira release" >> $GITHUB_STEP_SUMMARY
1275
+ if [[ -z "${JIRA_AUTOMATION_WEBHOOK}" ]]; then
1276
+ echo " - Missing: JIRA_AUTOMATION_WEBHOOK secret" >> $GITHUB_STEP_SUMMARY
1277
+ fi
1278
+ if [[ -z "${{ vars.JIRA_PROJECT_KEY }}" ]]; then
1279
+ echo " - Missing: JIRA_PROJECT_KEY variable" >> $GITHUB_STEP_SUMMARY
1280
+ fi
1281
+ echo "has_jira_setup=false" >> $GITHUB_OUTPUT
1282
+ else
1283
+ echo "✅ Jira configuration verified" >> $GITHUB_STEP_SUMMARY
1284
+ echo "has_jira_setup=true" >> $GITHUB_OUTPUT
1285
+ fi
1286
+ shell: bash
1287
+ env:
1288
+ JIRA_AUTOMATION_WEBHOOK: ${{ secrets[env.JIRA_AUTOMATION_WEBHOOK_NAME] }}
1289
+
1290
+ # Create Jira Release
1291
+ jira_release:
1292
+ name: 📋 Create Jira Release
1293
+ runs-on: ubuntu-latest
1294
+ if: |
1295
+ always() &&
1296
+ !cancelled() &&
1297
+ needs.version.result == 'success' &&
1298
+ needs.checks_jira_setup.outputs.has_jira_setup == 'true'
1299
+ needs: [version, checks_jira_setup]
1300
+ outputs:
1301
+ jira_release_created: ${{ steps.create.outputs.created }}
1302
+ jira_release_url: ${{ steps.create.outputs.url }}
1303
+ steps:
1304
+ - name: Checkout
1305
+ uses: actions/checkout@v4
1306
+ - uses: noliran/branch-based-secrets@v1
1307
+ with:
1308
+ secrets: JIRA_AUTOMATION_WEBHOOK
1309
+ - name: Create Jira Release
1310
+ id: create
1311
+ continue-on-error: true
1312
+ uses: GeoWerkstatt/create-jira-release@v1
1313
+ with:
1314
+ jira-project-key: ${{ vars.JIRA_PROJECT_KEY }}
1315
+ jira-automation-webhook: ${{ secrets[env.JIRA_AUTOMATION_WEBHOOK_NAME] }}
1316
+ build-version: '${{ github.event.repository.name }} v${{ needs.version.outputs.version }}'
1317
+ - name: Update Release Summary
1318
+ if: steps.create.outcome == 'success'
1319
+ run: |
1320
+ echo "## 📋 Jira Release" >> $GITHUB_STEP_SUMMARY
1321
+ echo "" >> $GITHUB_STEP_SUMMARY
1322
+ echo "✅ Jira release created successfully" >> $GITHUB_STEP_SUMMARY
1323
+ echo "- **Version**: ${{ needs.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
1324
+ echo "- **Project**: ${{ vars.JIRA_PROJECT_KEY }}" >> $GITHUB_STEP_SUMMARY
1325
+ echo "- **Build Version**: ${{ github.event.repository.name }} v${{ needs.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
1326
+ - name: Set Outputs
1327
+ if: always()
1328
+ run: |
1329
+ if [ "${{ steps.create.outcome }}" == "success" ]; then
1330
+ echo "created=true" >> $GITHUB_OUTPUT
1331
+ echo "url=https://${{ vars.JIRA_BASE_URL || 'company.atlassian.net' }}/projects/${{ vars.JIRA_PROJECT_KEY }}/versions" >> $GITHUB_OUTPUT
1332
+ else
1333
+ echo "created=false" >> $GITHUB_OUTPUT
1334
+ echo "url=" >> $GITHUB_OUTPUT
1335
+ fi
1336
+
1337
+ # Release compliance validation
1338
+ release_compliance:
1339
+ name: ✅ Release Compliance
1340
+ needs:
1341
+ [
1342
+ release_init,
1343
+ version,
1344
+ quality,
1345
+ release_signing,
1346
+ release_attestation,
1347
+ github_release,
1348
+ sentry_release,
1349
+ jira_release,
1350
+ ]
1351
+ if: always()
1352
+ runs-on: ubuntu-latest
1353
+ outputs:
1354
+ status: ${{ steps.compliance.outputs.status }}
1355
+ report_url: ${{ steps.compliance.outputs.report_url }}
1356
+ steps:
1357
+ - name: Download All Artifacts
1358
+ uses: actions/download-artifact@v4
1359
+ with:
1360
+ pattern: '*-${{ github.run_id }}'
1361
+
1362
+ - name: Compliance Validation
1363
+ id: compliance
1364
+ run: |
1365
+ # Initialize compliance checks
1366
+ COMPLIANCE_PASSED=true
1367
+ COMPLIANCE_ISSUES=()
1368
+
1369
+ # Check 1: Required approvals
1370
+ if [ "${{ inputs.require_approval }}" == "true" ]; then
1371
+ if [ "${{ needs.release_approval.outputs.approved }}" != "true" ]; then
1372
+ COMPLIANCE_PASSED=false
1373
+ COMPLIANCE_ISSUES+=("Missing required approval")
1374
+ fi
1375
+ fi
1376
+
1377
+ # Check 2: Required signatures
1378
+ if [ "${{ inputs.require_signatures }}" == "true" ]; then
1379
+ if [ "${{ needs.release_signing.outputs.signed }}" != "true" ]; then
1380
+ COMPLIANCE_PASSED=false
1381
+ COMPLIANCE_ISSUES+=("Missing required signatures")
1382
+ fi
1383
+ fi
1384
+
1385
+ # Check 3: Quality gates
1386
+ if [ "${{ needs.quality.result }}" != "success" ] && [ "${{ needs.quality.result }}" != "skipped" ]; then
1387
+ COMPLIANCE_PASSED=false
1388
+ COMPLIANCE_ISSUES+=("Quality checks failed")
1389
+ fi
1390
+
1391
+ # Check 4: Attestation
1392
+ if [ ! -f "release-attestations-${{ needs.version.outputs.version }}/release-attestation.json" ]; then
1393
+ COMPLIANCE_PASSED=false
1394
+ COMPLIANCE_ISSUES+=("Missing release attestation")
1395
+ fi
1396
+
1397
+ # Check 5: Emergency release documentation
1398
+ if [ "${{ inputs.emergency_release }}" == "true" ]; then
1399
+ echo "⚠️ Emergency release - additional documentation required"
1400
+ fi
1401
+
1402
+ # Generate compliance report
1403
+ cat > compliance-report.json << EOF
1404
+ {
1405
+ "release_version": "${{ needs.version.outputs.version }}",
1406
+ "compliance_status": "$([[ $COMPLIANCE_PASSED == true ]] && echo 'PASSED' || echo 'FAILED')",
1407
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
1408
+ "correlation_id": "${{ needs.release_init.outputs.correlation_id }}",
1409
+ "frameworks": {
1410
+ "SOC2": {
1411
+ "CC8.1_change_management": true,
1412
+ "CC7.2_monitoring": true,
1413
+ "CC7.3_security_monitoring": ${{ needs.release_signing.outputs.signed == 'true' }},
1414
+ "CC6.1_logical_access": ${{ inputs.require_approval }}
1415
+ },
1416
+ "ISO27001": {
1417
+ "A.12.1_operational_procedures": true,
1418
+ "A.14.2_secure_development": true,
1419
+ "A.12.4_logging_monitoring": true,
1420
+ "A.14.2.8_security_testing": ${{ needs.quality.result == 'success' }}
1421
+ },
1422
+ "SLSA": {
1423
+ "level": ${{ needs.release_signing.outputs.signed == 'true' && '2' || '1' }},
1424
+ "source": true,
1425
+ "build": true,
1426
+ "provenance": true,
1427
+ "common_requirements": true
1428
+ }
1429
+ },
1430
+ "checks": {
1431
+ "approvals": {
1432
+ "required": ${{ inputs.require_approval }},
1433
+ "completed": ${{ needs.release_approval.outputs.approved == 'true' || inputs.require_approval == false }}
1434
+ },
1435
+ "signatures": {
1436
+ "required": ${{ inputs.require_signatures }},
1437
+ "completed": ${{ needs.release_signing.outputs.signed == 'true' || inputs.require_signatures == false }}
1438
+ },
1439
+ "quality": {
1440
+ "passed": ${{ needs.quality.result == 'success' || needs.quality.result == 'skipped' }},
1441
+ "skipped_checks": "${{ inputs.skip_jobs }}"
1442
+ },
1443
+ "attestation": {
1444
+ "created": ${{ needs.release_attestation.outputs.attestation_created == 'true' }}
1445
+ },
1446
+ "emergency_release": ${{ inputs.emergency_release }},
1447
+ "blackout_override": ${{ inputs.override_blackout }}
1448
+ },
1449
+ "issues": $(printf '%s\n' "${COMPLIANCE_ISSUES[@]}" | jq -R . | jq -s .)
1450
+ }
1451
+ EOF
1452
+
1453
+ # Set outputs
1454
+ echo "status=$([[ $COMPLIANCE_PASSED == true ]] && echo 'PASSED' || echo 'FAILED')" >> $GITHUB_OUTPUT
1455
+ echo "report_url=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" >> $GITHUB_OUTPUT
1456
+
1457
+ # Summary
1458
+ echo "## ✅ Compliance Validation" >> $GITHUB_STEP_SUMMARY
1459
+ echo "" >> $GITHUB_STEP_SUMMARY
1460
+ echo "**Status**: $([[ $COMPLIANCE_PASSED == true ]] && echo '✅ PASSED' || echo '❌ FAILED')" >> $GITHUB_STEP_SUMMARY
1461
+ echo "**Frameworks**: SOC2, ISO27001, SLSA Level ${{ needs.release_signing.outputs.signed == 'true' && '2' || '1' }}" >> $GITHUB_STEP_SUMMARY
1462
+
1463
+ if [ ${#COMPLIANCE_ISSUES[@]} -gt 0 ]; then
1464
+ echo "" >> $GITHUB_STEP_SUMMARY
1465
+ echo "### Issues Found" >> $GITHUB_STEP_SUMMARY
1466
+ printf -- '- %s\n' "${COMPLIANCE_ISSUES[@]}" >> $GITHUB_STEP_SUMMARY
1467
+ fi
1468
+
1469
+ - name: Generate Compliance Evidence Package
1470
+ run: |
1471
+ # Create evidence package
1472
+ mkdir -p compliance-evidence
1473
+
1474
+ # Copy all compliance-related artifacts
1475
+ find . -name "release-attestation*.json" -exec cp {} compliance-evidence/ \;
1476
+ find . -name "provenance.json" -exec cp {} compliance-evidence/ \;
1477
+ find . -name "compliance-report.json" -exec cp {} compliance-evidence/ \;
1478
+ find . -name "release-manifest.json*" -exec cp {} compliance-evidence/ \;
1479
+ find . -name "*.sig" -exec cp {} compliance-evidence/ \;
1480
+ find . -name "*.sha256" -exec cp {} compliance-evidence/ \;
1481
+
1482
+ # Create evidence summary
1483
+ cat > compliance-evidence/README.md << EOF
1484
+ # Release Compliance Evidence Package
1485
+
1486
+ **Release Version**: ${{ needs.version.outputs.version }}
1487
+ **Generated**: $(date -u +%Y-%m-%dT%H:%M:%SZ)
1488
+ **Environment**: ${{ inputs.environment }}
1489
+ **Correlation ID**: ${{ needs.release_init.outputs.correlation_id }}
1490
+
1491
+ ## Contents
1492
+
1493
+ - Release attestations and provenance
1494
+ - Digital signatures (if applicable)
1495
+ - Compliance validation report
1496
+ - Release manifest with checksums
1497
+ - Approval records (if applicable)
1498
+
1499
+ ## Compliance Status
1500
+
1501
+ **Overall Status**: ${{ steps.compliance.outputs.status }}
1502
+
1503
+ This release complies with:
1504
+ - SOC 2 Type II
1505
+ - ISO 27001:2022
1506
+ - SLSA Level ${{ needs.release_signing.outputs.signed == 'true' && '2' || '1' }}
1507
+
1508
+ ## Verification Instructions
1509
+
1510
+ 1. Verify signatures using the provided .sig files
1511
+ 2. Check SHA256 checksums against artifacts
1512
+ 3. Review attestation for build provenance
1513
+ 4. Validate compliance report status
1514
+
1515
+ ## Retention
1516
+
1517
+ This evidence package should be retained for 7 years per compliance requirements.
1518
+ EOF
1519
+
1520
+ # Create tarball
1521
+ tar -czf compliance-evidence-${{ needs.version.outputs.version }}.tar.gz compliance-evidence/
1522
+
1523
+ - name: Store Compliance Evidence
1524
+ uses: actions/upload-artifact@v4
1525
+ with:
1526
+ name: compliance-evidence-${{ needs.version.outputs.version }}
1527
+ path: compliance-evidence-${{ needs.version.outputs.version }}.tar.gz
1528
+ retention-days: 2555 # 7 years for compliance
1529
+
1530
+ # Final release summary
1531
+ release_summary:
1532
+ name: 📋 Release Summary
1533
+ needs:
1534
+ [
1535
+ release_init,
1536
+ quality,
1537
+ version,
1538
+ release_signing,
1539
+ release_attestation,
1540
+ github_release,
1541
+ sentry_release,
1542
+ jira_release,
1543
+ release_compliance,
1544
+ ]
1545
+ if: always()
1546
+ runs-on: ubuntu-latest
1547
+ steps:
1548
+ - name: Generate Final Summary
1549
+ run: |
1550
+ # Create comprehensive summary
1551
+ cat >> $GITHUB_STEP_SUMMARY << 'EOF'
1552
+
1553
+ # 🎉 Enterprise Release Complete
1554
+
1555
+ ## Release Information
1556
+ - **Version**: ${{ needs.version.outputs.version }}
1557
+ - **Tag**: ${{ needs.version.outputs.tag }}
1558
+ - **Environment**: ${{ inputs.environment }}
1559
+ - **Correlation ID**: ${{ needs.release_init.outputs.correlation_id }}
1560
+
1561
+ ## Enterprise Features
1562
+ - **Approval Required**: ${{ inputs.require_approval && '✅ Yes' || '❌ No' }}
1563
+ - **Signatures**: ${{ needs.release_signing.outputs.signed == 'true' && '✅ Signed' || '❌ Not Signed' }}
1564
+ - **SBOM**: ${{ inputs.generate_sbom && '✅ Generated' || '❌ Not Generated' }}
1565
+ - **Compliance**: ${{ needs.release_compliance.outputs.status }}
1566
+
1567
+ ## Execution Summary
1568
+ - **Total Duration**: $(($(date +%s) - ${{ needs.release_init.outputs.start_time }}))s
1569
+ - **Quality Checks**: ${{ needs.quality.result }}
1570
+ - **Version Creation**: ${{ needs.version.result }}
1571
+ - **GitHub Release**: ${{ needs.github_release.result }}
1572
+
1573
+ ## Outputs
1574
+ - **Release URL**: ${{ needs.github_release.outputs.release_url || 'N/A' }}
1575
+ - **Attestation**: ${{ needs.release_attestation.outputs.attestation_url || 'N/A' }}
1576
+ - **Compliance Report**: ${{ needs.release_compliance.outputs.report_url || 'N/A' }}
1577
+ - **Sentry Release**: ${{ needs.sentry_release.outputs.sentry_release_url || 'Not configured' }}
1578
+ - **Jira Release**: ${{ needs.jira_release.outputs.jira_release_url || 'Not configured' }}
1579
+
1580
+ ## Security & Compliance
1581
+ - ✅ Audit trail generated
1582
+ - ✅ Release attestation created
1583
+ - ✅ Compliance evidence packaged
1584
+ - ${{ needs.release_signing.outputs.signed == 'true' && '✅ Release signed' || '⚠️ Release not signed' }}
1585
+
1586
+ ## Next Steps
1587
+ 1. Review the release at ${{ needs.github_release.outputs.release_url || 'GitHub Releases page' }}
1588
+ 2. Verify signatures if applicable
1589
+ 3. Review compliance report
1590
+ 4. Deploy using your deployment workflow
1591
+ 5. Run load tests if needed
1592
+ EOF
1593
+
1594
+ # Set workflow status
1595
+ if [ "${{ needs.github_release.result }}" == "success" ] && [ "${{ needs.release_compliance.outputs.status }}" == "PASSED" ]; then
1596
+ echo "✅ Enterprise release completed successfully!" >> $GITHUB_STEP_SUMMARY
1597
+ else
1598
+ echo "⚠️ Release completed with issues. Please review the compliance report." >> $GITHUB_STEP_SUMMARY
1599
+ fi