#!/usr/bin/env -S deno run --check --allow-write=. --allow-read=. --lock=./tools/deno.lock.json
// Copyright 2018-2026 the Deno authors. MIT license.
import { conditions, createWorkflow, job, step } from "jsr:@david/gagen@0.3.1";

const isDenoland = conditions.isRepository("denoland/deno");

const windowsJob = job("promote-to-release-windows", {
  name: "Promote Windows to Release",
  runsOn: "windows-2022",
  if: isDenoland,
  strategy: {
    matrix: {
      target: ["x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc"],
    },
    failFast: false,
  },
  permissions: {
    contents: "write",
    "id-token": "write",
  },
  environment: { name: "build" },
  steps: [
    step({
      name: "Clone repository",
      uses: "actions/checkout@v6",
      with: {
        token: "${{ secrets.DENOBOT_PAT }}",
        submodules: false,
      },
    }),
    step({
      name: "Install deno",
      uses: "denoland/setup-deno@v2",
      with: { "deno-version": "v2.x" },
    }),
    step({
      name: "Download Windows binaries",
      run: [
        "# LTS is cut from a release branch with no canary build, so re-stamp",
        "# the already-published stable release binaries. RC still promotes",
        "# canary binaries by commit hash.",
        'if ("${{github.event.inputs.releaseKind}}" -eq "lts") {',
        '  $Version = "${{github.event.inputs.commitHash}}" -replace "^v", ""',
        '  $SrcUrl = "https://dl.deno.land/release/v$Version"',
        "} else {",
        '  $SrcUrl = "https://dl.deno.land/canary/${{github.event.inputs.commitHash}}"',
        "}",
        'Invoke-WebRequest -Uri "$SrcUrl/deno-${{ matrix.target }}.zip" -OutFile "deno-windows.zip"',
        'Invoke-WebRequest -Uri "$SrcUrl/denort-${{ matrix.target }}.zip" -OutFile "denort-windows.zip"',
        'Expand-Archive -Path "deno-windows.zip" -DestinationPath "."',
        'Expand-Archive -Path "denort-windows.zip" -DestinationPath "."',
      ],
    }),
    step({
      name: "Run patchver for Windows",
      run:
        "deno run -A --minimum-dependency-age=0 ./tools/release/promote_to_release_windows.ts ${{github.event.inputs.releaseKind}}",
    }),
    step({
      name: "Authenticate with Azure",
      uses: "azure/login@v1",
      with: {
        "client-id": "${{ secrets.AZURE_CLIENT_ID }}",
        "tenant-id": "${{ secrets.AZURE_TENANT_ID }}",
        "subscription-id": "${{ secrets.AZURE_SUBSCRIPTION_ID }}",
        "enable-AzPSSession": true,
      },
    }),
    step({
      name: "Code sign deno.exe",
      uses: "Azure/artifact-signing-action@v0",
      with: {
        "cache-dependencies": false,
        trace: true,
        endpoint: "https://eus.codesigning.azure.net/",
        "trusted-signing-account-name": "deno-cli-code-signing",
        "certificate-profile-name": "deno-cli-code-signing-cert",
        "files-folder": ".",
        "files-folder-filter": "deno.exe",
        "file-digest": "SHA256",
        "timestamp-rfc3161": "http://timestamp.acs.microsoft.com",
        "timestamp-digest": "SHA256",
        "exclude-environment-credential": true,
        "exclude-workload-identity-credential": true,
        "exclude-managed-identity-credential": true,
        "exclude-shared-token-cache-credential": true,
        "exclude-visual-studio-credential": true,
        "exclude-visual-studio-code-credential": true,
        "exclude-azure-cli-credential": false,
      },
    }),
    step({
      name: "Verify signature",
      shell: "pwsh",
      run: [
        '$SignTool = Get-ChildItem -Path "C:\\Program Files*\\Windows Kits\\*\\bin\\*\\x64\\signtool.exe" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1',
        "$SignToolPath = $SignTool.FullName",
        '& $SignToolPath verify /pa /v "deno.exe"',
      ],
    }),
    step({
      name: "Create archives",
      run: [
        'Compress-Archive -Path "deno.exe" -DestinationPath "deno-${{ matrix.target }}.zip" -Force',
        'Compress-Archive -Path "denort.exe" -DestinationPath "denort-${{ matrix.target }}.zip" -Force',
      ],
    }),
    step({
      name: "Upload Windows archives",
      uses: "actions/upload-artifact@v6",
      with: {
        name: "windows-binaries-${{ matrix.target }}",
        path: "deno-${{ matrix.target }}.zip\ndenort-${{ matrix.target }}.zip",
      },
    }),
  ],
});

const workflow = createWorkflow({
  name: "promote_to_release",
  on: {
    workflow_dispatch: {
      inputs: {
        releaseKind: {
          description: "Kind of release",
          type: "choice",
          options: ["rc", "lts"],
          required: true,
        },
        commitHash: {
          description:
            "rc: canary commit hash to promote. lts: release version to re-stamp, e.g. v2.9.3",
          required: true,
        },
      },
    },
  },
  jobs: [
    windowsJob,
    {
      id: "promote-to-release",
      name: "Promote to Release",
      runsOn: "macOS-latest",
      if: isDenoland,
      needs: [windowsJob],
      steps: [
        step({
          name: "Clone repository",
          uses: "actions/checkout@v6",
          with: {
            token: "${{ secrets.DENOBOT_PAT }}",
            submodules: "recursive",
          },
        }),
        step({
          name: "Install deno",
          uses: "denoland/setup-deno@v2",
          with: { "deno-version": "v2.x" },
        }),
        step({
          name: "Install rust-codesign",
          run: [
            "./tools/install_prebuilt.js rcodesign",
            "echo $GITHUB_WORKSPACE/third_party/prebuilt/mac >> $GITHUB_PATH",
          ],
        }),
        step({
          name: "Promote to Release (non-Windows)",
          env: {
            APPLE_CODESIGN_KEY: "${{ secrets.APPLE_CODESIGN_KEY }}",
            APPLE_CODESIGN_PASSWORD: "${{ secrets.APPLE_CODESIGN_PASSWORD }}",
          },
          run:
            "deno run -A --minimum-dependency-age=0 ./tools/release/promote_to_release.ts ${{github.event.inputs.releaseKind}} ${{github.event.inputs.commitHash}}",
        }),
        step({
          name: "Download Windows binaries",
          uses: "actions/download-artifact@v7",
          with: {
            pattern: "windows-binaries-*",
            "merge-multiple": true,
            path: ".",
          },
        }),
        step({
          name: "Create version file",
          run: [
            "# Unzip a binary to get the version",
            "unzip -o deno-x86_64-apple-darwin.zip",
            "DENO_VERSION=$(./deno -V | cut -d ' ' -f 2 | cut -d '+' -f 1)",
            'echo "v${DENO_VERSION}" > release-${{github.event.inputs.releaseKind}}-latest.txt',
            "rm -f ./deno",
          ],
        }),
        step({
          name: "Generate checksums",
          run: [
            "# Re-stamping (patchver + re-sign) produces binaries that differ",
            "# byte-for-byte from the stable/canary artifacts they were promoted",
            "# from, so the checksums change too. Regenerate the sidecars here;",
            "# otherwise the source release's stale *.sha256sum stays in the",
            "# bucket and no longer matches the promoted zip (see #36235).",
            "for f in deno-*.zip denort-*.zip; do",
            '  shasum -a 256 "$f" > "$f.sha256sum"',
            '  tmp="$(mktemp -d)"',
            '  unzip -o -q "$f" -d "$tmp"',
            '  ( cd "$tmp" && shasum -a 256 deno* ) > "${f%.zip}.sha256sum"',
            '  rm -rf "$tmp"',
            "done",
          ],
        }),
        step({
          name: "Upload archives to dl.deno.land",
          env: {
            AWS_ACCESS_KEY_ID: "${{ vars.S3_ACCESS_KEY_ID }}",
            AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRET_ACCESS_KEY }}",
            AWS_ENDPOINT_URL_S3: "${{ vars.S3_ENDPOINT }}",
            AWS_DEFAULT_REGION: "${{vars.S3_REGION }}",
          },
          run: [
            "VERSION=$(cat release-${{github.event.inputs.releaseKind}}-latest.txt)",
            "# Upload only the promoted release archives and their freshly",
            "# regenerated checksums. `aws s3 sync ./` walked the whole checkout",
            "# (submodules included), uploading stray *.zip test fixtures and",
            "# tripping over a broken symlink (exit 2).",
            "for f in deno-*.zip denort-*.zip; do",
            '  aws s3 cp "./$f" "s3://dl-deno-land/release/$VERSION/$f"',
            '  aws s3 cp "./$f.sha256sum" "s3://dl-deno-land/release/$VERSION/$f.sha256sum"',
            '  aws s3 cp "./${f%.zip}.sha256sum" "s3://dl-deno-land/release/$VERSION/${f%.zip}.sha256sum"',
            "done",
            "aws s3 cp release-${{github.event.inputs.releaseKind}}-latest.txt s3://dl-deno-land/release-${{github.event.inputs.releaseKind}}-latest.txt",
          ],
        }),
        step({
          name: "Purge CDN cache",
          env: {
            CLOUDFLARE_ZONE_ID: "${{ vars.CLOUDFLARE_ZONE_ID }}",
            CLOUDFLARE_API_TOKEN: "${{ secrets.CLOUDFLARE_API_TOKEN }}",
          },
          run: [
            "# The latest.txt file lives at a stable URL but changes every",
            "# release, so the edge has to be told to drop it. Otherwise",
            "# `deno upgrade` keeps serving the previous version until the",
            "# 4 hour TTL expires.",
            "deno run -A --lock=tools/deno.lock.json tools/release/purge_cdn_cache.ts release-${{github.event.inputs.releaseKind}}-latest.txt",
          ],
        }),
      ],
    },
  ],
});

const header = "# GENERATED BY ./promote_to_release.ts -- DO NOT DIRECTLY EDIT";

export function generate() {
  return workflow.toYamlString({ header });
}

export const PROMOTE_TO_RELEASE_YML_URL = new URL(
  "./promote_to_release.generated.yml",
  import.meta.url,
);

if (import.meta.main) {
  workflow.writeOrLint({ filePath: PROMOTE_TO_RELEASE_YML_URL, header });
}
