@_linked/cli 1.17.0 → 1.19.0
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/CHANGELOG.md +93 -0
- package/README.md +124 -5
- package/defaults/setup-publish/github/workflows/pr.yml +29 -0
- package/defaults/setup-publish/github/workflows/publish.yml +17 -70
- package/lib/esm/app-release/app-assets-store.d.ts +23 -0
- package/lib/esm/app-release/app-assets-store.d.ts.map +1 -0
- package/lib/esm/app-release/app-assets-store.js +33 -0
- package/lib/esm/app-release/app-assets-store.js.map +1 -0
- package/lib/esm/app-release/create-release-manifest.d.ts +114 -0
- package/lib/esm/app-release/create-release-manifest.d.ts.map +1 -0
- package/lib/esm/app-release/create-release-manifest.js +238 -0
- package/lib/esm/app-release/create-release-manifest.js.map +1 -0
- package/lib/esm/app-release/paths.d.ts +5 -0
- package/lib/esm/app-release/paths.d.ts.map +1 -0
- package/lib/esm/app-release/paths.js +44 -0
- package/lib/esm/app-release/paths.js.map +1 -0
- package/lib/esm/app-release/publisher.d.ts +25 -0
- package/lib/esm/app-release/publisher.d.ts.map +1 -0
- package/lib/esm/app-release/publisher.js +238 -0
- package/lib/esm/app-release/publisher.js.map +1 -0
- package/lib/esm/app-release/resolve-build-target.d.ts +19 -0
- package/lib/esm/app-release/resolve-build-target.d.ts.map +1 -0
- package/lib/esm/app-release/resolve-build-target.js +30 -0
- package/lib/esm/app-release/resolve-build-target.js.map +1 -0
- package/lib/esm/app-release/static-assets.d.ts +13 -0
- package/lib/esm/app-release/static-assets.d.ts.map +1 -0
- package/lib/esm/app-release/static-assets.js +55 -0
- package/lib/esm/app-release/static-assets.js.map +1 -0
- package/lib/esm/app-release/types.d.ts +71 -0
- package/lib/esm/app-release/types.d.ts.map +1 -0
- package/lib/esm/app-release/types.js +2 -0
- package/lib/esm/app-release/types.js.map +1 -0
- package/lib/esm/cli.js +81 -19
- package/lib/esm/cli.js.map +1 -1
- package/lib/esm/commands/build-app.d.ts +55 -0
- package/lib/esm/commands/build-app.d.ts.map +1 -0
- package/lib/esm/commands/build-app.js +178 -0
- package/lib/esm/commands/build-app.js.map +1 -0
- package/lib/esm/commands/publish-app.d.ts +18 -0
- package/lib/esm/commands/publish-app.d.ts.map +1 -0
- package/lib/esm/commands/publish-app.js +124 -0
- package/lib/esm/commands/publish-app.js.map +1 -0
- package/lib/esm/commands/serve-app.d.ts +14 -0
- package/lib/esm/commands/serve-app.d.ts.map +1 -0
- package/lib/esm/commands/serve-app.js +49 -0
- package/lib/esm/commands/serve-app.js.map +1 -0
- package/lib/esm/commands/setup-publish.d.ts +39 -4
- package/lib/esm/commands/setup-publish.d.ts.map +1 -1
- package/lib/esm/commands/setup-publish.js +142 -83
- package/lib/esm/commands/setup-publish.js.map +1 -1
- package/lib/esm/index.d.ts +11 -0
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +9 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/lifecycle.d.ts +1 -1
- package/lib/esm/lifecycle.d.ts.map +1 -1
- package/lib/esm/lifecycle.js +17 -8
- package/lib/esm/lifecycle.js.map +1 -1
- package/package.json +2 -2
- package/defaults/setup-publish/dual-branch/changeset/README.md +0 -20
- package/defaults/setup-publish/dual-branch/changeset/config.json +0 -14
- package/defaults/setup-publish/dual-branch/github/workflows/changeset-check.yml +0 -43
- package/defaults/setup-publish/dual-branch/github/workflows/ci.yml +0 -44
- package/defaults/setup-publish/dual-branch/github/workflows/publish.yml +0 -191
- package/defaults/setup-publish/github/workflows/changeset-check.yml +0 -44
- package/defaults/setup-publish/github/workflows/ci.yml +0 -43
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IFileStore } from '@_linked/core/interfaces/IFileStore';
|
|
2
|
+
export interface PublishAppOptions {
|
|
3
|
+
appRoot?: string;
|
|
4
|
+
manifestPath?: string;
|
|
5
|
+
/** An already-resolved store (the `build-app --publish` path). */
|
|
6
|
+
store?: IFileStore;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the store lazily. Used instead of `store` so the release manifest
|
|
9
|
+
* is read and validated first: a Capacitor (non-publishable) manifest then
|
|
10
|
+
* fails with "only publishable web release manifests may be uploaded"
|
|
11
|
+
* instead of a confusing storage-configuration error.
|
|
12
|
+
*/
|
|
13
|
+
resolveStore?: () => Promise<IFileStore>;
|
|
14
|
+
yes?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const redactPublishError: (error: unknown) => Error;
|
|
17
|
+
export declare const publishApp: (options: PublishAppOptions) => Promise<import("../index.js").PublishResult>;
|
|
18
|
+
//# sourceMappingURL=publish-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-app.d.ts","sourceRoot":"","sources":["../../../src/commands/publish-app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qCAAqC,CAAC;AAapE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACzC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAWD,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,KAcnD,CAAC;AAaF,eAAO,MAAM,UAAU,GAAU,SAAS,iBAAiB,iDAsF1D,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { loadReleaseManifest, planReleasePublish, publishRelease, } from '../app-release/publisher.js';
|
|
11
|
+
const secretEnvironmentKeys = [
|
|
12
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
13
|
+
'STATIC_AWS_SECRET_ACCESS_KEY',
|
|
14
|
+
'UPLOADS_AWS_SECRET_ACCESS_KEY',
|
|
15
|
+
'AWS_ACCESS_KEY_ID',
|
|
16
|
+
'STATIC_AWS_ACCESS_KEY_ID',
|
|
17
|
+
'UPLOADS_AWS_ACCESS_KEY_ID',
|
|
18
|
+
];
|
|
19
|
+
export const redactPublishError = (error) => {
|
|
20
|
+
const source = error instanceof Error ? error : new Error(String(error));
|
|
21
|
+
let message = source.message;
|
|
22
|
+
let stack = source.stack;
|
|
23
|
+
for (const key of secretEnvironmentKeys) {
|
|
24
|
+
const value = process.env[key];
|
|
25
|
+
if (value) {
|
|
26
|
+
message = message.split(value).join('[REDACTED]');
|
|
27
|
+
stack = stack === null || stack === void 0 ? void 0 : stack.split(value).join('[REDACTED]');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
source.message = message;
|
|
31
|
+
if (stack)
|
|
32
|
+
source.stack = stack;
|
|
33
|
+
return source;
|
|
34
|
+
};
|
|
35
|
+
const resolveStore = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
+
if (!options.resolveStore) {
|
|
37
|
+
throw new Error('publishApp needs either a store or a resolveStore function to reach one');
|
|
38
|
+
}
|
|
39
|
+
return options.resolveStore();
|
|
40
|
+
});
|
|
41
|
+
export const publishApp = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
const appRoot = options.appRoot || process.cwd();
|
|
43
|
+
let progressSpinner;
|
|
44
|
+
try {
|
|
45
|
+
// This command reuses a completed build. It validates the manifest and
|
|
46
|
+
// destination before dry-run output or any confirmed upload.
|
|
47
|
+
console.log('🔄 Validating release manifest and destination...');
|
|
48
|
+
// Read the manifest before touching storage config, so a manifest that can
|
|
49
|
+
// never be published says so before a store is resolved.
|
|
50
|
+
loadReleaseManifest(appRoot, options.manifestPath);
|
|
51
|
+
const store = options.store || (yield resolveStore(options));
|
|
52
|
+
const plan = planReleasePublish({
|
|
53
|
+
appRoot,
|
|
54
|
+
manifestPath: options.manifestPath,
|
|
55
|
+
store,
|
|
56
|
+
});
|
|
57
|
+
console.log(`Release: ${plan.releaseId}`);
|
|
58
|
+
console.log(`Destination: ${plan.destination.accessURL || '(no accessURL)'} under ${plan.destination.releasePrefix}/`);
|
|
59
|
+
console.log(`Artifacts: ${plan.files.length} (${plan.totalBytes} bytes)`);
|
|
60
|
+
if (!options.yes) {
|
|
61
|
+
console.log('Dry run only; pass --yes to upload this release.');
|
|
62
|
+
}
|
|
63
|
+
console.log('✅ Release plan validated');
|
|
64
|
+
if (options.yes) {
|
|
65
|
+
console.log('🔄 Uploading and verifying release artifacts...');
|
|
66
|
+
const total = plan.files.length + 1;
|
|
67
|
+
if (process.stdout.isTTY) {
|
|
68
|
+
// ora is ESM-only, so load it only for an interactive terminal. CI and
|
|
69
|
+
// PM2 use the periodic plain-text progress branch below.
|
|
70
|
+
const { default: ora } = yield import('ora');
|
|
71
|
+
progressSpinner = ora({
|
|
72
|
+
discardStdin: true,
|
|
73
|
+
text: `Publishing 0/${total} files`,
|
|
74
|
+
}).start();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let lastLoggedProgress = 0;
|
|
78
|
+
const result = yield publishRelease({
|
|
79
|
+
appRoot,
|
|
80
|
+
manifestPath: options.manifestPath,
|
|
81
|
+
store,
|
|
82
|
+
yes: options.yes,
|
|
83
|
+
onWarning: (message) => {
|
|
84
|
+
// Printed once per run, not once per file.
|
|
85
|
+
if (progressSpinner) {
|
|
86
|
+
progressSpinner.text = message;
|
|
87
|
+
}
|
|
88
|
+
console.warn(`⚠️ ${message}`);
|
|
89
|
+
},
|
|
90
|
+
onProgress: ({ completed, total }) => {
|
|
91
|
+
const message = `Publishing ${completed}/${total} files`;
|
|
92
|
+
if (progressSpinner) {
|
|
93
|
+
progressSpinner.text = message;
|
|
94
|
+
}
|
|
95
|
+
else if (completed === total || completed - lastLoggedProgress >= 25) {
|
|
96
|
+
console.log(message);
|
|
97
|
+
lastLoggedProgress = completed;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
if (result.dryRun) {
|
|
102
|
+
console.log('✅ Dry run complete; no files uploaded');
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const completedMessage = `Release ${result.releaseId} uploaded: ${result.uploadedFiles} files, ${result.uploadedBytes} bytes`;
|
|
106
|
+
progressSpinner === null || progressSpinner === void 0 ? void 0 : progressSpinner.succeed(completedMessage);
|
|
107
|
+
if (!progressSpinner)
|
|
108
|
+
console.log(`✅ ${completedMessage}`);
|
|
109
|
+
progressSpinner = undefined;
|
|
110
|
+
if (result.unverified.length) {
|
|
111
|
+
console.warn(`⚠️ ${result.unverified.length} of ${result.uploadedFiles} objects could not be verified against a store hash.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const reported = redactPublishError(error);
|
|
118
|
+
// The spinner line is the last thing on screen, so it carries the real
|
|
119
|
+
// reason rather than a bare "Release upload failed".
|
|
120
|
+
progressSpinner === null || progressSpinner === void 0 ? void 0 : progressSpinner.fail(reported.message);
|
|
121
|
+
throw reported;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=publish-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-app.js","sourceRoot":"","sources":["../../../src/commands/publish-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ServeAppOptions {
|
|
2
|
+
appRoot?: string;
|
|
3
|
+
allowDevelopment?: boolean;
|
|
4
|
+
/** Profiles from `--env a,b`, passed on to the environment loader. */
|
|
5
|
+
environmentNames?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface ServeAppDependencies {
|
|
8
|
+
loadEnvironment?: () => Promise<void>;
|
|
9
|
+
startCompiledServer?: () => Promise<unknown>;
|
|
10
|
+
}
|
|
11
|
+
export declare const validateCompiledAppArtifacts: (appRoot: string) => void;
|
|
12
|
+
/** Start a compiled Linked backend without creating a Vite server or watcher. */
|
|
13
|
+
export declare const serveCompiledApp: (options?: ServeAppOptions, dependencies?: ServeAppDependencies) => Promise<unknown>;
|
|
14
|
+
//# sourceMappingURL=serve-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-app.d.ts","sourceRoot":"","sources":["../../../src/commands/serve-app.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C;AASD,eAAO,MAAM,4BAA4B,GAAI,SAAS,MAAM,KAAG,IAoB9D,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,GAC3B,UAAS,eAAoB,EAC7B,eAAc,oBAAyB,KACtC,OAAO,CAAC,OAAO,CAsBjB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { ensureEnvironmentLoaded } from '../lifecycle.js';
|
|
13
|
+
const requiredBuildArtifacts = [
|
|
14
|
+
'lib/App.js',
|
|
15
|
+
'lib/routes.js',
|
|
16
|
+
'lib/backend.js',
|
|
17
|
+
'public/bundles/.vite/manifest.json',
|
|
18
|
+
];
|
|
19
|
+
export const validateCompiledAppArtifacts = (appRoot) => {
|
|
20
|
+
for (const relativePath of requiredBuildArtifacts) {
|
|
21
|
+
const absolutePath = path.join(appRoot, relativePath);
|
|
22
|
+
if (!fs.existsSync(absolutePath)) {
|
|
23
|
+
throw new Error(`Compiled app artifact is missing: ${relativePath}. Run linked build-app before linked serve-app.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const manifestPath = path.join(appRoot, 'public/bundles/.vite/manifest.json');
|
|
27
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
28
|
+
if (!manifest || typeof manifest !== 'object' || Object.keys(manifest).length === 0) {
|
|
29
|
+
throw new Error('Compiled app artifact is invalid: public/bundles/.vite/manifest.json is empty');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/** Start a compiled Linked backend without creating a Vite server or watcher. */
|
|
33
|
+
export const serveCompiledApp = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}, dependencies = {}) {
|
|
34
|
+
const appRoot = options.appRoot || process.cwd();
|
|
35
|
+
const loadEnvironment = dependencies.loadEnvironment ||
|
|
36
|
+
(() => ensureEnvironmentLoaded(options.environmentNames));
|
|
37
|
+
const startCompiledServer = dependencies.startCompiledServer ||
|
|
38
|
+
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
const { startServer } = yield import('../cli-methods.js');
|
|
40
|
+
return startServer(false);
|
|
41
|
+
}));
|
|
42
|
+
yield loadEnvironment();
|
|
43
|
+
if (process.env.NODE_ENV === 'development' && !options.allowDevelopment) {
|
|
44
|
+
throw new Error('linked serve-app only runs compiled non-development builds. Use linked start for development.');
|
|
45
|
+
}
|
|
46
|
+
validateCompiledAppArtifacts(appRoot);
|
|
47
|
+
return startCompiledServer();
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=serve-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-app.js","sourceRoot":"","sources":["../../../src/commands/serve-app.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAC,uBAAuB,EAAC,MAAM,iBAAiB,CAAC;AAcxD,MAAM,sBAAsB,GAAG;IAC7B,YAAY;IACZ,eAAe;IACf,gBAAgB;IAChB,oCAAoC;CACrC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,OAAe,EAAQ,EAAE;IACpE,KAAK,MAAM,YAAY,IAAI,sBAAsB,EAAE,CAAC;QAClD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,qCAAqC,YAAY,iDAAiD,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,OAAO,EACP,oCAAoC,CACrC,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IACnE,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpF,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAGZ,EAAE,mDAFpB,UAA2B,EAAE,EAC7B,eAAqC,EAAE;IAEvC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACjD,MAAM,eAAe,GACnB,YAAY,CAAC,eAAe;QAC5B,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC5D,MAAM,mBAAmB,GACvB,YAAY,CAAC,mBAAmB;QAChC,CAAC,GAAS,EAAE;YACV,MAAM,EAAC,WAAW,EAAC,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACxD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAA,CAAC,CAAC;IAEL,MAAM,eAAe,EAAE,CAAC;IAExB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,+FAA+F,CAChG,CAAC;IACJ,CAAC;IAED,4BAA4B,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,mBAAmB,EAAE,CAAC;AAC/B,CAAC,CAAA,CAAC"}
|
|
@@ -1,14 +1,47 @@
|
|
|
1
|
+
export declare const WORKFLOW_FILES: readonly ["pr.yml", "publish.yml"];
|
|
2
|
+
/**
|
|
3
|
+
* The uniform branch-protection profile every `@_linked` repo runs on `main`.
|
|
4
|
+
*
|
|
5
|
+
* The required check is `checks / Build & Test`, not `Build & Test`: GitHub reports a job invoked
|
|
6
|
+
* through `workflow_call` as `<caller-job-id> / <job name>`, and the caller job in pr.yml is
|
|
7
|
+
* `checks`. A repo pinning the bare name waits forever on a check that never reports.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildBranchProtectionPayload(): {
|
|
10
|
+
required_status_checks: {
|
|
11
|
+
strict: boolean;
|
|
12
|
+
contexts: string[];
|
|
13
|
+
};
|
|
14
|
+
enforce_admins: boolean;
|
|
15
|
+
required_pull_request_reviews: {
|
|
16
|
+
required_approving_review_count: number;
|
|
17
|
+
dismiss_stale_reviews: boolean;
|
|
18
|
+
require_code_owner_reviews: boolean;
|
|
19
|
+
};
|
|
20
|
+
restrictions: any;
|
|
21
|
+
allow_force_pushes: boolean;
|
|
22
|
+
allow_deletions: boolean;
|
|
23
|
+
};
|
|
1
24
|
export type SetupPublishOptions = {
|
|
2
25
|
configureGithub?: boolean;
|
|
3
26
|
scope?: 'core' | 'community';
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated The dual-branch (`main` + `dev`) flow and its `@next` prereleases are retired —
|
|
29
|
+
* every package repo is `main`-only. Kept as an accepted no-op rather than removed because
|
|
30
|
+
* commander aborts the whole command on an unknown option, so a stale script or copied
|
|
31
|
+
* command line would fail to set the repo up at all instead of setting it up correctly.
|
|
32
|
+
*/
|
|
4
33
|
dualBranch?: boolean;
|
|
5
34
|
grantTeam?: string;
|
|
6
35
|
};
|
|
7
36
|
/**
|
|
8
|
-
* Set up
|
|
37
|
+
* Set up the changesets publish pipeline in the current package repo.
|
|
38
|
+
*
|
|
39
|
+
* The workflows are thin callers of the shared reusable workflows in `linked-fw/.github`
|
|
40
|
+
* (pinned `@v1`, a deliberately moving tag) — one edit there changes every package's pipeline,
|
|
41
|
+
* which is why nothing about the build or publish steps is scaffolded per repo any more.
|
|
9
42
|
*
|
|
10
43
|
* Installs:
|
|
11
|
-
* - .github/workflows/{
|
|
44
|
+
* - .github/workflows/{pr,publish}.yml — caller stubs, nothing else
|
|
12
45
|
* - .changeset/config.json + README.md
|
|
13
46
|
* - .changeset/initial-release.md
|
|
14
47
|
* - .gitignore entries for node_modules, lib, yarn.lock, src-compiled-artifacts
|
|
@@ -16,8 +49,10 @@ export type SetupPublishOptions = {
|
|
|
16
49
|
* publishConfig: {access: public}
|
|
17
50
|
* - package-lock.json generated via npm install --package-lock-only
|
|
18
51
|
*
|
|
19
|
-
* With --configure-github: if `gh` CLI is available and authenticated, also
|
|
20
|
-
* branch
|
|
52
|
+
* With --configure-github: if `gh` CLI is available and authenticated, also applies the uniform
|
|
53
|
+
* branch-protection profile on main and enables auto-merge.
|
|
21
54
|
*/
|
|
22
55
|
export declare function setupPublish(opts?: SetupPublishOptions): Promise<void>;
|
|
56
|
+
/** Mutates `pkgJson` in place; returns whether anything changed. Pure enough to test. */
|
|
57
|
+
export declare function applyPackageJsonPatches(pkgJson: any, repoSlug: string): boolean;
|
|
23
58
|
//# sourceMappingURL=setup-publish.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-publish.d.ts","sourceRoot":"","sources":["../../../src/commands/setup-publish.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup-publish.d.ts","sourceRoot":"","sources":["../../../src/commands/setup-publish.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,cAAc,oCAAqC,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAgB,4BAA4B;;;;;;;;;;;;;;EAkB3C;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DhF;AAwHD,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAkD/E"}
|
|
@@ -19,11 +19,45 @@ const dirname__ = typeof __dirname !== 'undefined'
|
|
|
19
19
|
// This file compiles to lib/{esm,cjs}/commands/setup-publish.js — go up three
|
|
20
20
|
// levels to reach the package root, then into defaults/setup-publish.
|
|
21
21
|
const TEMPLATE_ROOT = path.resolve(dirname__, '..', '..', '..', 'defaults', 'setup-publish');
|
|
22
|
+
// Both are callers of the shared reusable workflows. `publish.yml` in particular cannot be
|
|
23
|
+
// renamed: npm's trusted-publisher check validates the calling workflow's filename, and a
|
|
24
|
+
// mismatch only shows up as releases quietly needing manual approval again.
|
|
25
|
+
export const WORKFLOW_FILES = ['pr.yml', 'publish.yml'];
|
|
22
26
|
/**
|
|
23
|
-
*
|
|
27
|
+
* The uniform branch-protection profile every `@_linked` repo runs on `main`.
|
|
28
|
+
*
|
|
29
|
+
* The required check is `checks / Build & Test`, not `Build & Test`: GitHub reports a job invoked
|
|
30
|
+
* through `workflow_call` as `<caller-job-id> / <job name>`, and the caller job in pr.yml is
|
|
31
|
+
* `checks`. A repo pinning the bare name waits forever on a check that never reports.
|
|
32
|
+
*/
|
|
33
|
+
export function buildBranchProtectionPayload() {
|
|
34
|
+
return {
|
|
35
|
+
required_status_checks: { strict: false, contexts: ['checks / Build & Test'] },
|
|
36
|
+
// Admins included: the point of the gate is that no release skips the build, and the people
|
|
37
|
+
// most likely to push straight to main are the admins.
|
|
38
|
+
enforce_admins: true,
|
|
39
|
+
// Reviews enabled but zero approvals required. The publish workflow direct-merges the version
|
|
40
|
+
// PR (GitHub's auto-merge queue does not honor review-bypass allowances), so a non-zero count
|
|
41
|
+
// would stall every release; the block itself still has to exist for the profile to match.
|
|
42
|
+
required_pull_request_reviews: {
|
|
43
|
+
required_approving_review_count: 0,
|
|
44
|
+
dismiss_stale_reviews: false,
|
|
45
|
+
require_code_owner_reviews: false,
|
|
46
|
+
},
|
|
47
|
+
restrictions: null,
|
|
48
|
+
allow_force_pushes: false,
|
|
49
|
+
allow_deletions: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set up the changesets publish pipeline in the current package repo.
|
|
54
|
+
*
|
|
55
|
+
* The workflows are thin callers of the shared reusable workflows in `linked-fw/.github`
|
|
56
|
+
* (pinned `@v1`, a deliberately moving tag) — one edit there changes every package's pipeline,
|
|
57
|
+
* which is why nothing about the build or publish steps is scaffolded per repo any more.
|
|
24
58
|
*
|
|
25
59
|
* Installs:
|
|
26
|
-
* - .github/workflows/{
|
|
60
|
+
* - .github/workflows/{pr,publish}.yml — caller stubs, nothing else
|
|
27
61
|
* - .changeset/config.json + README.md
|
|
28
62
|
* - .changeset/initial-release.md
|
|
29
63
|
* - .gitignore entries for node_modules, lib, yarn.lock, src-compiled-artifacts
|
|
@@ -31,18 +65,18 @@ const TEMPLATE_ROOT = path.resolve(dirname__, '..', '..', '..', 'defaults', 'set
|
|
|
31
65
|
* publishConfig: {access: public}
|
|
32
66
|
* - package-lock.json generated via npm install --package-lock-only
|
|
33
67
|
*
|
|
34
|
-
* With --configure-github: if `gh` CLI is available and authenticated, also
|
|
35
|
-
* branch
|
|
68
|
+
* With --configure-github: if `gh` CLI is available and authenticated, also applies the uniform
|
|
69
|
+
* branch-protection profile on main and enables auto-merge.
|
|
36
70
|
*/
|
|
37
71
|
export function setupPublish() {
|
|
38
72
|
return __awaiter(this, arguments, void 0, function* (opts = {}) {
|
|
39
73
|
const cwd = process.cwd();
|
|
40
74
|
const scope = opts.scope || 'core';
|
|
41
75
|
const npmSecretName = scope === 'community' ? 'NPM_AUTH_TOKEN_CM' : 'NPM_AUTH_TOKEN';
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
|
|
76
|
+
console.log(chalk.magenta('Setting up the publish pipeline (main only)...'));
|
|
77
|
+
if (opts.dualBranch) {
|
|
78
|
+
console.log(chalk.yellow(' ⚠ --dual-branch is deprecated and ignored: the main + dev flow and its @next prereleases are retired.'));
|
|
79
|
+
}
|
|
46
80
|
console.log(` target: ${cwd}`);
|
|
47
81
|
console.log(` npm secret: ${npmSecretName} (${scope})`);
|
|
48
82
|
const pkgJsonPath = path.join(cwd, 'package.json');
|
|
@@ -55,9 +89,9 @@ export function setupPublish() {
|
|
|
55
89
|
console.log(` repo: ${repoSlug}`);
|
|
56
90
|
console.log('');
|
|
57
91
|
// 1. Workflow files (with {{NPM_SECRET_NAME}} substitution in publish.yml)
|
|
58
|
-
yield copyWorkflows(cwd, npmSecretName
|
|
92
|
+
yield copyWorkflows(cwd, npmSecretName);
|
|
59
93
|
// 2. Changesets config + README
|
|
60
|
-
yield copyChangesetConfig(cwd, repoSlug
|
|
94
|
+
yield copyChangesetConfig(cwd, repoSlug);
|
|
61
95
|
// 3. Initial changeset
|
|
62
96
|
yield writeInitialChangeset(cwd, pkgJson.name);
|
|
63
97
|
// 4. .gitignore
|
|
@@ -106,22 +140,33 @@ function resolveRepoSlug(cwd, pkgJson) {
|
|
|
106
140
|
return 'OWNER/REPO';
|
|
107
141
|
});
|
|
108
142
|
}
|
|
109
|
-
function copyWorkflows(cwd, npmSecretName
|
|
143
|
+
function copyWorkflows(cwd, npmSecretName) {
|
|
110
144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
-
const srcDir = path.join(
|
|
145
|
+
const srcDir = path.join(TEMPLATE_ROOT, 'github', 'workflows');
|
|
112
146
|
const dstDir = path.join(cwd, '.github', 'workflows');
|
|
113
147
|
fs.mkdirpSync(dstDir);
|
|
114
|
-
for (const file of
|
|
148
|
+
for (const file of WORKFLOW_FILES) {
|
|
115
149
|
let content = fs.readFileSync(path.join(srcDir, file), 'utf8');
|
|
116
150
|
content = content.replace(/\{\{NPM_SECRET_NAME\}\}/g, npmSecretName);
|
|
117
151
|
fs.writeFileSync(path.join(dstDir, file), content);
|
|
118
152
|
console.log(chalk.green(' ✓') + ` .github/workflows/${file}`);
|
|
119
153
|
}
|
|
154
|
+
// ci.yml and changeset-check.yml became the two jobs of the shared pr.yml. Left behind on a
|
|
155
|
+
// repo that was set up before the consolidation they keep running, so the old bare
|
|
156
|
+
// `Build & Test` check reports alongside `checks / Build & Test` and re-setting a repo up
|
|
157
|
+
// looks like it worked while the duplicate gate quietly stays.
|
|
158
|
+
for (const stale of ['ci.yml', 'changeset-check.yml']) {
|
|
159
|
+
const stalePath = path.join(dstDir, stale);
|
|
160
|
+
if (fs.existsSync(stalePath)) {
|
|
161
|
+
fs.removeSync(stalePath);
|
|
162
|
+
console.log(chalk.green(' ✓') + ` removed superseded .github/workflows/${stale}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
120
165
|
});
|
|
121
166
|
}
|
|
122
|
-
function copyChangesetConfig(cwd, repoSlug
|
|
167
|
+
function copyChangesetConfig(cwd, repoSlug) {
|
|
123
168
|
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
-
const srcDir = path.join(
|
|
169
|
+
const srcDir = path.join(TEMPLATE_ROOT, 'changeset');
|
|
125
170
|
const dstDir = path.join(cwd, '.changeset');
|
|
126
171
|
fs.mkdirpSync(dstDir);
|
|
127
172
|
let configContent = fs.readFileSync(path.join(srcDir, 'config.json'), 'utf8');
|
|
@@ -180,55 +225,7 @@ function updateGitignore(cwd) {
|
|
|
180
225
|
}
|
|
181
226
|
function patchPackageJson(pkgJsonPath, pkgJson, repoSlug) {
|
|
182
227
|
return __awaiter(this, void 0, void 0, function* () {
|
|
183
|
-
|
|
184
|
-
let modified = false;
|
|
185
|
-
// `repository.url` must be set, and must match the repo the workflow builds in.
|
|
186
|
-
//
|
|
187
|
-
// `create-package` scaffolds it EMPTY, and npm rejects a provenance-signed publish whose
|
|
188
|
-
// attestation cannot be matched against it:
|
|
189
|
-
//
|
|
190
|
-
// 422 Error verifying sigstore provenance bundle: Failed to validate repository
|
|
191
|
-
// information: package.json "repository.url" is "", expected to match <repo>
|
|
192
|
-
//
|
|
193
|
-
// The slug is already resolved from `git remote` above, so there is no reason to leave a
|
|
194
|
-
// package to discover this at its first release.
|
|
195
|
-
if (repoSlug !== 'OWNER/REPO') {
|
|
196
|
-
const url = `git+https://github.com/${repoSlug}.git`;
|
|
197
|
-
const current = typeof pkgJson.repository === 'string'
|
|
198
|
-
? pkgJson.repository
|
|
199
|
-
: (_a = pkgJson.repository) === null || _a === void 0 ? void 0 : _a.url;
|
|
200
|
-
if (!current) {
|
|
201
|
-
pkgJson.repository = { type: 'git', url };
|
|
202
|
-
modified = true;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (!pkgJson.publishConfig) {
|
|
206
|
-
pkgJson.publishConfig = { access: 'public' };
|
|
207
|
-
modified = true;
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
if (pkgJson.publishConfig.access !== 'public') {
|
|
211
|
-
pkgJson.publishConfig.access = 'public';
|
|
212
|
-
modified = true;
|
|
213
|
-
}
|
|
214
|
-
// Strip provenance: true — it requires OIDC (id-token: write permission +
|
|
215
|
-
// npm Trusted Publisher config), which conflicts with our NPM_AUTH_TOKEN
|
|
216
|
-
// flow. Users who want provenance should use OIDC separately.
|
|
217
|
-
if (pkgJson.publishConfig.provenance) {
|
|
218
|
-
delete pkgJson.publishConfig.provenance;
|
|
219
|
-
modified = true;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
pkgJson.devDependencies = pkgJson.devDependencies || {};
|
|
223
|
-
if (!pkgJson.devDependencies['@changesets/cli']) {
|
|
224
|
-
pkgJson.devDependencies['@changesets/cli'] = '^2.29.8';
|
|
225
|
-
modified = true;
|
|
226
|
-
}
|
|
227
|
-
if (!pkgJson.devDependencies['@changesets/changelog-github']) {
|
|
228
|
-
pkgJson.devDependencies['@changesets/changelog-github'] = '^0.5.2';
|
|
229
|
-
modified = true;
|
|
230
|
-
}
|
|
231
|
-
if (modified) {
|
|
228
|
+
if (applyPackageJsonPatches(pkgJson, repoSlug)) {
|
|
232
229
|
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n');
|
|
233
230
|
console.log(chalk.green(' ✓') + ' package.json (publishConfig + changesets devDeps)');
|
|
234
231
|
}
|
|
@@ -237,6 +234,55 @@ function patchPackageJson(pkgJsonPath, pkgJson, repoSlug) {
|
|
|
237
234
|
}
|
|
238
235
|
});
|
|
239
236
|
}
|
|
237
|
+
/** Mutates `pkgJson` in place; returns whether anything changed. Pure enough to test. */
|
|
238
|
+
export function applyPackageJsonPatches(pkgJson, repoSlug) {
|
|
239
|
+
var _a;
|
|
240
|
+
let modified = false;
|
|
241
|
+
// `repository.url` must be set, and must match the repo the workflow builds in.
|
|
242
|
+
//
|
|
243
|
+
// `create-package` scaffolds it EMPTY, and npm rejects a provenance-signed publish whose
|
|
244
|
+
// attestation cannot be matched against it:
|
|
245
|
+
//
|
|
246
|
+
// 422 Error verifying sigstore provenance bundle: Failed to validate repository
|
|
247
|
+
// information: package.json "repository.url" is "", expected to match <repo>
|
|
248
|
+
//
|
|
249
|
+
// The slug is already resolved from `git remote` above, so there is no reason to leave a
|
|
250
|
+
// package to discover this at its first release.
|
|
251
|
+
if (repoSlug !== 'OWNER/REPO') {
|
|
252
|
+
const url = `git+https://github.com/${repoSlug}.git`;
|
|
253
|
+
const current = typeof pkgJson.repository === 'string'
|
|
254
|
+
? pkgJson.repository
|
|
255
|
+
: (_a = pkgJson.repository) === null || _a === void 0 ? void 0 : _a.url;
|
|
256
|
+
if (!current) {
|
|
257
|
+
pkgJson.repository = { type: 'git', url };
|
|
258
|
+
modified = true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!pkgJson.publishConfig) {
|
|
262
|
+
pkgJson.publishConfig = { access: 'public' };
|
|
263
|
+
modified = true;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
if (pkgJson.publishConfig.access !== 'public') {
|
|
267
|
+
pkgJson.publishConfig.access = 'public';
|
|
268
|
+
modified = true;
|
|
269
|
+
}
|
|
270
|
+
// `provenance` is deliberately left alone. It used to be stripped because publishing went
|
|
271
|
+
// through NPM_AUTH_TOKEN only; the shared workflow now publishes with OIDC trusted publishing
|
|
272
|
+
// first (`npm publish --provenance`, id-token: write on both caller and reusable workflow) and
|
|
273
|
+
// falls back to the staged token release, so provenance is wanted, not a conflict.
|
|
274
|
+
}
|
|
275
|
+
pkgJson.devDependencies = pkgJson.devDependencies || {};
|
|
276
|
+
if (!pkgJson.devDependencies['@changesets/cli']) {
|
|
277
|
+
pkgJson.devDependencies['@changesets/cli'] = '^2.29.8';
|
|
278
|
+
modified = true;
|
|
279
|
+
}
|
|
280
|
+
if (!pkgJson.devDependencies['@changesets/changelog-github']) {
|
|
281
|
+
pkgJson.devDependencies['@changesets/changelog-github'] = '^0.5.2';
|
|
282
|
+
modified = true;
|
|
283
|
+
}
|
|
284
|
+
return modified;
|
|
285
|
+
}
|
|
240
286
|
function ensureLockfile(cwd) {
|
|
241
287
|
return __awaiter(this, void 0, void 0, function* () {
|
|
242
288
|
// Always regenerate — package.json has just been patched (new devDeps etc.),
|
|
@@ -294,27 +340,23 @@ function configureGithub(repoSlug) {
|
|
|
294
340
|
console.warn(chalk.yellow(' ⚠ `gh` CLI is not authenticated. Run `gh auth login` and retry.'));
|
|
295
341
|
return;
|
|
296
342
|
}
|
|
297
|
-
const payload = JSON.stringify(
|
|
298
|
-
required_status_checks: { strict: true, contexts: ['Build & Test'] },
|
|
299
|
-
enforce_admins: false,
|
|
300
|
-
required_pull_request_reviews: null,
|
|
301
|
-
restrictions: null,
|
|
302
|
-
allow_force_pushes: false,
|
|
303
|
-
allow_deletions: false,
|
|
304
|
-
});
|
|
343
|
+
const payload = JSON.stringify(buildBranchProtectionPayload());
|
|
305
344
|
try {
|
|
306
345
|
// Use --input - to pass the JSON payload via stdin
|
|
307
346
|
yield execPromise(`echo '${payload.replace(/'/g, "'\\''")}' | gh api -X PUT /repos/${repoSlug}/branches/main/protection --input -`, false, false);
|
|
308
347
|
console.log(chalk.green(' ✓') + ` branch protection enabled on ${repoSlug}/main`);
|
|
348
|
+
// A repo can pin a required check in a *ruleset* as well as in classic branch protection, and
|
|
349
|
+
// the two are stored separately — this call only writes the classic one, so a leftover ruleset
|
|
350
|
+
// keeps blocking merges on a check name that no longer reports.
|
|
351
|
+
console.log(chalk.gray(` · check for a competing ruleset: gh api /repos/${repoSlug}/rulesets`));
|
|
309
352
|
}
|
|
310
353
|
catch (err) {
|
|
311
354
|
console.warn(chalk.yellow(' ⚠ Failed to set branch protection. Do it manually:'));
|
|
312
355
|
console.warn(chalk.yellow(` https://github.com/${repoSlug}/settings/branches`));
|
|
313
356
|
}
|
|
314
|
-
// Enable "Allow auto-merge" so
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
// runs without a manual approval gate.
|
|
357
|
+
// Enable "Allow auto-merge" so a PR can be queued to merge itself once checks pass. The publish
|
|
358
|
+
// workflow authors the Release PR via the org App token (org secrets RELEASE_APP_ID /
|
|
359
|
+
// RELEASE_APP_PRIVATE_KEY) so its CI runs without a manual approval gate.
|
|
318
360
|
try {
|
|
319
361
|
yield execPromise(`gh api -X PATCH /repos/${repoSlug} -F allow_auto_merge=true`, false, false);
|
|
320
362
|
console.log(chalk.green(' ✓') + ` auto-merge enabled on ${repoSlug}`);
|
|
@@ -356,24 +398,41 @@ function printNextSteps(repoSlug, npmSecretName, configuredGithub, grantedTeam)
|
|
|
356
398
|
console.log('');
|
|
357
399
|
console.log(chalk.bold('Next steps:'));
|
|
358
400
|
console.log('');
|
|
401
|
+
const [owner, repo] = repoSlug.split('/');
|
|
359
402
|
console.log(` 1. Review the generated files, adjust as needed.`);
|
|
360
403
|
console.log(` 2. Commit:`);
|
|
361
|
-
console.log(chalk.cyan(' git add . && git commit -m "set up
|
|
404
|
+
console.log(chalk.cyan(' git add . && git commit -m "set up the publish pipeline"'));
|
|
362
405
|
console.log(` 3. Push to main — the publish workflow will trigger.`);
|
|
363
406
|
console.log('');
|
|
364
|
-
console.log(chalk.bold('
|
|
407
|
+
console.log(chalk.bold('Register the trusted publisher on npmjs.com (before the first release):'));
|
|
408
|
+
console.log('');
|
|
409
|
+
console.log(` package → Settings → Trusted Publisher, matching the caller workflow exactly:`);
|
|
410
|
+
console.log(` organization: ${chalk.cyan(owner)} repository: ${chalk.cyan(repo)}`);
|
|
411
|
+
console.log(` workflow filename: ${chalk.cyan('publish.yml')} environment: ${chalk.cyan('(empty)')}`);
|
|
412
|
+
console.log(` "Allow npm publish": ${chalk.cyan('ticked')}`);
|
|
413
|
+
console.log('');
|
|
414
|
+
console.log(chalk.gray(` Without it the release still ships — the workflow falls back to a staged`));
|
|
415
|
+
console.log(chalk.gray(` npm release that a maintainer approves with 2FA. A typo in the organization`));
|
|
416
|
+
console.log(chalk.gray(` field looks exactly the same, so check that field first if a package that`));
|
|
417
|
+
console.log(chalk.gray(` should publish directly keeps staging instead.`));
|
|
418
|
+
console.log('');
|
|
419
|
+
// No per-repo secret grant step: NPM_AUTH_TOKEN, RELEASE_APP_ID and RELEASE_APP_PRIVATE_KEY are
|
|
420
|
+
// "All repositories" org secrets, which is the whole reason first-party and community packages
|
|
421
|
+
// live in separate orgs. Only mention what is genuinely still one-time-per-org.
|
|
422
|
+
console.log(chalk.bold('One-time org-level setup (already done on linked-fw):'));
|
|
365
423
|
console.log('');
|
|
366
|
-
console.log(` a.
|
|
367
|
-
console.log(` (
|
|
368
|
-
console.log(` → https://github.com/organizations/${
|
|
424
|
+
console.log(` a. Org secrets ${chalk.cyan(npmSecretName)}, ${chalk.cyan('RELEASE_APP_ID')} and ${chalk.cyan('RELEASE_APP_PRIVATE_KEY')},`);
|
|
425
|
+
console.log(` visible to ${chalk.cyan('All repositories')} — there is no per-repo grant step.`);
|
|
426
|
+
console.log(` → https://github.com/organizations/${owner}/settings/secrets/actions`);
|
|
369
427
|
console.log('');
|
|
370
428
|
console.log(` b. Enable "Allow GitHub Actions to create and approve pull requests":`);
|
|
371
|
-
console.log(` → https://github.com/organizations/${
|
|
429
|
+
console.log(` → https://github.com/organizations/${owner}/settings/actions`);
|
|
372
430
|
if (!configuredGithub) {
|
|
373
431
|
console.log('');
|
|
374
432
|
console.log(chalk.bold('Branch protection:'));
|
|
375
433
|
console.log(` Rerun with ${chalk.cyan('linked setup-publish --configure-github')} to configure automatically,`);
|
|
376
|
-
console.log(` or set manually at https://github.com/${repoSlug}/settings/branches
|
|
434
|
+
console.log(` or set manually at https://github.com/${repoSlug}/settings/branches —`);
|
|
435
|
+
console.log(` the required check is ${chalk.cyan('checks / Build & Test')}, not ${chalk.cyan('Build & Test')}.`);
|
|
377
436
|
}
|
|
378
437
|
console.log('');
|
|
379
438
|
}
|