Repository: denoland/deno
Pull request: ci: make uploading canary less racy, take 2

Another try at https://github.com/denoland/deno/issues/25464
using a Deno script.

Changed files:
filename: .github/workflows/ci.generate.ts
patch: @@ -177,7 +177,7 @@ const cloneRepoStep = [{
     // Use depth > 1, because sometimes we need to rebuild main and if
     // other commits have landed it will become impossible to rebuild if
     // the checkout is too shallow.
-    "fetch-depth": 5,
+    "fetch-depth": 15,
     submodules: false,
   },
 }];
@@ -930,11 +930,7 @@ const ci = {
             "github.ref == 'refs/heads/main'",
           ].join("\n"),
           run: [
-            'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/canary/$(git rev-parse HEAD)/',
-            'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/canary/$(git rev-parse HEAD)/',
-            'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.symcache gs://dl.deno.land/canary/$(git rev-parse HEAD)/',
-            "echo ${{ github.sha }} > canary-latest.txt",
-            'gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-$(rustc -vV | sed -n "s|host: ||p")-latest.txt',
+            "./tools/release/upload_canary.ts arch ${{ github.sha }}",
           ].join("\n"),
         },
         {
@@ -1248,8 +1244,7 @@ const ci = {
         {
           name: "Upload canary version file to dl.deno.land",
           run: [
-            "echo ${{ github.sha }} > canary-latest.txt",
-            'gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-latest.txt',
+            "./tools/release/upload_canary.ts latest ${{ github.sha }}",
           ].join("\n"),
         },
       ],

filename: .github/workflows/ci.yml
patch: @@ -34,7 +34,7 @@ jobs:
       - name: Clone repository
         uses: actions/checkout@v4
         with:
-          fetch-depth: 5
+          fetch-depth: 15
           submodules: false
         if: github.event.pull_request.draft == true
       - id: check
@@ -151,7 +151,7 @@ jobs:
       - name: Clone repository
         uses: actions/checkout@v4
         with:
-          fetch-depth: 5
+          fetch-depth: 15
           submodules: false
         if: '!(matrix.skip)'
       - name: Clone submodule ./tests/util/std
@@ -566,12 +566,7 @@ jobs:
           matrix.profile == 'release' &&
           github.repository == 'denoland/deno' &&
           github.ref == 'refs/heads/main')
-        run: |-
-          gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/canary/$(git rev-parse HEAD)/
-          gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/canary/$(git rev-parse HEAD)/
-          gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.symcache gs://dl.deno.land/canary/$(git rev-parse HEAD)/
-          echo ${{ github.sha }} > canary-latest.txt
-          gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-$(rustc -vV | sed -n "s|host: ||p")-latest.txt
+        run: './tools/release/upload_canary.ts arch ${{ github.sha }}'
       - name: Autobahn testsuite
         if: |-
           !(matrix.skip) && ((matrix.os == 'linux' && matrix.arch != 'aarch64') &&
@@ -787,7 +782,7 @@ jobs:
       - name: Clone repository
         uses: actions/checkout@v4
         with:
-          fetch-depth: 5
+          fetch-depth: 15
           submodules: false
         if: '!(matrix.skip)'
       - uses: dsherret/rust-toolchain-file@v1
@@ -820,6 +815,4 @@ jobs:
         with:
           project_id: denoland
       - name: Upload canary version file to dl.deno.land
-        run: |-
-          echo ${{ github.sha }} > canary-latest.txt
-          gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-latest.txt
+        run: './tools/release/upload_canary.ts latest ${{ github.sha }}'

filename: tools/release/upload_canary.ts
patch: @@ -0,0 +1,59 @@
+#!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+// deno-lint-ignore-file no-console
+
+import { $ } from "jsr:@david/dax@0.41.0";
+
+async function uploadCanaryForCurrentArch(currentGitSha) {
+  const currentGitRev = await $`git rev-parse HEAD`;
+  await $`gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/canary/${currentGitRev}/`;
+  await $`gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/canary/${currentGitRev}/`;
+  await $`gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.symcache gs://dl.deno.land/canary/${currentGitRev}/`;
+  const llvmTriple = await $`rustc -vV | sed -n "s|host: ||p"`;
+  const latestCanaryHash =
+    await $`gsutil cat gs://dl.deno.land/canary-${llvmTriple}-latest.txt`;
+  const commitExistsInHistory = await $`git cat-file -e ${latestCanaryHash}`;
+
+  if (commitExistsInHistory.code === 0) {
+    await Deno.writeTextFile("canary-latest.txt", currentGitSha);
+    await $`gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-${llvmTriple}-latest.txt`;
+  } else {
+    console.log("Skipping upload, newer canary version is already available");
+  }
+}
+
+async function uploadCanaryLatest(currentGitSha) {
+  const latestCanaryHash = $`gsutil cat gs://dl.deno.land/canary-latest.txt`;
+  const commitExistsInHistory = await $`git cat-file -e ${latestCanaryHash}`;
+
+  if (commitExistsInHistory.code === 0) {
+    await Deno.writeTextFile("canary-latest.txt", currentGitSha);
+    await $`gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-latest.txt`;
+  } else {
+    console.log("Skipping upload, newer canary version is already available");
+  }
+}
+
+function printUsage() {
+  throw new Error(
+    "Usage: `./upload_canary.ts arch|latest <currentGitSha>",
+  );
+}
+
+async function main() {
+  const kind = Deno.args[0];
+  const currentGitSha = Deno.args[1];
+  if (!kind || !currentGitSha) {
+    printUsage();
+  }
+  if (kind === "arch") {
+    await uploadCanaryForCurrentArch(currentGitSha);
+  } else if (kind === "latest") {
+    await uploadCanaryLatest(currentGitSha);
+  } else {
+    printUsage();
+  }
+}
+
+await main();