@i-santos/create-package-starter 1.5.0-beta.13 → 1.5.0-beta.14

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 (2) hide show
  1. package/lib/run.js +157 -36
  2. package/package.json +1 -1
package/lib/run.js CHANGED
@@ -1699,7 +1699,7 @@ function getLatestWorkflowRunForBranch(repo, branch, deps) {
1699
1699
  '--branch',
1700
1700
  branch,
1701
1701
  '--json',
1702
- 'databaseId,workflowName,status,conclusion,url',
1702
+ 'databaseId,workflowName,status,conclusion,url,updatedAt,createdAt,event',
1703
1703
  '--limit',
1704
1704
  '10'
1705
1705
  ]);
@@ -1912,17 +1912,41 @@ function waitForReleasePr(repo, timeoutMinutes, deps, options = {}) {
1912
1912
  const expectedBase = options.expectedBase || '';
1913
1913
  const onProgress = typeof options.onProgress === 'function' ? options.onProgress : null;
1914
1914
  const progressIntervalMs = Number.isFinite(options.progressIntervalMs) ? options.progressIntervalMs : 30_000;
1915
+ const allowDirectPublish = Boolean(options.allowDirectPublish);
1916
+ const waitStartedAtMs = nowMs(deps);
1915
1917
  let lastProgressAt = 0;
1916
1918
  const timeoutAt = nowMs(deps) + timeoutMinutes * 60 * 1000;
1917
1919
  while (nowMs(deps) <= timeoutAt) {
1918
1920
  const releasePrs = findReleasePrs(repo, deps, { expectedBase });
1919
1921
  if (releasePrs.length === 1) {
1920
- return releasePrs[0];
1922
+ return {
1923
+ type: 'release_pr',
1924
+ releasePr: releasePrs[0]
1925
+ };
1921
1926
  }
1922
1927
  if (releasePrs.length > 1) {
1923
1928
  throw new Error(`Multiple release PRs detected: ${releasePrs.map((item) => item.url).join(', ')}`);
1924
1929
  }
1925
1930
 
1931
+ if (allowDirectPublish) {
1932
+ const run = getLatestWorkflowRunForBranch(repo, DEFAULT_BETA_BRANCH, deps);
1933
+ if (run) {
1934
+ const updatedAtMs = run.updatedAt ? Date.parse(run.updatedAt) : 0;
1935
+ const recentlyUpdated = Number.isFinite(updatedAtMs) && updatedAtMs >= waitStartedAtMs;
1936
+ const completed = String(run.status || '').toLowerCase() === 'completed';
1937
+ const success = ['success', 'neutral', 'skipped'].includes(String(run.conclusion || '').toLowerCase());
1938
+ const looksLikeReleaseFlow = String(run.workflowName || '').toLowerCase().includes('release')
1939
+ || String(run.event || '').toLowerCase() === 'push';
1940
+
1941
+ if (recentlyUpdated && completed && success && looksLikeReleaseFlow) {
1942
+ return {
1943
+ type: 'direct_publish',
1944
+ workflowRun: run
1945
+ };
1946
+ }
1947
+ }
1948
+ }
1949
+
1926
1950
  const now = nowMs(deps);
1927
1951
  if (onProgress && (lastProgressAt === 0 || now - lastProgressAt >= progressIntervalMs)) {
1928
1952
  lastProgressAt = now;
@@ -2037,7 +2061,91 @@ function listPullRequestFiles(repo, prNumber, deps) {
2037
2061
  return Array.isArray(files) ? files : [];
2038
2062
  }
2039
2063
 
2064
+ function listRepoContents(repo, ref, dirPath, deps) {
2065
+ const safePath = encodePathForGitHubContent(dirPath || '');
2066
+ const endpoint = safePath
2067
+ ? `/repos/${repo}/contents/${safePath}?ref=${encodeURIComponent(ref)}`
2068
+ : `/repos/${repo}/contents?ref=${encodeURIComponent(ref)}`;
2069
+ const content = ghApiJson(deps, 'GET', endpoint);
2070
+ return Array.isArray(content) ? content : [];
2071
+ }
2072
+
2073
+ function findWorkspacePackageJsonPaths(repo, ref, deps) {
2074
+ const root = getRemotePackageVersionFromPath(repo, ref, 'package.json', deps);
2075
+ const rootPackage = parseJsonSafely(
2076
+ Buffer.from(
2077
+ String(
2078
+ ghApiJson(deps, 'GET', `/repos/${repo}/contents/package.json?ref=${encodeURIComponent(ref)}`).content || ''
2079
+ ).replace(/\n/g, ''),
2080
+ 'base64'
2081
+ ).toString('utf8'),
2082
+ {}
2083
+ );
2084
+ const workspaces = Array.isArray(rootPackage.workspaces) ? rootPackage.workspaces : [];
2085
+ const paths = [];
2086
+ for (const workspace of workspaces) {
2087
+ if (!workspace.endsWith('/*')) {
2088
+ continue;
2089
+ }
2090
+
2091
+ const prefix = workspace.slice(0, -2);
2092
+ const entries = listRepoContents(repo, ref, prefix, deps);
2093
+ for (const entry of entries) {
2094
+ if (entry && entry.type === 'dir' && entry.path) {
2095
+ paths.push(`${entry.path}/package.json`);
2096
+ }
2097
+ }
2098
+ }
2099
+
2100
+ if (paths.length === 0) {
2101
+ return ['package.json'];
2102
+ }
2103
+
2104
+ return [...new Set(paths)];
2105
+ }
2106
+
2107
+ function resolveExpectedNpmPackagesFromRef(repo, targetRef, args, deps) {
2108
+ const explicitPackages = Array.isArray(args.npmPackages) ? args.npmPackages : [];
2109
+ const candidatePaths = findWorkspacePackageJsonPaths(repo, targetRef, deps);
2110
+ const resolved = [];
2111
+ for (const packageJsonPath of candidatePaths) {
2112
+ try {
2113
+ resolved.push(getRemotePackageVersionFromPath(repo, targetRef, packageJsonPath, deps));
2114
+ } catch (error) {
2115
+ // Ignore missing/invalid paths while scanning candidates.
2116
+ }
2117
+ }
2118
+
2119
+ const publishable = resolved.filter((pkg) => pkg && pkg.name && pkg.version);
2120
+ const byName = new Map(publishable.map((pkg) => [pkg.name, pkg]));
2121
+
2122
+ if (explicitPackages.length > 0) {
2123
+ const missing = explicitPackages.filter((name) => !byName.has(name));
2124
+ if (missing.length > 0) {
2125
+ throw new Error(
2126
+ [
2127
+ `Could not resolve explicit --npm-package values from ${targetRef}: ${missing.join(', ')}`,
2128
+ `Discovered packages: ${[...byName.keys()].join(', ') || 'none'}`
2129
+ ].join('\n')
2130
+ );
2131
+ }
2132
+
2133
+ return explicitPackages.map((name) => byName.get(name));
2134
+ }
2135
+
2136
+ return publishable;
2137
+ }
2138
+
2040
2139
  function resolveExpectedNpmPackages(repo, releasePrNumber, targetRef, expectedTag, args, deps) {
2140
+ if (!releasePrNumber) {
2141
+ const resolvedFromRef = resolveExpectedNpmPackagesFromRef(repo, targetRef, args, deps);
2142
+ if (resolvedFromRef.length > 0) {
2143
+ return resolvedFromRef;
2144
+ }
2145
+
2146
+ return [getRemotePackageVersion(repo, targetRef, deps)];
2147
+ }
2148
+
2041
2149
  const explicitPackages = Array.isArray(args.npmPackages) ? args.npmPackages : [];
2042
2150
  const files = listPullRequestFiles(repo, releasePrNumber, deps);
2043
2151
  const packageJsonPaths = [...new Set(
@@ -3191,8 +3299,9 @@ async function runReleaseCycle(args, dependencies = {}) {
3191
3299
  summary.releasePr = `dry-run: would wait release PR (${args.releasePrTimeout}m)`;
3192
3300
  } else {
3193
3301
  reporter.start('release-cycle-wait-release-pr', 'Waiting for release PR (changeset-release/*)...');
3194
- const releasePr = waitForReleasePr(gitContext.repo, args.releasePrTimeout, deps, {
3302
+ const releaseOutcome = waitForReleasePr(gitContext.repo, args.releasePrTimeout, deps, {
3195
3303
  expectedBase: releaseBaseBranchForTrack(requestedTrack),
3304
+ allowDirectPublish: true,
3196
3305
  onProgress: () => {
3197
3306
  const run = getLatestWorkflowRunForBranch(gitContext.repo, DEFAULT_BETA_BRANCH, deps);
3198
3307
  if (!run) {
@@ -3205,40 +3314,52 @@ async function runReleaseCycle(args, dependencies = {}) {
3205
3314
  );
3206
3315
  }
3207
3316
  });
3208
- reporter.ok('release-cycle-wait-release-pr', `Release PR found: #${releasePr.number}`);
3209
- summary.releasePr = `found (#${releasePr.number})`;
3210
- summary.actionsPerformed.push(`release pr discovered: #${releasePr.number}`);
3211
-
3212
- if (args.watchChecks) {
3213
- reporter.start('release-cycle-watch-release-checks', `Watching release PR checks #${releasePr.number}...`);
3214
- watchPrChecks(gitContext.repo, releasePr.number, args.checkTimeout, deps);
3215
- reporter.ok('release-cycle-watch-release-checks', `Release PR checks green (#${releasePr.number}).`);
3216
- }
3317
+ if (releaseOutcome.type === 'release_pr') {
3318
+ const releasePr = releaseOutcome.releasePr;
3319
+ reporter.ok('release-cycle-wait-release-pr', `Release PR found: #${releasePr.number}`);
3320
+ summary.releasePr = `found (#${releasePr.number})`;
3321
+ summary.actionsPerformed.push(`release pr discovered: #${releasePr.number}`);
3322
+
3323
+ if (args.watchChecks) {
3324
+ reporter.start('release-cycle-watch-release-checks', `Watching release PR checks #${releasePr.number}...`);
3325
+ watchPrChecks(gitContext.repo, releasePr.number, args.checkTimeout, deps);
3326
+ reporter.ok('release-cycle-watch-release-checks', `Release PR checks green (#${releasePr.number}).`);
3327
+ }
3217
3328
 
3218
- if (args.mergeReleasePr) {
3219
- reporter.start('release-cycle-merge-release-ready', `Checking merge readiness for release PR #${releasePr.number}...`);
3220
- const releaseReadiness = waitForPrMergeReadinessOrThrow(
3221
- gitContext.repo,
3222
- releasePr.number,
3223
- `Release PR #${releasePr.number}`,
3224
- args.checkTimeout,
3225
- deps,
3226
- { allowBehindTransient: true }
3227
- );
3228
- await confirmMergeIfNeeded(args, releaseReadiness, `Release PR #${releasePr.number}`);
3229
- reporter.ok('release-cycle-merge-release-ready', `Release PR #${releasePr.number} is ready for merge.`);
3230
- reporter.start('release-cycle-release-auto-merge', `Enabling auto-merge for release PR #${releasePr.number}...`);
3231
- enablePrAutoMerge(gitContext.repo, releasePr.number, args.mergeMethod, deps);
3232
- reporter.ok('release-cycle-release-auto-merge', `Auto-merge enabled for release PR #${releasePr.number}.`);
3233
- reporter.start('release-cycle-release-wait-merge', `Waiting for release PR #${releasePr.number} merge...`);
3234
- waitForPrMerged(gitContext.repo, releasePr.number, args.releasePrTimeout, deps);
3235
- reporter.ok('release-cycle-release-wait-merge', `Release PR #${releasePr.number} merged.`);
3236
- summary.releasePr = `merged (#${releasePr.number})`;
3237
- summary.actionsPerformed.push(`release pr merged: #${releasePr.number}`);
3238
- summary.autoMerge = 'enabled (code + release)';
3239
- mergedReleasePr = releasePr;
3329
+ if (args.mergeReleasePr) {
3330
+ reporter.start('release-cycle-merge-release-ready', `Checking merge readiness for release PR #${releasePr.number}...`);
3331
+ const releaseReadiness = waitForPrMergeReadinessOrThrow(
3332
+ gitContext.repo,
3333
+ releasePr.number,
3334
+ `Release PR #${releasePr.number}`,
3335
+ args.checkTimeout,
3336
+ deps,
3337
+ { allowBehindTransient: true }
3338
+ );
3339
+ await confirmMergeIfNeeded(args, releaseReadiness, `Release PR #${releasePr.number}`);
3340
+ reporter.ok('release-cycle-merge-release-ready', `Release PR #${releasePr.number} is ready for merge.`);
3341
+ reporter.start('release-cycle-release-auto-merge', `Enabling auto-merge for release PR #${releasePr.number}...`);
3342
+ enablePrAutoMerge(gitContext.repo, releasePr.number, args.mergeMethod, deps);
3343
+ reporter.ok('release-cycle-release-auto-merge', `Auto-merge enabled for release PR #${releasePr.number}.`);
3344
+ reporter.start('release-cycle-release-wait-merge', `Waiting for release PR #${releasePr.number} merge...`);
3345
+ waitForPrMerged(gitContext.repo, releasePr.number, args.releasePrTimeout, deps);
3346
+ reporter.ok('release-cycle-release-wait-merge', `Release PR #${releasePr.number} merged.`);
3347
+ summary.releasePr = `merged (#${releasePr.number})`;
3348
+ summary.actionsPerformed.push(`release pr merged: #${releasePr.number}`);
3349
+ summary.autoMerge = 'enabled (code + release)';
3350
+ mergedReleasePr = releasePr;
3351
+ } else {
3352
+ summary.actionsSkipped.push('merge release pr');
3353
+ }
3240
3354
  } else {
3241
- summary.actionsSkipped.push('merge release pr');
3355
+ reporter.ok('release-cycle-wait-release-pr', 'No release PR created; detected successful direct publish workflow on release/beta.');
3356
+ summary.releasePr = 'skipped (direct publish)';
3357
+ summary.actionsPerformed.push('direct publish detected: no release PR required');
3358
+ mergedReleasePr = {
3359
+ number: 0,
3360
+ url: '',
3361
+ directPublish: true
3362
+ };
3242
3363
  }
3243
3364
  }
3244
3365
  } else {
@@ -3253,7 +3374,7 @@ async function runReleaseCycle(args, dependencies = {}) {
3253
3374
  const expectedTag = requestedTrack === 'stable' ? 'latest' : 'beta';
3254
3375
  const targetPackages = resolveExpectedNpmPackages(
3255
3376
  gitContext.repo,
3256
- mergedReleasePr.number,
3377
+ mergedReleasePr && mergedReleasePr.number ? mergedReleasePr.number : 0,
3257
3378
  targetRef,
3258
3379
  expectedTag,
3259
3380
  args,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i-santos/create-package-starter",
3
- "version": "1.5.0-beta.13",
3
+ "version": "1.5.0-beta.14",
4
4
  "description": "Scaffold new npm packages with a standardized Changesets release workflow",
5
5
  "license": "MIT",
6
6
  "author": "Igor Santos",