@i-santos/create-package-starter 1.5.0-beta.12 → 1.5.0-beta.13
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/lib/run.js +173 -68
- package/package.json +1 -1
package/lib/run.js
CHANGED
|
@@ -45,7 +45,7 @@ function usage() {
|
|
|
45
45
|
' create-package-starter setup-github [--repo <owner/repo>] [--default-branch <branch>] [--ruleset <path>] [--dry-run]',
|
|
46
46
|
' create-package-starter setup-beta [--dir <directory>] [--repo <owner/repo>] [--beta-branch <branch>] [--default-branch <branch>] [--release-auth github-token|pat|app|manual-trigger] [--force] [--dry-run] [--yes]',
|
|
47
47
|
' create-package-starter open-pr [--repo <owner/repo>] [--base <branch>] [--head <branch>] [--title <text>] [--body <text>] [--body-file <path>] [--template <path>] [--draft] [--auto-merge] [--watch-checks] [--check-timeout <minutes>] [--yes] [--dry-run]',
|
|
48
|
-
' create-package-starter release-cycle [--repo <owner/repo>] [--mode auto|open-pr|publish] [--phase code|full] [--track auto|beta|stable] [--promote-stable] [--promote-type patch|minor|major] [--promote-summary <text>] [--head <branch>] [--base <branch>] [--title <text>] [--body-file <path>] [--update-pr-description] [--draft] [--auto-merge] [--watch-checks] [--check-timeout <minutes>] [--confirm-merges] [--merge-when-green] [--merge-method squash|merge|rebase] [--wait-release-pr] [--release-pr-timeout <minutes>] [--merge-release-pr] [--verify-npm] [--confirm-cleanup] [--sync-base auto|rebase|merge|off] [--no-resume] [--no-cleanup] [--yes] [--dry-run]',
|
|
48
|
+
' create-package-starter release-cycle [--repo <owner/repo>] [--mode auto|open-pr|publish] [--phase code|full] [--track auto|beta|stable] [--promote-stable] [--promote-type patch|minor|major] [--promote-summary <text>] [--head <branch>] [--base <branch>] [--title <text>] [--body-file <path>] [--npm-package <name>] [--update-pr-description] [--draft] [--auto-merge] [--watch-checks] [--check-timeout <minutes>] [--confirm-merges] [--merge-when-green] [--merge-method squash|merge|rebase] [--wait-release-pr] [--release-pr-timeout <minutes>] [--merge-release-pr] [--verify-npm] [--confirm-cleanup] [--sync-base auto|rebase|merge|off] [--no-resume] [--no-cleanup] [--yes] [--dry-run]',
|
|
49
49
|
' create-package-starter promote-stable [--dir <directory>] [--type patch|minor|major] [--summary <text>] [--dry-run]',
|
|
50
50
|
' create-package-starter setup-npm [--dir <directory>] [--publish-first] [--dry-run]',
|
|
51
51
|
'',
|
|
@@ -561,6 +561,7 @@ function parseReleaseCycleArgs(argv) {
|
|
|
561
561
|
base: '',
|
|
562
562
|
title: '',
|
|
563
563
|
bodyFile: '',
|
|
564
|
+
npmPackages: [],
|
|
564
565
|
updatePrDescription: false,
|
|
565
566
|
draft: false,
|
|
566
567
|
autoMerge: true,
|
|
@@ -645,6 +646,12 @@ function parseReleaseCycleArgs(argv) {
|
|
|
645
646
|
continue;
|
|
646
647
|
}
|
|
647
648
|
|
|
649
|
+
if (token === '--npm-package') {
|
|
650
|
+
args.npmPackages.push(parseValueFlag(argv, i, '--npm-package'));
|
|
651
|
+
i += 1;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
|
|
648
655
|
if (token === '--update-pr-description') {
|
|
649
656
|
args.updatePrDescription = true;
|
|
650
657
|
continue;
|
|
@@ -1992,64 +1999,145 @@ function waitForPromotionPr(repo, timeoutMinutes, deps) {
|
|
|
1992
1999
|
throw new Error(`Timed out waiting for promotion PR after ${timeoutMinutes} minutes.`);
|
|
1993
2000
|
}
|
|
1994
2001
|
|
|
1995
|
-
function
|
|
1996
|
-
|
|
2002
|
+
function encodePathForGitHubContent(pathValue) {
|
|
2003
|
+
return String(pathValue || '')
|
|
2004
|
+
.split('/')
|
|
2005
|
+
.filter(Boolean)
|
|
2006
|
+
.map((segment) => encodeURIComponent(segment))
|
|
2007
|
+
.join('/');
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
function getRemotePackageVersionFromPath(repo, ref, packageJsonPath, deps) {
|
|
2011
|
+
const safePath = encodePathForGitHubContent(packageJsonPath);
|
|
2012
|
+
const endpoint = `/repos/${repo}/contents/${safePath}?ref=${encodeURIComponent(ref)}`;
|
|
1997
2013
|
const contentResponse = ghApiJson(deps, 'GET', endpoint);
|
|
1998
2014
|
if (!contentResponse.content) {
|
|
1999
|
-
throw new Error(`Could not read
|
|
2015
|
+
throw new Error(`Could not read ${packageJsonPath} content from ${repo}@${ref}.`);
|
|
2000
2016
|
}
|
|
2001
2017
|
|
|
2002
2018
|
const decoded = Buffer.from(String(contentResponse.content).replace(/\n/g, ''), 'base64').toString('utf8');
|
|
2003
2019
|
const parsed = parseJsonSafely(decoded, {});
|
|
2004
2020
|
if (!parsed.name || !parsed.version) {
|
|
2005
|
-
throw new Error(
|
|
2021
|
+
throw new Error(`${packageJsonPath} from ${repo}@${ref} must include name and version.`);
|
|
2006
2022
|
}
|
|
2007
2023
|
|
|
2008
2024
|
return {
|
|
2009
2025
|
name: parsed.name,
|
|
2010
|
-
version: parsed.version
|
|
2026
|
+
version: parsed.version,
|
|
2027
|
+
packageJsonPath
|
|
2011
2028
|
};
|
|
2012
2029
|
}
|
|
2013
2030
|
|
|
2014
|
-
function
|
|
2031
|
+
function getRemotePackageVersion(repo, ref, deps) {
|
|
2032
|
+
return getRemotePackageVersionFromPath(repo, ref, 'package.json', deps);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
function listPullRequestFiles(repo, prNumber, deps) {
|
|
2036
|
+
const files = ghApiJson(deps, 'GET', `/repos/${repo}/pulls/${prNumber}/files?per_page=100`);
|
|
2037
|
+
return Array.isArray(files) ? files : [];
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
function resolveExpectedNpmPackages(repo, releasePrNumber, targetRef, expectedTag, args, deps) {
|
|
2041
|
+
const explicitPackages = Array.isArray(args.npmPackages) ? args.npmPackages : [];
|
|
2042
|
+
const files = listPullRequestFiles(repo, releasePrNumber, deps);
|
|
2043
|
+
const packageJsonPaths = [...new Set(
|
|
2044
|
+
files
|
|
2045
|
+
.filter((file) => file && file.status !== 'removed' && typeof file.filename === 'string')
|
|
2046
|
+
.map((file) => file.filename)
|
|
2047
|
+
.filter((fileName) => fileName.endsWith('/package.json') || fileName === 'package.json')
|
|
2048
|
+
)];
|
|
2049
|
+
|
|
2050
|
+
const fallbackPaths = packageJsonPaths.length > 0 ? packageJsonPaths : ['package.json'];
|
|
2051
|
+
const resolved = fallbackPaths
|
|
2052
|
+
.map((filePath) => getRemotePackageVersionFromPath(repo, targetRef, filePath, deps))
|
|
2053
|
+
.filter((pkg) => pkg && pkg.name && pkg.version);
|
|
2054
|
+
|
|
2055
|
+
const byName = new Map();
|
|
2056
|
+
for (const pkg of resolved) {
|
|
2057
|
+
if (!byName.has(pkg.name)) {
|
|
2058
|
+
byName.set(pkg.name, pkg);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
if (explicitPackages.length === 0) {
|
|
2063
|
+
return [...byName.values()];
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
const filtered = [];
|
|
2067
|
+
const missing = [];
|
|
2068
|
+
for (const pkgName of explicitPackages) {
|
|
2069
|
+
if (byName.has(pkgName)) {
|
|
2070
|
+
filtered.push(byName.get(pkgName));
|
|
2071
|
+
} else {
|
|
2072
|
+
missing.push(pkgName);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
if (missing.length > 0) {
|
|
2077
|
+
throw new Error(
|
|
2078
|
+
[
|
|
2079
|
+
`Could not resolve expected npm package(s) from release PR #${releasePrNumber}: ${missing.join(', ')}`,
|
|
2080
|
+
`Resolved package names: ${[...byName.keys()].join(', ') || 'none'}`,
|
|
2081
|
+
`Expected tag: ${expectedTag}`
|
|
2082
|
+
].join('\n')
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
return filtered;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
function validateNpmPublishedPackages(packageTargets, expectedTag, timeoutMinutes, deps) {
|
|
2015
2090
|
const timeoutAt = nowMs(deps) + timeoutMinutes * 60 * 1000;
|
|
2016
|
-
|
|
2017
|
-
let lastObservedTagVersion = '';
|
|
2091
|
+
const observations = {};
|
|
2018
2092
|
const isStableTrack = expectedTag === 'latest';
|
|
2019
2093
|
const onProgress = typeof deps.onNpmValidationProgress === 'function' ? deps.onNpmValidationProgress : null;
|
|
2020
2094
|
let lastProgressAt = 0;
|
|
2021
2095
|
|
|
2022
2096
|
while (nowMs(deps) <= timeoutAt) {
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
const
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
if (versionMatches && observedTagVersion === expectedVersion) {
|
|
2036
|
-
return {
|
|
2037
|
-
status: 'pass',
|
|
2038
|
-
observedVersion,
|
|
2039
|
-
observedTagVersion
|
|
2040
|
-
};
|
|
2097
|
+
let allPass = true;
|
|
2098
|
+
for (const target of packageTargets) {
|
|
2099
|
+
const versionResult = deps.exec('npm', ['view', target.name, 'version', '--json']);
|
|
2100
|
+
const tagsResult = deps.exec('npm', ['view', target.name, 'dist-tags', '--json']);
|
|
2101
|
+
let observedVersion = '';
|
|
2102
|
+
let observedTagVersion = '';
|
|
2103
|
+
if (versionResult.status === 0) {
|
|
2104
|
+
observedVersion = String(parseJsonSafely(versionResult.stdout || '""', '') || '');
|
|
2105
|
+
}
|
|
2106
|
+
if (tagsResult.status === 0) {
|
|
2107
|
+
const tags = parseJsonSafely(tagsResult.stdout || '{}', {});
|
|
2108
|
+
observedTagVersion = tags && tags[expectedTag] ? String(tags[expectedTag]) : '';
|
|
2041
2109
|
}
|
|
2042
2110
|
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
observedTagVersion,
|
|
2049
|
-
expectedVersion,
|
|
2050
|
-
expectedTag
|
|
2051
|
-
});
|
|
2111
|
+
const versionMatches = isStableTrack ? observedVersion === target.version : true;
|
|
2112
|
+
const tagMatches = observedTagVersion === target.version;
|
|
2113
|
+
const passed = versionMatches && tagMatches;
|
|
2114
|
+
if (!passed) {
|
|
2115
|
+
allPass = false;
|
|
2052
2116
|
}
|
|
2117
|
+
|
|
2118
|
+
observations[target.name] = {
|
|
2119
|
+
name: target.name,
|
|
2120
|
+
expectedVersion: target.version,
|
|
2121
|
+
observedVersion,
|
|
2122
|
+
observedTagVersion,
|
|
2123
|
+
passed
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
if (allPass) {
|
|
2128
|
+
return {
|
|
2129
|
+
status: 'pass',
|
|
2130
|
+
observations
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
const now = nowMs(deps);
|
|
2135
|
+
if (onProgress && (lastProgressAt === 0 || now - lastProgressAt >= 30_000)) {
|
|
2136
|
+
lastProgressAt = now;
|
|
2137
|
+
onProgress({
|
|
2138
|
+
expectedTag,
|
|
2139
|
+
observations
|
|
2140
|
+
});
|
|
2053
2141
|
}
|
|
2054
2142
|
|
|
2055
2143
|
waitForNextPoll(timeoutAt, 10000, deps);
|
|
@@ -2057,8 +2145,7 @@ function validateNpmPublishedVersionAndTag(packageName, expectedVersion, expecte
|
|
|
2057
2145
|
|
|
2058
2146
|
return {
|
|
2059
2147
|
status: 'timeout',
|
|
2060
|
-
|
|
2061
|
-
observedTagVersion: lastObservedTagVersion
|
|
2148
|
+
observations
|
|
2062
2149
|
};
|
|
2063
2150
|
}
|
|
2064
2151
|
|
|
@@ -3164,36 +3251,45 @@ async function runReleaseCycle(args, dependencies = {}) {
|
|
|
3164
3251
|
reporter.start('release-cycle-verify-npm', 'Validating npm publish and dist-tag...');
|
|
3165
3252
|
const targetRef = args.promoteStable ? DEFAULT_BASE_BRANCH : DEFAULT_BETA_BRANCH;
|
|
3166
3253
|
const expectedTag = requestedTrack === 'stable' ? 'latest' : 'beta';
|
|
3167
|
-
const
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3254
|
+
const targetPackages = resolveExpectedNpmPackages(
|
|
3255
|
+
gitContext.repo,
|
|
3256
|
+
mergedReleasePr.number,
|
|
3257
|
+
targetRef,
|
|
3258
|
+
expectedTag,
|
|
3259
|
+
args,
|
|
3260
|
+
deps
|
|
3261
|
+
);
|
|
3262
|
+
const npmValidation = validateNpmPublishedPackages(
|
|
3263
|
+
targetPackages,
|
|
3171
3264
|
expectedTag,
|
|
3172
3265
|
args.releasePrTimeout,
|
|
3173
3266
|
{
|
|
3174
3267
|
...deps,
|
|
3175
|
-
onNpmValidationProgress: ({
|
|
3176
|
-
|
|
3177
|
-
'
|
|
3178
|
-
|
|
3179
|
-
);
|
|
3268
|
+
onNpmValidationProgress: ({ expectedTag: expectedTagValue, observations }) => {
|
|
3269
|
+
const statusLine = Object.values(observations)
|
|
3270
|
+
.map((entry) => `${entry.name}: expected ${expectedTagValue}=${entry.expectedVersion}, observed version=${entry.observedVersion || 'n/a'}, ${expectedTagValue}=${entry.observedTagVersion || 'n/a'}`)
|
|
3271
|
+
.join(' | ');
|
|
3272
|
+
logStep('run', `Waiting npm propagation... ${statusLine}`);
|
|
3180
3273
|
}
|
|
3181
3274
|
}
|
|
3182
3275
|
);
|
|
3183
3276
|
if (npmValidation.status !== 'pass') {
|
|
3184
3277
|
summary.npmValidation = `failed (${expectedTag})`;
|
|
3278
|
+
const observedLines = Object.values(npmValidation.observations || {})
|
|
3279
|
+
.map((entry) => `${entry.name}: version=${entry.observedVersion || 'n/a'}, ${expectedTag}=${entry.observedTagVersion || 'n/a'}`);
|
|
3280
|
+
const expectedLines = targetPackages
|
|
3281
|
+
.map((pkg) => `${pkg.name}@${pkg.version}`);
|
|
3185
3282
|
throw new Error(
|
|
3186
3283
|
[
|
|
3187
3284
|
'npm validation failed after release merge.',
|
|
3188
|
-
`Expected
|
|
3189
|
-
|
|
3190
|
-
`Observed tag (${expectedTag}): ${npmValidation.observedTagVersion || 'n/a'}`
|
|
3285
|
+
`Expected (${expectedTag}): ${expectedLines.join(', ')}`,
|
|
3286
|
+
...observedLines
|
|
3191
3287
|
].join('\n')
|
|
3192
3288
|
);
|
|
3193
3289
|
}
|
|
3194
|
-
reporter.ok('release-cycle-verify-npm', `${
|
|
3195
|
-
summary.actionsPerformed.push(`npm validation: ${
|
|
3196
|
-
summary.npmValidation = `pass (${expectedTag} -> ${
|
|
3290
|
+
reporter.ok('release-cycle-verify-npm', `${targetPackages.length} package(s) validated on tag ${expectedTag}.`);
|
|
3291
|
+
summary.actionsPerformed.push(`npm validation: ${targetPackages.map((pkg) => `${pkg.name}@${pkg.version}`).join(', ')} (${expectedTag})`);
|
|
3292
|
+
summary.npmValidation = `pass (${expectedTag} -> ${targetPackages.map((pkg) => pkg.version).join(', ')})`;
|
|
3197
3293
|
npmValidationPassed = true;
|
|
3198
3294
|
} else if (!args.verifyNpm) {
|
|
3199
3295
|
summary.actionsSkipped.push('verify npm');
|
|
@@ -3303,36 +3399,45 @@ async function runReleaseCycle(args, dependencies = {}) {
|
|
|
3303
3399
|
reporter.start('release-cycle-verify-npm', 'Validating npm publish and dist-tag...');
|
|
3304
3400
|
const targetRef = effectivePublishTrack === 'stable' ? DEFAULT_BASE_BRANCH : DEFAULT_BETA_BRANCH;
|
|
3305
3401
|
const expectedTag = effectivePublishTrack === 'stable' ? 'latest' : 'beta';
|
|
3306
|
-
const
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3402
|
+
const targetPackages = resolveExpectedNpmPackages(
|
|
3403
|
+
gitContext.repo,
|
|
3404
|
+
releasePr.number,
|
|
3405
|
+
targetRef,
|
|
3406
|
+
expectedTag,
|
|
3407
|
+
args,
|
|
3408
|
+
deps
|
|
3409
|
+
);
|
|
3410
|
+
const npmValidation = validateNpmPublishedPackages(
|
|
3411
|
+
targetPackages,
|
|
3310
3412
|
expectedTag,
|
|
3311
3413
|
args.releasePrTimeout,
|
|
3312
3414
|
{
|
|
3313
3415
|
...deps,
|
|
3314
|
-
onNpmValidationProgress: ({
|
|
3315
|
-
|
|
3316
|
-
'
|
|
3317
|
-
|
|
3318
|
-
);
|
|
3416
|
+
onNpmValidationProgress: ({ expectedTag: expectedTagValue, observations }) => {
|
|
3417
|
+
const statusLine = Object.values(observations)
|
|
3418
|
+
.map((entry) => `${entry.name}: expected ${expectedTagValue}=${entry.expectedVersion}, observed version=${entry.observedVersion || 'n/a'}, ${expectedTagValue}=${entry.observedTagVersion || 'n/a'}`)
|
|
3419
|
+
.join(' | ');
|
|
3420
|
+
logStep('run', `Waiting npm propagation... ${statusLine}`);
|
|
3319
3421
|
}
|
|
3320
3422
|
}
|
|
3321
3423
|
);
|
|
3322
3424
|
if (npmValidation.status !== 'pass') {
|
|
3323
3425
|
summary.npmValidation = `failed (${expectedTag})`;
|
|
3426
|
+
const observedLines = Object.values(npmValidation.observations || {})
|
|
3427
|
+
.map((entry) => `${entry.name}: version=${entry.observedVersion || 'n/a'}, ${expectedTag}=${entry.observedTagVersion || 'n/a'}`);
|
|
3428
|
+
const expectedLines = targetPackages
|
|
3429
|
+
.map((pkg) => `${pkg.name}@${pkg.version}`);
|
|
3324
3430
|
throw new Error(
|
|
3325
3431
|
[
|
|
3326
3432
|
'npm validation failed after release merge.',
|
|
3327
|
-
`Expected
|
|
3328
|
-
|
|
3329
|
-
`Observed tag (${expectedTag}): ${npmValidation.observedTagVersion || 'n/a'}`
|
|
3433
|
+
`Expected (${expectedTag}): ${expectedLines.join(', ')}`,
|
|
3434
|
+
...observedLines
|
|
3330
3435
|
].join('\n')
|
|
3331
3436
|
);
|
|
3332
3437
|
}
|
|
3333
|
-
reporter.ok('release-cycle-verify-npm', `${
|
|
3334
|
-
summary.actionsPerformed.push(`npm validation: ${
|
|
3335
|
-
summary.npmValidation = `pass (${expectedTag} -> ${
|
|
3438
|
+
reporter.ok('release-cycle-verify-npm', `${targetPackages.length} package(s) validated on tag ${expectedTag}.`);
|
|
3439
|
+
summary.actionsPerformed.push(`npm validation: ${targetPackages.map((pkg) => `${pkg.name}@${pkg.version}`).join(', ')} (${expectedTag})`);
|
|
3440
|
+
summary.npmValidation = `pass (${expectedTag} -> ${targetPackages.map((pkg) => pkg.version).join(', ')})`;
|
|
3336
3441
|
npmValidationPassed = true;
|
|
3337
3442
|
} else if (!args.verifyNpm) {
|
|
3338
3443
|
summary.npmValidation = 'skipped';
|
package/package.json
CHANGED