-
Notifications
You must be signed in to change notification settings - Fork 688
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ci: easy pre-releases for new packages (#4757)
* ci: easy pre-releases for new packages * fixup! ci: easy pre-releases for new packages Expose ACTIONS_RUNTIME_TOKEN and ACTIONS_RESULTS_URL variables * fixup! ci: easy pre-releases for new packages Switch to `workers-sdk` object in `package.json` * fixup! ci: easy pre-releases for new packages Remove extraneous `|` in pre-release comment * fixup! ci: easy pre-releases for new packages Simplify validation/extraction of `githubPullRequestNumber` * fixup! ci: easy pre-releases for new packages Use `forEach` instead of `for of` * fixup! ci: easy pre-releases for new packages Explain why we need custom action for extracting action variables * fixup! fixup! ci: easy pre-releases for new packages
- Loading branch information
Showing
15 changed files
with
973 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
name: "Expose Actions Variables" | ||
description: "Expose ACTIONS_RUNTIME_TOKEN and ACTIONS_RESULTS_URL on GITHUB_ENV" | ||
runs: | ||
using: "node20" | ||
main: "index.mjs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import fs from "node:fs"; | ||
|
||
fs.appendFileSync( | ||
process.env.GITHUB_ENV, | ||
`GITHUB_ACTIONS_RUNTIME_TOKEN=${process.env.ACTIONS_RUNTIME_TOKEN}\n` + | ||
`GITHUB_ACTIONS_RESULTS_URL=${process.env.ACTIONS_RESULTS_URL}\n` | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import assert from "node:assert"; | ||
import { execSync } from "node:child_process"; | ||
import fs from "node:fs"; | ||
import path from "node:path"; | ||
import { fileURLToPath } from "node:url"; | ||
|
||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = path.dirname(__filename); | ||
export const projectRoot = path.resolve(__dirname, "../.."); | ||
|
||
const githubRunId = parseInt(process.env.GITHUB_RUN_ID); | ||
assert( | ||
!Number.isNaN(githubRunId), | ||
"Expected GITHUB_RUN_ID variable to be a number" | ||
); | ||
const githubEventPath = process.env.GITHUB_EVENT_PATH; | ||
assert(githubEventPath, "Expected GITHUB_EVENT_PATH variable to be defined"); | ||
const githubEventContents = fs.readFileSync(githubEventPath, "utf8"); | ||
const githubEvent = JSON.parse(githubEventContents); | ||
const githubPullRequestNumber = githubEvent?.pull_request?.number; | ||
assert( | ||
typeof githubPullRequestNumber === "number", | ||
`Expected valid pull_request event, got ${githubEventContents}` | ||
); | ||
|
||
/** | ||
* @typedef {object} ~PackageJsonWorkersSdk | ||
* @property {boolean} [prerelease] | ||
*/ | ||
|
||
/** | ||
* @typedef {object} ~PackageJson | ||
* @property {string} name | ||
* @property {string} version | ||
* @property {Record<string, string>} [dependencies] | ||
* @property {Record<string, string>} [devDependencies] | ||
* @property {Record<string, string>} [peerDependencies] | ||
* @property {Record<string, string>} [optionalDependencies] | ||
* @property {~PackageJsonWorkersSdk} [workers-sdk] | ||
*/ | ||
|
||
/** | ||
* @typedef {object} ~Package | ||
* @property {string} path | ||
* @property {~PackageJson} json | ||
*/ | ||
|
||
/** @returns {string[]} */ | ||
function getPackagePaths() { | ||
const stdout = execSync( | ||
'pnpm list --filter="./packages/*" --recursive --depth=-1 --parseable', | ||
{ cwd: projectRoot, encoding: "utf8" } | ||
); | ||
return stdout.split("\n").filter((pkgPath) => path.isAbsolute(pkgPath)); | ||
} | ||
|
||
/** | ||
* @param {string} pkgPath | ||
* @returns {~Package} | ||
*/ | ||
function getPackage(pkgPath) { | ||
const json = fs.readFileSync(path.join(pkgPath, "package.json"), "utf8"); | ||
return { | ||
path: pkgPath, | ||
json: JSON.parse(json), | ||
}; | ||
} | ||
|
||
/** @param {~Package} pkg */ | ||
export function setPackage(pkg) { | ||
const json = JSON.stringify(pkg.json, null, "\t"); | ||
fs.writeFileSync(path.join(pkg.path, "package.json"), json); | ||
} | ||
|
||
/** @returns {~Package[]} */ | ||
function getPackages() { | ||
return getPackagePaths().map(getPackage); | ||
} | ||
|
||
/** @returns {~Package[]} */ | ||
export function getPackagesForPrerelease() { | ||
return getPackages().filter((pkg) => pkg.json["workers-sdk"]?.prerelease); | ||
} | ||
|
||
/** @param {string} pkgName */ | ||
export function getPrereleaseArtifactName(pkgName) { | ||
const name = pkgName.replaceAll("@", "").replaceAll("/", "-"); | ||
return `npm-package-${name}-${githubPullRequestNumber}`; | ||
} | ||
|
||
/** @param {string} pkgName */ | ||
export function getPrereleaseArtifactUrl(pkgName) { | ||
const artifactName = getPrereleaseArtifactName(pkgName); | ||
return `https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/${githubRunId}/${artifactName}`; | ||
} | ||
|
||
/** @param {string} pkgName */ | ||
export function getPrereleasePRArtifactUrl(pkgName) { | ||
const artifactName = getPrereleaseArtifactName(pkgName); | ||
return `https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/prs/${githubPullRequestNumber}/${artifactName}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { execSync } from "node:child_process"; | ||
import { | ||
getPackagesForPrerelease, | ||
getPrereleaseArtifactUrl, | ||
setPackage, | ||
} from "./0-packages.mjs"; | ||
|
||
function getPrereleaseVersion() { | ||
const sha = execSync("git rev-parse --short HEAD", { encoding: "utf8" }); | ||
return `0.0.0-${sha.trim()}`; | ||
} | ||
|
||
/** | ||
* @param {~Package[]} pkgs | ||
* @param {string} newVersion | ||
*/ | ||
function updateVersions(pkgs, newVersion) { | ||
for (const pkg of pkgs) pkg.json.version = newVersion; | ||
} | ||
|
||
/** | ||
* @param {~Package[]} pkgs | ||
* @param {string} newVersion | ||
*/ | ||
function updateDependencyVersions(pkgs, newVersion) { | ||
const prereleaseNames = new Set(pkgs.map((pkg) => pkg.json.name)); | ||
for (const pkg of pkgs) { | ||
for (const dependency of Object.keys(pkg.json.dependencies ?? {})) { | ||
if (prereleaseNames.has(dependency)) { | ||
pkg.json.dependencies[dependency] = | ||
getPrereleaseArtifactUrl(dependency); | ||
} | ||
} | ||
} | ||
} | ||
|
||
{ | ||
const pkgs = getPackagesForPrerelease(); | ||
const newVersion = getPrereleaseVersion(); | ||
updateVersions(pkgs, newVersion); | ||
updateDependencyVersions(pkgs, newVersion); | ||
pkgs.forEach(setPackage); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import assert from "node:assert"; | ||
import { execSync } from "node:child_process"; | ||
import path from "node:path"; | ||
import { DefaultArtifactClient } from "@actions/artifact"; | ||
import { | ||
getPackagesForPrerelease, | ||
getPrereleaseArtifactName, | ||
projectRoot, | ||
} from "./0-packages.mjs"; | ||
|
||
const artifact = new DefaultArtifactClient(); | ||
|
||
function buildAllPackages() { | ||
execSync("pnpm build", { cwd: projectRoot, stdio: "inherit" }); | ||
} | ||
|
||
/** | ||
* @param {~Package} pkg | ||
* @returns {string} | ||
*/ | ||
function packPackage(pkg) { | ||
const stdout = execSync("pnpm pack", { cwd: pkg.path, encoding: "utf8" }); | ||
const name = stdout.split("\n").find((line) => line.endsWith(".tgz")); | ||
assert(name !== undefined, `Expected ${stdout} to include tarball name`); | ||
return path.join(pkg.path, name); | ||
} | ||
|
||
/** | ||
* @param {~Package} pkg | ||
* @param {string} tarballPath | ||
*/ | ||
async function uploadPackageTarball(pkg, tarballPath) { | ||
const name = getPrereleaseArtifactName(pkg.json.name); | ||
console.log(`Uploading ${tarballPath} as ${name}...`); | ||
await artifact.uploadArtifact(name, [tarballPath], pkg.path); | ||
} | ||
|
||
{ | ||
buildAllPackages(); | ||
const pkgs = getPackagesForPrerelease(); | ||
for (const pkg of pkgs) { | ||
const tarballPath = packPackage(pkg); | ||
await uploadPackageTarball(pkg, tarballPath); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import assert from "node:assert"; | ||
import fs from "node:fs"; | ||
import path from "node:path"; | ||
import { DefaultArtifactClient } from "@actions/artifact"; | ||
import { | ||
getPackagesForPrerelease, | ||
getPrereleaseArtifactUrl, | ||
getPrereleasePRArtifactUrl, | ||
projectRoot, | ||
} from "./0-packages.mjs"; | ||
|
||
const artifact = new DefaultArtifactClient(); | ||
|
||
/** | ||
* @param {~Package} pkg | ||
* @returns {string} | ||
*/ | ||
function buildWranglerArtifactReport(pkg) { | ||
const name = pkg.json.name; | ||
assert.strictEqual(name, "wrangler"); | ||
const url = getPrereleaseArtifactUrl(name); | ||
const prUrl = getPrereleasePRArtifactUrl(name); | ||
return `A wrangler prerelease is available for testing. You can install this latest build in your project with: | ||
\`\`\`sh | ||
npm install --save-dev ${url} | ||
\`\`\` | ||
You can reference the automatically updated head of this PR with: | ||
\`\`\`sh | ||
npm install --save-dev ${prUrl} | ||
\`\`\` | ||
Or you can use \`npx\` with this latest build directly: | ||
\`\`\`sh | ||
npx ${url} dev path/to/script.js | ||
\`\`\``; | ||
} | ||
|
||
/** | ||
* @param {~Package} pkg | ||
* @returns {string} | ||
*/ | ||
function buildAdditionalArtifactReport(pkg) { | ||
const name = pkg.json.name; | ||
const url = getPrereleaseArtifactUrl(name); | ||
if (name === "create-cloudflare") { | ||
return `\`\`\`sh\nnpx ${url} --no-auto-update\n\`\`\``; | ||
} else { | ||
return `\`\`\`sh\nnpm install ${url}\n\`\`\``; | ||
} | ||
} | ||
|
||
/** | ||
* @param {~Package[]} pkgs | ||
* @returns {string} | ||
*/ | ||
function buildReport(pkgs) { | ||
const wranglerPkgIndex = pkgs.findIndex( | ||
(pkg) => pkg.json.name === "wrangler" | ||
); | ||
assert(wranglerPkgIndex !== -1, "Expected wrangler to be pre-released"); | ||
const [wranglerPkg] = pkgs.splice(wranglerPkgIndex, 1); | ||
|
||
const wranglerReport = buildWranglerArtifactReport(wranglerPkg); | ||
const additionalReports = pkgs.map(buildAdditionalArtifactReport); | ||
|
||
return `${wranglerReport} | ||
<details><summary>Additional artifacts:</summary> | ||
${additionalReports.join("\n\n")} | ||
Note that these links will no longer work once [the GitHub Actions artifact expires](https://docs.github.com/en/organizations/managing-organization-settings/configuring-the-retention-period-for-github-actions-artifacts-and-logs-in-your-organization). | ||
</details> | ||
`; | ||
} | ||
|
||
{ | ||
const pkgs = getPackagesForPrerelease(); | ||
const report = buildReport(pkgs); | ||
const reportName = "prerelease-report.md"; | ||
const reportPath = path.join(projectRoot, reportName); | ||
fs.writeFileSync(reportPath, report); | ||
console.log(`Uploading ${reportPath} as ${reportName}...`); | ||
await artifact.uploadArtifact(reportName, [reportPath], projectRoot); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.