name: Publish Supported Platform Target on: push: branches: - main paths: - container/environment/definition.json - release/supported-target.json - release/official-dsh-policy.json workflow_dispatch: concurrency: group: production-supported-target cancel-in-progress: false permissions: contents: read jobs: publish: name: Sign and publish Supported Target if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest timeout-minutes: 30 environment: production-release permissions: contents: write steps: - name: Checkout protected branch uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: 24 cache: npm cache-dependency-path: | container/control-plane/services/management/package-lock.json container/environment/resources/plugins/platform-management/package/package-lock.json - name: Install Management runtime dependencies run: | npm ci --omit=dev --ignore-scripts --no-audit --no-fund --prefix container/control-plane/services/management npm ci --omit=dev --ignore-scripts --no-audit --no-fund --legacy-peer-deps --prefix container/environment/resources/plugins/platform-management/package - name: Verify merged Supported Target id: target run: | result="$(node container/platform/tools/supported-target.mjs validate \ release/supported-target.json container/environment/definition.json)" dsh_version="$(jq -er '.target.latestSupportedDsh' <<<"$result")" environment_version="$(jq -er '.target.environment' <<<"$result")" [[ "$environment_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] printf 'dsh_version=%s\n' "$dsh_version" >> "$GITHUB_OUTPUT" printf 'environment_version=%s\n' "$environment_version" >> "$GITHUB_OUTPUT" - name: Run release contract tests run: | npm test --prefix container/platform npm test --prefix container/control-plane/services/gateway node container/test/compose-config.mjs - name: Install protected public trust bundle env: RECOVERY_ROOT_PUBLIC_KEY: ${{ secrets.DSH_RECOVERY_ROOT_PUBLIC_KEY }} KEYRING_JSON_BASE64: ${{ secrets.DSH_KEYRING_JSON_BASE64 }} KEYRING_SIGNATURE_BASE64: ${{ secrets.DSH_KEYRING_SIGNATURE_BASE64 }} run: | test -n "$RECOVERY_ROOT_PUBLIC_KEY" test -n "$KEYRING_JSON_BASE64" test -n "$KEYRING_SIGNATURE_BASE64" mkdir -p "$RUNNER_TEMP/production-trust" printf '%s\n' "$RECOVERY_ROOT_PUBLIC_KEY" > "$RUNNER_TEMP/production-trust/recovery-root.spki.base64" printf '%s' "$KEYRING_JSON_BASE64" | base64 --decode > "$RUNNER_TEMP/production-trust/keyring.json" printf '%s' "$KEYRING_SIGNATURE_BASE64" | base64 --decode > "$RUNNER_TEMP/production-trust/keyring.sig.json" - name: Resolve signed channel and target sequence id: sequence run: | channel="$RUNNER_TEMP/release-channel" keyring_sha256="$(sha256sum "$RUNNER_TEMP/production-trust/keyring.json" | cut -d ' ' -f 1)" previous=- reuse=false if git ls-remote --exit-code origin refs/heads/release-channel >/dev/null 2>&1; then git fetch origin refs/heads/release-channel channel_commit="$(git rev-parse FETCH_HEAD)" git worktree add --detach "$channel" "$channel_commit" current_sequence="$(jq -er .targetSequence "$channel/stable.json")" previous="$channel/targets/$current_sequence" test -d "$previous" for name in \ stable.json \ stable.sig.json \ keyring.json \ keyring.sig.json \ bootstrap.manifest.json \ bootstrap.manifest.sig.json; do test -f "$previous/$name" done for name in stable.json stable.sig.json keyring.json keyring.sig.json; do cmp "$channel/$name" "$previous/$name" done if test -f "$channel/channel.json" \ && test "$(jq -er .sourceCommit "$channel/channel.json")" = "$GITHUB_SHA" \ && test "$(jq -er .keyringSha256 "$channel/channel.json")" = "$keyring_sha256"; then sequence="$current_sequence" reuse=true else test "$current_sequence" -lt 9007199254740991 sequence="$((current_sequence + 1))" fi else git worktree add --detach "$channel" "$GITHUB_SHA" git -C "$channel" checkout --orphan release-channel git -C "$channel" rm -rf . sequence=1 channel_commit= fi { printf 'channel=%s\n' "$channel" printf 'channel_commit=%s\n' "$channel_commit" printf 'keyring_sha256=%s\n' "$keyring_sha256" printf 'previous=%s\n' "$previous" printf 'reuse=%s\n' "$reuse" printf 'sequence=%s\n' "$sequence" } >> "$GITHUB_OUTPUT" printf 'Target sequence: %s (reuse: %s)\n' "$sequence" "$reuse" >> "$GITHUB_STEP_SUMMARY" - name: Download reviewed DSH npm Artifact if: steps.sequence.outputs.reuse != 'true' env: DSH_VERSION: ${{ steps.target.outputs.dsh_version }} run: | mkdir -p "$RUNNER_TEMP/platform-release-input" npm pack "@deepseek-ai/dsh@${DSH_VERSION}" --pack-destination "$RUNNER_TEMP/platform-release-input" >/dev/null tarball="$(find "$RUNNER_TEMP/platform-release-input" -maxdepth 1 -type f -name '*.tgz' -print -quit)" test -n "$tarball" printf 'DSH_TARBALL=%s\n' "$tarball" >> "$GITHUB_ENV" - name: Prepare signed immutable target if: steps.sequence.outputs.reuse != 'true' env: RELEASE_PRIVATE_KEY: ${{ secrets.DSH_RELEASE_PRIVATE_KEY }} TARGET_SEQUENCE: ${{ steps.sequence.outputs.sequence }} PREVIOUS_STABLE: ${{ steps.sequence.outputs.previous }} run: | test -n "$RELEASE_PRIVATE_KEY" umask 077 private_key="$RUNNER_TEMP/current-release-private.pem" trap 'rm -f "$private_key"' EXIT printf '%s\n' "$RELEASE_PRIVATE_KEY" > "$private_key" artifact_base_url="https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/release-channel/targets/${TARGET_SEQUENCE}/" SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")" export SOURCE_DATE_EPOCH node container/platform/tools/prepare-release.mjs \ release/supported-target.json container/environment/definition.json release/official-dsh-policy.json \ "$RUNNER_TEMP/production-trust" "$private_key" "$DSH_TARBALL" "$PREVIOUS_STABLE" \ "$TARGET_SEQUENCE" "$artifact_base_url" "$RUNNER_TEMP/platform-release" rm -f "$private_key" trap - EXIT - name: Reuse an already published target after retry if: steps.sequence.outputs.reuse == 'true' env: TARGET_SEQUENCE: ${{ steps.sequence.outputs.sequence }} CHANNEL: ${{ steps.sequence.outputs.channel }} DSH_VERSION: ${{ steps.target.outputs.dsh_version }} ENVIRONMENT_VERSION: ${{ steps.target.outputs.environment_version }} run: | cp -a "$CHANNEL/targets/$TARGET_SEQUENCE" "$RUNNER_TEMP/platform-release" test "$(jq -er .desired.dsh.version "$RUNNER_TEMP/platform-release/stable.json")" = "$DSH_VERSION" test "$(jq -er .desired.environment.version "$RUNNER_TEMP/platform-release/stable.json")" = "$ENVIRONMENT_VERSION" - name: Derive Environment release identity run: | node container/platform/tools/environment-release.mjs \ "$RUNNER_TEMP/platform-release/bootstrap.manifest.json" \ "$RUNNER_TEMP/platform-release/environment.manifest.json" \ release/official-dsh-policy.json \ "$RUNNER_TEMP/environment-release.json" - name: Validate Environment version ownership id: environment-check env: GH_TOKEN: ${{ github.token }} ENVIRONMENT_VERSION: ${{ steps.target.outputs.environment_version }} run: | tag="v${ENVIRONMENT_VERSION}" publish=true if gh release view "$tag" --json isDraft > "$RUNNER_TEMP/environment-release-state.json" 2>/dev/null; then if test "$(jq -er .isDraft "$RUNNER_TEMP/environment-release-state.json")" = false; then mkdir -p "$RUNNER_TEMP/published-environment" gh release download "$tag" --pattern environment-release.json --dir "$RUNNER_TEMP/published-environment" cmp "$RUNNER_TEMP/environment-release.json" "$RUNNER_TEMP/published-environment/environment-release.json" publish=false else mkdir -p "$RUNNER_TEMP/draft-environment" if gh release download "$tag" --pattern environment-release.json --dir "$RUNNER_TEMP/draft-environment" 2>/dev/null; then cmp "$RUNNER_TEMP/environment-release.json" "$RUNNER_TEMP/draft-environment/environment-release.json" fi fi elif git ls-remote --exit-code origin "refs/tags/$tag" >/dev/null 2>&1; then echo "Git tag $tag exists without a matching Environment Release" >&2 exit 1 fi printf 'publish=%s\n' "$publish" >> "$GITHUB_OUTPUT" printf 'tag=%s\n' "$tag" >> "$GITHUB_OUTPUT" - name: Publish signed channel target env: CHANNEL: ${{ steps.sequence.outputs.channel }} EXISTING_CHANNEL_COMMIT: ${{ steps.sequence.outputs.channel_commit }} KEYRING_SHA256: ${{ steps.sequence.outputs.keyring_sha256 }} REUSE: ${{ steps.sequence.outputs.reuse }} TARGET_SEQUENCE: ${{ steps.sequence.outputs.sequence }} DSH_VERSION: ${{ steps.target.outputs.dsh_version }} ENVIRONMENT_VERSION: ${{ steps.target.outputs.environment_version }} run: | if test "$REUSE" = true; then commit="$EXISTING_CHANNEL_COMMIT" else target="$CHANNEL/targets/$TARGET_SEQUENCE" test ! -e "$target" mkdir -p "$target" cp -a "$RUNNER_TEMP/platform-release/." "$target/" for name in stable.json stable.sig.json keyring.json keyring.sig.json; do cp "$target/$name" "$CHANNEL/$name"; done # shellcheck disable=SC2016 jq -n --arg sourceCommit "$GITHUB_SHA" --arg keyringSha256 "$KEYRING_SHA256" \ --arg dshVersion "$DSH_VERSION" --arg environmentVersion "$ENVIRONMENT_VERSION" \ --argjson targetSequence "$TARGET_SEQUENCE" \ '{schema:1,sourceCommit:$sourceCommit,keyringSha256:$keyringSha256,targetSequence:$targetSequence,dshVersion:$dshVersion,environmentVersion:$environmentVersion}' \ > "$CHANNEL/channel.json" git -C "$CHANNEL" config user.name github-actions git -C "$CHANNEL" config user.email github-actions@github.com git -C "$CHANNEL" add channel.json keyring.json keyring.sig.json stable.json stable.sig.json "targets/$TARGET_SEQUENCE" git -C "$CHANNEL" commit -m "release: publish target sequence $TARGET_SEQUENCE" commit="$(git -C "$CHANNEL" rev-parse HEAD)" git -C "$CHANNEL" push origin HEAD:refs/heads/release-channel fi # shellcheck disable=SC2016 printf 'Published signed channel sequence `%s` at `%s`.\n' "$TARGET_SEQUENCE" "$commit" >> "$GITHUB_STEP_SUMMARY" - name: Publish Environment Release env: GH_TOKEN: ${{ github.token }} PUBLISH_ENVIRONMENT: ${{ steps.environment-check.outputs.publish }} ENVIRONMENT_TAG: ${{ steps.environment-check.outputs.tag }} ENVIRONMENT_VERSION: ${{ steps.target.outputs.environment_version }} DSH_VERSION: ${{ steps.target.outputs.dsh_version }} TARGET_SEQUENCE: ${{ steps.sequence.outputs.sequence }} run: | prune_environment_assets() { gh release view "$ENVIRONMENT_TAG" --json assets --jq '.assets[].name' | while IFS= read -r name; do if test "$name" != environment-release.json; then gh release delete-asset "$ENVIRONMENT_TAG" "$name" --yes fi done } if test "$PUBLISH_ENVIRONMENT" != true; then prune_environment_assets test "$(gh release view "$ENVIRONMENT_TAG" --json assets --jq '.assets[].name')" = environment-release.json exit 0 fi assets="$RUNNER_TEMP/environment-release-assets" mkdir -p "$assets" cp "$RUNNER_TEMP/environment-release.json" "$assets/" notes="$RUNNER_TEMP/environment-release-notes.md" # shellcheck disable=SC2016 { printf 'Container Environment: `%s`\n\n' "$ENVIRONMENT_VERSION" printf 'Supported DSH at publication: `%s`\n\n' "$DSH_VERSION" printf 'Initial signed target sequence: `%s`\n' "$TARGET_SEQUENCE" } > "$notes" if ! gh release view "$ENVIRONMENT_TAG" >/dev/null 2>&1; then gh release create "$ENVIRONMENT_TAG" --draft --target "$GITHUB_SHA" \ --title "$ENVIRONMENT_TAG" --notes-file "$notes" fi gh release edit "$ENVIRONMENT_TAG" --draft --target "$GITHUB_SHA" \ --title "$ENVIRONMENT_TAG" --notes-file "$notes" gh release upload "$ENVIRONMENT_TAG" "$assets"/* --clobber prune_environment_assets find "$assets" -maxdepth 1 -type f -printf '%f\n' | sort > "$RUNNER_TEMP/local-environment-assets" gh release view "$ENVIRONMENT_TAG" --json assets --jq '.assets[].name' | sort > "$RUNNER_TEMP/remote-environment-assets" diff -u "$RUNNER_TEMP/local-environment-assets" "$RUNNER_TEMP/remote-environment-assets" gh release edit "$ENVIRONMENT_TAG" --draft=false --latest \ --title "$ENVIRONMENT_TAG" --notes-file "$notes"