Skip to content

feat: add github-actions-deploy solution - #233

Merged
jeff-schnitter merged 83 commits into
mainfrom
cx-6-github-actions-deploy-solution
Aug 13, 2026
Merged

feat: add github-actions-deploy solution#233
jeff-schnitter merged 83 commits into
mainfrom
cx-6-github-actions-deploy-solution

Conversation

@jeff-schnitter

Copy link
Copy Markdown
Collaborator

Summary

  • Adds the github-actions-deploy solution bundle: entity, scorecard, Cortex workflow, and GitHub Actions workflow (cortex-deploy.yml)
  • Interactive post-install setup script that creates a GitHub repo, seeds the deploy workflow, sets secrets, links the entity, and triggers a test deploy
  • cortex solutions post-install subcommand for running setup later
  • Persists setup answers to ~/.cortex/solutions/ with --no-prompt replay support
  • Two reusable GitHub composite actions: cortex-async-callback and cortex-register-deploy
  • SolutionSetup base class for future solution setup scripts

Test plan

  • just test tests/test_setup_base.py — base class unit tests
  • just test tests/test_github_actions_setup.py — setup script unit tests
  • just test tests/test_solutions_postinstall.py — post-install command tests
  • cortex solutions install -s github-actions-deploy --skip-post-install-setup — install without setup
  • cortex solutions post-install -s github-actions-deploy — interactive setup
  • cortex solutions post-install -s github-actions-deploy --no-prompt — non-interactive replay

🤖 Generated with Claude Code

jeff-schnitter and others added 30 commits August 10, 2026 16:03
Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Catalog entity, deploy health scorecard, GitHub Actions workflow template,
and README for the GitHub Actions deploy tracking solution.

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Provides prompt collection with env var fallback, Y/N confirmation,
idempotency state tracking via ~/.cortex/setup-{solution}.json, step
execution with progress display, and post_steps() hook for subclasses.

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Required by github-actions-deploy setup script to encrypt secrets
before storing them via the GitHub API (libsodium sealed box).

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Interactive wizard that creates a GitHub repo, seeds the Cortex deploy
workflow, sets CORTEX_API_KEY and CORTEX_BASE_URL secrets, and optionally
triggers the first workflow run. All steps are idempotent.

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ey bytes()

- _create_repo now raises RuntimeError on any non-200, non-404 response
  when checking repo existence (previously proceeded to POST on any non-200)
- Remove unused `from typing import Optional` import from setup.py
- Use bytes() instead of private ._public_key attribute when serializing
  the nacl PublicKey in test_set_secret
- Add test_create_repo_raises_on_unexpected_status to cover 403 case

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New `cortex solutions post-install -s <solution>` subcommand
- `cortex solutions install` prompts for post-install setup when setup.py present
- `--skip-post-install-setup` flag bypasses the prompt
- Helper functions _has_post_install and _run_post_install_script for reuse

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add test coverage for --skip-post-install-setup flag (skips script,
  prints 'Run setup later' message)
- Add test coverage for install prompt when setup.py present (user
  answers 'y', script is called)
- Add is_flag=True to --skip-post-install-setup Option
- Remove dead code: inner `if not setup_path.exists()` branch inside
  as_file() block in _run_post_install_script (unreachable; outer
  except FileNotFoundError already handles missing file)

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
deploys() is not a valid Cortex CQL function. Replace with
customMetrics(key="deploy-count", lookback=...) expressions, and
update the GitHub Actions workflow template to post a deploy-count
metric alongside the deploy event so the scorecard evaluates correctly.

Linear: CX-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use OSC 8 hyperlinks in post_steps output (clickable in iTerm2)
- Secret prompts now hidden via getpass (no echo on terminal)
- API key and base URL pre-filled from active CLI session
- Show "Use current Cortex API key?" / "Use current Cortex base URL?" instead of generic env var prompts
- Install hook shows solution-specific SETUP_DESCRIPTION instead of generic message
- Pass CLI client context to post-install script for both install and post-install commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use /admin/resources?tag=<tag> instead of /catalog/<tag>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… CX-6

- New workflow: github-actions-trigger-deploy (HTTP_REQUEST_ASYNC)
  Triggers GitHub Actions dispatch and waits for callback from the workflow run.
  Surfaces conclusion, sha, and run URL in the Cortex workflow result.

- Updated cortex-deploy.yml template:
  Accepts optional cortex_callback_url workflow_dispatch input.
  Final step in notify-cortex job POSTs back to the callback URL on success,
  completing the async Cortex workflow run with structured output.

- Updated setup.py post_steps():
  Replaces direct GitHub API trigger with Cortex workflow run via API.
  Polls for COMPLETED/FAILED/CANCELLED status (up to 5 min).
  Marks first_deploy in persistent state on success.
  Non-fatal on failure — prints retry instructions instead of exiting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When backup.import_tenant() raises (e.g. invalid workflow YAML),
_run_import_with_toggle was silently swallowing all captured output.
Now flushes the buffer to real stdout before re-raising, so errors
are always visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cortex only allows a-z, 0-9, and hyphens in workflow variable slugs.
github_token/github_owner/repo_name → github-token/github-owner/repo-name

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Cortex workflow run API expects variable values as top-level keys
in initialContext, not nested under a "variables" wrapper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…for CX-6

- collect_prompts: fetch GitHub integrations from Cortex API first.
  If found, present numbered list (default = isDefault), no GITHUB_TOKEN prompt.
  If none, fall through to GITHUB_TOKEN prompt as before.
- Setup steps (create repo, seed, set secrets) use GITHUB_TOKEN from env var
  silently when an integration is configured; error naturally if not set.
- Workflow YAML: replace github-token variable + Authorization header with
  integrationAlias: "{{variables.github-integration}}" (no raw token in workflow)
- post_steps: when integration selected, triggers via Cortex workflow (async,
  waits for callback); when no integration, triggers GitHub API directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- integration: GitHub (required alongside integrationAlias)
- URL is relative (base URL prepended by integration automatically)
- headers: {} (empty object, not list)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The callback URL requires auth like any Cortex API endpoint.
Uses the CORTEX_API_KEY repo secret already set during post-install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions deploy

- Move trigger-github-deploy.yaml to _templates/ (Cortex validates integrationAlias
  at import time and rejects template variable expressions like {{variables.xxx}})
- Add setup step to import the Cortex workflow with the selected alias substituted
- Add always() to callback step so it fires even if deploy registration fails
- Add required status/message fields to Cortex callback payload
- Update runResponseTemplate to use variables (result is null for HTTP_REQUEST_ASYNC
  when initial request returns 204 with no body)
- Fix headers: {} required even with integration auth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
actions is a list in the workflow run response, not a dict.
Also remove stale [5/5] step numbers that became inaccurate
when the import step was added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…a in template

- Map GitHub job.status to SUCCESS/FAILURE/CANCELLED before POSTing callback
  (was hardcoded SUCCESS, causing Cortex to show success on failed GH Actions runs)
- Add conclusion, sha, run_id, run_url to callback output payload
- Update runResponseTemplate to render conclusion, SHA, and direct run link
  via {{actions.trigger-deploy.outputs.output.*}} (callback output path)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…) CQL

Deploy data is already captured via the deploys API — no need for a redundant
custom metric. Scorecard rules now use deploys(lookback=...) directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… template path

Three bugs fixed:
- Fragile multi-line -d string caused output:null in callback; switch to jq
- job.status reflected notify-cortex infra failures, not build outcome;
  use needs.build.result so only actual build failures report FAILURE
- Template used outputs.output.* (wrong); correct path is outputs.result.output.*
  since Cortex stores the callback payload in result for HTTP_REQUEST_ASYNC

Also adds continue-on-error on the register-deploy step so a Cortex API
error doesn't pollute the callback status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sends two intermediate IN_PROGRESS updates (10s apart) before the final
SUCCESS/FAILURE/CANCELLED callback, demonstrating Cortex's support for
streaming progress updates from async actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t to 120s

Cortex callback endpoint only accepts terminal statuses (SUCCESS/FAILURE/CANCELLED).
IN_PROGRESS caused a 4xx, killing the step before the terminal callback could fire.
Reduced timeoutInSeconds from 300 to 120 — GH Actions run completes in ~20-30s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jeff-schnitter and others added 28 commits August 13, 2026 09:40
- Remove trigger-github-deploy.yaml (variables+branch pattern preserved in skill)
- Rename deploy-from-entity.yaml → github-actions-deploy.yaml, tag → github-actions-deploy
- setup.py: single import step, entityId lookup before entity-scoped run
Add cortex-async-callback and cortex-register-deploy as local composite
actions seeded into .github/actions/. cortex-deploy.yml now uses both.
setup.py seeds all three files via a generic _seed_file helper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tput.sha

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n response cid

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…esponseTemplate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…apt section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e in API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jeff-schnitter
jeff-schnitter merged commit 0eb2706 into main Aug 13, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant