diff --git a/.github/actions/resolve-manifest-meta/action.yml b/.github/actions/resolve-manifest-meta/action.yml index 65acc34..14326bb 100644 --- a/.github/actions/resolve-manifest-meta/action.yml +++ b/.github/actions/resolve-manifest-meta/action.yml @@ -5,6 +5,10 @@ inputs: description: 'Directory to search for *-manifest.json' required: true default: 'promote-assets' + expected_image: + description: 'Optional image name used to select the correct manifest file when multiple manifests are present.' + required: false + default: '' outputs: commit: description: 'Commit SHA from manifest or GITHUB_SHA' @@ -18,6 +22,9 @@ outputs: ssh_mode: description: 'SSH mode from manifest or fallback' value: ${{ steps.resolve.outputs.ssh_mode }} + manifest_path: + description: 'Resolved manifest path used for metadata extraction.' + value: ${{ steps.resolve.outputs.manifest_path }} runs: using: 'composite' steps: @@ -25,18 +32,54 @@ runs: shell: bash run: | set -euo pipefail - MANIFEST=$(find "${{ inputs.manifest_dir }}" -maxdepth 1 -name '*-manifest.json' | head -1 || true) + EXPECTED_IMAGE="${{ inputs.expected_image }}" + MANIFEST="" + + if [[ -n "$EXPECTED_IMAGE" ]]; then + MANIFEST=$(find "${{ inputs.manifest_dir }}" -maxdepth 1 -name "*${EXPECTED_IMAGE}*-manifest.json" | sort | head -1 || true) + fi + + if [[ -z "$MANIFEST" ]]; then + MANIFEST=$(find "${{ inputs.manifest_dir }}" -maxdepth 1 -name '*-manifest.json' | sort | head -1 || true) + fi + + if [[ -z "$MANIFEST" ]]; then + echo "No manifest JSON found in '${{ inputs.manifest_dir }}'. Cannot resolve release metadata safely." + exit 1 + fi + COMMIT="" SHORT_SHA="" IMAGE_NAME="" SSH_MODE="public-hardened" - if [[ -n "$MANIFEST" ]]; then - mapfile -t META_FIELDS < <( - python3 "${{ github.action_path }}/resolve_manifest_meta.py" "$MANIFEST" - ) - COMMIT="${META_FIELDS[0]:-}" - IMAGE_NAME="${META_FIELDS[1]:-}" - SSH_MODE="${META_FIELDS[2]:-public-hardened}" + mapfile -t META_FIELDS < <( + python3 "${{ github.action_path }}/resolve_manifest_meta.py" "$MANIFEST" + ) + COMMIT="${META_FIELDS[0]:-}" + IMAGE_NAME="${META_FIELDS[1]:-}" + SSH_MODE="${META_FIELDS[2]:-}" + + # Backward compatibility for older manifests. + if [[ "$SSH_MODE" == "public-keyed" ]]; then + SSH_MODE="public-hardened" + fi + + if [[ -z "$SSH_MODE" ]]; then + echo "Manifest '$MANIFEST' does not contain ssh_mode (legacy artifact)." + else + case "$SSH_MODE" in + public-hardened|internal-keyed) + ;; + *) + echo "Manifest '$MANIFEST' contains unsupported ssh_mode '$SSH_MODE'." + exit 1 + ;; + esac + fi + + if [[ -n "$EXPECTED_IMAGE" && -n "$IMAGE_NAME" && "$IMAGE_NAME" != "$EXPECTED_IMAGE" ]]; then + echo "Resolved manifest image '$IMAGE_NAME' does not match expected image '$EXPECTED_IMAGE'." + exit 1 fi [[ -z "$COMMIT" ]] && COMMIT="${GITHUB_SHA}" SHORT_SHA="${COMMIT::7}" @@ -45,3 +88,4 @@ runs: echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT" echo "image=$IMAGE_NAME" >> "$GITHUB_OUTPUT" echo "ssh_mode=$SSH_MODE" >> "$GITHUB_OUTPUT" + echo "manifest_path=$MANIFEST" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/core-image-minimal-ondemand.yml b/.github/workflows/core-image-minimal-ondemand.yml index f924f2e..4848093 100644 --- a/.github/workflows/core-image-minimal-ondemand.yml +++ b/.github/workflows/core-image-minimal-ondemand.yml @@ -401,7 +401,7 @@ jobs: image_name: ${{ env.CI_BUILD_IMAGE }} machine: ${{ github.event.inputs.machine }} commit: ${{ github.sha }} - ssh_mode: ${{ github.event.inputs.ssh_access_mode || 'public-hardened' }} + ssh_mode: ${{ env.SSH_ACCESS_MODE || 'public-hardened' }} - name: Prepare private Vigiles bundle if: env.VIGILES_ENABLED == 'true' diff --git a/.github/workflows/device-build-smart.yml b/.github/workflows/device-build-smart.yml index c554e0b..824873f 100644 --- a/.github/workflows/device-build-smart.yml +++ b/.github/workflows/device-build-smart.yml @@ -699,7 +699,7 @@ jobs: image_name: ${{ env.CI_BUILD_IMAGE }} machine: qemuarm64 commit: ${{ github.sha }} - ssh_mode: ${{ github.event.inputs.ssh_access_mode || 'public-hardened' }} + ssh_mode: ${{ env.SSH_ACCESS_MODE || 'public-hardened' }} - name: Prepare private Vigiles bundle if: env.VIGILES_ENABLED == 'true' diff --git a/.github/workflows/promote-prerelease-release.yml b/.github/workflows/promote-prerelease-release.yml index c8e4d27..1b1b7e2 100644 --- a/.github/workflows/promote-prerelease-release.yml +++ b/.github/workflows/promote-prerelease-release.yml @@ -84,6 +84,15 @@ on: options: - release - prerelease + ssh_mode_override: + description: "Optional override for release notes SSH mode. Use when promoting older artifacts missing ssh_mode metadata." + required: false + default: "auto" + type: choice + options: + - auto + - public-hardened + - internal-keyed permissions: contents: write @@ -110,6 +119,7 @@ jobs: RELEASE_STRATEGY: ${{ github.event.inputs.release_strategy }} CUSTOM_VERSION: ${{ github.event.inputs.version }} RELEASE_CHANNEL: ${{ github.event.inputs.release_channel }} + SSH_MODE_OVERRIDE: ${{ github.event.inputs.ssh_mode_override }} steps: - uses: actions/checkout@v4 @@ -315,6 +325,61 @@ jobs: uses: ./.github/actions/resolve-manifest-meta with: manifest_dir: promote-assets + expected_image: ${{ env.IMAGE_TYPE }} + + - name: Write resolved metadata summary + run: | + { + echo "## Resolved Build Metadata" + echo "- Resolved manifest: ${{ steps.source_meta.outputs.manifest_path }}" + echo "- Resolved image: ${{ steps.source_meta.outputs.image }}" + echo "- Resolved commit: ${{ steps.source_meta.outputs.commit }}" + echo "- Resolved ssh_mode: ${{ steps.source_meta.outputs.ssh_mode }}" + } >> "$GITHUB_STEP_SUMMARY" + + - name: Resolve effective SSH mode + id: effective_ssh + run: | + set -euo pipefail + RESOLVED_MODE="${{ steps.source_meta.outputs.ssh_mode }}" + OVERRIDE_MODE="${SSH_MODE_OVERRIDE:-auto}" + + case "$OVERRIDE_MODE" in + auto|public-hardened|internal-keyed) + ;; + *) + echo "Invalid ssh_mode_override: $OVERRIDE_MODE" + exit 1 + ;; + esac + + if [[ "$OVERRIDE_MODE" != "auto" ]]; then + FINAL_MODE="$OVERRIDE_MODE" + SOURCE_LABEL="override" + else + FINAL_MODE="$RESOLVED_MODE" + SOURCE_LABEL="manifest" + fi + + if [[ -z "$FINAL_MODE" ]]; then + echo "Could not resolve ssh_mode from manifest metadata." + echo "For older artifacts, re-run promotion with ssh_mode_override=public-hardened or internal-keyed." + exit 1 + fi + + case "$FINAL_MODE" in + public-hardened|internal-keyed) + ;; + *) + echo "Unsupported effective ssh_mode: $FINAL_MODE" + exit 1 + ;; + esac + + echo "ssh_mode=$FINAL_MODE" >> "$GITHUB_OUTPUT" + { + echo "- Effective ssh_mode: $FINAL_MODE (${SOURCE_LABEL})" + } >> "$GITHUB_STEP_SUMMARY" - name: Validate stable release notes template run: | @@ -356,7 +421,7 @@ jobs: IMAGE="${{ steps.source_meta.outputs.image }}" COMMIT="${{ steps.source_meta.outputs.commit }}" SHORT_SHA="${{ steps.source_meta.outputs.short_sha }}" - SSH_MODE="${{ steps.source_meta.outputs.ssh_mode }}" + SSH_MODE="${{ steps.effective_ssh.outputs.ssh_mode }}" for value_name in TARGET_TAG CHANNEL IMAGE COMMIT SHORT_SHA SSH_MODE REPO_URL; do if [[ -z "${!value_name}" ]]; then