cortexapps-cli 1.30.0__tar.gz → 1.31.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/PKG-INFO +2 -1
  2. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/solutions.py +111 -14
  3. cortexapps_cli-1.31.0/cortexapps_cli/solutions/_lib/__init__.py +0 -0
  4. cortexapps_cli-1.31.0/cortexapps_cli/solutions/_lib/setup_base.py +169 -0
  5. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/README.md +97 -0
  6. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/_templates/cortex-async-callback.yml +39 -0
  7. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/_templates/cortex-deploy.yml +51 -0
  8. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/_templates/cortex-register-deploy.yml +51 -0
  9. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/_templates/github-actions-deploy.yaml +132 -0
  10. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/catalog/github-actions-demo.yaml +9 -0
  11. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/scorecards/deploy-health.yaml +51 -0
  12. cortexapps_cli-1.31.0/cortexapps_cli/solutions/github-actions-deploy/setup.py +511 -0
  13. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/pyproject.toml +2 -1
  14. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/LICENSE +0 -0
  15. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/README.rst +0 -0
  16. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/__init__.py +0 -0
  17. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/cli.py +0 -0
  18. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/command_options.py +0 -0
  19. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/ai_skills.py +0 -0
  20. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/api_keys.py +0 -0
  21. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/audit_logs.py +0 -0
  22. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/backup.py +0 -0
  23. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/backup_commands/cortex_export.py +0 -0
  24. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/catalog.py +0 -0
  25. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/custom_data.py +0 -0
  26. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/custom_events.py +0 -0
  27. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/custom_metrics.py +0 -0
  28. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/dependencies.py +0 -0
  29. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/deploys.py +0 -0
  30. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/discovery_audit.py +0 -0
  31. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/docs.py +0 -0
  32. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/entity_relationship_types.py +0 -0
  33. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/entity_relationships.py +0 -0
  34. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/entity_types.py +0 -0
  35. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/gitops_logs.py +0 -0
  36. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/groups.py +0 -0
  37. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/initiatives.py +0 -0
  38. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations.py +0 -0
  39. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/apiiro.py +0 -0
  40. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/argocd.py +0 -0
  41. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/aws.py +0 -0
  42. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/azure_active_directory.py +0 -0
  43. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/azure_devops.py +0 -0
  44. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/azure_resources.py +0 -0
  45. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/bamboohr.py +0 -0
  46. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/bitbucket.py +0 -0
  47. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/bugsnag.py +0 -0
  48. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/buildkite.py +0 -0
  49. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/checkmarx_sast.py +0 -0
  50. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/circleci.py +0 -0
  51. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/clickup.py +0 -0
  52. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/codecov.py +0 -0
  53. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/coralogix.py +0 -0
  54. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/datadog.py +0 -0
  55. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/dynatrace.py +0 -0
  56. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/firehydrant.py +0 -0
  57. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/github.py +0 -0
  58. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/gitlab.py +0 -0
  59. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/incidentio.py +0 -0
  60. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/instana.py +0 -0
  61. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/jenkins.py +0 -0
  62. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/jira.py +0 -0
  63. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/launchdarkly.py +0 -0
  64. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/mend_sast.py +0 -0
  65. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/mend_sca.py +0 -0
  66. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/newrelic.py +0 -0
  67. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/okta.py +0 -0
  68. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/opsgenie.py +0 -0
  69. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/pagerduty.py +0 -0
  70. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/prometheus.py +0 -0
  71. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/rollbar.py +0 -0
  72. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/rootly.py +0 -0
  73. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/semgrep.py +0 -0
  74. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/sentry.py +0 -0
  75. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/servicenow.py +0 -0
  76. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/servicenow_cloud_observability.py +0 -0
  77. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/snyk.py +0 -0
  78. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/sonarqube.py +0 -0
  79. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/splunk_observability_cloud.py +0 -0
  80. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/splunk_on_call.py +0 -0
  81. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/sumologic.py +0 -0
  82. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/veracode.py +0 -0
  83. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/wiz.py +0 -0
  84. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/workday.py +0 -0
  85. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/integrations_commands/xmatters.py +0 -0
  86. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/ip_allowlist.py +0 -0
  87. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/on_call.py +0 -0
  88. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/packages.py +0 -0
  89. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/packages_commands/go.py +0 -0
  90. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/packages_commands/java.py +0 -0
  91. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/packages_commands/node.py +0 -0
  92. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/packages_commands/nuget.py +0 -0
  93. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/packages_commands/python.py +0 -0
  94. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/plugins.py +0 -0
  95. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/queries.py +0 -0
  96. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/rest.py +0 -0
  97. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/scim.py +0 -0
  98. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/scorecards.py +0 -0
  99. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/scorecards_commands/exemptions.py +0 -0
  100. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/secrets.py +0 -0
  101. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/teams.py +0 -0
  102. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/users.py +0 -0
  103. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/users_commands/roles.py +0 -0
  104. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/commands/workflows.py +0 -0
  105. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/cortex_client.py +0 -0
  106. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/models/team.py +0 -0
  107. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/__init__.py +0 -0
  108. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/README.md +0 -0
  109. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/address-reviews.yaml +0 -0
  110. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/audit-flags.yaml +0 -0
  111. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/code-review.yaml +0 -0
  112. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/fix-issue.yaml +0 -0
  113. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/incident-trends.yaml +0 -0
  114. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/jira-to-kb.yaml +0 -0
  115. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/release-to-sha.yaml +0 -0
  116. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/search-troubleshooting-kb.yaml +0 -0
  117. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/catalog/simplify-test.yaml +0 -0
  118. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/entity-types/ai-agent.json +0 -0
  119. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-agents/scorecards/ai-agent-governance.yaml +0 -0
  120. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/README.md +0 -0
  121. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/catalog/api-doc-sync.yaml +0 -0
  122. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/catalog/changelog-writer.yaml +0 -0
  123. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/catalog/docs-generator.yaml +0 -0
  124. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/catalog/docs-portal.yaml +0 -0
  125. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/entity-relationship-types/ai-plugin-service.json +0 -0
  126. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/entity-relationship-types/ai-plugin-skills.json +0 -0
  127. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/entity-relationship-types/ai-skill-service.json +0 -0
  128. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/entity-types/ai-plugin.json +0 -0
  129. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/entity-types/ai-skill.json +0 -0
  130. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-skills/scorecards/ai-skills-quality.yaml +0 -0
  131. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/.github/workflows/sync-claude-spend.yaml +0 -0
  132. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/README.md +0 -0
  133. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/employee-alice-chen.yaml +0 -0
  134. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/employee-bob-martinez.yaml +0 -0
  135. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/employee-carol-kim.yaml +0 -0
  136. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/employee-david-osei.yaml +0 -0
  137. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/employee-emma-johnson.yaml +0 -0
  138. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/team-data.yaml +0 -0
  139. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/team-engineering.yaml +0 -0
  140. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/team-frontend.yaml +0 -0
  141. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/catalog/team-platform.yaml +0 -0
  142. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/custom-metrics/ai-spend.json +0 -0
  143. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/entity-relationship-types/team-member.json +0 -0
  144. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/entity-types/employee.json +0 -0
  145. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/plugins/team-ai-spend.json +0 -0
  146. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/scorecards/ai-spend-scorecard.yaml +0 -0
  147. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/ai-spend/scripts/sync-claude-spend.py +0 -0
  148. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/.github/workflows/cortex-service-version.yaml +0 -0
  149. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/README.md +0 -0
  150. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/api-gateway-3.0.1.yaml +0 -0
  151. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/api-gateway-3.1.0.yaml +0 -0
  152. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/api-gateway-3.1.1.yaml +0 -0
  153. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/api-gateway.yaml +0 -0
  154. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/auth-2.1.0.yaml +0 -0
  155. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/auth-2.1.1.yaml +0 -0
  156. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/auth-2.2.0.yaml +0 -0
  157. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/auth.yaml +0 -0
  158. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/aws-dev-us-west-2.yaml +0 -0
  159. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/billing-1.1.0.yaml +0 -0
  160. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/billing-1.2.0.yaml +0 -0
  161. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/billing.yaml +0 -0
  162. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/cart-1.0.2.yaml +0 -0
  163. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/cart.yaml +0 -0
  164. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/checkout-2.2.0.yaml +0 -0
  165. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/checkout-2.3.0.yaml +0 -0
  166. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/checkout-2.4.0.yaml +0 -0
  167. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/checkout.yaml +0 -0
  168. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/gcp-prod-us-east-1.yaml +0 -0
  169. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/gcp-prod-us-west-1.yaml +0 -0
  170. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/gcp-staging-us-east-1.yaml +0 -0
  171. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/notifications-1.0.0.yaml +0 -0
  172. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/notifications-1.0.1.yaml +0 -0
  173. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/notifications-1.1.0.yaml +0 -0
  174. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/notifications.yaml +0 -0
  175. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/order-service-2.0.1.yaml +0 -0
  176. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/order-service-2.1.0.yaml +0 -0
  177. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/order-service.yaml +0 -0
  178. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/payments-1.6.1.yaml +0 -0
  179. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/payments-1.7.0.yaml +0 -0
  180. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/payments-1.7.1.yaml +0 -0
  181. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/payments.yaml +0 -0
  182. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/release-2026-06-20.yaml +0 -0
  183. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/release-2026-07-01.yaml +0 -0
  184. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/release-2026-07-09.yaml +0 -0
  185. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/release-2026-07-10.yaml +0 -0
  186. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/search-4.0.0.yaml +0 -0
  187. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/search-4.1.0.yaml +0 -0
  188. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/search.yaml +0 -0
  189. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/user-service-1.2.0.yaml +0 -0
  190. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/user-service-1.3.0.yaml +0 -0
  191. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/catalog/user-service.yaml +0 -0
  192. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/entity-relationship-types/environments.json +0 -0
  193. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/entity-relationship-types/versions.json +0 -0
  194. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/entity-types/environment.json +0 -0
  195. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/entity-types/release.json +0 -0
  196. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/entity-types/service-version.json +0 -0
  197. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/workflows/create-environment.yaml +0 -0
  198. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/workflows/create-release.yaml +0 -0
  199. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/solutions/environments/workflows/trigger-github-release.yaml +0 -0
  200. {cortexapps_cli-1.30.0 → cortexapps_cli-1.31.0}/cortexapps_cli/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortexapps-cli
3
- Version: 1.30.0
3
+ Version: 1.31.0
4
4
  Summary: Command Line Interface for cortexapps
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: PyNaCl (>=1.5.0)
17
18
  Requires-Dist: pyyaml (>=6.0.1,<7)
18
19
  Requires-Dist: requests (>=2.33.0,<3.0.0)
19
20
  Requires-Dist: typer (>=0.15,<1.0)
@@ -74,13 +74,26 @@ class _ToggleableCapture:
74
74
 
75
75
 
76
76
  def _run_import_with_toggle(fn) -> str:
77
- """Run fn() capturing stdout. On a TTY, Ctrl+o toggles live output."""
77
+ """Run fn() capturing stdout. On a TTY, Ctrl+o toggles live output.
78
+
79
+ If fn() raises for any reason, all buffered output is flushed to the
80
+ terminal before the exception propagates — no silent failures.
81
+ """
78
82
  real_stdout = sys.stdout
79
83
  capture = _ToggleableCapture(real_stdout)
80
84
 
81
85
  if not (_TTY_SUPPORT and sys.stdin.isatty()):
82
- with contextlib.redirect_stdout(capture):
83
- fn()
86
+ success = False
87
+ try:
88
+ with contextlib.redirect_stdout(capture):
89
+ fn()
90
+ success = True
91
+ finally:
92
+ if not success:
93
+ buffered = capture.getvalue()
94
+ if buffered:
95
+ real_stdout.write(buffered)
96
+ real_stdout.flush()
84
97
  return capture.getvalue()
85
98
 
86
99
  done = threading.Event()
@@ -112,9 +125,11 @@ def _run_import_with_toggle(fn) -> str:
112
125
 
113
126
  t = threading.Thread(target=_listen, daemon=True)
114
127
  t.start()
128
+ success = False
115
129
  try:
116
130
  with contextlib.redirect_stdout(capture):
117
131
  fn()
132
+ success = True
118
133
  finally:
119
134
  done.set()
120
135
  t.join(timeout=0.5)
@@ -122,6 +137,11 @@ def _run_import_with_toggle(fn) -> str:
122
137
  _termios.tcsetattr(fd, _termios.TCSADRAIN, old_settings)
123
138
  except Exception:
124
139
  pass
140
+ if not success:
141
+ buffered = capture.getvalue()
142
+ if buffered:
143
+ real_stdout.write(buffered)
144
+ real_stdout.flush()
125
145
 
126
146
  return capture.getvalue()
127
147
 
@@ -175,6 +195,53 @@ def _get_readme(tag: str, path: str | None = None) -> str | None:
175
195
  return None
176
196
 
177
197
 
198
+ def _has_post_install(tag: str, path: str | None = None) -> bool:
199
+ """Return True if this solution has a post-install setup.py."""
200
+ try:
201
+ (_solutions_root(path) / tag / "setup.py").read_bytes()
202
+ return True
203
+ except Exception:
204
+ return False
205
+
206
+
207
+ def _load_setup_module(solution_tag: str, solutions_dir: str | None = None):
208
+ """Load a solution's setup.py module. Returns the module or None if not found."""
209
+ import importlib.util
210
+
211
+ root = _solutions_root(solutions_dir)
212
+ try:
213
+ with as_file(root / solution_tag / "setup.py") as setup_path:
214
+ spec = importlib.util.spec_from_file_location(f"{solution_tag}_setup", setup_path)
215
+ module = importlib.util.module_from_spec(spec)
216
+ spec.loader.exec_module(module)
217
+ return module
218
+ except FileNotFoundError:
219
+ return None
220
+
221
+
222
+ def _get_setup_description(solution_tag: str, solutions_dir: str | None = None) -> str:
223
+ """Return the SETUP_DESCRIPTION from a solution's setup.py, or a generic fallback."""
224
+ module = _load_setup_module(solution_tag, solutions_dir)
225
+ if module:
226
+ return getattr(module, "SETUP_DESCRIPTION", "This solution includes a post-install setup script.")
227
+ return "This solution includes a post-install setup script."
228
+
229
+
230
+ def _run_post_install_script(solution_tag: str, solutions_dir: str | None = None, ctx=None, no_prompt: bool = False) -> None:
231
+ """Find and invoke the solution's setup.py main() function."""
232
+ module = _load_setup_module(solution_tag, solutions_dir)
233
+ if module is None:
234
+ typer.echo("No post-install setup available for this solution.")
235
+ return
236
+ kwargs = {}
237
+ if ctx and ctx.obj and "client" in ctx.obj:
238
+ client = ctx.obj["client"]
239
+ kwargs["cortex_api_key"] = client.api_key
240
+ kwargs["cortex_base_url"] = client.base_url
241
+ kwargs["no_prompt"] = no_prompt
242
+ module.main(**kwargs)
243
+
244
+
178
245
  def _extract_first_codeblock(text: str) -> str | None:
179
246
  """Return content of the first fenced code block."""
180
247
  m = re.search(r"```[^\n]*\n(.*?)```", text, re.DOTALL)
@@ -538,17 +605,6 @@ def _show_next_steps(readme: str) -> None:
538
605
  section = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'\1 (\2)', section)
539
606
  console.print()
540
607
  console.print(Markdown(section))
541
- console.print()
542
- console.print(
543
- "[magenta]Planned for Q4 2027:[/magenta] CQL metadata traversal will enable scorecard rules "
544
- "across relationship chains — for example, a Vulnerability Scorecard checking that no "
545
- "deployed service-version has open Snyk issues:"
546
- )
547
- console.print(
548
- " [dim]entity.destinations(relationshipType = \"environments\", depth = 3)\n"
549
- " .filter((d) => d.type == \"service-version\")\n"
550
- " .all((sv) => sv.snyk.issues == 0)[/dim]"
551
- )
552
608
 
553
609
 
554
610
  def _post_install_menu(
@@ -604,6 +660,12 @@ def install(
604
660
  ctx: typer.Context,
605
661
  solution: str = typer.Option(..., "--solution", "-s", help="Solution tag"),
606
662
  no_prompt: bool = typer.Option(False, "--no-prompt", help="Skip the post-install interactive menu"),
663
+ skip_post_install_setup: bool = typer.Option(
664
+ False,
665
+ "--skip-post-install-setup",
666
+ help="Skip the post-install setup script prompt",
667
+ is_flag=True,
668
+ ),
607
669
  ):
608
670
  """Install a solution."""
609
671
  solutions_dir = ctx.obj.get("solutions_dir") if ctx.obj else None
@@ -643,6 +705,20 @@ def install(
643
705
  else:
644
706
  typer.echo(output)
645
707
 
708
+ # Post-install setup hook — runs before the informational menu
709
+ if not no_prompt and not skip_post_install_setup and _has_post_install(solution, solutions_dir):
710
+ state_file = Path.home() / ".cortex" / "solutions" / f"{solution}.json"
711
+ if state_file.exists():
712
+ typer.echo(f"\nRetrieving previous responses from: {state_file}")
713
+ desc = _get_setup_description(solution, solutions_dir)
714
+ typer.echo(f"\n{desc}")
715
+ if typer.confirm("Run setup now?", default=True):
716
+ _run_post_install_script(solution, solutions_dir=solutions_dir, ctx=ctx)
717
+ else:
718
+ typer.echo(f"\nRun setup later with: cortex solutions post-install -s {solution}")
719
+ elif skip_post_install_setup and _has_post_install(solution, solutions_dir):
720
+ typer.echo(f"\nRun setup later with: cortex solutions post-install -s {solution}")
721
+
646
722
  if not no_prompt:
647
723
  readme = _get_readme(solution, solutions_dir)
648
724
  if readme:
@@ -660,6 +736,27 @@ def install(
660
736
  _post_install_menu(readme, import_report=output, entity_tags=entity_tags, ui_url=ui_url)
661
737
 
662
738
 
739
+ @app.command(name="post-install")
740
+ def post_install(
741
+ ctx: typer.Context,
742
+ solution: str = typer.Option(..., "--solution", "-s", help="Solution tag"),
743
+ no_prompt: bool = typer.Option(
744
+ False,
745
+ "--no-prompt",
746
+ "-N",
747
+ help="Use saved answers from ~/.cortex/solutions/<tag>.json without prompting.",
748
+ ),
749
+ ):
750
+ """Run post-install setup for a solution."""
751
+ solutions_dir = ctx.obj.get("solutions_dir") if ctx.obj else None
752
+ if solution not in _list_solution_tags(solutions_dir):
753
+ avail = ", ".join(_list_solution_tags(solutions_dir))
754
+ typer.echo(f"Error: Solution '{solution}' not found. Available: {avail}")
755
+ raise typer.Exit(1)
756
+ ctx.obj["client"] = _build_client(ctx)
757
+ _run_post_install_script(solution, solutions_dir=solutions_dir, ctx=ctx, no_prompt=no_prompt)
758
+
759
+
663
760
  @app.command()
664
761
  def uninstall(
665
762
  ctx: typer.Context,
@@ -0,0 +1,169 @@
1
+ import getpass
2
+ import json
3
+ import os
4
+ import sys
5
+ from abc import ABC, abstractmethod
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+
10
+ class SolutionSetup(ABC):
11
+ """
12
+ Base class for solution post-install setup scripts.
13
+ Subclasses define solution_tag, collect_prompts(), and steps().
14
+ """
15
+
16
+ solution_tag: str # must be set by subclass
17
+
18
+ def __init__(self, state_dir: Optional[Path] = None, no_prompt: bool = False):
19
+ self._no_prompt = no_prompt
20
+ self._secret_keys: set = set()
21
+ self._answers: dict = {}
22
+
23
+ solutions_dir = state_dir or Path.home() / ".cortex" / "solutions"
24
+ solutions_dir.mkdir(parents=True, exist_ok=True)
25
+ self._state_file = solutions_dir / f"{self.solution_tag}.json"
26
+
27
+ data = self._load_file()
28
+ self._answers = data.get("answers", {})
29
+ self._state: dict = data.get("state", {})
30
+
31
+ self._migrate_old_state()
32
+
33
+ def _load_file(self) -> dict:
34
+ if self._state_file.exists():
35
+ try:
36
+ return json.loads(self._state_file.read_text())
37
+ except (json.JSONDecodeError, OSError):
38
+ return {}
39
+ return {}
40
+
41
+ def _save_file(self) -> None:
42
+ data = {
43
+ "answers": {k: v for k, v in self._answers.items() if k not in self._secret_keys},
44
+ "state": self._state,
45
+ }
46
+ self._state_file.write_text(json.dumps(data, indent=2))
47
+
48
+ def _save_state(self) -> None:
49
+ self._save_file()
50
+
51
+ def _migrate_old_state(self) -> None:
52
+ """Move state from the old flat ~/.cortex/setup-<tag>.json into the new file."""
53
+ old_file = Path.home() / ".cortex" / f"setup-{self.solution_tag}.json"
54
+ if not old_file.exists():
55
+ return
56
+ try:
57
+ old_data = json.loads(old_file.read_text())
58
+ if old_data and not self._state:
59
+ self._state.update(old_data)
60
+ self._save_file()
61
+ old_file.unlink()
62
+ except Exception:
63
+ pass
64
+
65
+ def prompt(
66
+ self,
67
+ key: str,
68
+ message: str,
69
+ env_var: Optional[str] = None,
70
+ default: Optional[str] = None,
71
+ secret: bool = False,
72
+ ) -> str:
73
+ """Prompt for a value. Uses saved answer or env var when available."""
74
+ if secret:
75
+ self._secret_keys.add(key)
76
+
77
+ # Non-secret: use saved answer when --no-prompt
78
+ if self._no_prompt and not secret and key in self._answers:
79
+ return self._answers[key]
80
+
81
+ # Non-secret: saved answer takes precedence over any derived default
82
+ if not secret and key in self._answers:
83
+ default = self._answers[key]
84
+
85
+ if env_var:
86
+ env_val = os.environ.get(env_var)
87
+ if env_val:
88
+ masked = "********" if secret else env_val
89
+ if self._no_prompt or self.confirm(f"{message} [{masked} from {env_var}]", default=True):
90
+ self._answers[key] = env_val
91
+ return env_val
92
+
93
+ # Secrets in --no-prompt mode still need a prompt if no env var provided
94
+ if self._no_prompt and secret and key not in self._answers:
95
+ print(f" (secret required — no env var set for {key})", file=sys.stderr)
96
+
97
+ prompt_str = message
98
+ if default:
99
+ prompt_str += f" [{default}]"
100
+ prompt_str += ": "
101
+
102
+ if secret:
103
+ value = getpass.getpass(prompt_str).strip()
104
+ else:
105
+ value = input(prompt_str).strip()
106
+ if not value:
107
+ value = default or ""
108
+ self._answers[key] = value
109
+ return value
110
+
111
+ def confirm(self, message: str, default: bool = True) -> bool:
112
+ """Y|N confirmation prompt. Auto-accepts default when --no-prompt."""
113
+ if self._no_prompt:
114
+ return default
115
+ hint = "[Y/n]" if default else "[y/N]"
116
+ response = input(f"{message} {hint}: ").strip().lower()
117
+ if not response:
118
+ return default
119
+ return response in ("y", "yes")
120
+
121
+ def already_done(self, key: str) -> bool:
122
+ """Return True if this step was previously completed."""
123
+ return self._state.get(key, False)
124
+
125
+ def mark_done(self, key: str) -> None:
126
+ """Mark a step as completed in the persistent state file."""
127
+ self._state[key] = True
128
+ self._save_file()
129
+
130
+ @abstractmethod
131
+ def collect_prompts(self) -> None:
132
+ """Collect all user inputs upfront before executing steps."""
133
+
134
+ @abstractmethod
135
+ def steps(self) -> list[tuple[str, callable]]:
136
+ """Return ordered list of (label, callable) tuples."""
137
+
138
+ def post_steps(self) -> None:
139
+ """Optional hook called after all steps complete. Override in subclass."""
140
+
141
+ def run(self) -> None:
142
+ """Collect prompts then execute steps with progress display."""
143
+ if self._no_prompt and self._answers:
144
+ saved = {k: v for k, v in self._answers.items() if k not in self._secret_keys}
145
+ if saved:
146
+ print("Using saved configuration:")
147
+ for k, v in saved.items():
148
+ print(f" {k}: {v}")
149
+ print()
150
+
151
+ self.collect_prompts()
152
+ self._save_file()
153
+
154
+ print()
155
+ step_list = self.steps()
156
+ total = len(step_list)
157
+ for i, (label, fn) in enumerate(step_list, 1):
158
+ try:
159
+ detail = fn()
160
+ print(f"[{i}/{total}] {label}... \u2713")
161
+ if detail:
162
+ lines = [detail] if isinstance(detail, str) else detail
163
+ for line in lines:
164
+ print(f" {line}")
165
+ print()
166
+ except Exception as e:
167
+ print(f"[{i}/{total}] {label}... \u2717 {e}", file=sys.stderr)
168
+ raise SystemExit(1)
169
+ self.post_steps()
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: GitHub Actions Deploy Tracking
3
+ description: Track deployments from GitHub Actions in Cortex, with a deploy health scorecard measuring delivery cadence.
4
+ ---
5
+
6
+ # GitHub Actions Deploy Tracking
7
+
8
+ Trigger deploys from Cortex, track them as they run in GitHub Actions, and surface deploy health back in your service catalog.
9
+
10
+ ```
11
+ ┌─────────────────────────────────┐
12
+ │ Cortex Catalog │
13
+ │ │
14
+ │ github-actions-demo (service) │
15
+ │ ├── x-cortex-git.github │
16
+ │ │ repository: owner/repo │
17
+ │ └── Scorecard: Deploy Health │
18
+ │ Bronze / Silver / Gold │
19
+ └──────────────┬──────────────────┘
20
+
21
+ │ Run workflow from entity page
22
+ │ (or: cortex workflows run -t
23
+ │ github-actions-deploy
24
+ │ --scope ENTITY --entity <tag>)
25
+
26
+ ┌─────────────────────────────────┐
27
+ │ Cortex Workflow │
28
+ │ Solution: Add Cortex Deploy │
29
+ │ from GitHub Actions │
30
+ │ │
31
+ │ 1. Read linked repo from │
32
+ │ entity catalog config │
33
+ │ 2. POST workflow_dispatch │
34
+ │ to GitHub Actions │
35
+ │ 3. Wait for callback │
36
+ └──────────────┬──────────────────┘
37
+ │ POST /dispatches (GitHub integration)
38
+
39
+ ┌─────────────────────────────────┐
40
+ │ GitHub Actions │
41
+ │ cortex-deploy.yml │
42
+ │ │
43
+ │ job: build │
44
+ │ └── run your deploy steps │
45
+ │ │
46
+ │ job: cortex-callback │
47
+ │ ├── POST /deploys │◄── registers deploy event
48
+ │ │ (entity: github-actions- │ on the Cortex entity
49
+ │ │ demo) │
50
+ │ └── POST callbackUrl ───────┼──► Cortex marks workflow
51
+ │ status: SUCCESS/FAILURE │ run complete
52
+ └─────────────────────────────────┘
53
+ ```
54
+
55
+ ## What's Included
56
+
57
+ - **Entity:** `github-actions-demo` service — a sample entity to receive deploy events
58
+ - **Scorecard:** Deploy Health — Bronze/Silver/Gold based on deploy frequency
59
+ - **GitHub Actions workflow:** `cortex-deploy.yml` — a two-job workflow (build → notify) seeded into your GitHub repo
60
+ - **Cortex workflow:** `github-actions-deploy` — reads the linked repo from the entity, triggers the GitHub Actions deploy, and waits for the result
61
+ - **Setup script:** Interactive wizard that creates and seeds a GitHub repo end-to-end
62
+
63
+ ## Quick Start
64
+
65
+ 1. Install the solution:
66
+
67
+ ```
68
+ cortex solutions install -s github-actions-deploy
69
+ ```
70
+
71
+ 2. Follow the post-install setup prompts, or run later:
72
+
73
+ ```
74
+ cortex solutions post-install -s github-actions-deploy
75
+ ```
76
+
77
+ ## How It Works
78
+
79
+ The Cortex workflow reads `x-cortex-git.github.repository` from the entity's catalog config to determine which GitHub repo to deploy. It triggers `cortex-deploy.yml` via `workflow_dispatch` and waits asynchronously for a callback.
80
+
81
+ GitHub Actions runs the build, then notifies Cortex twice on completion:
82
+ - **Deploy registration** (`POST /api/v1/catalog/{tag}/deploys`) — records the deploy event on the entity, feeding the Deploy Health scorecard
83
+ - **Workflow callback** — signals the Cortex workflow run as SUCCESS or FAILURE
84
+
85
+ ## After Installing
86
+
87
+ If you ran the post-install setup, you're already done — it created the GitHub repo, seeded the workflow, set secrets, linked the entity, and triggered a test deploy.
88
+
89
+ To roll the pattern out to your own services, add `cortex-deploy.yml` to any GitHub repo and link that repo to its Cortex entity. The same **Solution: Add Cortex Deploy from GitHub Actions** workflow will work across all of them — it reads the linked repo from the entity automatically.
90
+
91
+ ## Customizing for Production
92
+
93
+ - Point the workflow at your real entity by replacing `github-actions-demo` with your service tag
94
+ - Add `CORTEX_API_KEY` and `CORTEX_BASE_URL` secrets to your real repos
95
+ - The Deploy Health scorecard is scoped to `demo-github-actions-deploys` to avoid affecting your
96
+ existing services. To roll it out broadly, remove the group filter from the scorecard.
97
+ To opt in individual services, add the `demo-github-actions-deploys` group to them.
@@ -0,0 +1,39 @@
1
+ name: Cortex Async Callback
2
+ description: Post a completion status back to a Cortex async workflow callback URL.
3
+ inputs:
4
+ cortex_api_key:
5
+ description: Cortex API key
6
+ required: true
7
+ cortex_callback_url:
8
+ description: Cortex async workflow callback URL
9
+ required: true
10
+ status:
11
+ description: Completion status — SUCCESS, FAILURE, or CANCELLED
12
+ required: true
13
+ message:
14
+ description: Optional status message shown in the Cortex workflow run
15
+ required: false
16
+ default: ''
17
+ sha:
18
+ description: Git commit SHA to include in callback output
19
+ required: false
20
+ default: ''
21
+ run_url:
22
+ description: GitHub Actions run URL to include in callback output
23
+ required: false
24
+ default: ''
25
+ runs:
26
+ using: composite
27
+ steps:
28
+ - name: Post callback
29
+ shell: bash
30
+ run: |
31
+ curl -s -f -X POST "${{ inputs.cortex_callback_url }}" \
32
+ -H "Authorization: Bearer ${{ inputs.cortex_api_key }}" \
33
+ -H "Content-Type: application/json" \
34
+ -d "$(jq -n \
35
+ --arg status "${{ inputs.status }}" \
36
+ --arg message "${{ inputs.message }}" \
37
+ --arg sha "${{ inputs.sha }}" \
38
+ --arg run_url "${{ inputs.run_url }}" \
39
+ '{status: $status, message: $message, output: {sha: $sha, run_url: $run_url}}')"
@@ -0,0 +1,51 @@
1
+ name: Cortex Deploy
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+ inputs:
8
+ cortex_callback_url:
9
+ description: 'Cortex async workflow callback URL (set automatically when triggered via Cortex workflow)'
10
+ required: false
11
+ default: ''
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Build
18
+ run: |
19
+ echo "Starting deployment..."
20
+ sleep 10
21
+ for i in 1 2 3; do
22
+ sleep 5
23
+ echo "Deployment progress: step $i/3"
24
+ done
25
+ echo "Build complete!"
26
+
27
+ cortex-callback:
28
+ needs: build
29
+ runs-on: ubuntu-latest
30
+ if: always()
31
+ steps:
32
+ - name: Checkout (for local actions)
33
+ uses: actions/checkout@v4
34
+
35
+ - name: Register deploy in Cortex
36
+ if: needs.build.result == 'success'
37
+ uses: ./.github/actions/cortex-register-deploy
38
+ with:
39
+ cortex_api_key: ${{ secrets.CORTEX_API_KEY }}
40
+ cortex_base_url: ${{ secrets.CORTEX_BASE_URL }}
41
+ entity_tag: github-actions-demo
42
+
43
+ - name: Cortex async callback
44
+ if: inputs.cortex_callback_url != ''
45
+ uses: ./.github/actions/cortex-async-callback
46
+ with:
47
+ cortex_api_key: ${{ secrets.CORTEX_API_KEY }}
48
+ cortex_callback_url: ${{ inputs.cortex_callback_url }}
49
+ status: ${{ needs.build.result == 'success' && 'SUCCESS' || needs.build.result == 'cancelled' && 'CANCELLED' || 'FAILURE' }}
50
+ sha: ${{ github.sha }}
51
+ run_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
@@ -0,0 +1,51 @@
1
+ name: Cortex Register Deploy
2
+ description: Register a deploy event on a Cortex entity.
3
+ inputs:
4
+ cortex_api_key:
5
+ description: Cortex API key
6
+ required: true
7
+ cortex_base_url:
8
+ description: Cortex base URL
9
+ required: false
10
+ default: https://api.getcortexapp.com
11
+ entity_tag:
12
+ description: Cortex entity tag to register the deploy against
13
+ required: true
14
+ environment:
15
+ description: Deployment environment
16
+ required: false
17
+ default: production
18
+ sha:
19
+ description: Git commit SHA (defaults to github.sha)
20
+ required: false
21
+ default: ''
22
+ runs:
23
+ using: composite
24
+ steps:
25
+ - name: Register deploy
26
+ shell: bash
27
+ run: |
28
+ SHA="${{ inputs.sha }}"
29
+ SHA="${SHA:-${{ github.sha }}}"
30
+ curl -s -f -X POST \
31
+ "${{ inputs.cortex_base_url }}/api/v1/catalog/${{ inputs.entity_tag }}/deploys" \
32
+ -H "Authorization: Bearer ${{ inputs.cortex_api_key }}" \
33
+ -H "Content-Type: application/json" \
34
+ -d "$(jq -n \
35
+ --arg sha "$SHA" \
36
+ --arg actor "${{ github.actor }}" \
37
+ --arg branch "${{ github.ref_name }}" \
38
+ --arg run_id "${{ github.run_id }}" \
39
+ --arg run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
40
+ --arg trigger "${{ github.event_name }}" \
41
+ --arg env "${{ inputs.environment }}" \
42
+ --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
43
+ '{
44
+ sha: $sha,
45
+ timestamp: $timestamp,
46
+ environment: $env,
47
+ type: "DEPLOY",
48
+ title: ("Triggered by " + $actor),
49
+ deployer: {name: $actor},
50
+ customData: {branch: $branch, runId: $run_id, runUrl: $run_url, trigger: $trigger}
51
+ }')"