@dbx-tools/projen 0.6.40 → 0.6.41
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.
- package/index.ts +2 -2
- package/package.json +22 -20
- package/src/bun-app.ts +164 -0
- package/src/packages.ts +6 -4
- package/src/pnpm-workspace.ts +73 -12
- package/src/project.ts +91 -46
- package/src/release.ts +114 -38
- package/src/tags.ts +28 -14
- package/tasks/bump.ts +41 -87
- package/tasks/publish.ts +156 -0
- package/src/vite.ts +0 -99
package/src/release.ts
CHANGED
|
@@ -11,7 +11,44 @@ import { applyTasks, taskScript, type DBXToolsNodeProject } from "./project.ts";
|
|
|
11
11
|
|
|
12
12
|
const NODE_VERSION = "lts/*";
|
|
13
13
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
14
|
-
const
|
|
14
|
+
const BUN_VERSION = "1.3.14";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The `release` workflow's version-stamp + publish step, as a shell script.
|
|
18
|
+
*
|
|
19
|
+
* Bun has no `pnpm -r publish` equivalent, so this drives the engine's
|
|
20
|
+
* `tasks/publish.ts` (shipped in the engine tarball, run via bun): it reads the
|
|
21
|
+
* workspace members from the root `package.json`, stamps the tag version onto
|
|
22
|
+
* every manifest (rewriting `@dbx-tools/*` `workspace:*` sibling deps to
|
|
23
|
+
* `^<version>` so the tarballs resolve each other), then `bun publish`es each
|
|
24
|
+
* non-`private` package. `bun publish` substitutes `publishConfig` (the compiled
|
|
25
|
+
* `lib/` entry points) at pack time, runs `prepack` (compile) so `lib/` exists,
|
|
26
|
+
* and honors `NPM_CONFIG_PROVENANCE`.
|
|
27
|
+
*
|
|
28
|
+
* Two ways in: a pushed `<prefix>*` tag (the real release - `GITHUB_REF_NAME` is
|
|
29
|
+
* the version) and a manual `workflow_dispatch` (no tag, so a throwaway
|
|
30
|
+
* `0.0.0-dry.<run>` version is used and `--dry-run` is FORCED regardless of the
|
|
31
|
+
* input, since a dispatch never has a tag to publish as). The `dry_run` input
|
|
32
|
+
* (default true) is what lets a maintainer exercise the whole workflow - setup,
|
|
33
|
+
* install, stamp, compile, pack, validate - with nothing reaching npm.
|
|
34
|
+
*/
|
|
35
|
+
function BUN_PUBLISH_SCRIPT(tagPrefix: string, excludeDirs: readonly string[]): string {
|
|
36
|
+
const script = "node_modules/@dbx-tools/projen/tasks/publish.ts";
|
|
37
|
+
const excludes = excludeDirs.map((dir) => ` --exclude ${dir}`).join("");
|
|
38
|
+
return [
|
|
39
|
+
'if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then',
|
|
40
|
+
// A manual run has no tag: use a throwaway version and never really publish.
|
|
41
|
+
' VERSION="0.0.0-dry.${GITHUB_RUN_NUMBER}"',
|
|
42
|
+
" DRY_RUN=--dry-run",
|
|
43
|
+
"else",
|
|
44
|
+
` VERSION="\${GITHUB_REF_NAME#${tagPrefix}}"`,
|
|
45
|
+
// A tag push honors the input too, so a dry-run tag can be tested if wanted.
|
|
46
|
+
' DRY_RUN="${DRY_RUN_INPUT}"',
|
|
47
|
+
"fi",
|
|
48
|
+
"chmod -R u+w . || true",
|
|
49
|
+
`bun ${script} "$VERSION"${excludes} $DRY_RUN`,
|
|
50
|
+
].join("\n");
|
|
51
|
+
}
|
|
15
52
|
|
|
16
53
|
interface PublishWorkflow {
|
|
17
54
|
readonly name: string;
|
|
@@ -24,17 +61,18 @@ interface PublishWorkflow {
|
|
|
24
61
|
function publishSetupSteps(): JobStep[] {
|
|
25
62
|
return [
|
|
26
63
|
{ name: "Checkout", uses: "actions/checkout@v6", with: { "fetch-depth": 0 } },
|
|
27
|
-
{ name: "Setup
|
|
64
|
+
{ name: "Setup Bun", uses: "oven-sh/setup-bun@v2", with: { "bun-version": BUN_VERSION } },
|
|
28
65
|
{
|
|
29
66
|
name: "Setup Node.js",
|
|
30
67
|
uses: "actions/setup-node@v6",
|
|
31
68
|
// setup-node writes the temporary npmrc that maps NODE_AUTH_TOKEN onto
|
|
32
69
|
// npmjs. Omitting this leaves the secret in the environment but gives npm
|
|
33
70
|
// no registry-scoped auth entry, and every publish fails with ENEEDAUTH.
|
|
71
|
+
// (Bun installs deps; publishing still goes through `npm publish`.)
|
|
34
72
|
with: { "node-version": NODE_VERSION, "registry-url": NPM_REGISTRY_URL },
|
|
35
73
|
},
|
|
36
|
-
//
|
|
37
|
-
{ name: "Install", run: "
|
|
74
|
+
// Bun's install; the lockfile may be absent or stale in CI so it is not frozen.
|
|
75
|
+
{ name: "Install", run: "bun install" },
|
|
38
76
|
];
|
|
39
77
|
}
|
|
40
78
|
|
|
@@ -160,12 +198,30 @@ export class DBXToolsRelease extends Component {
|
|
|
160
198
|
// the publish job below overrides it with the `id-token` it needs.
|
|
161
199
|
workflow.file?.addOverride("permissions", { contents: "read" });
|
|
162
200
|
workflow.on({ push: { tags: [`${tagPrefix}*`] } });
|
|
201
|
+
// Manual trigger for testing the workflow WITHOUT reaching npm: a
|
|
202
|
+
// `workflow_dispatch` run has no tag, so the publish script forces
|
|
203
|
+
// `--dry-run` (pack + validate only). The `dry_run` input (default true)
|
|
204
|
+
// additionally lets a tag push be dry-run on demand.
|
|
205
|
+
workflow.file?.addOverride("on.workflow_dispatch", {
|
|
206
|
+
inputs: {
|
|
207
|
+
dry_run: {
|
|
208
|
+
description: "Pack and validate but do not upload to npm",
|
|
209
|
+
type: "boolean",
|
|
210
|
+
default: true,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
});
|
|
163
214
|
workflow.addJob("publish", {
|
|
164
215
|
runsOn: ["ubuntu-latest"],
|
|
165
216
|
// `id-token: write` lets npm mint the OIDC token for provenance attestation.
|
|
166
217
|
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
167
218
|
timeoutMinutes: 30,
|
|
168
|
-
|
|
219
|
+
// `DRY_RUN_INPUT` is `--dry-run` when the dispatch input is true, else empty;
|
|
220
|
+
// the publish script also FORCES it on any `workflow_dispatch` run.
|
|
221
|
+
env: {
|
|
222
|
+
CI: "true",
|
|
223
|
+
DRY_RUN_INPUT: "${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}",
|
|
224
|
+
},
|
|
169
225
|
steps: [...publishSetupSteps(), ...steps],
|
|
170
226
|
...(workingDirectory ? { defaults: { run: { workingDirectory } } } : {}),
|
|
171
227
|
});
|
|
@@ -182,26 +238,25 @@ export class DBXToolsRelease extends Component {
|
|
|
182
238
|
name: "release",
|
|
183
239
|
tagPrefix: this.tagPrefix,
|
|
184
240
|
steps: [
|
|
185
|
-
// The pushed tag is the version: `<prefix>1.2.3` -> `1.2.3`.
|
|
186
|
-
// every package (manifests are projen-readonly, so unlock
|
|
241
|
+
// The pushed tag is the version: `<prefix>1.2.3` -> `1.2.3`. Stamp it on
|
|
242
|
+
// every workspace package (manifests are projen-readonly, so unlock
|
|
243
|
+
// first), rewriting `workspace:*` sibling deps to `^<version>` so the
|
|
244
|
+
// published tarballs resolve each other. `bun publish` honors
|
|
245
|
+
// `publishConfig` (compiled `lib/` entry points) and provenance.
|
|
187
246
|
{
|
|
188
|
-
name: "Set version from tag",
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
{
|
|
196
|
-
name: "Publish to npm",
|
|
197
|
-
// `pnpm -r publish` publishes every non-private package,
|
|
198
|
-
// rewriting `workspace:*` deps to the published version. Provenance is
|
|
199
|
-
// opt-in (omitted from each package's `publishConfig` so local
|
|
200
|
-
// publishes work); CI turns it on here via `npm_config_provenance`.
|
|
201
|
-
run: "pnpm -r publish --no-git-checks --access public",
|
|
247
|
+
name: "Set version from tag and publish",
|
|
248
|
+
// Exclude every standalone-release dir (e.g. `projen`): it publishes on
|
|
249
|
+
// its own `<prefix>-v*` tag via its own workflow, not the main `v*` one.
|
|
250
|
+
run: BUN_PUBLISH_SCRIPT(
|
|
251
|
+
this.tagPrefix,
|
|
252
|
+
this.standaloneReleases.map((s) => s.directory),
|
|
253
|
+
),
|
|
202
254
|
env: {
|
|
255
|
+
// `bun publish` authenticates via NPM_CONFIG_TOKEN (not NODE_AUTH_TOKEN,
|
|
256
|
+
// which is the `npm publish` convention). Set both so either tool works.
|
|
257
|
+
NPM_CONFIG_TOKEN: "${{ secrets.NPM_TOKEN }}",
|
|
203
258
|
NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}",
|
|
204
|
-
|
|
259
|
+
NPM_CONFIG_PROVENANCE: "true",
|
|
205
260
|
},
|
|
206
261
|
},
|
|
207
262
|
],
|
|
@@ -210,34 +265,55 @@ export class DBXToolsRelease extends Component {
|
|
|
210
265
|
|
|
211
266
|
/**
|
|
212
267
|
* Emit a {@link StandaloneRelease}'s workflow: push `<prefix>1.2.3` and the
|
|
213
|
-
* single package in `directory`
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
268
|
+
* single package in `directory` is published at 1.2.3 via `bun publish`.
|
|
269
|
+
*
|
|
270
|
+
* `directory` (e.g. `projen/`) is a WORKSPACE MEMBER whose `@dbx-tools/*` deps
|
|
271
|
+
* are `workspace:*`. `bun publish` resolves those to whatever version its
|
|
272
|
+
* SIBLINGS carry (via the lockfile), so before publishing we set the version on
|
|
273
|
+
* the package AND its in-scope siblings, then refresh the lockfile - otherwise
|
|
274
|
+
* the published engine would depend on the siblings' on-disk `0.0.0`. The
|
|
275
|
+
* `Install` step already ran `bun install` from the repo root (the member
|
|
276
|
+
* subdir walks up to it), so the workspace is linked. The manifests are
|
|
277
|
+
* projen-readonly, hence the `chmod`. A manual `workflow_dispatch` run has no
|
|
278
|
+
* tag, so it uses a throwaway version and forces `--dry-run` (nothing to npm).
|
|
217
279
|
*/
|
|
218
280
|
private authorStandaloneReleaseWorkflow(
|
|
219
281
|
project: DBXToolsNodeProject,
|
|
220
282
|
{ name, directory, tagPrefix }: StandaloneRelease,
|
|
221
283
|
): void {
|
|
284
|
+
// The engine's release also stamps its in-scope siblings so `bun publish`
|
|
285
|
+
// resolves their `workspace:*` to the release version. Stamping the WHOLE
|
|
286
|
+
// workspace is simplest and harmless (only `directory` is published here).
|
|
287
|
+
const stampScript = "node_modules/@dbx-tools/projen/tasks/publish.ts";
|
|
222
288
|
this.authorPublishWorkflow(project, {
|
|
223
289
|
name,
|
|
224
290
|
tagPrefix,
|
|
225
|
-
workingDirectory: directory,
|
|
226
291
|
steps: [
|
|
227
|
-
// The pushed tag is the version: `<prefix>1.2.3` -> `1.2.3`. package.json
|
|
228
|
-
// is projen-generated read-only, so unlock it before `npm version` writes.
|
|
229
292
|
{
|
|
230
|
-
name: "Set version from tag",
|
|
293
|
+
name: "Set version from tag and publish",
|
|
231
294
|
run: [
|
|
232
|
-
"
|
|
233
|
-
|
|
295
|
+
'if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then',
|
|
296
|
+
' VERSION="0.0.0-dry.${GITHUB_RUN_NUMBER}"',
|
|
297
|
+
" DRY_RUN=--dry-run",
|
|
298
|
+
"else",
|
|
299
|
+
` VERSION="\${GITHUB_REF_NAME#${tagPrefix}}"`,
|
|
300
|
+
' DRY_RUN="${DRY_RUN_INPUT}"',
|
|
301
|
+
"fi",
|
|
302
|
+
"chmod -R u+w . || true",
|
|
303
|
+
// Set the version across every member + refresh the lockfile (the
|
|
304
|
+
// publish task's stamp phase), so bun resolves the engine's
|
|
305
|
+
// `workspace:*` sibling deps to the release version at pack time.
|
|
306
|
+
`bun ${stampScript} "$VERSION" --stamp-only`,
|
|
307
|
+
// Then publish ONLY the standalone directory.
|
|
308
|
+
`cd ${directory} && bun publish --access public $DRY_RUN`,
|
|
234
309
|
].join("\n"),
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
310
|
+
env: {
|
|
311
|
+
// `bun publish` authenticates via NPM_CONFIG_TOKEN (not NODE_AUTH_TOKEN,
|
|
312
|
+
// which is the `npm publish` convention). Set both so either tool works.
|
|
313
|
+
NPM_CONFIG_TOKEN: "${{ secrets.NPM_TOKEN }}",
|
|
314
|
+
NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}",
|
|
315
|
+
NPM_CONFIG_PROVENANCE: "true",
|
|
316
|
+
},
|
|
241
317
|
},
|
|
242
318
|
],
|
|
243
319
|
});
|
package/src/tags.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { IMixin as ConstructsMixin } from "constructs";
|
|
17
17
|
import { javascript } from "projen";
|
|
18
|
+
import { BunBuildFile, BunDevServerFile, BunfigFile } from "./bun-app.ts";
|
|
18
19
|
import { create } from "./mixin.ts";
|
|
19
20
|
import {
|
|
20
21
|
addPackageFiles,
|
|
@@ -25,7 +26,6 @@ import {
|
|
|
25
26
|
srcModuleExports,
|
|
26
27
|
} from "./project.ts";
|
|
27
28
|
import * as projectPredicate from "./project-predicate.ts";
|
|
28
|
-
import { ViteConfigFile } from "./vite.ts";
|
|
29
29
|
|
|
30
30
|
/** Node compiler options: ES2022 lib + node types, deliberately no DOM. */
|
|
31
31
|
const NODE_COMPILER_OPTIONS: javascript.TypeScriptCompilerOptions = {
|
|
@@ -81,30 +81,42 @@ export const PACKAGE_TAG_MIXINS = {
|
|
|
81
81
|
"./package.json": "./package.json",
|
|
82
82
|
});
|
|
83
83
|
}),
|
|
84
|
-
// `app`: a full browser app built + served by
|
|
85
|
-
// entry). Self-contained React app: React + DOM lib + JSX +
|
|
86
|
-
//
|
|
87
|
-
// `
|
|
84
|
+
// `app`: a full browser app built + served by BUN (needs an `index.html`
|
|
85
|
+
// entry). Self-contained React app: React + DOM lib + JSX + bun's fullstack
|
|
86
|
+
// dev server (`dev.ts`, `Bun.serve` + HMR) and production bundle (`build.ts`,
|
|
87
|
+
// `Bun.build`). Tailwind v4 is compiled by `bun-plugin-tailwind` (wired in the
|
|
88
|
+
// generated `bunfig.toml`). No Vite.
|
|
88
89
|
app: create(projectPredicate.hasTag("app"), (p) => {
|
|
89
90
|
p.addDeps("react@catalog:", "react-dom@catalog:");
|
|
90
91
|
p.addDevDeps(
|
|
91
|
-
"vite@catalog:",
|
|
92
|
-
"@vitejs/plugin-react@catalog:",
|
|
93
92
|
"@types/react@catalog:",
|
|
94
93
|
"@types/react-dom@catalog:",
|
|
94
|
+
// The Tailwind plugin the dev server + build load. Tailwind itself is a
|
|
95
|
+
// catalog dep the app declares (it also owns the Tailwind entry CSS).
|
|
96
|
+
"bun-plugin-tailwind@catalog:",
|
|
95
97
|
);
|
|
96
98
|
applyCompilerOptions(p, {
|
|
97
99
|
target: "ES2022",
|
|
98
100
|
lib: [...DOM_LIB],
|
|
99
101
|
jsx: javascript.TypeScriptJsxMode.REACT_JSX,
|
|
100
|
-
types
|
|
102
|
+
// `@types/bun` (a root/subproject devDep) supplies the `Bun.*` globals the
|
|
103
|
+
// generated `dev.ts`/`build.ts` use; no `vite/client`.
|
|
104
|
+
types: ["bun"],
|
|
105
|
+
// `@/` -> `src/` alias, resolved by both tsc and bun's bundler (bun reads
|
|
106
|
+
// tsconfig `paths`). Replaces the old Vite `resolve.alias` for `@`.
|
|
107
|
+
baseUrl: ".",
|
|
108
|
+
paths: { "@/*": ["./src/*"] },
|
|
101
109
|
});
|
|
110
|
+
// bun runs the generated scripts directly. `build` resets the compile task so
|
|
111
|
+
// `compile` bundles with `Bun.build` rather than `tsc`.
|
|
102
112
|
applyTasks(p, {
|
|
103
|
-
dev: { exec: "
|
|
104
|
-
build: { exec: "
|
|
105
|
-
preview: { exec: "
|
|
113
|
+
dev: { exec: "bun dev.ts" },
|
|
114
|
+
build: { exec: "bun build.ts" },
|
|
115
|
+
preview: { exec: "bun dev.ts" },
|
|
106
116
|
});
|
|
107
|
-
new
|
|
117
|
+
new BunfigFile(p);
|
|
118
|
+
new BunDevServerFile(p);
|
|
119
|
+
new BunBuildFile(p);
|
|
108
120
|
// An app has a single root entry, not a component library's subpaths - so it
|
|
109
121
|
// replaces the `ui` tag's `./react`/`./styles.css` surface with a `.` root.
|
|
110
122
|
applyExports(p, {
|
|
@@ -147,9 +159,11 @@ export const PACKAGE_TAG_MIXINS = {
|
|
|
147
159
|
...NODE_COMPILER_OPTIONS,
|
|
148
160
|
experimentalDecorators: true,
|
|
149
161
|
});
|
|
162
|
+
// bun runs the server `.ts` directly (native TS, no tsx). `--watch` restarts
|
|
163
|
+
// on change - the tsx-watch replacement.
|
|
150
164
|
applyTasks(p, {
|
|
151
|
-
dev: { exec: "
|
|
152
|
-
start: { exec: "
|
|
165
|
+
dev: { exec: "bun --watch src/server.ts" },
|
|
166
|
+
start: { exec: "bun src/server.ts" },
|
|
153
167
|
});
|
|
154
168
|
}),
|
|
155
169
|
node: create(projectPredicate.hasTag("node"), (p) => {
|
package/tasks/bump.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env -S
|
|
1
|
+
#!/usr/bin/env -S bun
|
|
2
2
|
/**
|
|
3
3
|
* `projen bump` - synth, compute the next release version, then (by default)
|
|
4
4
|
* commit, tag, and push it. Pushing the tag is what triggers the release
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* - the local `package.json` version,
|
|
12
12
|
* then incremented by `--level` (patch | minor | major; default patch).
|
|
13
13
|
*
|
|
14
|
-
* `--sibling <dir>:<tagPrefix>` (repeatable) releases
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* `--sibling <dir>:<tagPrefix>` (repeatable) releases an in-repo project that
|
|
15
|
+
* publishes on its OWN tag namespace (e.g. `projen/`, tagged `projen-v*`) at the
|
|
16
|
+
* SAME version as the root, in the same run: its manifest version is stamped, its
|
|
17
17
|
* `<tagPrefix><version>` tag is cut and pushed (triggering its own workflow), and
|
|
18
18
|
* it is included in the local-registry publish. Taking the base version from
|
|
19
19
|
* every prefix at once is what keeps the two in lockstep: the engine sat at
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
*
|
|
33
33
|
* `--local-registry <value>` publishes the just-tagged version to a LOCAL
|
|
34
34
|
* registry (e.g. a verdaccio) right after the git tag is pushed - so a local
|
|
35
|
-
* `
|
|
35
|
+
* `bun run bump` both fires the GitHub release (public npm) and populates your
|
|
36
36
|
* local registry. Values:
|
|
37
37
|
* - `auto` (default): publish only when `npm config get registry` is a
|
|
38
38
|
* loopback host (`localhost` / `127.0.0.0/8` / `::1`); otherwise skip.
|
|
@@ -44,15 +44,10 @@ import { log, net } from "@dbx-tools/shared-core";
|
|
|
44
44
|
import { Command, Option } from "commander";
|
|
45
45
|
import { chmodSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
46
46
|
import { resolve } from "node:path";
|
|
47
|
+
import { fileURLToPath } from "node:url";
|
|
47
48
|
|
|
48
49
|
const logger = log.logger("projen:bump");
|
|
49
50
|
const LEVELS = ["patch", "minor", "major"] as const;
|
|
50
|
-
const DEPENDENCY_FIELDS = [
|
|
51
|
-
"dependencies",
|
|
52
|
-
"devDependencies",
|
|
53
|
-
"optionalDependencies",
|
|
54
|
-
"peerDependencies",
|
|
55
|
-
] as const;
|
|
56
51
|
type Level = (typeof LEVELS)[number];
|
|
57
52
|
|
|
58
53
|
/** A standalone in-repo project released alongside the root, on its own tag prefix. */
|
|
@@ -126,39 +121,30 @@ function readPackageVersion(pkgPath: string): [number, number, number] {
|
|
|
126
121
|
}
|
|
127
122
|
|
|
128
123
|
/**
|
|
129
|
-
* Write `version` into a manifest projen owns
|
|
130
|
-
*
|
|
131
|
-
*
|
|
124
|
+
* Write ONLY the `version` field into a manifest projen owns (read-only, so
|
|
125
|
+
* bracketed by a chmod that restores the mode). Used for the ROOT and the
|
|
126
|
+
* standalone `projen/` sibling so the committed release marks the version.
|
|
127
|
+
*
|
|
128
|
+
* It deliberately does NOT rewrite `@scope/*` dep ranges. `projen/` is a workspace
|
|
129
|
+
* member with `workspace:*` sibling deps; baking a `^version` for the just-bumped
|
|
130
|
+
* (not-yet-published) version into the COMMITTED manifest breaks the release
|
|
131
|
+
* workflow's initial `bun install`, which checks out that commit before anything
|
|
132
|
+
* is published. Sibling versions are resolved transiently at publish time instead:
|
|
133
|
+
* `projen-release` runs `tasks/publish.ts --stamp-only` (set versions + refresh
|
|
134
|
+
* lockfile), then `bun publish` in `projen/` strips `workspace:*` to those versions.
|
|
132
135
|
*/
|
|
133
|
-
function writeManifestVersion(pkgPath: string, version: string
|
|
136
|
+
function writeManifestVersion(pkgPath: string, version: string): void {
|
|
134
137
|
const { mode } = statSync(pkgPath);
|
|
135
138
|
chmodSync(pkgPath, mode | 0o200);
|
|
136
139
|
try {
|
|
137
140
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>;
|
|
138
141
|
pkg.version = version;
|
|
139
|
-
if (dependencyScope) {
|
|
140
|
-
for (const field of DEPENDENCY_FIELDS) {
|
|
141
|
-
const dependencies = pkg[field];
|
|
142
|
-
if (!dependencies || typeof dependencies !== "object") continue;
|
|
143
|
-
for (const name of Object.keys(dependencies)) {
|
|
144
|
-
if (name.startsWith(dependencyScope)) {
|
|
145
|
-
(dependencies as Record<string, string>)[name] = `^${version}`;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
142
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
151
143
|
} finally {
|
|
152
144
|
chmodSync(pkgPath, mode);
|
|
153
145
|
}
|
|
154
146
|
}
|
|
155
147
|
|
|
156
|
-
/** Scoped package prefix (`@scope/`) shared by the root's release packages. */
|
|
157
|
-
function releaseDependencyScope(pkgPath: string): string | undefined {
|
|
158
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { name?: string };
|
|
159
|
-
return /^@[^/]+\//.exec(pkg.name ?? "")?.[0];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
148
|
/**
|
|
163
149
|
* Resolve the `--local-registry` value to a registry URL to publish to, or
|
|
164
150
|
* `undefined` to skip. `false` skips; a URL is used as-is; `auto` uses the
|
|
@@ -217,7 +203,6 @@ program
|
|
|
217
203
|
}) => {
|
|
218
204
|
const pkgPath = resolve(process.cwd(), "package.json");
|
|
219
205
|
if (!existsSync(pkgPath)) throw new Error(`no package.json in ${process.cwd()}`);
|
|
220
|
-
const dependencyScope = releaseDependencyScope(pkgPath);
|
|
221
206
|
|
|
222
207
|
const siblings = opts.sibling.map((s) => ({ ...s, pkgPath: resolve(s.dir, "package.json") }));
|
|
223
208
|
for (const s of siblings) {
|
|
@@ -228,7 +213,7 @@ program
|
|
|
228
213
|
// manifests, workspace file, tasks, ...) rather than a stale one.
|
|
229
214
|
if (opts.synth) {
|
|
230
215
|
logger.info("synthesizing (projen)");
|
|
231
|
-
exec.spawnSync("
|
|
216
|
+
exec.spawnSync("bun", [".projenrc.ts"], {
|
|
232
217
|
cwd: process.cwd(),
|
|
233
218
|
stdout: "inherit",
|
|
234
219
|
stderr: "inherit",
|
|
@@ -265,7 +250,7 @@ program
|
|
|
265
250
|
|
|
266
251
|
if (opts.version) {
|
|
267
252
|
writeManifestVersion(pkgPath, version);
|
|
268
|
-
for (const s of siblings) writeManifestVersion(s.pkgPath, version
|
|
253
|
+
for (const s of siblings) writeManifestVersion(s.pkgPath, version);
|
|
269
254
|
const also = siblings.length ? ` (and ${siblings.map((s) => s.dir).join(", ")})` : "";
|
|
270
255
|
logger.info(`wrote version ${version} to package.json${also}`);
|
|
271
256
|
}
|
|
@@ -304,57 +289,26 @@ program
|
|
|
304
289
|
}
|
|
305
290
|
if (publishToLocalRegistry) {
|
|
306
291
|
logger.info(`publishing ${version} to local registry ${localRegistry}`);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
"npm",
|
|
328
|
-
"version",
|
|
329
|
-
version,
|
|
330
|
-
"--no-git-tag-version",
|
|
331
|
-
"--allow-same-version",
|
|
332
|
-
]);
|
|
333
|
-
// Provenance is opt-in (see `.projenrc.ts`): the generated
|
|
334
|
-
// `publishConfig` omits it, so local (verdaccio) publishes never try to
|
|
335
|
-
// attest. CI turns it on with `npm_config_provenance=true`.
|
|
336
|
-
runInRepo("pnpm", [
|
|
337
|
-
"-r",
|
|
338
|
-
"publish",
|
|
339
|
-
"--registry",
|
|
340
|
-
localRegistry,
|
|
341
|
-
"--no-git-checks",
|
|
342
|
-
"--access",
|
|
343
|
-
"public",
|
|
344
|
-
]);
|
|
345
|
-
// `pnpm -r` cannot see a sibling (not a workspace member), so publish each
|
|
346
|
-
// one on its own. Skipping this is what left a local registry serving a
|
|
347
|
-
// current CLI against a months-old engine.
|
|
348
|
-
for (const s of siblings) {
|
|
349
|
-
runIn(s.dir, "pnpm", [
|
|
350
|
-
"publish",
|
|
351
|
-
"--registry",
|
|
352
|
-
localRegistry,
|
|
353
|
-
"--no-git-checks",
|
|
354
|
-
"--access",
|
|
355
|
-
"public",
|
|
356
|
-
]);
|
|
357
|
-
}
|
|
292
|
+
// Mirror the CI `release` workflow via the shared publish task: it sets
|
|
293
|
+
// the release version on every workspace member (`bun pm pkg set`; they
|
|
294
|
+
// keep `0.0.0` on disk, projen-owned, so it unlocks each briefly), then
|
|
295
|
+
// `bun publish`es each non-private one - and bun natively strips the
|
|
296
|
+
// `workspace:`/`catalog:` protocols in the packed tarball, resolving each
|
|
297
|
+
// to the version just set. The next `projen` synth restores the manifests
|
|
298
|
+
// to `0.0.0`; the release version lives in the git tag. Provenance is off
|
|
299
|
+
// for a local registry (no OIDC), so `NPM_CONFIG_PROVENANCE` is unset.
|
|
300
|
+
// `publish.ts` is this task's SIBLING in the engine's `tasks/` dir; resolve
|
|
301
|
+
// it off `import.meta.url` (works whether the engine is source-linked in-repo
|
|
302
|
+
// or installed under node_modules) rather than a repo-relative `tasks/...`
|
|
303
|
+
// that only exists inside `projen/`.
|
|
304
|
+
const publishScript = fileURLToPath(new URL("./publish.ts", import.meta.url));
|
|
305
|
+
exec.spawnSync("bun", [publishScript, version, "--registry", localRegistry], {
|
|
306
|
+
cwd: process.cwd(),
|
|
307
|
+
stdout: "inherit",
|
|
308
|
+
stderr: "inherit",
|
|
309
|
+
stdin: "ignore",
|
|
310
|
+
check: true,
|
|
311
|
+
});
|
|
358
312
|
logger.success(`published ${version} to ${localRegistry}`);
|
|
359
313
|
}
|
|
360
314
|
|
|
@@ -364,7 +318,7 @@ program
|
|
|
364
318
|
// manifest finish the bump in lockstep.
|
|
365
319
|
if (opts.version) {
|
|
366
320
|
writeManifestVersion(pkgPath, version);
|
|
367
|
-
for (const s of siblings) writeManifestVersion(s.pkgPath, version
|
|
321
|
+
for (const s of siblings) writeManifestVersion(s.pkgPath, version);
|
|
368
322
|
logger.info(`synchronized release manifests at ${version}`);
|
|
369
323
|
}
|
|
370
324
|
},
|