summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xtools/release/01_bump_dependency_crate_versions.ts5
-rwxr-xr-xtools/release/02_publish_dependency_crates.ts3
-rwxr-xr-xtools/release/03_bump_cli_version.ts31
-rw-r--r--tools/release/deno_workspace.ts70
-rw-r--r--tools/release/deps.ts3
-rw-r--r--tools/release/helpers/cargo.ts72
-rw-r--r--tools/release/helpers/crates_io.ts22
-rw-r--r--tools/release/helpers/deno_workspace.ts228
-rw-r--r--tools/release/helpers/helpers.ts104
-rw-r--r--tools/release/helpers/mod.ts10
10 files changed, 87 insertions, 461 deletions
diff --git a/tools/release/01_bump_dependency_crate_versions.ts b/tools/release/01_bump_dependency_crate_versions.ts
index 61555f831..931b7d199 100755
--- a/tools/release/01_bump_dependency_crate_versions.ts
+++ b/tools/release/01_bump_dependency_crate_versions.ts
@@ -1,6 +1,6 @@
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-import { DenoWorkspace } from "./helpers/mod.ts";
+import { DenoWorkspace } from "./deno_workspace.ts";
const workspace = await DenoWorkspace.load();
@@ -8,4 +8,5 @@ for (const crate of workspace.getDependencyCrates()) {
await crate.increment("minor");
}
-await workspace.updateLockFile();
+// update the lock file
+await workspace.getCliCrate().cargoCheck();
diff --git a/tools/release/02_publish_dependency_crates.ts b/tools/release/02_publish_dependency_crates.ts
index 44b5fe969..d210971a1 100755
--- a/tools/release/02_publish_dependency_crates.ts
+++ b/tools/release/02_publish_dependency_crates.ts
@@ -1,6 +1,7 @@
#!/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, getCratesPublishOrder } from "./helpers/mod.ts";
+import { DenoWorkspace } from "./deno_workspace.ts";
+import { getCratesPublishOrder } from "./deps.ts";
const workspace = await DenoWorkspace.load();
diff --git a/tools/release/03_bump_cli_version.ts b/tools/release/03_bump_cli_version.ts
index e2a64ede0..bd1f3d1c6 100755
--- a/tools/release/03_bump_cli_version.ts
+++ b/tools/release/03_bump_cli_version.ts
@@ -1,19 +1,17 @@
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run=cargo,git
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-import {
- DenoWorkspace,
- formatGitLogForMarkdown,
- getGitLogFromTag,
-} from "./helpers/mod.ts";
+import { DenoWorkspace } from "./deno_workspace.ts";
const workspace = await DenoWorkspace.load();
+const repo = workspace.repo;
const cliCrate = workspace.getCliCrate();
const originalVersion = cliCrate.version;
// increment the version
-await cliCrate.increment(getVersionIncrement());
-await workspace.updateLockFile();
+await cliCrate.promptAndIncrement();
+// update the lock file
+await cliCrate.cargoCheck();
// output the Releases.md markdown text
console.log(
@@ -21,24 +19,13 @@ console.log(
);
console.log(await getReleasesMdText());
-function getVersionIncrement() {
- if (confirm("Increment patch?")) {
- return "patch";
- } else if (confirm("Increment minor?")) {
- return "minor";
- } else if (confirm("Increment major?")) {
- return "major";
- } else {
- throw new Error("No decision.");
- }
-}
-
async function getReleasesMdText() {
- const gitLogOutput = await getGitLogFromTag(
- DenoWorkspace.rootDirPath,
+ const gitLog = await repo.getGitLogFromTags(
+ "upstream",
`v${originalVersion}`,
+ undefined,
);
- const formattedGitLog = formatGitLogForMarkdown(gitLogOutput);
+ const formattedGitLog = gitLog.formatForReleaseMarkdown();
const formattedDate = getFormattedDate(new Date());
return `### ${cliCrate.version} / ${formattedDate}\n\n` +
diff --git a/tools/release/deno_workspace.ts b/tools/release/deno_workspace.ts
new file mode 100644
index 000000000..725d647df
--- /dev/null
+++ b/tools/release/deno_workspace.ts
@@ -0,0 +1,70 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+import { path, Repo } from "./deps.ts";
+
+export class DenoWorkspace {
+ #repo: Repo;
+
+ static get rootDirPath() {
+ const currentDirPath = path.dirname(path.fromFileUrl(import.meta.url));
+ return path.resolve(currentDirPath, "../../");
+ }
+
+ static async load(): Promise<DenoWorkspace> {
+ return new DenoWorkspace(
+ await Repo.load("deno", DenoWorkspace.rootDirPath),
+ );
+ }
+
+ private constructor(repo: Repo) {
+ this.#repo = repo;
+ }
+
+ get repo() {
+ return this.#repo;
+ }
+
+ get crates() {
+ return this.#repo.crates;
+ }
+
+ /** Gets the dependency crates used for the first part of the release process. */
+ getDependencyCrates() {
+ return [
+ this.getBenchUtilCrate(),
+ this.getSerdeV8Crate(),
+ this.getCoreCrate(),
+ ...this.getExtCrates(),
+ this.getRuntimeCrate(),
+ ];
+ }
+
+ getSerdeV8Crate() {
+ return this.getCrate("serde_v8");
+ }
+
+ getCliCrate() {
+ return this.getCrate("deno");
+ }
+
+ getCoreCrate() {
+ return this.getCrate("deno_core");
+ }
+
+ getRuntimeCrate() {
+ return this.getCrate("deno_runtime");
+ }
+
+ getBenchUtilCrate() {
+ return this.getCrate("deno_bench_util");
+ }
+
+ getExtCrates() {
+ const extPath = path.join(this.#repo.folderPath, "ext");
+ return this.crates.filter((c) => c.manifestPath.startsWith(extPath));
+ }
+
+ getCrate(name: string) {
+ return this.#repo.getCrate(name);
+ }
+}
diff --git a/tools/release/deps.ts b/tools/release/deps.ts
new file mode 100644
index 000000000..df7a4fc60
--- /dev/null
+++ b/tools/release/deps.ts
@@ -0,0 +1,3 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+export * from "https://raw.githubusercontent.com/denoland/automation/0.2.0/mod.ts";
diff --git a/tools/release/helpers/cargo.ts b/tools/release/helpers/cargo.ts
deleted file mode 100644
index 15dd0c5b6..000000000
--- a/tools/release/helpers/cargo.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-
-import { runCommand } from "./helpers.ts";
-
-export interface CargoMetadata {
- packages: CargoPackageMetadata[];
- /** Identifiers in the `packages` array of the workspace members. */
- "workspace_members": string[];
- /** The absolute workspace root directory path. */
- "workspace_root": string;
-}
-
-export interface CargoPackageMetadata {
- id: string;
- name: string;
- version: string;
- dependencies: CargoDependencyMetadata[];
- /** Path to Cargo.toml */
- "manifest_path": string;
-}
-
-export interface CargoDependencyMetadata {
- name: string;
- /** Version requrement (ex. ^0.1.0) */
- req: string;
-}
-
-export async function getMetadata(directory: string) {
- const result = await runCommand({
- cwd: directory,
- cmd: ["cargo", "metadata", "--format-version", "1"],
- });
- return JSON.parse(result!) as CargoMetadata;
-}
-
-export function publishCrate(directory: string) {
- return runCargoSubCommand({
- directory,
- args: ["publish"],
- });
-}
-
-export function build(directory: string) {
- return runCargoSubCommand({
- directory,
- args: ["build", "-vv"],
- });
-}
-
-export function check(directory: string) {
- return runCargoSubCommand({
- directory,
- args: ["check"],
- });
-}
-
-async function runCargoSubCommand(params: {
- args: string[];
- directory: string;
-}) {
- const p = Deno.run({
- cwd: params.directory,
- cmd: ["cargo", ...params.args],
- stderr: "inherit",
- stdout: "inherit",
- });
-
- const status = await p.status();
- if (!status.success) {
- throw new Error("Failed");
- }
-}
diff --git a/tools/release/helpers/crates_io.ts b/tools/release/helpers/crates_io.ts
deleted file mode 100644
index af26f55af..000000000
--- a/tools/release/helpers/crates_io.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-
-export interface CratesIoMetadata {
- crate: {
- id: string;
- name: string;
- };
- versions: {
- crate: string;
- num: string;
- }[];
-}
-
-export async function getCratesIoMetadata(crateName: string) {
- // rate limit
- await new Promise((resolve) => setTimeout(resolve, 100));
-
- const response = await fetch(`https://crates.io/api/v1/crates/${crateName}`);
- const data = await response.json();
-
- return data as CratesIoMetadata;
-}
diff --git a/tools/release/helpers/deno_workspace.ts b/tools/release/helpers/deno_workspace.ts
deleted file mode 100644
index 6d7a03cb7..000000000
--- a/tools/release/helpers/deno_workspace.ts
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-
-import * as path from "https://deno.land/std@0.105.0/path/mod.ts";
-import * as semver from "https://deno.land/x/semver@v1.4.0/mod.ts";
-import * as cargo from "./cargo.ts";
-import { getCratesIoMetadata } from "./crates_io.ts";
-import { withRetries } from "./helpers.ts";
-
-export class DenoWorkspace {
- #workspaceCrates: readonly DenoWorkspaceCrate[];
- #workspaceRootDirPath: string;
-
- static get rootDirPath() {
- const currentDirPath = path.dirname(path.fromFileUrl(import.meta.url));
- return path.resolve(currentDirPath, "../../../");
- }
-
- static async load(): Promise<DenoWorkspace> {
- return new DenoWorkspace(
- await cargo.getMetadata(DenoWorkspace.rootDirPath),
- );
- }
-
- private constructor(metadata: cargo.CargoMetadata) {
- const crates = [];
- for (const memberId of metadata.workspace_members) {
- const pkg = metadata.packages.find((pkg) => pkg.id === memberId);
- if (!pkg) {
- throw new Error(`Could not find package with id ${memberId}`);
- }
- crates.push(new DenoWorkspaceCrate(this, pkg));
- }
-
- this.#workspaceCrates = crates;
- this.#workspaceRootDirPath = metadata.workspace_root;
- }
-
- get crates() {
- return this.#workspaceCrates;
- }
-
- /** Gets the dependency crates used for the first part of the release process. */
- getDependencyCrates() {
- return [
- this.getBenchUtilCrate(),
- this.getSerdeV8Crate(),
- this.getCoreCrate(),
- ...this.getExtCrates(),
- this.getRuntimeCrate(),
- ];
- }
-
- getSerdeV8Crate() {
- return this.getCrateByNameOrThrow("serde_v8");
- }
-
- getCliCrate() {
- return this.getCrateByNameOrThrow("deno");
- }
-
- getCoreCrate() {
- return this.getCrateByNameOrThrow("deno_core");
- }
-
- getRuntimeCrate() {
- return this.getCrateByNameOrThrow("deno_runtime");
- }
-
- getBenchUtilCrate() {
- return this.getCrateByNameOrThrow("deno_bench_util");
- }
-
- getExtCrates() {
- const extPath = path.join(this.#workspaceRootDirPath, "ext");
- return this.#workspaceCrates.filter((c) =>
- c.manifestPath.startsWith(extPath)
- );
- }
-
- getCrateByNameOrThrow(name: string) {
- const crate = this.#workspaceCrates.find((c) => c.name === name);
- if (!crate) {
- throw new Error(`Could not find crate: ${name}`);
- }
- return crate;
- }
-
- build() {
- return cargo.build(DenoWorkspace.rootDirPath);
- }
-
- updateLockFile() {
- return cargo.check(DenoWorkspace.rootDirPath);
- }
-}
-
-export class DenoWorkspaceCrate {
- #workspace: DenoWorkspace;
- #pkg: cargo.CargoPackageMetadata;
- #isUpdatingManifest = false;
-
- constructor(workspace: DenoWorkspace, pkg: cargo.CargoPackageMetadata) {
- this.#workspace = workspace;
- this.#pkg = pkg;
- }
-
- get manifestPath() {
- return this.#pkg.manifest_path;
- }
-
- get directoryPath() {
- return path.dirname(this.#pkg.manifest_path);
- }
-
- get name() {
- return this.#pkg.name;
- }
-
- get version() {
- return this.#pkg.version;
- }
-
- getDependencies() {
- const dependencies = [];
- for (const dependency of this.#pkg.dependencies) {
- const crate = this.#workspace.crates.find((c) =>
- c.name === dependency.name
- );
- if (crate != null) {
- dependencies.push(crate);
- }
- }
- return dependencies;
- }
-
- async isPublished() {
- const cratesIoMetadata = await getCratesIoMetadata(this.name);
- return cratesIoMetadata.versions.some((v) => v.num === this.version);
- }
-
- async publish() {
- if (await this.isPublished()) {
- console.log(`Already published ${this.name} ${this.version}`);
- return false;
- }
-
- console.log(`Publishing ${this.name} ${this.version}...`);
-
- // Sometimes a publish may fail due to the crates.io index
- // not being updated yet. Usually it will be resolved after
- // retrying, so try a few times before failing hard.
- return await withRetries({
- action: async () => {
- await cargo.publishCrate(this.directoryPath);
- return true;
- },
- retryCount: 5,
- retryDelaySeconds: 10,
- });
- }
-
- build() {
- return cargo.build(this.directoryPath);
- }
-
- updateLockFile() {
- return cargo.check(this.directoryPath);
- }
-
- increment(part: "major" | "minor" | "patch") {
- const newVersion = semver.parse(this.version)!.inc(part).toString();
- return this.setVersion(newVersion);
- }
-
- async setVersion(version: string) {
- console.log(`Setting ${this.name} to ${version}...`);
- for (const crate of this.#workspace.crates) {
- await crate.setDependencyVersion(this.name, version);
- }
- await this.#updateManifestVersion(version);
- }
-
- async setDependencyVersion(dependencyName: string, version: string) {
- const dependency = this.#pkg.dependencies.find((d) =>
- d.name === dependencyName
- );
- if (dependency != null) {
- await this.#updateManifestFile((fileText) => {
- // simple for now...
- const findRegex = new RegExp(
- `^(\\b${dependencyName}\\b\\s.*)"([=\\^])?[0-9]+[^"]+"`,
- "gm",
- );
- return fileText.replace(findRegex, `$1"${version}"`);
- });
-
- dependency.req = `^${version}`;
- }
- }
-
- async #updateManifestVersion(version: string) {
- await this.#updateManifestFile((fileText) => {
- const findRegex = new RegExp(
- `^(version\\s*=\\s*)"${this.#pkg.version}"$`,
- "m",
- );
- return fileText.replace(findRegex, `$1"${version}"`);
- });
- this.#pkg.version = version;
- }
-
- async #updateManifestFile(action: (fileText: string) => string) {
- if (this.#isUpdatingManifest) {
- throw new Error("Cannot update manifest while updating manifest.");
- }
- this.#isUpdatingManifest = true;
- try {
- const originalText = await Deno.readTextFile(this.#pkg.manifest_path);
- const newText = action(originalText);
- if (originalText === newText) {
- throw new Error(`The file didn't change: ${this.manifestPath}`);
- }
- await Deno.writeTextFile(this.manifestPath, newText);
- } finally {
- this.#isUpdatingManifest = false;
- }
- }
-}
diff --git a/tools/release/helpers/helpers.ts b/tools/release/helpers/helpers.ts
deleted file mode 100644
index 454a66373..000000000
--- a/tools/release/helpers/helpers.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-
-import type { DenoWorkspaceCrate } from "./deno_workspace.ts";
-
-export function getCratesPublishOrder(crates: DenoWorkspaceCrate[]) {
- const pendingCrates = [...crates];
- const sortedCrates = [];
-
- while (pendingCrates.length > 0) {
- for (let i = pendingCrates.length - 1; i >= 0; i--) {
- const crate = pendingCrates[i];
- const hasPendingDependency = crate.getDependencies()
- .some((c) => pendingCrates.includes(c));
- if (!hasPendingDependency) {
- sortedCrates.push(crate);
- pendingCrates.splice(i, 1);
- }
- }
- }
-
- return sortedCrates;
-}
-
-export function getGitLogFromTag(directory: string, tagName: string) {
- return runCommand({
- cwd: directory,
- cmd: ["git", "log", "--oneline", `${tagName}..`],
- });
-}
-
-const IGNORED_COMMIT_PREFIX = [
- "build",
- "chore",
- "ci",
- "docs",
- "refactor",
- "test",
-];
-
-export function formatGitLogForMarkdown(text: string) {
- return text.split(/\r?\n/)
- .map((line) => line.replace(/^[a-f0-9]{9} /i, "").trim())
- .filter((l) => {
- return !IGNORED_COMMIT_PREFIX.some((prefix) => l.startsWith(prefix)) &&
- l.length > 0;
- })
- .sort()
- .map((line) => `- ${line}`)
- .join("\n");
-}
-
-export async function runCommand(params: {
- cwd: string;
- cmd: string[];
-}) {
- const p = Deno.run({
- cwd: params.cwd,
- cmd: params.cmd,
- stderr: "piped",
- stdout: "piped",
- });
-
- const [status, stdout, stderr] = await Promise.all([
- p.status(),
- p.output(),
- p.stderrOutput(),
- ]);
- p.close();
-
- if (!status.success) {
- throw new Error(
- `Error executing ${params.cmd[0]}: ${new TextDecoder().decode(stderr)}`,
- );
- }
-
- return new TextDecoder().decode(stdout);
-}
-
-export async function withRetries<TReturn>(params: {
- action: () => Promise<TReturn>;
- retryCount: number;
- retryDelaySeconds: number;
-}) {
- for (let i = 0; i < params.retryCount; i++) {
- if (i > 0) {
- console.log(
- `Failed. Trying again in ${params.retryDelaySeconds} seconds...`,
- );
- await delay(params.retryDelaySeconds * 1000);
- console.log(`Attempt ${i + 1}/${params.retryCount}...`);
- }
- try {
- return await params.action();
- } catch (err) {
- console.error(err);
- }
- }
-
- throw new Error(`Failed after ${params.retryCount} attempts.`);
-}
-
-function delay(ms: number) {
- return new Promise((resolve) => setTimeout(resolve, ms));
-}
diff --git a/tools/release/helpers/mod.ts b/tools/release/helpers/mod.ts
deleted file mode 100644
index 2cf00c352..000000000
--- a/tools/release/helpers/mod.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-
-export * from "./cargo.ts";
-export * from "./crates_io.ts";
-export * from "./deno_workspace.ts";
-export {
- formatGitLogForMarkdown,
- getCratesPublishOrder,
- getGitLogFromTag,
-} from "./helpers.ts";