@dbx-tools/projen 0.9.18 → 0.9.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/._LICENSE +0 -0
  2. package/._README.md +0 -0
  3. package/._index.ts +0 -0
  4. package/._package.json +0 -0
  5. package/README.md +72 -88
  6. package/index.ts +2 -7
  7. package/package.json +9 -5
  8. package/src/.__lazy-require.ts +0 -0
  9. package/src/.__release-platform.ts +0 -0
  10. package/src/.__rust-project.ts +0 -0
  11. package/src/.__rust-release-workflow.ts +0 -0
  12. package/src/.__task-command.ts +0 -0
  13. package/src/._barrels.ts +0 -0
  14. package/src/._bun-app.ts +0 -0
  15. package/src/._bun-workflow.ts +0 -0
  16. package/src/._clean.ts +0 -0
  17. package/src/._codegen.ts +0 -0
  18. package/src/._dbx-tools-config.ts +0 -0
  19. package/src/._engine-root.ts +0 -0
  20. package/src/._generated.ts +0 -0
  21. package/src/._mixin.ts +0 -0
  22. package/src/._module-exports.ts +0 -0
  23. package/src/._openapi.ts +0 -0
  24. package/src/._packages.ts +0 -0
  25. package/src/._pnpm-workspace.ts +0 -0
  26. package/src/._project-js.ts +0 -0
  27. package/src/._project-predicate.ts +0 -0
  28. package/src/._project-py.ts +0 -0
  29. package/src/._project-rs.ts +0 -0
  30. package/src/._project.ts +0 -0
  31. package/src/._projen-version.ts +0 -0
  32. package/src/._publish.ts +0 -0
  33. package/src/._release-catalog.ts +0 -0
  34. package/src/._release-dispatch.ts +0 -0
  35. package/src/._release-github.ts +0 -0
  36. package/src/._release.ts +0 -0
  37. package/src/._scaffold.ts +0 -0
  38. package/src/._tags.ts +0 -0
  39. package/src/._tsconfig.ts +0 -0
  40. package/src/._uniffi.ts +0 -0
  41. package/src/._vscode.ts +0 -0
  42. package/src/._watch.ts +0 -0
  43. package/src/._workspace-lock.ts +0 -0
  44. package/src/._workspace-version.ts +0 -0
  45. package/src/_rust-release-workflow.ts +18 -582
  46. package/src/_task-command.ts +75 -0
  47. package/src/project-js.ts +2 -42
  48. package/src/project-py.ts +60 -162
  49. package/src/project-rs.ts +4 -100
  50. package/src/release-catalog.ts +37 -203
  51. package/src/release.ts +96 -775
  52. package/src/scaffold.ts +2 -6
  53. package/src/workspace-version.ts +9 -121
  54. package/tasks/._barrels.ts +0 -0
  55. package/tasks/._bump.ts +0 -0
  56. package/tasks/._clean.ts +0 -0
  57. package/tasks/._compile-workspace.ts +0 -0
  58. package/tasks/._local-publish.ts +0 -0
  59. package/tasks/._openapi.ts +0 -0
  60. package/tasks/._projenrc.ts +0 -0
  61. package/tasks/._publish-npm.ts +0 -0
  62. package/tasks/._publish-python.ts +0 -0
  63. package/tasks/._publish-uniffi-local.ts +0 -0
  64. package/tasks/._publish.ts +0 -0
  65. package/tasks/._python-registry.ts +0 -0
  66. package/tasks/._release-assets.ts +0 -0
  67. package/tasks/._release-candidate.ts +0 -0
  68. package/tasks/._release-manifest.ts +0 -0
  69. package/tasks/._release-pr.ts +0 -0
  70. package/tasks/._release-summary.ts +0 -0
  71. package/tasks/._release-version.ts +0 -0
  72. package/tasks/._rust.ts +0 -0
  73. package/tasks/._sync.ts +0 -0
  74. package/tasks/._uniffi-python.d.ts +0 -0
  75. package/tasks/._uniffi-python.js +0 -0
  76. package/tasks/._uniffi-release.mjs +0 -0
  77. package/tasks/._uniffi.ts +0 -0
  78. package/tasks/._version-check.ts +0 -0
  79. package/tasks/bump.ts +17 -34
  80. package/tasks/local-publish.ts +124 -117
  81. package/tasks/publish-npm.ts +125 -41
  82. package/tasks/publish-python.ts +79 -106
  83. package/tasks/publish-uniffi-local.ts +129 -84
  84. package/tasks/publish.ts +44 -139
  85. package/tasks/python-registry.ts +2 -9
  86. package/tasks/release-assets.ts +56 -65
  87. package/tasks/release-candidate.ts +416 -0
  88. package/tasks/release-manifest.ts +273 -0
  89. package/tasks/release-pr.ts +312 -369
  90. package/tasks/release-summary.ts +8 -10
  91. package/tasks/release-version.ts +23 -2
  92. package/tasks/rust.ts +8 -13
  93. package/tasks/uniffi-python.d.ts +2 -3
  94. package/tasks/uniffi-python.js +7 -20
  95. package/tasks/uniffi-release.mjs +19 -13
  96. package/tasks/uniffi.ts +15 -24
  97. package/tasks/version-check.ts +24 -56
  98. package/src/release-plan.ts +0 -269
  99. package/src/release-please.ts +0 -242
  100. package/tasks/release-bootstrap.ts +0 -59
  101. package/tasks/release-plan.ts +0 -89
  102. package/tasks/release-please.ts +0 -162
  103. package/tasks/release-request.ts +0 -166
  104. package/tasks/release-summary-units.ts +0 -75
  105. package/tasks/stamp-python.ts +0 -13
@@ -1,162 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- /** Run Release Please with the generated cross-language release-unit plugin. */
3
-
4
- import { appendFileSync, readFileSync } from "node:fs";
5
- import { resolve } from "node:path";
6
- import * as exec from "@dbx-tools/core/exec";
7
- import { log } from "@dbx-tools/shared-core";
8
- import { Command } from "commander";
9
- import { GitHub } from "release-please";
10
- import { Manifest } from "release-please/build/src/manifest.js";
11
- import type { Scm } from "release-please/build/src/scm.js";
12
- import type { ReleaseUnitGraph } from "../src/release-catalog.ts";
13
- import { addReleaseUnitPlugin } from "../src/release-please.ts";
14
-
15
- const logger = log.logger("projen:release-please");
16
-
17
- export interface ReleasePleaseRunOptions {
18
- readonly github: Scm;
19
- readonly targetBranch: string;
20
- readonly graph: ReleaseUnitGraph;
21
- readonly configFile?: string;
22
- readonly manifestFile?: string;
23
- }
24
-
25
- /** Create component releases and refresh the combined release pull request. */
26
- export async function runReleasePlease(options: ReleasePleaseRunOptions) {
27
- const configFile = options.configFile ?? "release-please-config.json";
28
- const manifestFile = options.manifestFile ?? ".release-please-manifest.json";
29
- const manifest = await Manifest.fromManifest(
30
- options.github,
31
- options.targetBranch,
32
- configFile,
33
- manifestFile,
34
- );
35
- addReleaseUnitPlugin(
36
- manifest.plugins,
37
- options.github,
38
- options.targetBranch,
39
- manifest.repositoryConfig,
40
- options.graph,
41
- manifestFile,
42
- );
43
- const releases = (await manifest.createReleases()).filter(
44
- (release): release is NonNullable<typeof release> => release !== undefined,
45
- );
46
- const pullRequests = (await manifest.createPullRequests()).filter(
47
- (pullRequest): pullRequest is NonNullable<typeof pullRequest> => pullRequest !== undefined,
48
- );
49
- return { releases, pullRequests };
50
- }
51
-
52
- function writeOutput(name: string, value: unknown): void {
53
- const output = process.env.GITHUB_OUTPUT;
54
- if (!output) return;
55
- appendFileSync(output, `${name}=${JSON.stringify(value)}\n`);
56
- }
57
-
58
- /** Parse GitHub owner and repository from HTTPS, SSH, or SSH-alias syntax. */
59
- export function parseGitHubRepository(remote: string): { owner: string; repo: string } {
60
- const location = remote
61
- .replace(/^git@[^:]+:/, "")
62
- .replace(/^ssh:\/\/git@[^/]+\//, "")
63
- .replace(/^https?:\/\/[^/]+\//, "")
64
- .replace(/\.git$/, "");
65
- const [owner, repo, ...extra] = location.split("/");
66
- if (!owner || !repo || extra.length > 0) {
67
- throw new Error(`Could not resolve GitHub repository from ${remote}`);
68
- }
69
- return { owner, repo };
70
- }
71
-
72
- /** Resolve GitHub owner and repository from environment or the configured remote. */
73
- export function repositoryCoordinates(root: string): { owner: string; repo: string } {
74
- const configured = process.env.GITHUB_REPOSITORY;
75
- const remote = configured
76
- ? configured
77
- : (exec
78
- .spawnSync("git", ["remote", "get-url", "origin"], {
79
- cwd: root,
80
- stdout: "capture",
81
- stderr: "ignore",
82
- stdin: "ignore",
83
- check: true,
84
- })
85
- .stdout?.trim() ?? "");
86
- return parseGitHubRepository(remote);
87
- }
88
-
89
- function githubToken(): string {
90
- if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
91
- return (
92
- exec
93
- .spawnSync("gh", ["auth", "token"], {
94
- stdout: "capture",
95
- stderr: "inherit",
96
- stdin: "ignore",
97
- check: true,
98
- })
99
- .stdout?.trim() ?? ""
100
- );
101
- }
102
-
103
- if (import.meta.main) {
104
- await new Command()
105
- .option("--owner <owner>", "GitHub repository owner")
106
- .option("--repo <repo>", "GitHub repository name")
107
- .option("--target-branch <branch>", "release target branch", "main")
108
- .option("--root <path>", "repository root", ".")
109
- .option("--config-file <path>", "Release Please config", "release-please-config.json")
110
- .option(
111
- "--manifest-file <path>",
112
- "Release Please version manifest",
113
- ".release-please-manifest.json",
114
- )
115
- .action(
116
- async (options: {
117
- owner?: string;
118
- repo?: string;
119
- targetBranch: string;
120
- root: string;
121
- configFile: string;
122
- manifestFile: string;
123
- }) => {
124
- const root = resolve(options.root);
125
- const repository =
126
- options.owner && options.repo
127
- ? { owner: options.owner, repo: options.repo }
128
- : repositoryCoordinates(root);
129
- const graph = JSON.parse(
130
- readFileSync(resolve(root, ".projen/release-units.json"), "utf8"),
131
- ) as ReleaseUnitGraph;
132
- const github = await GitHub.create({
133
- owner: repository.owner,
134
- repo: repository.repo,
135
- defaultBranch: options.targetBranch,
136
- token: githubToken(),
137
- ...(process.env.GITHUB_API_URL ? { apiUrl: process.env.GITHUB_API_URL } : {}),
138
- ...(process.env.GITHUB_GRAPHQL_URL
139
- ? {
140
- graphqlUrl: process.env.GITHUB_GRAPHQL_URL.replace(/\/graphql\/?$/, ""),
141
- }
142
- : {}),
143
- });
144
- const result = await runReleasePlease({
145
- github,
146
- targetBranch: options.targetBranch,
147
- graph,
148
- configFile: options.configFile,
149
- manifestFile: options.manifestFile,
150
- });
151
- logger.success("release state refreshed", {
152
- releases: result.releases.length,
153
- pullRequests: result.pullRequests.length,
154
- });
155
- writeOutput("releases_created", result.releases.length > 0);
156
- writeOutput("prs_created", result.pullRequests.length > 0);
157
- writeOutput("releases", result.releases);
158
- writeOutput("prs", result.pullRequests);
159
- },
160
- )
161
- .parseAsync();
162
- }
@@ -1,166 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- /** Commit and push a source branch for the generated release-request workflow. */
3
-
4
- import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
- import { dirname, resolve } from "node:path";
6
- import * as projectUtils from "@dbx-tools/core/project-utils";
7
- import { log, stringUtils } from "@dbx-tools/shared-core";
8
- import { Command } from "commander";
9
- import { selectReleaseSummary } from "./release-summary.ts";
10
- import { captureTaskCommand, runTaskCommand } from "../src/_task-command.ts";
11
-
12
- const logger = log.logger("projen:release-request");
13
-
14
- export interface PrepareReleaseRequestOptions {
15
- readonly root: string;
16
- readonly baseBranch: string;
17
- readonly message?: string;
18
- readonly notes?: string;
19
- readonly notesFile?: string;
20
- readonly push?: boolean;
21
- readonly synthesize?: boolean;
22
- readonly generateNotes?: (root: string, range: string) => Promise<string>;
23
- }
24
-
25
- /** Commit real changes, attach notes to one request commit, and push the branch. */
26
- export async function prepareReleaseRequest(
27
- options: PrepareReleaseRequestOptions,
28
- ): Promise<boolean> {
29
- const root = resolve(options.root);
30
- const branch = capture(root, ["branch", "--show-current"]);
31
- if (!branch) throw new Error("Release requests require a named source branch");
32
- if (options.push !== false) {
33
- run(root, ["fetch", "origin", options.baseBranch]);
34
- }
35
- const baseRef = options.push === false ? options.baseBranch : `origin/${options.baseBranch}`;
36
- if (options.synthesize !== false) {
37
- runTaskCommand(root, process.execPath, ["x", "projen"]);
38
- }
39
- const dirty = capture(root, ["status", "--porcelain"]);
40
- if (dirty) {
41
- const message = options.message ?? "fix: prepare release";
42
- if (!isReleasableMessage(message)) {
43
- throw new Error("Release commit message must use fix, feat, or a breaking-change marker");
44
- }
45
- run(root, ["add", "-A"]);
46
- run(root, ["commit", "-m", message]);
47
- }
48
- const ahead = Number(capture(root, ["rev-list", "--count", `${baseRef}..HEAD`]));
49
- if (!Number.isInteger(ahead) || ahead < 1) {
50
- logger.info("no changes from release base; skipping release request", {
51
- branch,
52
- base: options.baseBranch,
53
- });
54
- return false;
55
- }
56
- const messages = capture(root, ["log", "--format=%B%x00", `${baseRef}..HEAD`]);
57
- if (!messages.split("\0").some(isReleasableMessage)) {
58
- logger.info("branch has no releasable conventional commits; skipping release request", {
59
- branch,
60
- });
61
- return false;
62
- }
63
- const latest = capture(root, ["log", "-1", "--format=%B"]);
64
- if (/^Release-Request:\s*true$/m.test(latest)) {
65
- logger.info("release request already exists at branch head; skipping", { branch });
66
- return false;
67
- }
68
- if (branch === options.baseBranch) {
69
- throw new Error(`Release requests must run from a branch other than ${options.baseBranch}`);
70
- }
71
-
72
- const customNotes = stringUtils.trimToNull(
73
- options.notesFile ? readFileSync(resolve(root, options.notesFile), "utf8") : options.notes,
74
- );
75
- const notes =
76
- customNotes ??
77
- (await (options.generateNotes ?? generateRequestNotes)(root, `${baseRef}..HEAD`));
78
- const notesPath = `.release-notes/requests/${branch.replace(/[^a-zA-Z0-9-]+/g, "-")}.md`;
79
- const absoluteNotesPath = resolve(root, notesPath);
80
- mkdirSync(dirname(absoluteNotesPath), { recursive: true });
81
- writeFileSync(absoluteNotesPath, `# Release request: ${branch}\n\n${notes.trim()}\n`);
82
- run(root, ["add", notesPath]);
83
- run(root, [
84
- "commit",
85
- "-m",
86
- "chore: request release",
87
- "-m",
88
- [
89
- "Release-Request: true",
90
- `Release-Source-Branch: ${branch}`,
91
- `Release-Notes-Path: ${notesPath}`,
92
- ].join("\n"),
93
- ]);
94
- if (options.push !== false) run(root, ["push", "--set-upstream", "origin", branch]);
95
- logger.success("release request pushed", { branch });
96
- return true;
97
- }
98
-
99
- /** Whether one conventional commit can produce a Release Please increment. */
100
- export function isReleasableMessage(message: string): boolean {
101
- return (
102
- /^(?:fix|feat)(?:\([^)\r\n]+\))?!?:/m.test(message) || /^BREAKING[ -]CHANGE:/m.test(message)
103
- );
104
- }
105
-
106
- async function generateRequestNotes(root: string, range: string): Promise<string> {
107
- const commits = capture(root, ["log", "--no-merges", "--format=%s%n%b", range]);
108
- const files = capture(root, ["diff", "--name-status", range, "--", "."]);
109
- const result = await selectReleaseSummary(
110
- root,
111
- [
112
- "Write concise release-request notes for the reviewed changes below.",
113
- "Focus on user-visible behavior and important compatibility details.",
114
- "",
115
- "Commits:",
116
- commits,
117
- "",
118
- "Files:",
119
- files,
120
- ].join("\n"),
121
- );
122
- if (result?.summary.trim()) return result.summary.trim();
123
- const subjects = capture(root, ["log", "--no-merges", "--format=- %s", range]);
124
- return subjects || "Release source changes are ready for review.";
125
- }
126
-
127
- function capture(root: string, args: string[]): string {
128
- return captureTaskCommand(root, "git", args, { check: true, stderr: "inherit" });
129
- }
130
-
131
- function run(root: string, args: string[]): void {
132
- runTaskCommand(root, "git", args);
133
- }
134
-
135
- /** Restore option text after Projen forwards task arguments without shell quoting. */
136
- function optionText(values?: readonly string[]): string | undefined {
137
- return values?.join(" ");
138
- }
139
-
140
- if (import.meta.main) {
141
- await new Command()
142
- .option("--base <branch>", "release target branch", "main")
143
- .option("--message <message...>", "commit message for uncommitted source changes")
144
- .option("--notes <notes...>", "custom release notes")
145
- .option("--notes-file <path>", "read custom release notes from a file")
146
- .action(
147
- async (options: {
148
- base: string;
149
- message?: string[];
150
- notes?: string[];
151
- notesFile?: string;
152
- }) => {
153
- if (options.notes && options.notesFile) {
154
- throw new Error("Use --notes or --notes-file, not both");
155
- }
156
- await prepareReleaseRequest({
157
- root: projectUtils.root() ?? process.cwd(),
158
- baseBranch: options.base,
159
- message: optionText(options.message),
160
- notes: optionText(options.notes),
161
- notesFile: options.notesFile,
162
- });
163
- },
164
- )
165
- .parseAsync();
166
- }
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- /** Generate component-qualified release notes from an affected release plan. */
3
-
4
- import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
5
- import { join, resolve } from "node:path";
6
- import { log } from "@dbx-tools/shared-core";
7
- import { Command } from "commander";
8
- import { generateReleaseSummary } from "./release-summary.ts";
9
- import { captureTaskCommand } from "../src/_task-command.ts";
10
- import type { ReleaseUnitGraph } from "../src/release-catalog.ts";
11
- import type { ReleasePlan } from "../src/release-plan.ts";
12
-
13
- const logger = log.logger("projen:release-summary-units");
14
-
15
- if (import.meta.main) {
16
- await new Command()
17
- .option("--root <path>", "repository root", ".")
18
- .option("--plan <path>", "affected release plan", "dist/release-plan.json")
19
- .option("--from-ref <ref>", "summary base ref", "origin/main")
20
- .option("--to-ref <ref>", "summary target ref", "HEAD")
21
- .action(async (options: { root: string; plan: string; fromRef: string; toRef: string }) => {
22
- const root = resolve(options.root);
23
- const plan = JSON.parse(readFileSync(resolve(root, options.plan), "utf8")) as ReleasePlan;
24
- const graph = JSON.parse(
25
- readFileSync(resolve(root, ".projen/release-units.json"), "utf8"),
26
- ) as ReleaseUnitGraph;
27
- const request = requestNotes(root);
28
- for (const planned of plan.units) {
29
- const unit = graph.units.find((candidate) => candidate.id === planned.id);
30
- if (!unit) throw new Error(`Release plan references unknown unit ${planned.id}`);
31
- const paths = graph.projects
32
- .filter((project) => project.unit === unit.id)
33
- .map((project) => project.path);
34
- const componentTag = `${planned.component}-v${planned.oldVersion}`;
35
- const fromRef = gitCapture(root, ["rev-parse", "--verify", componentTag])
36
- ? componentTag
37
- : options.fromRef;
38
- await generateReleaseSummary({
39
- root,
40
- component: planned.component,
41
- version: planned.newVersion,
42
- paths,
43
- customSummary: request.summary,
44
- outputFile: `.release-notes/final/${planned.component}-v${planned.newVersion}.md`,
45
- fromRef,
46
- toRef: options.toRef,
47
- });
48
- }
49
- for (const path of request.paths) rmSync(path);
50
- logger.success(`generated ${plan.units.length} component release summaries`);
51
- })
52
- .parseAsync();
53
- }
54
-
55
- function requestNotes(root: string): { summary?: string; paths: string[] } {
56
- const directory = resolve(root, ".release-notes/requests");
57
- if (!existsSync(directory)) return { paths: [] };
58
- const paths = readdirSync(directory)
59
- .filter((name) => name.endsWith(".md"))
60
- .sort()
61
- .map((name) => join(directory, name));
62
- const summary = paths
63
- .map((path) =>
64
- readFileSync(path, "utf8")
65
- .replace(/^# .+\n+/, "")
66
- .trim(),
67
- )
68
- .filter(Boolean)
69
- .join("\n\n");
70
- return { ...(summary ? { summary } : {}), paths };
71
- }
72
-
73
- function gitCapture(root: string, args: string[]): string {
74
- return captureTaskCommand(root, "git", args);
75
- }
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- import { Command } from "commander";
3
- import { stampPythonProjects } from "./publish-python.ts";
4
-
5
- const program = new Command();
6
- program
7
- .argument("<version>", "Python package version")
8
- .option("--root <path>", "Python workspace package root", "packages/py")
9
- .action((version: string, options: { root: string }) => {
10
- stampPythonProjects(options.root, version);
11
- });
12
-
13
- await program.parseAsync();