@astra-kit/rp 0.0.0-stage → 0.10.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +352 -2
  4. package/bin/astra-rp.js +51 -0
  5. package/package.json +93 -4
  6. package/src/build-gate.js +157 -0
  7. package/src/changelog-blocks.js +53 -0
  8. package/src/changelog.js +108 -0
  9. package/src/constants/build-gate.js +30 -0
  10. package/src/constants/changelog-draft.js +180 -0
  11. package/src/constants/changelog.js +74 -0
  12. package/src/constants/ci-release.js +346 -0
  13. package/src/constants/cli.js +12 -0
  14. package/src/constants/create-version.js +523 -0
  15. package/src/constants/guard-publish.js +29 -0
  16. package/src/constants/index.js +18 -0
  17. package/src/constants/monorepo.js +57 -0
  18. package/src/constants/package-manager.js +56 -0
  19. package/src/constants/registry.js +72 -0
  20. package/src/constants/terminal-ui.js +93 -0
  21. package/src/constants/version-files.js +63 -0
  22. package/src/constants/versions.js +60 -0
  23. package/src/create-version/artifact.js +343 -0
  24. package/src/create-version/changelog-draft-workspace.js +124 -0
  25. package/src/create-version/changelog-draft.js +380 -0
  26. package/src/create-version/changelog.js +264 -0
  27. package/src/create-version/ci-config.js +54 -0
  28. package/src/create-version/ci-publication-history.js +113 -0
  29. package/src/create-version/ci-release-identity-step.js +58 -0
  30. package/src/create-version/ci-setup.js +794 -0
  31. package/src/create-version/ci.js +555 -0
  32. package/src/create-version/committed-tree.js +75 -0
  33. package/src/create-version/config.js +489 -0
  34. package/src/create-version/errors.js +46 -0
  35. package/src/create-version/github-workflow.js +199 -0
  36. package/src/create-version/index.js +54 -0
  37. package/src/create-version/json.js +32 -0
  38. package/src/create-version/jsonc.js +76 -0
  39. package/src/create-version/jsr.js +96 -0
  40. package/src/create-version/local-changes.js +178 -0
  41. package/src/create-version/npm-auth.js +142 -0
  42. package/src/create-version/npm.js +812 -0
  43. package/src/create-version/plan.js +1200 -0
  44. package/src/create-version/process.js +334 -0
  45. package/src/create-version/registry-config.js +121 -0
  46. package/src/create-version/registry-http.js +62 -0
  47. package/src/create-version/registry.js +220 -0
  48. package/src/create-version/release-execution.js +45 -0
  49. package/src/create-version/release-tag-config.js +76 -0
  50. package/src/create-version/run.js +2087 -0
  51. package/src/create-version/state.js +461 -0
  52. package/src/create-version/strict-utf8.js +11 -0
  53. package/src/create-version/templates/release.yml +52 -0
  54. package/src/create-version/templates/vercel-ignore-build.mjs +66 -0
  55. package/src/guard-publish.js +77 -0
  56. package/src/index.js +42 -0
  57. package/src/monorepo/plan.js +475 -0
  58. package/src/monorepo/release-commit.js +46 -0
  59. package/src/monorepo/run.js +927 -0
  60. package/src/monorepo/state.js +206 -0
  61. package/src/monorepo/workspaces.js +327 -0
  62. package/src/package-manager.js +107 -0
  63. package/src/terminal-ui.js +548 -0
  64. package/src/testing.js +70 -0
  65. package/src/version-files.js +142 -0
  66. package/src/versions.js +303 -0
  67. package/types/build-gate.d.ts +43 -0
  68. package/types/changelog-blocks.d.ts +33 -0
  69. package/types/changelog.d.ts +61 -0
  70. package/types/constants/build-gate.d.ts +23 -0
  71. package/types/constants/changelog-draft.d.ts +138 -0
  72. package/types/constants/changelog.d.ts +56 -0
  73. package/types/constants/ci-release.d.ts +284 -0
  74. package/types/constants/cli.d.ts +11 -0
  75. package/types/constants/create-version.d.ts +397 -0
  76. package/types/constants/guard-publish.d.ts +25 -0
  77. package/types/constants/index.d.ts +17 -0
  78. package/types/constants/monorepo.d.ts +45 -0
  79. package/types/constants/package-manager.d.ts +40 -0
  80. package/types/constants/registry.d.ts +79 -0
  81. package/types/constants/terminal-ui.d.ts +73 -0
  82. package/types/constants/version-files.d.ts +51 -0
  83. package/types/constants/versions.d.ts +47 -0
  84. package/types/create-version/artifact.d.ts +113 -0
  85. package/types/create-version/changelog-draft-workspace.d.ts +33 -0
  86. package/types/create-version/changelog-draft.d.ts +127 -0
  87. package/types/create-version/changelog.d.ts +141 -0
  88. package/types/create-version/ci-config.d.ts +33 -0
  89. package/types/create-version/ci-publication-history.d.ts +51 -0
  90. package/types/create-version/ci-release-identity-step.d.ts +12 -0
  91. package/types/create-version/ci-setup.d.ts +148 -0
  92. package/types/create-version/ci.d.ts +145 -0
  93. package/types/create-version/committed-tree.d.ts +55 -0
  94. package/types/create-version/config.d.ts +128 -0
  95. package/types/create-version/errors.d.ts +39 -0
  96. package/types/create-version/github-workflow.d.ts +41 -0
  97. package/types/create-version/index.d.ts +28 -0
  98. package/types/create-version/json.d.ts +21 -0
  99. package/types/create-version/jsonc.d.ts +21 -0
  100. package/types/create-version/jsr.d.ts +52 -0
  101. package/types/create-version/local-changes.d.ts +52 -0
  102. package/types/create-version/npm-auth.d.ts +60 -0
  103. package/types/create-version/npm.d.ts +331 -0
  104. package/types/create-version/plan.d.ts +369 -0
  105. package/types/create-version/process.d.ts +151 -0
  106. package/types/create-version/registry-config.d.ts +56 -0
  107. package/types/create-version/registry-http.d.ts +22 -0
  108. package/types/create-version/registry.d.ts +86 -0
  109. package/types/create-version/release-execution.d.ts +22 -0
  110. package/types/create-version/release-tag-config.d.ts +18 -0
  111. package/types/create-version/run.d.ts +407 -0
  112. package/types/create-version/state.d.ts +204 -0
  113. package/types/create-version/strict-utf8.d.ts +10 -0
  114. package/types/guard-publish.d.ts +49 -0
  115. package/types/index.d.ts +23 -0
  116. package/types/monorepo/plan.d.ts +68 -0
  117. package/types/monorepo/release-commit.d.ts +29 -0
  118. package/types/monorepo/run.d.ts +68 -0
  119. package/types/monorepo/state.d.ts +96 -0
  120. package/types/monorepo/workspaces.d.ts +86 -0
  121. package/types/package-manager.d.ts +58 -0
  122. package/types/terminal-ui.d.ts +196 -0
  123. package/types/testing.d.ts +31 -0
  124. package/types/version-files.d.ts +59 -0
  125. package/types/versions.d.ts +158 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Coordinates one selected registry through a shared lookup/authentication/publication contract.
3
+ *
4
+ * @module create-version/registry
5
+ */
6
+
7
+ import {
8
+ DEFAULT_NPM_REGISTRY_URL, NPM_AUTH_STATUS, NPM_DIST_TAG, NPM_LOOKUP_STATUS, NPM_PACKAGE_NAME_PATTERN, PROJECT_NPM_CONFIG_FILE,
9
+ } from "../constants/create-version.js";
10
+ import {
11
+ BROWSER_AUTHENTICATION, GITHUB_PACKAGES_HOST, GITHUB_PACKAGES_REGISTRY_URL, GITHUB_REGISTRY_PROVIDER, GITLAB_PROJECT_REGISTRY_PATTERN,
12
+ GITLAB_REGISTRY_PROVIDER, JSR_NPM_COMPATIBILITY_HOSTNAME, JSR_REGISTRY_PROVIDER, JSR_REGISTRY_URL, JSR_SHELL_SAFE_TOKEN_PATTERN,
13
+ NPM_OIDC_MINIMUM_NODE_VERSION, NPM_OIDC_MINIMUM_VERSION, NPM_REGISTRY_PROVIDER, OIDC_AUTHENTICATION, REGISTRY_LABELS, TOKEN_AUTHENTICATION,
14
+ } from "../constants/registry.js";
15
+ import { compareReleaseVersions, isStableReleaseVersion } from "../versions.js";
16
+ import { buildNpmAuthConfigLine, checkNpmPublishAccess, failedLookup, findProjectNpmCredentialKey, lookupPublishedVersions, publishToNpm, resolveNpmToken, resolvePublishRegistry, runNpmCaptured } from "./npm.js";
17
+ import { publishToJsr, readJsrManifest } from "./jsr.js";
18
+ import { isPlainHttpUrl, isRegistryProvider, resolvePublicationOptions } from "./registry-config.js";
19
+ import { lookupHttpRegistry } from "./registry-http.js";
20
+
21
+ /**
22
+ * @typedef {{ provider: import("./registry-config.js").RegistryProvider, options: import("./registry-config.js").ResolvedPublicationOptions }} RegistrySelection
23
+ * @typedef {RegistrySelection & { registryUrl: string, packageName: string, label: string, tag: string | null }} SelectedRegistry
24
+ */
25
+
26
+ /**
27
+ * Selects the configured provider without changing legacy npm hooks or disabled tracking.
28
+ *
29
+ * @param {import("./config.js").ResolvedCreateVersionConfig} config - Project configuration.
30
+ * @returns {RegistrySelection} Selected provider and options.
31
+ */
32
+ export function selectProjectRegistry(config) {
33
+ const provider = isRegistryProvider(config.publish) ? config.publish : config.registry ?? NPM_REGISTRY_PROVIDER;
34
+ return { provider, options: config.publication ?? resolvePublicationOptions(undefined, provider) };
35
+ }
36
+
37
+ /**
38
+ * Resolves package identity and endpoint once for the operation; known npm endpoints use their provider policy.
39
+ *
40
+ * @param {RegistrySelection} selection - Project selection.
41
+ * @param {Record<string, unknown>} manifest - Node package manifest.
42
+ * @param {string} repositoryRoot - Root where registry routing and credentials are read.
43
+ * @param {string} [packageRoot] - Workspace or checkout directory.
44
+ * @returns {Promise<SelectedRegistry>} Registry descriptor without secret values.
45
+ * @throws {Error} When the endpoint or package identity is invalid for its provider.
46
+ */
47
+ export async function resolveRegistry(selection, manifest, repositoryRoot, packageRoot = repositoryRoot) {
48
+ let { provider } = selection;
49
+ const { options } = selection;
50
+ let registryUrl;
51
+ let packageName = String(manifest.name);
52
+ if (provider === JSR_REGISTRY_PROVIDER) {
53
+ registryUrl = options.registryUrl ?? JSR_REGISTRY_URL;
54
+ packageName = readJsrManifest(packageRoot, options.configFile).packageName;
55
+ } else {
56
+ registryUrl = options.registryUrl ?? (provider === GITHUB_REGISTRY_PROVIDER ? GITHUB_PACKAGES_REGISTRY_URL : await resolvePublishRegistry(manifest, repositoryRoot));
57
+ const url = new URL(registryUrl);
58
+ if (provider === NPM_REGISTRY_PROVIDER && url.hostname === GITHUB_PACKAGES_HOST) provider = GITHUB_REGISTRY_PROVIDER;
59
+ else if (provider === NPM_REGISTRY_PROVIDER && GITLAB_PROJECT_REGISTRY_PATTERN.test(url.pathname)) provider = GITLAB_REGISTRY_PROVIDER;
60
+ if (!NPM_PACKAGE_NAME_PATTERN.test(packageName)) throw new Error(`Nombre de paquete inválido para ${REGISTRY_LABELS[provider]}`);
61
+ if (provider === GITHUB_REGISTRY_PROVIDER && !packageName.startsWith("@")) throw new Error("GitHub Packages requiere name con scope (@owner/package) en package.json");
62
+ if (provider === GITLAB_REGISTRY_PROVIDER && !GITLAB_PROJECT_REGISTRY_PATTERN.test(url.pathname)) throw new Error("GitLab requiere el endpoint /api/v4/projects/<id>/packages/npm/ para publicar");
63
+ if (url.hostname === JSR_NPM_COMPATIBILITY_HOSTNAME) throw new Error(`${JSR_NPM_COMPATIBILITY_HOSTNAME} permite instalar, pero publicar requiere publish: jsr`);
64
+ }
65
+ const url = new URL(registryUrl);
66
+ if (!isPlainHttpUrl(url)) throw new Error("El registry debe ser una URL http(s) sin credenciales, query ni fragmento");
67
+ if (options.packageName !== null && options.packageName !== packageName) throw new Error("publication.packageName debe coincidir con el name del manifest que se publica");
68
+ return { provider, options, registryUrl: url.href, packageName, label: REGISTRY_LABELS[provider], tag: options.tag };
69
+ }
70
+
71
+ /**
72
+ * Reads the selected registry through the common version contract, preserving injected legacy npm adapters.
73
+ *
74
+ * @param {RegistrySelection} selection - Selected provider.
75
+ * @param {Record<string, unknown>} manifest - Source manifest.
76
+ * @param {string} repositoryRoot - Repository containing credentials and routing.
77
+ * @param {string} [packageRoot] - Workspace package root.
78
+ * @param {typeof lookupPublishedVersions} [lookupNpm] - Legacy npm boundary.
79
+ * @returns {Promise<import("./npm.js").NpmLookup>} Published-version state.
80
+ */
81
+ export async function lookupRegistryVersions(selection, manifest, repositoryRoot, packageRoot = repositoryRoot, lookupNpm = lookupPublishedVersions) {
82
+ let registry;
83
+ try {
84
+ registry = await resolveRegistry(selection, manifest, repositoryRoot, packageRoot);
85
+ } catch (error) {
86
+ return describeFailedRegistryLookup(error, selection.provider);
87
+ }
88
+ return lookupResolvedRegistryVersions(registry, repositoryRoot, lookupNpm, selection.provider);
89
+ }
90
+
91
+ /**
92
+ * Reads the published versions of an already resolved registry through the common version contract.
93
+ *
94
+ * @param {SelectedRegistry} registry - Resolved registry.
95
+ * @param {string} repositoryRoot - Repository containing credentials and routing.
96
+ * @param {typeof lookupPublishedVersions} lookupNpm - Legacy npm boundary.
97
+ * @param {import("./registry-config.js").RegistryProvider} selectedProvider - Configured provider, named when the lookup fails.
98
+ * @returns {Promise<import("./npm.js").NpmLookup>} Published-version state.
99
+ */
100
+ async function lookupResolvedRegistryVersions(registry, repositoryRoot, lookupNpm, selectedProvider) {
101
+ try {
102
+ if (registry.provider === JSR_REGISTRY_PROVIDER || registry.provider === GITLAB_REGISTRY_PROVIDER) {
103
+ const { token } = resolveNpmToken(repositoryRoot, { tokenVariable: registry.options.tokenEnv });
104
+ return await lookupHttpRegistry(registry, token);
105
+ }
106
+ const lookup = await lookupNpm(registry.packageName, repositoryRoot, registry.registryUrl, { tokenVariable: registry.options.tokenEnv, tag: registry.tag ?? NPM_DIST_TAG, pinScope: true });
107
+ return registry.provider === NPM_REGISTRY_PROVIDER && registry.tag === NPM_DIST_TAG ? lookup : { ...lookup, registryLabel: registry.label, tag: registry.tag };
108
+ } catch (error) {
109
+ return describeFailedRegistryLookup(error, selectedProvider);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Builds the failed lookup of a registry that could not be resolved or read.
115
+ *
116
+ * @param {unknown} error - Thrown value.
117
+ * @param {import("./registry-config.js").RegistryProvider} selectedProvider - Configured provider.
118
+ * @returns {import("./npm.js").NpmLookup} Failed lookup labeled with the configured provider.
119
+ */
120
+ function describeFailedRegistryLookup(error, selectedProvider) {
121
+ return { ...failedLookup(error instanceof Error ? error.message : "No se pudo resolver el registry"), registryLabel: REGISTRY_LABELS[selectedProvider] };
122
+ }
123
+
124
+ /**
125
+ * Checks the destination of an npm-protocol OIDC publication: npm trusted publishing only exists on
126
+ * the public npm registry, so GitHub Packages, GitLab and any other endpoint can never accept it.
127
+ * Shared by the worker and by CI preparation, which must reject it before the release tag exists.
128
+ *
129
+ * @param {SelectedRegistry} registry - Resolved descriptor of a non-JSR provider.
130
+ * @returns {string | null} Blocking reason, or `null` when the destination is the public npm registry.
131
+ */
132
+ export function findNpmOidcRegistryProblem(registry) {
133
+ return registry.provider !== NPM_REGISTRY_PROVIDER || new URL(registry.registryUrl).href !== DEFAULT_NPM_REGISTRY_URL ? "OIDC de npm requiere el registry público de npm" : null;
134
+ }
135
+
136
+ /**
137
+ * Checks a configured OIDC runtime without making a token mandatory or claiming provider write access.
138
+ *
139
+ * @param {SelectedRegistry} registry - Resolved descriptor.
140
+ * @returns {Promise<string | null>} Blocking reason, or `null` when the supported environment is available.
141
+ */
142
+ async function oidcEnvironmentProblem(registry) {
143
+ const github = Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL && process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN);
144
+ const gitlab = Boolean(process.env.GITLAB_CI && process.env.NPM_ID_TOKEN);
145
+ if (registry.provider === JSR_REGISTRY_PROVIDER) return github ? null : "JSR con OIDC requiere GitHub Actions con id-token: write y el paquete vinculado al repositorio";
146
+ const registryProblem = findNpmOidcRegistryProblem(registry);
147
+ if (registryProblem !== null) return registryProblem;
148
+ if (!github && !gitlab) return "npm con OIDC requiere GitHub Actions o GitLab CI con sus credenciales OIDC configuradas";
149
+ if (!isStableReleaseVersion(process.versions.node) || compareReleaseVersions(process.versions.node, NPM_OIDC_MINIMUM_NODE_VERSION) < 0) return `OIDC requiere Node >= ${NPM_OIDC_MINIMUM_NODE_VERSION}`;
150
+ const result = await runNpmCaptured(["--version"], process.cwd());
151
+ return result.status === 0 && isStableReleaseVersion(result.stdout) && compareReleaseVersions(result.stdout, NPM_OIDC_MINIMUM_VERSION) >= 0 ? null : `OIDC requiere npm >= ${NPM_OIDC_MINIMUM_VERSION}`;
152
+ }
153
+
154
+ /**
155
+ * Checks only capabilities the selected provider exposes; unsupported permission checks remain unknown.
156
+ *
157
+ * @param {RegistrySelection} selection - Project selection.
158
+ * @param {Record<string, unknown>} manifest - Node package manifest.
159
+ * @param {string} repositoryRoot - Credential root.
160
+ * @param {string} [packageRoot] - Package checkout.
161
+ * @param {typeof checkNpmPublishAccess} [checkNpmAccess] - Legacy npm check boundary.
162
+ * @returns {Promise<import("./npm.js").NpmAuthCheck>} Safe credential diagnostic.
163
+ */
164
+ export async function checkRegistryAccess(selection, manifest, repositoryRoot, packageRoot = repositoryRoot, checkNpmAccess = checkNpmPublishAccess) {
165
+ const check = { status: NPM_AUTH_STATUS.unknown, user: null, source: null, registryUrl: "", packageName: String(manifest.name), owners: [], firstPublication: false, reason: null, tokenVariable: selection.options.tokenEnv, registryLabel: REGISTRY_LABELS[selection.provider] };
166
+ /** @type {import("./npm.js").NpmAuthCheck} */
167
+ let diagnostic = check;
168
+ try {
169
+ const registry = await resolveRegistry(selection, manifest, repositoryRoot, packageRoot);
170
+ diagnostic = { ...diagnostic, registryUrl: registry.registryUrl, packageName: registry.packageName, registryLabel: registry.label };
171
+ const projectCredentialKey = registry.provider === JSR_REGISTRY_PROVIDER ? null : findProjectNpmCredentialKey(repositoryRoot, registry.registryUrl);
172
+ if (projectCredentialKey) return { ...diagnostic, status: NPM_AUTH_STATUS.projectCredentials, reason: `${PROJECT_NPM_CONFIG_FILE} define ${projectCredentialKey}` };
173
+ if (registry.options.authentication === OIDC_AUTHENTICATION) {
174
+ const reason = await oidcEnvironmentProblem(registry);
175
+ return { ...diagnostic, status: reason ? NPM_AUTH_STATUS.unsupportedAuth : NPM_AUTH_STATUS.unknown, source: "OIDC", reason: reason ?? `${registry.label}: OIDC configurado; permiso de publicación no verificable sin publicar` };
176
+ }
177
+ if (registry.options.authentication === BROWSER_AUTHENTICATION) {
178
+ const ci = process.env.CI === "true";
179
+ return { ...diagnostic, status: ci ? NPM_AUTH_STATUS.unsupportedAuth : NPM_AUTH_STATUS.unknown, source: "navegador", reason: ci ? "JSR en CI requiere token u OIDC; no puede autorizarse desde el navegador" : "JSR autoriza la publicación desde el navegador; no se verifican permisos de escritura antes" };
180
+ }
181
+ const resolution = resolveNpmToken(repositoryRoot, { tokenVariable: registry.options.tokenEnv });
182
+ diagnostic = { ...diagnostic, source: resolution.source };
183
+ if (!resolution.token) return { ...diagnostic, status: NPM_AUTH_STATUS.missingToken };
184
+ if (registry.provider === JSR_REGISTRY_PROVIDER && !JSR_SHELL_SAFE_TOKEN_PATTERN.test(resolution.token)) return { ...diagnostic, status: NPM_AUTH_STATUS.unsupportedAuth, reason: "El token JSR no se puede pasar de forma segura al cliente oficial" };
185
+ if (registry.provider !== NPM_REGISTRY_PROVIDER) return { ...diagnostic, reason: `${registry.label}: permiso de escritura no verificable; no se ejecutan npm whoami ni npm owner ls` };
186
+ const npmAuth = await checkNpmAccess(registry.packageName, repositoryRoot, registry.registryUrl, { tokenVariable: registry.options.tokenEnv, pinScope: true });
187
+ return { ...npmAuth, tokenVariable: registry.options.tokenEnv, registryLabel: registry.label };
188
+ } catch (error) {
189
+ return { ...diagnostic, reason: error instanceof Error ? error.message : "No se pudo verificar la autenticación" };
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Publishes with the chosen protocol and reconciles a failed client before declaring an unpublished version.
195
+ *
196
+ * @param {RegistrySelection} selection - One configured provider.
197
+ * @param {Record<string, unknown>} manifest - Release manifest.
198
+ * @param {string} repositoryRoot - Routing and credential root.
199
+ * @param {string} packageRoot - Actual release checkout.
200
+ * @param {string} version - Stable release version.
201
+ * @param {string | null} [artifactPath] - Verified npm archive; unused by native JSR.
202
+ * @returns {Promise<{ registry: SelectedRegistry, exitCode: number, missingToken: boolean, confirmed: boolean }>} Publication and reconciliation result.
203
+ */
204
+ export async function publishRegistryRelease(selection, manifest, repositoryRoot, packageRoot, version, artifactPath = null) {
205
+ const registry = await resolveRegistry(selection, manifest, repositoryRoot, packageRoot);
206
+ const token = registry.options.authentication === TOKEN_AUTHENTICATION ? resolveNpmToken(repositoryRoot, { tokenVariable: registry.options.tokenEnv }).token : null;
207
+ if (registry.options.authentication === TOKEN_AUTHENTICATION && !token) return { registry, exitCode: 1, missingToken: true, confirmed: false };
208
+ const jsrManifest = registry.provider === JSR_REGISTRY_PROVIDER ? readJsrManifest(packageRoot, registry.options.configFile) : null;
209
+ if (jsrManifest && jsrManifest.manifest.version !== version) throw new Error(`JSR: ${jsrManifest.filePath} no declara la versión del release ${version}`);
210
+ const result = jsrManifest
211
+ ? { exitCode: await publishToJsr(packageRoot, registry, token, jsrManifest), missingToken: false }
212
+ : await publishToNpm(repositoryRoot, {
213
+ authConfigLine: token ? buildNpmAuthConfigLine(registry.registryUrl) : "", artifactPath, packageRoot, registryUrl: registry.registryUrl,
214
+ tokenVariable: registry.options.tokenEnv, authentication: registry.options.authentication === OIDC_AUTHENTICATION ? OIDC_AUTHENTICATION : TOKEN_AUTHENTICATION, access: registry.options.access, tag: registry.options.tag, packageName: registry.packageName,
215
+ });
216
+ if (result.exitCode === 0 && registry.provider !== JSR_REGISTRY_PROVIDER) return { registry, ...result, confirmed: true };
217
+ const lookup = await lookupResolvedRegistryVersions(registry, repositoryRoot, lookupPublishedVersions, selection.provider);
218
+ const confirmed = lookup.status === NPM_LOOKUP_STATUS.ok && lookup.publishedVersions.includes(version);
219
+ return { registry, ...result, confirmed };
220
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Records and reads, in the release commit itself, where a release was prepared (`local` or `ci`).
3
+ * The mark lives in a commit trailer so it covers every release without adding tracked files that the
4
+ * Vercel build gate would interpret.
5
+ * @module create-version/release-execution
6
+ */
7
+
8
+ import { RELEASE_EXECUTION, RELEASE_EXECUTION_TRAILER } from "../constants/ci-release.js";
9
+ import { ReleaseStepError } from "./errors.js";
10
+
11
+ /**
12
+ * Builds the trailer paragraph appended to the release commit message.
13
+ * @param {"local" | "ci"} execution - Location where the release commit is prepared.
14
+ * @returns {string} Trailer line such as `Astra-Rp-Execution: ci`.
15
+ */
16
+ export function formatReleaseExecutionTrailer(execution) {
17
+ return `${RELEASE_EXECUTION_TRAILER}: ${execution}`;
18
+ }
19
+
20
+ /**
21
+ * Reads the execution location recorded in the trailer of a release commit.
22
+ * Commits created before the trailer existed (or by other tools) have none and yield null.
23
+ * @param {import("./process.js").GitReader} reader - Git reader of the release checkout.
24
+ * @param {string} version - Version of the release commit, used in diagnostics.
25
+ * @param {string} [revision] - Commit holding the trailer; defaults to `HEAD`.
26
+ * @returns {Promise<"local" | "ci" | null>} Recorded location, or null when the commit carries no trailer.
27
+ * @throws {ReleaseStepError} When the commit cannot be read, or its trailers are unknown or contradictory.
28
+ */
29
+ export async function readReleaseExecutionTrailer(reader, version, revision = "HEAD") {
30
+ const trailerOutput = await reader.tryGit(["log", "-1", `--format=%(trailers:key=${RELEASE_EXECUTION_TRAILER},valueonly=true)`, revision]);
31
+ if (trailerOutput === null) {
32
+ throw new ReleaseStepError(`No se pudo leer el trailer ${RELEASE_EXECUTION_TRAILER} del commit de release ${version} (${revision}).`, "Revisá el repositorio con git log -1; no se subió ni publicó nada.");
33
+ }
34
+ const recordedValues = [...new Set(trailerOutput.split("\n").map((value) => value.trim()).filter(Boolean))];
35
+ if (recordedValues.length === 0) return null;
36
+ const knownExecutions = /** @type {readonly string[]} */ (Object.values(RELEASE_EXECUTION));
37
+ const [recordedExecution] = recordedValues;
38
+ if (recordedValues.length > 1 || !knownExecutions.includes(recordedExecution)) {
39
+ throw new ReleaseStepError(
40
+ `El commit de release ${version} declara un modo de ejecución inválido en ${RELEASE_EXECUTION_TRAILER}: ${recordedValues.join(", ")} (se espera ${knownExecutions.join(" o ")}).`,
41
+ "Corregilo con un commit de release nuevo; no se subió ni publicó nada."
42
+ );
43
+ }
44
+ return /** @type {"local" | "ci"} */ (recordedExecution);
45
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Loads the `create-version` configuration stored in a release tag. `--retry-ci` dispatches the
3
+ * workflow of the tag with `--ref <tag>` and the worker reloads its configuration from that tag, so
4
+ * the workflow name and the Actions bindings checked before the retry must come from the tag too,
5
+ * not from a working tree that later commits may have changed without bumping the version.
6
+ * @module create-version/release-tag-config
7
+ */
8
+
9
+ import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
10
+ import path from "node:path";
11
+
12
+ import { CI_RELEASE_TAG_CONFIG_DIRECTORY_PREFIX } from "../constants/ci-release.js";
13
+ import { CREATE_VERSION_CONFIG_FILES, CREATE_VERSION_FLAG } from "../constants/create-version.js";
14
+ import { loadCreateVersionConfig } from "./config.js";
15
+ import { ReleaseStepError } from "./errors.js";
16
+ import { runCaptured } from "./process.js";
17
+
18
+ /** Name of the temporary Git index used to check out the tag without touching the project's index. */
19
+ const TEMPORARY_INDEX_FILE = "index";
20
+
21
+ /** Directory, inside the temporary one, that receives the tagged tree. */
22
+ const TAGGED_TREE_DIRECTORY = "tree";
23
+
24
+ /**
25
+ * Runs Git in the repository and fails with a retry-specific diagnostic.
26
+ * @param {string} repositoryRoot - Project root.
27
+ * @param {string[]} gitArguments - Git arguments.
28
+ * @param {string} tag - Release tag being retried, for diagnostics.
29
+ * @param {NodeJS.ProcessEnv} [environment] - Environment of the Git process.
30
+ * @returns {Promise<string>} Captured standard output.
31
+ * @throws {ReleaseStepError} When Git exits with a non-zero status.
32
+ */
33
+ async function runTagGit(repositoryRoot, gitArguments, tag, environment) {
34
+ const result = await runCaptured("git", gitArguments, { cwd: repositoryRoot, env: environment });
35
+ if (result.status !== 0) {
36
+ throw new ReleaseStepError(
37
+ `No se pudo leer la configuración guardada en ${tag} (git ${gitArguments[0]} terminó con status ${result.status}: ${result.stderr}).`,
38
+ `Revisá el repositorio Git y volvé a correr --${CREATE_VERSION_FLAG.retryCi} ${tag}; no se envió ninguna ejecución.`
39
+ );
40
+ }
41
+ return result.stdout;
42
+ }
43
+
44
+ /**
45
+ * Checks out a release tag into a temporary directory inside the repository's Git directory and
46
+ * loads the configuration stored there. The checkout uses its own index, so the project's index,
47
+ * working tree and refs stay untouched, and it is removed before returning. Placing it under the
48
+ * project lets the tagged configuration import the dependencies installed in the project root.
49
+ * @param {string} repositoryRoot - Project root.
50
+ * @param {string} tag - Release tag already verified against origin, such as `v1.2.4`.
51
+ * @returns {Promise<import("./config.js").ResolvedCreateVersionConfig>} Configuration the worker loads from the tag.
52
+ * @throws {ReleaseStepError} When the tag cannot be checked out or its configuration is missing or invalid.
53
+ */
54
+ export async function loadReleaseTagConfig(repositoryRoot, tag) {
55
+ const gitDirectory = await runTagGit(repositoryRoot, ["rev-parse", "--absolute-git-dir"], tag);
56
+ const temporaryDirectory = mkdtempSync(path.join(gitDirectory, CI_RELEASE_TAG_CONFIG_DIRECTORY_PREFIX));
57
+ try {
58
+ const taggedTree = path.join(temporaryDirectory, TAGGED_TREE_DIRECTORY);
59
+ mkdirSync(taggedTree);
60
+ const environment = { ...process.env, GIT_INDEX_FILE: path.join(temporaryDirectory, TEMPORARY_INDEX_FILE) };
61
+ await runTagGit(repositoryRoot, ["read-tree", `refs/tags/${tag}^{tree}`], tag, environment);
62
+ // Git on Windows needs `/` separators in the prefix; the trailing `/` makes it a directory.
63
+ await runTagGit(repositoryRoot, ["checkout-index", "--all", "--force", `--prefix=${taggedTree.replaceAll("\\", "/")}/`], tag, environment);
64
+ try {
65
+ return await loadCreateVersionConfig(taggedTree);
66
+ } catch (error) {
67
+ throw new ReleaseStepError(
68
+ `No se pudo cargar ${CREATE_VERSION_CONFIG_FILES.join(" o ")} desde ${tag}: el worker usa la configuración del tag, no la del working tree.`,
69
+ `Revisá la configuración commiteada en ${tag} y las dependencias que importa; no se envió ninguna ejecución.`,
70
+ { cause: error }
71
+ );
72
+ }
73
+ } finally {
74
+ rmSync(temporaryDirectory, { recursive: true, force: true });
75
+ }
76
+ }