From 5cab3e7dba54643f95c147b48696c8af8a26632c Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 30 Mar 2022 16:37:00 -0400 Subject: [PATCH] build: use workflows for bumping versions and cargo publishing on the CI (#13995) --- .github/workflows/cargo_publish.yml | 42 +++++++++++++ .github/workflows/ci.yml | 6 +- .github/workflows/version_bump.yml | 57 ++++++++++++++++++ cli/compat/esm_resolver.rs | 7 ++- cli/compat/mod.rs | 2 +- tools/cut_a_release.md | 79 ++++++++++++------------- tools/release/01_bump_crate_versions.ts | 40 ++++++++++++- tools/release/02_create_pr.ts | 51 ++++++++++++++++ tools/release/02_publish_crates.ts | 23 ------- tools/release/03_publish_crates.ts | 48 +++++++++++++++ tools/release/04_create_release_tag.ts | 17 ++++++ tools/release/deno_workspace.ts | 5 +- tools/release/deps.ts | 3 +- 13 files changed, 306 insertions(+), 74 deletions(-) create mode 100644 .github/workflows/cargo_publish.yml create mode 100644 .github/workflows/version_bump.yml create mode 100755 tools/release/02_create_pr.ts delete mode 100755 tools/release/02_publish_crates.ts create mode 100755 tools/release/03_publish_crates.ts create mode 100755 tools/release/04_create_release_tag.ts diff --git a/.github/workflows/cargo_publish.yml b/.github/workflows/cargo_publish.yml new file mode 100644 index 0000000000..29eee22faf --- /dev/null +++ b/.github/workflows/cargo_publish.yml @@ -0,0 +1,42 @@ +name: cargo_publish + +on: workflow_dispatch + +jobs: + build: + name: cargo publish + runs-on: ubuntu-20.04-xl + timeout-minutes: 90 + + env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + RUSTC_FORCE_INCREMENTAL: 1 + + steps: + - name: Configure git + run: | + git config --global core.symlinks true + git config --global fetch.parallel 32 + + - name: Clone repository + uses: actions/checkout@v2 + with: + submodules: recursive + + - uses: dtolnay/rust-toolchain@stable + + - name: Install deno + uses: denoland/setup-deno@v1 + with: + deno-version: v1.19.3 + + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: ./tools/release/03_publish_crates.ts --real + + - name: Create release tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./tools/release/04_create_release_tag.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 113ee4db04..fe45bda4e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,9 +89,9 @@ jobs: - name: Install Deno if: matrix.job == 'lint' - run: | - curl -fsSL https://deno.land/x/install/install.sh | sh -s v1.13.2 - echo "$HOME/.deno/bin" >> $GITHUB_PATH + uses: denoland/setup-deno@v1 + with: + deno-version: v1.19.3 - name: Install Python uses: actions/setup-python@v1 diff --git a/.github/workflows/version_bump.yml b/.github/workflows/version_bump.yml new file mode 100644 index 0000000000..caba2e0143 --- /dev/null +++ b/.github/workflows/version_bump.yml @@ -0,0 +1,57 @@ +name: version_bump + +on: + workflow_dispatch: + inputs: + releaseKind: + description: 'Kind of version bump' + default: 'patch' + type: choice + options: + - patch + - minor + - major + required: true + +jobs: + build: + name: version bump + runs-on: ubuntu-20.04-xl + timeout-minutes: 90 + + env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + RUSTC_FORCE_INCREMENTAL: 1 + + steps: + - name: Configure git + run: | + git config --global core.symlinks true + git config --global fetch.parallel 32 + + - name: Clone repository + uses: actions/checkout@v2 + with: + submodules: recursive + + - uses: dtolnay/rust-toolchain@stable + + - name: Install deno + uses: denoland/setup-deno@v1 + with: + deno-version: v1.19.3 + + - name: Run version bump + run: | + git remote add upstream https://github.com/denoland/deno + ./tools/release/01_bump_crate_versions.ts --${{github.event.inputs.releaseKind}} + + - name: Create PR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_WORKFLOW_ACTOR: ${{ github.actor }} + run: | + git config user.email "${{ github.actor }}@users.noreply.github.com" + git config user.name "${{ github.actor }}" + ./tools/release/02_create_pr.ts diff --git a/cli/compat/esm_resolver.rs b/cli/compat/esm_resolver.rs index c7483b77fe..d8aee1d092 100644 --- a/cli/compat/esm_resolver.rs +++ b/cli/compat/esm_resolver.rs @@ -1155,6 +1155,7 @@ fn legacy_main_resolve( #[cfg(test)] mod tests { use super::*; + use crate::compat::STD_URL_STR; fn testdir(name: &str) -> PathBuf { let c = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -1233,8 +1234,10 @@ mod tests { fn builtin_http() { let cwd = testdir("basic"); let main = Url::from_file_path(cwd.join("main.js")).unwrap(); - let expected = - Url::parse("https://deno.land/std@0.132.0/node/http.ts").unwrap(); + let expected = Url::parse(STD_URL_STR) + .unwrap() + .join("node/http.ts") + .unwrap(); let actual = node_resolve("http", main.as_str(), &cwd).unwrap(); assert!(matches!(actual, ResolveResponse::Esm(_))); diff --git a/cli/compat/mod.rs b/cli/compat/mod.rs index 07cd170307..3429fe5f22 100644 --- a/cli/compat/mod.rs +++ b/cli/compat/mod.rs @@ -20,7 +20,7 @@ pub use esm_resolver::NodeEsmResolver; // each release, a better mechanism is preferable, but it's a quick and dirty // solution to avoid printing `X-Deno-Warning` headers when the compat layer is // downloaded -static STD_URL_STR: &str = "https://deno.land/std@0.132.0/"; +pub(crate) static STD_URL_STR: &str = "https://deno.land/std@0.132.0/"; static SUPPORTED_MODULES: &[&str] = &[ "assert", diff --git a/tools/cut_a_release.md b/tools/cut_a_release.md index aa5e0eb47d..ca2d2d2739 100644 --- a/tools/cut_a_release.md +++ b/tools/cut_a_release.md @@ -64,63 +64,62 @@ previous release and start cherry-picking newer commits from the `main`. Once all relevant commits are cherry-picked, push the branch to the upstream and verify on GitHub that everything looks correct. -1. Update your local branch (`v1.XX` for patch or `main` for minor) and checkout - another branch (e.g. `release_#.#.#`). +### Phase 1: Bumping versions -2. Run `./tools/release/01_bump_crate_versions.ts` to increase the versions of - all crates including the CLI. If you are doing a CLI patch release, answer - `y` to the _Increment patch?_ prompt. +1. After releasing deno_std, go to the "version_bump" workflow in the CLI repo's + actions: https://github.com/denoland/deno/actions/workflows/version_bump.yml -3. The above command will update the `Releases.md` file. Review it and ensure - its output is correct. **If you are cutting a minor release**: make sure that - there are no duplicate entries in previous releases; most often commits with - `fix` prefix would have been included in patch releases. +2. Click on the "Run workflow" button. + 1. In the drop down, select the minor branch if doing a path release or the + main branch if doing a minor release. + 2. For the kind of release, select either "patch", "minor", or "major". + 3. Run the workflow. -4. Update link in `cli/compat/mod.rs` with the released version of `deno_std` - and do a search through the tests to find std urls that need to be updated. +3. Wait for the workflow to complete and for a pull request to be automatically + opened. -5. Create a PR for these changes. **If you are cutting a patch release**: make - sure to target `v1.XX` branch instead of `main` in your PR. +4. Review the pull request and make any necessary changes. -6. Make sure CI pipeline passes (DO NOT merge yet). +5. Merge it. -7. Publish the crates to `crates.io` by running - `./tools/release/02_publish_crates.ts` +### Phase 2: Publish -8. Merge the PR. +1. Go to the "cargo_publish" workflow in the CLI repo's actions: + https://github.com/denoland/deno/actions/workflows/cargo_publish.yml -9. Create a tag with the version number (with `v` prefix). +2. Run it on the same branch that you used before and wait for it to complete. -10. Wait for CI pipeline on the created tag branch to pass. +3. This CI run create a tag which triggers a second CI run that publishes the + GitHub draft release. - The CI pipeline will create a release draft on GitHub - (https://github.com/denoland/deno/releases). Update the draft with the - contents of `Releases.md` that you previously added. + The CI pipeline will create a release draft on GitHub + (https://github.com/denoland/deno/releases). Update the draft with the + contents of `Releases.md` that you previously added. -11. Upload Apple M1 build (`deno-aarch64-apple-darwin.zip`) to the release draft - and to https://console.cloud.google.com/storage/browser/dl.deno.land +4. Upload Apple M1 build (`deno-aarch64-apple-darwin.zip`) to the release draft + and to https://console.cloud.google.com/storage/browser/dl.deno.land - ``` - cargo build --release - cd target/release - zip -r deno-aarch64-apple-darwin.zip deno - ``` + ``` + cargo build --release + cd target/release + zip -r deno-aarch64-apple-darwin.zip deno + ``` -12. Publish the release on Github +5. Publish the release on Github -13. Update the Deno version on the website by updating - https://github.com/denoland/dotland/blob/main/versions.json. +6. Update the Deno version on the website by updating + https://github.com/denoland/dotland/blob/main/versions.json. -14. Push a new tag to [`manual`](https://github.com/denoland/manual). The tag - must match the CLI tag; you don't need to create dedicated commit for that - purpose, it's enough to tag the latest commit in that repo. +7. Push a new tag to [`manual`](https://github.com/denoland/manual). The tag + must match the CLI tag; you don't need to create dedicated commit for that + purpose, it's enough to tag the latest commit in that repo. -15. For minor releases: make sure https://github.com/mdn/browser-compat-data has - been updated to reflect Web API changes in this release. Usually done ahead - of time by @lucacasonato. +8. For minor releases: make sure https://github.com/mdn/browser-compat-data has + been updated to reflect Web API changes in this release. Usually done ahead + of time by @lucacasonato. -16. **If you are cutting a patch release**: open a PR that forwards all commits - created in the release process to the `main` branch. +9. **If you are cutting a patch release**: open a PR that forwards all commits + created in the release process to the `main` branch. ## Updating `doc.deno.land` diff --git a/tools/release/01_bump_crate_versions.ts b/tools/release/01_bump_crate_versions.ts index eea49279f3..70c92637e7 100755 --- a/tools/release/01_bump_crate_versions.ts +++ b/tools/release/01_bump_crate_versions.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo +#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo,git --allow-net --no-check // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. import { DenoWorkspace } from "./deno_workspace.ts"; import { GitLogOutput, path, semver } from "./deps.ts"; @@ -8,8 +8,19 @@ const repo = workspace.repo; const cliCrate = workspace.getCliCrate(); const originalCliVersion = cliCrate.version; +// update the std version used in the code +await updateStdVersion(); + // increment the cli version -await cliCrate.promptAndIncrement(); +if (Deno.args.some((a) => a === "--patch")) { + await cliCrate.increment("patch"); +} else if (Deno.args.some((a) => a === "--minor")) { + await cliCrate.increment("minor"); +} else if (Deno.args.some((a) => a === "--major")) { + await cliCrate.increment("major"); +} else { + await cliCrate.promptAndIncrement(); +} // increment the dependency crate versions for (const crate of workspace.getCliDependencyCrates()) { @@ -17,7 +28,7 @@ for (const crate of workspace.getCliDependencyCrates()) { } // update the lock file -await workspace.getCliCrate().cargoCheck(); +await workspace.getCliCrate().cargoUpdate("--workspace"); // try to update the Releases.md markdown text try { @@ -101,3 +112,26 @@ async function getGitLog() { ); } } + +async function updateStdVersion() { + const newStdVersion = await getLatestStdVersion(); + const compatFilePath = path.join(cliCrate.folderPath, "compat/mod.rs"); + const text = Deno.readTextFileSync(compatFilePath); + Deno.writeTextFileSync( + compatFilePath, + text.replace(/std@[0-9]+\.[0-9]+\.[0-9]+/, `std@${newStdVersion}`), + ); +} + +async function getLatestStdVersion() { + const url = + "https://raw.githubusercontent.com/denoland/deno_std/main/version.ts"; + const result = await fetch(url); + const text = await result.text(); + const version = /"([0-9]+\.[0-9]+\.[0-9]+)"/.exec(text); + if (version == null) { + throw new Error(`Could not find version in text: ${text}`); + } else { + return version[1]; + } +} diff --git a/tools/release/02_create_pr.ts b/tools/release/02_create_pr.ts new file mode 100755 index 0000000000..e64638277e --- /dev/null +++ b/tools/release/02_create_pr.ts @@ -0,0 +1,51 @@ +#!/usr/bin/env -S deno run --allow-read --allow-write --allow-env --allow-net --allow-run=cargo,git --no-check +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +import { DenoWorkspace } from "./deno_workspace.ts"; +import { createOctoKit, getGitHubRepository } from "./deps.ts"; + +const octoKit = createOctoKit(); +const workspace = await DenoWorkspace.load(); +const repo = workspace.repo; +const cliCrate = workspace.getCliCrate(); + +const originalBranch = await repo.gitCurrentBranch(); +const newBranchName = `release_${cliCrate.version.replace(/\./, "_")}`; + +// Create and push branch +console.log(`Creating branch ${newBranchName}...`); +await repo.gitBranch(newBranchName); +await repo.gitAdd(); +await repo.gitCommit(cliCrate.version); +console.log("Pushing branch..."); +await repo.gitPush("-u", "origin", "HEAD"); + +// Open PR +console.log("Opening PR..."); +const openedPr = await octoKit.request("POST /repos/{owner}/{repo}/pulls", { + ...getGitHubRepository(), + base: originalBranch, + head: newBranchName, + draft: true, + title: cliCrate.version, + body: getPrBody(), +}); +console.log(`Opened PR at ${openedPr.data.url}`); + +function getPrBody() { + let text = `Bumped versions for ${cliCrate.version}\n\n` + + `Please ensure:\n` + + `- [ ] Crate versions are bumped correctly\n` + + `- [ ] deno_std version is incremented in the code\n` + + `- [ ] Releases.md is updated correctly\n\n` + + `To make edits to this PR:\n` + + "```shell\n" + + `git fetch upstream ${newBranchName} && git checkout -b ${newBranchName} upstream/${newBranchName}\n` + + "```\n"; + + const actor = Deno.env.get("GH_WORKFLOW_ACTOR"); + if (actor != null) { + text += `\ncc @${actor}`; + } + + return text; +} diff --git a/tools/release/02_publish_crates.ts b/tools/release/02_publish_crates.ts deleted file mode 100755 index 5910827c35..0000000000 --- a/tools/release/02_publish_crates.ts +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo --allow-net=crates.io -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -import { DenoWorkspace } from "./deno_workspace.ts"; -import { getCratesPublishOrder } from "./deps.ts"; - -const workspace = await DenoWorkspace.load(); -const cliCrate = workspace.getCliCrate(); - -const dependencyCrates = getCratesPublishOrder( - workspace.getCliDependencyCrates(), -); - -try { - for (const [i, crate] of dependencyCrates.entries()) { - await crate.publish(); - console.log(`Published ${i + 1} of ${dependencyCrates.length} crates.`); - } - - await cliCrate.publish(); -} finally { - // system beep to notify error or completion - console.log("\x07"); -} diff --git a/tools/release/03_publish_crates.ts b/tools/release/03_publish_crates.ts new file mode 100755 index 0000000000..4c9cb23d3b --- /dev/null +++ b/tools/release/03_publish_crates.ts @@ -0,0 +1,48 @@ +#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo --allow-net=crates.io --no-check +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +import { DenoWorkspace } from "./deno_workspace.ts"; +import { Crate, getCratesPublishOrder } from "./deps.ts"; + +const isReal = parseIsReal(); +console.log(`Running a ${isReal ? "real" : "dry"} cargo publish...`); + +const workspace = await DenoWorkspace.load(); +const cliCrate = workspace.getCliCrate(); + +const dependencyCrates = getCratesPublishOrder( + workspace.getCliDependencyCrates(), +); + +try { + for (const [i, crate] of dependencyCrates.entries()) { + await publishCrate(crate); + console.log(`Finished ${i + 1} of ${dependencyCrates.length} crates.`); + } + + await publishCrate(cliCrate); +} finally { + // system beep to notify error or completion + console.log("\x07"); +} + +async function publishCrate(crate: Crate) { + if (isReal) { + await crate.publish(); + } else { + await crate.publishDryRun(); + } +} + +function parseIsReal() { + const isReal = Deno.args.some((a) => a === "--real"); + const isDry = Deno.args.some((a) => a === "--dry"); + + // force the call to be explicit and provide one of these + // so that it's obvious what's happening + if (!isDry && !isReal) { + console.error("Please run with `--dry` or `--real`."); + Deno.exit(1); + } + + return isReal; +} diff --git a/tools/release/04_create_release_tag.ts b/tools/release/04_create_release_tag.ts new file mode 100755 index 0000000000..1a770e6cd8 --- /dev/null +++ b/tools/release/04_create_release_tag.ts @@ -0,0 +1,17 @@ +#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo --no-check +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +import { DenoWorkspace } from "./deno_workspace.ts"; + +const workspace = await DenoWorkspace.load(); +const repo = workspace.repo; +const cliCrate = workspace.getCliCrate(); + +await repo.gitFetchTags("origin"); +const tags = await repo.getGitTags(); + +if (tags.has(cliCrate.version)) { + console.log(`Tag ${cliCrate.version} already exists.`); +} else { + await repo.gitTag(cliCrate.version); + await repo.gitPush(cliCrate.version); +} diff --git a/tools/release/deno_workspace.ts b/tools/release/deno_workspace.ts index 6f5b64590e..d36a2a22fc 100644 --- a/tools/release/deno_workspace.ts +++ b/tools/release/deno_workspace.ts @@ -12,7 +12,10 @@ export class DenoWorkspace { static async load(): Promise { return new DenoWorkspace( - await Repo.load("deno", DenoWorkspace.rootDirPath), + await Repo.load({ + name: "deno", + path: DenoWorkspace.rootDirPath, + }), ); } diff --git a/tools/release/deps.ts b/tools/release/deps.ts index 86f1db10f1..9c23e509e7 100644 --- a/tools/release/deps.ts +++ b/tools/release/deps.ts @@ -1,3 +1,4 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -export * from "https://raw.githubusercontent.com/denoland/automation/0.5.0/mod.ts"; +export * from "https://raw.githubusercontent.com/denoland/automation/0.9.0/mod.ts"; +export * from "https://raw.githubusercontent.com/denoland/automation/0.9.0/github_actions.ts";