# Nova CLI Action — composite action (REQ-326, NFR-11) # # Runs a Nova CLI command (`nova `) in a consumer repository. # Python 3.12 is pinned (REQ-326 AC3). The same action.yml is discovered # by both the production forge (GitHub Actions) and the dev forge # (act_runner) via the shared .github/actions/nova-cli/ path — there is # no separate dev-forge action file. Consumers reference it via a # versioned tag pin: # # uses: //.github/actions/nova-cli@v1.28 # # Wheel index selection (CodeArtifact default + fallback): # - CodeArtifact mode: set the NOVA_CODEARTIFACT_DOMAIN repository # secret/env. The action runs # `aws codeartifact login --tool pip --domain $NOVA_CODEARTIFACT_DOMAIN # --repository nova-pypi` before `pip install nova`. # - Fallback mode: leave NOVA_CODEARTIFACT_DOMAIN unset and provide # NOVA_WHEEL_INDEX env pointing at any PEP 503 simple index (a # private package registry). The action runs # `pip install --index-url $NOVA_WHEEL_INDEX nova==`. # See docs/codeartifact-provisioning.md for the index shape. # # Byte-identical cross-platform verification (NFR-11, REQ-326 AC2): # the full byte-identical test runs as a CI matrix job on the # production forge (ubuntu-latest) + the dev forge (act_runner) with # identical inputs, asserting same stdout + exit code. That matrix is # not reproducible in a unit test; the structural invariants (valid # YAML, python 3.12 pin, install + run steps present) are asserted by # tests/test_forge_action_byte_identical.py. name: "Nova CLI Action" description: "Run a Nova CLI command (`nova `) with Python 3.12 pinned" inputs: command: description: "The Nova subcommand + args to run (e.g. `apply --local`, `init`, `idp setup --check-only`). Passed verbatim to `nova`." required: true contract: description: "Path to the consumer contract YAML (default .nova/contract.yml). Forwarded to nova via the NOVA_CONTRACT env var." required: false default: ".nova/contract.yml" mode: description: "Nova client mode override (e.g. agent, interactive, plan-only, check-only). Forwarded to nova via the NOVA_CLIENT_MODE env var. Empty = let nova resolve (TTY + credentials)." required: false default: "" version: description: "nova package version to install (default `latest`). Pin to a released wheel version for reproducible runs." required: false default: "latest" runs: using: "composite" steps: - name: Set up Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install Nova (CodeArtifact default + fallback index) shell: bash env: NOVA_CODEARTIFACT_DOMAIN: ${{ env.NOVA_CODEARTIFACT_DOMAIN }} NOVA_WHEEL_INDEX: ${{ env.NOVA_WHEEL_INDEX }} NOVA_INSTALL_VERSION: ${{ inputs.version }} run: | set -e if [ "$NOVA_INSTALL_VERSION" = "latest" ]; then PIP_SPEC="nova" else PIP_SPEC="nova==$NOVA_INSTALL_VERSION" fi if [ -n "$NOVA_CODEARTIFACT_DOMAIN" ]; then echo "CodeArtifact mode: domain=$NOVA_CODEARTIFACT_DOMAIN repository=nova-pypi" aws codeartifact login --tool pip \ --domain "$NOVA_CODEARTIFACT_DOMAIN" --repository nova-pypi pip install $PIP_SPEC else echo "Fallback-index mode: NOVA_WHEEL_INDEX=$NOVA_WHEEL_INDEX" if [ -z "$NOVA_WHEEL_INDEX" ]; then echo "FAIL: NOVA_CODEARTIFACT_DOMAIN is unset and NOVA_WHEEL_INDEX is empty. Set one of them." exit 1 fi pip install --index-url "$NOVA_WHEEL_INDEX" $PIP_SPEC fi nova --version || true - name: Run Nova shell: bash env: NOVA_CLIENT_MODE: ${{ inputs.mode }} NOVA_CONTRACT: ${{ inputs.contract }} run: | set -e echo "nova ${{ inputs.command }}" nova ${{ inputs.command }}