@quark-hq/quark-scripts 0.0.1
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/LICENSE +21 -0
- package/dist/app/base-application.js +67 -0
- package/dist/app/buildApplication.js +20 -0
- package/dist/app/copyStaticApplication.js +34 -0
- package/dist/app/devApplication.js +59 -0
- package/dist/app/prod-publish-application.js +124 -0
- package/dist/app/publish-dev-application.js +104 -0
- package/dist/app/publishDevApplication.js +16 -0
- package/dist/app/release-application.js +85 -0
- package/dist/app/releaseApplication.js +154 -0
- package/dist/app/unfreeze-application.js +75 -0
- package/dist/app/watchStaticApplication.js +34 -0
- package/dist/cli/cli-containers.js +70 -0
- package/dist/cli/cliCommand.js +2 -0
- package/dist/cli/cliContainers.js +62 -0
- package/dist/cli/commands/buildCommands.js +17 -0
- package/dist/cli/commands/copyStaticCommands.js +17 -0
- package/dist/cli/commands/devCommands.js +17 -0
- package/dist/cli/commands/prod-publish-commands.js +18 -0
- package/dist/cli/commands/publish-dev-commands.js +17 -0
- package/dist/cli/commands/publishDevCommands.js +17 -0
- package/dist/cli/commands/release-commands.js +17 -0
- package/dist/cli/commands/releaseCommands.js +17 -0
- package/dist/cli/commands/relesaeCommand.js +15 -0
- package/dist/cli/commands/unfreeze-commands.js +17 -0
- package/dist/cli/commands/watchStaticCommands.js +17 -0
- package/dist/cli/program.js +16 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.js +132 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/domain/graph.js +2 -0
- package/dist/domain/new-package-freeze-from-map.js +21 -0
- package/dist/domain/resolve-workspace-dependency-specifier-for-freeze-map.js +33 -0
- package/dist/domain/reverse-dependents.js +45 -0
- package/dist/domain/topological-sorting.js +43 -0
- package/dist/domain/transitive-dependents.js +25 -0
- package/dist/domain/workspace-transitive-dependencies.js +25 -0
- package/dist/errors/unfreeze-blocked-error.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/adapters/base-release-adapter.js +281 -0
- package/dist/infrastructure/adapters/maven-prod-publish-adapter.js +48 -0
- package/dist/infrastructure/adapters/maven-release-adapter.js +58 -0
- package/dist/infrastructure/adapters/node-prod-publish-adapter.js +111 -0
- package/dist/infrastructure/adapters/node-release-adapter/node-file-update.js +1 -0
- package/dist/infrastructure/adapters/node-release-adapter/node-release-adapter.js +289 -0
- package/dist/infrastructure/adapters/node-release-adapter.js +202 -0
- package/dist/infrastructure/config/loadDotEnv.js +38 -0
- package/dist/infrastructure/config/nodeRegistryEnv.js +41 -0
- package/dist/infrastructure/config/quarkConfigProvider.js +50 -0
- package/dist/infrastructure/git/gitService.js +179 -0
- package/dist/infrastructure/graph/nxGraphProvide.js +84 -0
- package/dist/infrastructure/logging/consoleLogger.js +25 -0
- package/dist/infrastructure/process/nodeProcessRunner.js +14 -0
- package/dist/infrastructure/release/git-release-map-store.js +64 -0
- package/dist/ports/config.js +2 -0
- package/dist/ports/git.js +2 -0
- package/dist/ports/graph.js +2 -0
- package/dist/ports/logger.js +2 -0
- package/dist/ports/map.js +2 -0
- package/dist/ports/platform-dev-publish-adapter.js +2 -0
- package/dist/ports/platform-prod-publish-adapter.js +2 -0
- package/dist/ports/platform-release-adapter.js +2 -0
- package/dist/ports/processRunner.js +2 -0
- package/dist/ports/prompts.js +2 -0
- package/dist/ports/release-map-store.js +2 -0
- package/dist/scripts/dev.d.ts +20 -0
- package/dist/scripts/dev.js +204 -0
- package/dist/scripts/dev.js.map +1 -0
- package/dist/scripts/map.json +1616 -0
- package/dist/scripts/prod.d.ts +1 -0
- package/dist/scripts/prod.js +143 -0
- package/dist/scripts/prod.js.map +1 -0
- package/dist/scripts/release.d.ts +51 -0
- package/dist/scripts/release.js +833 -0
- package/dist/scripts/release.js.map +1 -0
- package/dist/tests/index.test.js +13 -0
- package/dist/tests/mocks/release.js +8 -0
- package/dist/tests/unit/release/index.test.js +32 -0
- package/dist/utils/checkIfFreeze.js +1 -0
- package/dist/utils/commit.js +39 -0
- package/dist/utils/drawRepoStatus.js +66 -0
- package/dist/utils/file.js +14 -0
- package/dist/utils/metrics.js +22 -0
- package/package.json +64 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.NodeReleaseAdapter = void 0;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
42
|
+
const semver = __importStar(require("semver"));
|
|
43
|
+
const path_1 = __importDefault(require("path"));
|
|
44
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
45
|
+
class NodeReleaseAdapter {
|
|
46
|
+
constructor(git, graphProvider, quarkConfig) {
|
|
47
|
+
this.git = git;
|
|
48
|
+
this.graphProvider = graphProvider;
|
|
49
|
+
this.quarkConfig = quarkConfig;
|
|
50
|
+
this.userPrompts = {};
|
|
51
|
+
}
|
|
52
|
+
supports(meta) {
|
|
53
|
+
return meta.platform === "node";
|
|
54
|
+
}
|
|
55
|
+
async execute(packages, graph, sorted, masterBranch) {
|
|
56
|
+
const sortedPackages = sorted.filter((pkg) => packages.includes(pkg));
|
|
57
|
+
let mapJson = '';
|
|
58
|
+
try {
|
|
59
|
+
mapJson = await this.git.readFile(".release/map.json", masterBranch);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
mapJson = "{}";
|
|
63
|
+
}
|
|
64
|
+
// console.log(mapJson, "mapJson");
|
|
65
|
+
const mapJsonObject = JSON.parse(mapJson);
|
|
66
|
+
for (const pkg of sortedPackages) {
|
|
67
|
+
if (!this.userPrompts[pkg]) {
|
|
68
|
+
this.userPrompts[pkg] = await this.askUser(pkg, graph, mapJsonObject, sorted);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return this.userPrompts;
|
|
72
|
+
}
|
|
73
|
+
async askUser(pkg, graph, mapJsonObject, sorted) {
|
|
74
|
+
console.log("\n" + chalk_1.default.gray("──────────────────────────────────────────"));
|
|
75
|
+
console.log(chalk_1.default.bold.cyan(`📦 Releasing: ${pkg}`));
|
|
76
|
+
console.log(chalk_1.default.gray("──────────────────────────────────────────\n"));
|
|
77
|
+
let isNewPackage = !mapJsonObject[pkg];
|
|
78
|
+
let isFrozenPackage = mapJsonObject[pkg]?.frozen;
|
|
79
|
+
let isMajorBumpDisabled = isNewPackage || isFrozenPackage;
|
|
80
|
+
const response = await (0, prompts_1.default)([
|
|
81
|
+
{
|
|
82
|
+
type: "select",
|
|
83
|
+
name: "bump",
|
|
84
|
+
message: chalk_1.default.yellow("Select version bump type"),
|
|
85
|
+
choices: [
|
|
86
|
+
{
|
|
87
|
+
title: `🆕 New Package`,
|
|
88
|
+
description: !isNewPackage ? "Disabled as package already published" : "New Package Found",
|
|
89
|
+
value: "new",
|
|
90
|
+
disabled: !isNewPackage,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: `🐛 Patch (bug fixes) ${isNewPackage ? "Disabled as package is new" : `→ ${semver.inc("1.0.0", "patch")}`}`,
|
|
94
|
+
description: `→ ${semver.inc("1.0.0", "patch")}`,
|
|
95
|
+
value: "patch",
|
|
96
|
+
disabled: isNewPackage,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
title: `✨ Minor (new features) ${isNewPackage ? "Disabled as package is new" : `→ ${semver.inc("1.0.0", "minor")}`}`,
|
|
100
|
+
description: `→ ${semver.inc("1.0.0", "minor")}`,
|
|
101
|
+
value: "minor",
|
|
102
|
+
disabled: isNewPackage,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: `💥 Major (breaking changes) ${isNewPackage ? "Disabled as package is new" : isFrozenPackage ? "Disabled as package is frozen" : `→ ${semver.inc("1.0.0", "major")}`}`,
|
|
106
|
+
description: `→ ${semver.inc("1.0.0", "major")}`,
|
|
107
|
+
value: "major",
|
|
108
|
+
disabled: isMajorBumpDisabled
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: "text",
|
|
114
|
+
name: "changelog",
|
|
115
|
+
message: chalk_1.default.yellow("Enter changelog description"),
|
|
116
|
+
validate: (value) => value.trim().length === 0
|
|
117
|
+
? "Changelog cannot be empty"
|
|
118
|
+
: true,
|
|
119
|
+
},
|
|
120
|
+
], {
|
|
121
|
+
onCancel: () => {
|
|
122
|
+
console.log(chalk_1.default.red("\n❌ Release cancelled by user"));
|
|
123
|
+
process.exit(1);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
const result = {
|
|
127
|
+
bump: response.bump,
|
|
128
|
+
frozen: mapJsonObject[pkg]?.frozen || false,
|
|
129
|
+
baseVersion: mapJsonObject[pkg]?.newVersion || "1.0.0",
|
|
130
|
+
newVersion: response.bump === "new" ? "1.0.0" : (semver.inc(mapJsonObject[pkg]?.newVersion || "1.0.0", response.bump) ?? "1.0.0"),
|
|
131
|
+
changelog: response.changelog,
|
|
132
|
+
};
|
|
133
|
+
if (result.bump === "major") {
|
|
134
|
+
await this.handleMajorVersionBumpConfig(pkg, graph, mapJsonObject, sorted);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
async handleMajorVersionBumpConfig(pkg, graph, mapJsonObject, sorted) {
|
|
139
|
+
const invertedAdjacenyList = await this.graphProvider.getInvertedAdjacencyList(graph.adjacency);
|
|
140
|
+
const dependents = sorted.filter((dep) => invertedAdjacenyList[pkg].includes(dep));
|
|
141
|
+
let choise = "none";
|
|
142
|
+
if (this.quarkConfig?.release?.freeze) {
|
|
143
|
+
const response = await (0, prompts_1.default)({
|
|
144
|
+
type: "select",
|
|
145
|
+
name: "dependencies",
|
|
146
|
+
message: "Select the dependencies to freeze",
|
|
147
|
+
choices: [
|
|
148
|
+
{
|
|
149
|
+
title: "All",
|
|
150
|
+
description: "Freeze all dependents at their current published version",
|
|
151
|
+
value: "all"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
title: "None",
|
|
155
|
+
description: "Auto-cascade major bump to all dependents",
|
|
156
|
+
value: "none"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
title: "Selective",
|
|
160
|
+
description: "Select which dependents to freeze and which to cascade",
|
|
161
|
+
value: "selective"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
choise = response.dependencies;
|
|
166
|
+
}
|
|
167
|
+
if (choise === "all") {
|
|
168
|
+
await this.handleFrozenDependents(pkg, graph, mapJsonObject, dependents);
|
|
169
|
+
}
|
|
170
|
+
else if (choise === "none") {
|
|
171
|
+
await this.handleMajorVersionBump(pkg, graph, mapJsonObject, dependents);
|
|
172
|
+
}
|
|
173
|
+
else if (choise === "selective") {
|
|
174
|
+
await this.handleSelectiveMajorVersionBump(pkg, graph, mapJsonObject, dependents, invertedAdjacenyList);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
isValidTopologicalSelection(ordered, selected, invertedAdjacencyList) {
|
|
178
|
+
if (selected.length === 0)
|
|
179
|
+
return true;
|
|
180
|
+
const selectedSet = new Set(selected);
|
|
181
|
+
for (const node of selected) {
|
|
182
|
+
// invertedAdjacencyList[node] = nodes that depend ON this node
|
|
183
|
+
const dependents = invertedAdjacencyList[node] ?? [];
|
|
184
|
+
for (const dependent of dependents) {
|
|
185
|
+
// dependent is in our candidate list but not selected — invalid
|
|
186
|
+
if (ordered.includes(dependent) && !selectedSet.has(dependent)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
async handleSelectiveMajorVersionBump(pkg, graph, mapJsonObject, dependents, invertedAdjacenyList) {
|
|
194
|
+
const selected = await this.handleSelectiveMajorVersionBumpPrompt(dependents);
|
|
195
|
+
// Handle case where user presses enter or doesn't select any items (prompt cancelled)
|
|
196
|
+
if (!selected || !Array.isArray(selected)) {
|
|
197
|
+
console.log(chalk_1.default.yellow("No selection made. Operation cancelled."));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (this.isValidTopologicalSelection(dependents, selected, invertedAdjacenyList)) {
|
|
201
|
+
const unSelected = dependents.filter((dependent) => !selected.includes(dependent));
|
|
202
|
+
await this.handleMajorVersionBump(pkg, graph, mapJsonObject, unSelected);
|
|
203
|
+
await this.handleFrozenDependents(pkg, graph, mapJsonObject, selected);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
console.log(chalk_1.default.red("Selection must be a continuous suffix of the list. Please select a continuous suffix of the list."));
|
|
207
|
+
await this.handleSelectiveMajorVersionBump(pkg, graph, mapJsonObject, dependents, invertedAdjacenyList);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async handleSelectiveMajorVersionBumpPrompt(dependents) {
|
|
211
|
+
const response = await (0, prompts_1.default)({
|
|
212
|
+
type: "multiselect",
|
|
213
|
+
name: "dependencies",
|
|
214
|
+
message: "Select the dependencies to freeze",
|
|
215
|
+
choices: dependents.map((dependent) => ({
|
|
216
|
+
title: dependent,
|
|
217
|
+
value: dependent
|
|
218
|
+
})),
|
|
219
|
+
});
|
|
220
|
+
return response.dependencies;
|
|
221
|
+
}
|
|
222
|
+
async handleMajorVersionBump(pkg, graph, mapJsonObject, dependents) {
|
|
223
|
+
dependents.forEach(async (dependent) => {
|
|
224
|
+
this.userPrompts[dependent] = {
|
|
225
|
+
bump: "major",
|
|
226
|
+
frozen: false,
|
|
227
|
+
baseVersion: mapJsonObject[dependent]?.newVersion || "1.0.0",
|
|
228
|
+
newVersion: semver.inc(mapJsonObject[dependent]?.newVersion || "1.0.0", "major") || "1.0.0",
|
|
229
|
+
changelog: "Dependency version bumped to major version due to major version bump of " + pkg
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async handleFrozenDependents(pkg, graph, mapJsonObject, dependents) {
|
|
234
|
+
console.log("🔄 Handling frozen dependents for ", pkg);
|
|
235
|
+
dependents.forEach(async (dependent) => {
|
|
236
|
+
this.userPrompts[dependent] = {
|
|
237
|
+
bump: mapJsonObject[dependent]?.bumpType,
|
|
238
|
+
frozen: true,
|
|
239
|
+
baseVersion: mapJsonObject[dependent]?.baseVersion || "1.0.0",
|
|
240
|
+
newVersion: mapJsonObject[dependent]?.newVersion || "1.0.0",
|
|
241
|
+
changelog: "Dependency version frozen due to major version bump of " + pkg
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
async writeUserPromptsToFiles(pkg, prompt, mapJsonObject) {
|
|
246
|
+
console.log("prompt", pkg);
|
|
247
|
+
mapJsonObject[pkg] = {
|
|
248
|
+
bumpType: prompt.bump,
|
|
249
|
+
baseVersion: prompt.baseVersion,
|
|
250
|
+
newVersion: prompt.newVersion,
|
|
251
|
+
changeLog: prompt.changelog,
|
|
252
|
+
frozen: prompt.frozen,
|
|
253
|
+
};
|
|
254
|
+
const pathToPackage = await this.graphProvider.getPackageJson(pkg);
|
|
255
|
+
const pacakgeJSONpath = path_1.default.resolve(process.cwd(), pathToPackage, 'package.json');
|
|
256
|
+
// Read the existing package.json, update version, and write back
|
|
257
|
+
const packageJsonContent = await promises_1.default.readFile(pacakgeJSONpath, 'utf8');
|
|
258
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
259
|
+
if (this.userPrompts[pkg].frozen) {
|
|
260
|
+
packageJson.version = prompt.newVersion;
|
|
261
|
+
if (packageJson?.dependencies) {
|
|
262
|
+
Object.keys(packageJson?.dependencies)?.forEach((dependency) => {
|
|
263
|
+
if (dependency in mapJsonObject) {
|
|
264
|
+
packageJson.dependencies[dependency] = mapJsonObject[dependency].newVersion;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (packageJson?.peerDependencies) {
|
|
269
|
+
Object.keys(packageJson?.peerDependencies)?.forEach((dependency) => {
|
|
270
|
+
if (dependency in mapJsonObject) {
|
|
271
|
+
packageJson.peerDependencies[dependency] = mapJsonObject[dependency].newVersion;
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (packageJson?.devDependencies) {
|
|
276
|
+
Object.keys(packageJson?.devDependencies)?.forEach((dependency) => {
|
|
277
|
+
if (dependency in mapJsonObject) {
|
|
278
|
+
packageJson.devDependencies[dependency] = mapJsonObject[dependency].newVersion;
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
packageJson.version = prompt.newVersion;
|
|
285
|
+
}
|
|
286
|
+
await promises_1.default.writeFile(pacakgeJSONpath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
exports.NodeReleaseAdapter = NodeReleaseAdapter;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NodeReleaseAdapter = void 0;
|
|
7
|
+
const quark_security_1 = require("@quark-hq/quark-security");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const workspace_transitive_dependencies_1 = require("../../domain/workspace-transitive-dependencies");
|
|
10
|
+
const base_release_adapter_1 = require("./base-release-adapter");
|
|
11
|
+
const resolve_workspace_dependency_specifier_for_freeze_map_1 = require("../../domain/resolve-workspace-dependency-specifier-for-freeze-map");
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const nodeRegistryEnv_1 = require("../config/nodeRegistryEnv");
|
|
14
|
+
class NodeReleaseAdapter extends base_release_adapter_1.BaseReleaseAdapter {
|
|
15
|
+
constructor(graphProvider, quarkConfig, logger) {
|
|
16
|
+
super(graphProvider, quarkConfig, logger);
|
|
17
|
+
}
|
|
18
|
+
supports(meta) {
|
|
19
|
+
return meta.platform === "node";
|
|
20
|
+
}
|
|
21
|
+
getPlatformLabel(pkg) {
|
|
22
|
+
return chalk_1.default.bold.cyan(`📦 Releasing: ${pkg}`);
|
|
23
|
+
}
|
|
24
|
+
// ── Release: file writes ───────────────────────────────────────────
|
|
25
|
+
async writePackageFiles(pkg, prompt, mapJsonObject, graph) {
|
|
26
|
+
const rootPath = await this.graphProvider.getPackageJson(pkg);
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
// bearer:disable javascript_lang_path_traversal
|
|
29
|
+
const absRoot = (0, quark_security_1.assertPathInsideRoot)(cwd, path_1.default.resolve(cwd, rootPath));
|
|
30
|
+
const packageJsonPath = path_1.default.join(absRoot, "package.json");
|
|
31
|
+
const raw = await (0, quark_security_1.readFileSafe)(cwd, packageJsonPath);
|
|
32
|
+
const packageJson = JSON.parse(raw);
|
|
33
|
+
packageJson.version = prompt.newVersion;
|
|
34
|
+
if (prompt.frozen) {
|
|
35
|
+
this.syncDependencyVersions(packageJson, "dependencies", mapJsonObject);
|
|
36
|
+
this.syncDependencyVersions(packageJson, "peerDependencies", mapJsonObject);
|
|
37
|
+
this.syncDependencyVersions(packageJson, "devDependencies", mapJsonObject);
|
|
38
|
+
this.syncDependencyVersions(packageJson, "optionalDependencies", mapJsonObject);
|
|
39
|
+
const pinned = this.collectPinnedWorkspaceDependencyVersions(pkg, packageJson, mapJsonObject, graph.adjacency);
|
|
40
|
+
mapJsonObject[pkg] = {
|
|
41
|
+
...mapJsonObject[pkg],
|
|
42
|
+
...(Object.keys(pinned).length > 0
|
|
43
|
+
? { pinnedDependencies: pinned }
|
|
44
|
+
: {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
await (0, quark_security_1.writeFileSafe)(cwd, packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* After pinning, records each workspace package in the dependency closure (direct +
|
|
51
|
+
* transitive in the graph) with the exact specifier used at freeze time (see
|
|
52
|
+
* {@link resolveWorkspaceDependencySpecifierForFreezeMap}). package.json is only
|
|
53
|
+
* updated for direct dependencies.
|
|
54
|
+
*/
|
|
55
|
+
collectPinnedWorkspaceDependencyVersions(pkg, packageJson, mapJsonObject, adjacency) {
|
|
56
|
+
const pinned = {};
|
|
57
|
+
const depFields = [
|
|
58
|
+
"dependencies",
|
|
59
|
+
"peerDependencies",
|
|
60
|
+
"devDependencies",
|
|
61
|
+
"optionalDependencies",
|
|
62
|
+
];
|
|
63
|
+
for (const field of depFields) {
|
|
64
|
+
const deps = packageJson[field];
|
|
65
|
+
if (!deps || typeof deps !== "object")
|
|
66
|
+
continue;
|
|
67
|
+
for (const [depName, spec] of Object.entries(deps)) {
|
|
68
|
+
if (typeof spec !== "string")
|
|
69
|
+
continue;
|
|
70
|
+
if (spec.startsWith("workspace:"))
|
|
71
|
+
continue;
|
|
72
|
+
if ((0, resolve_workspace_dependency_specifier_for_freeze_map_1.resolveWorkspaceDependencySpecifierForFreezeMap)(depName, mapJsonObject) === undefined) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
pinned[depName] = spec;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const transitive = (0, workspace_transitive_dependencies_1.collectTransitiveWorkspaceDependencies)(pkg, adjacency);
|
|
79
|
+
for (const depName of transitive) {
|
|
80
|
+
const spec = (0, resolve_workspace_dependency_specifier_for_freeze_map_1.resolveWorkspaceDependencySpecifierForFreezeMap)(depName, mapJsonObject);
|
|
81
|
+
if (spec !== undefined) {
|
|
82
|
+
pinned[depName] = spec;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const sortedKeys = Object.keys(pinned).sort((a, b) => a.localeCompare(b));
|
|
86
|
+
const ordered = {};
|
|
87
|
+
for (const k of sortedKeys) {
|
|
88
|
+
ordered[k] = pinned[k];
|
|
89
|
+
}
|
|
90
|
+
return ordered;
|
|
91
|
+
}
|
|
92
|
+
syncDependencyVersions(packageJson, depField, mapJsonObject) {
|
|
93
|
+
const deps = packageJson[depField];
|
|
94
|
+
if (!deps)
|
|
95
|
+
return;
|
|
96
|
+
for (const depName of Object.keys(deps)) {
|
|
97
|
+
const spec = (0, resolve_workspace_dependency_specifier_for_freeze_map_1.resolveWorkspaceDependencySpecifierForFreezeMap)(depName, mapJsonObject);
|
|
98
|
+
if (spec !== undefined) {
|
|
99
|
+
deps[depName] = spec;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Restore workspace dependency versions to workspace:* (used when unfreezing). */
|
|
104
|
+
async restoreWorkspaceVersions(pkg, workspacePackageNames) {
|
|
105
|
+
const rootPath = await this.graphProvider.getPackageJson(pkg);
|
|
106
|
+
const cwd = process.cwd();
|
|
107
|
+
// bearer:disable javascript_lang_path_traversal
|
|
108
|
+
const absRoot = (0, quark_security_1.assertPathInsideRoot)(cwd, path_1.default.resolve(cwd, rootPath));
|
|
109
|
+
const packageJsonPath = path_1.default.join(absRoot, "package.json");
|
|
110
|
+
const raw = await (0, quark_security_1.readFileSafe)(cwd, packageJsonPath);
|
|
111
|
+
const packageJson = JSON.parse(raw);
|
|
112
|
+
const depFields = [
|
|
113
|
+
"dependencies",
|
|
114
|
+
"devDependencies",
|
|
115
|
+
"peerDependencies",
|
|
116
|
+
"optionalDependencies",
|
|
117
|
+
];
|
|
118
|
+
for (const field of depFields) {
|
|
119
|
+
const deps = packageJson[field];
|
|
120
|
+
if (!deps || typeof deps !== "object")
|
|
121
|
+
continue;
|
|
122
|
+
for (const depName of Object.keys(deps)) {
|
|
123
|
+
if (workspacePackageNames.has(depName)) {
|
|
124
|
+
deps[depName] = "workspace:*";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
await (0, quark_security_1.writeFileSafe)(cwd, packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
129
|
+
}
|
|
130
|
+
// ── Dev-publish ────────────────────────────────────────────────────
|
|
131
|
+
async getCurrentVersion(packageDir) {
|
|
132
|
+
const pkgJsonPath = path_1.default.join(packageDir, "package.json");
|
|
133
|
+
const raw = await (0, quark_security_1.readFileSafe)(packageDir, pkgJsonPath);
|
|
134
|
+
return JSON.parse(raw).version ?? "1.0.0";
|
|
135
|
+
}
|
|
136
|
+
async publish(ctx, alphaVersion) {
|
|
137
|
+
const { packageName, packageDir, repoRoot } = ctx;
|
|
138
|
+
const safePackageDir = (0, quark_security_1.assertPathInsideRoot)(repoRoot, packageDir);
|
|
139
|
+
const pkgJsonPath = path_1.default.join(safePackageDir, "package.json");
|
|
140
|
+
const originalPkgJson = await (0, quark_security_1.readFileSafe)(repoRoot, pkgJsonPath);
|
|
141
|
+
const pkgJson = JSON.parse(originalPkgJson);
|
|
142
|
+
pkgJson.version = alphaVersion;
|
|
143
|
+
await (0, quark_security_1.writeFileSafe)(repoRoot, pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
144
|
+
this.logger.info(`Updated version to ${alphaVersion}`);
|
|
145
|
+
const npmrcPath = path_1.default.join(repoRoot, ".npmrc");
|
|
146
|
+
const originalNpmrc = await this.readFileOrNull(repoRoot, npmrcPath);
|
|
147
|
+
await (0, quark_security_1.writeFileSafe)(repoRoot, npmrcPath, this.buildDevNpmrc());
|
|
148
|
+
this.logger.info("Overwrote project .npmrc with dev registry");
|
|
149
|
+
try {
|
|
150
|
+
this.logger.info(`Building package: ${packageName}`);
|
|
151
|
+
(0, quark_security_1.assertSpawnOk)((0, quark_security_1.spawnSyncSafe)("pnpm", ["run", "build"], {
|
|
152
|
+
cwd: safePackageDir,
|
|
153
|
+
stdio: "inherit",
|
|
154
|
+
}), "pnpm run build");
|
|
155
|
+
this.logger.info(`Publishing ${packageName}@${alphaVersion}...`);
|
|
156
|
+
(0, quark_security_1.assertSpawnOk)((0, quark_security_1.spawnSyncSafe)("pnpm", ["publish", "--no-git-check", "--access", "public"], { cwd: safePackageDir, stdio: "inherit" }), "pnpm publish");
|
|
157
|
+
this.logger.success(`Published ${packageName}@${alphaVersion} to dev registry`);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
if (originalNpmrc !== null) {
|
|
161
|
+
await (0, quark_security_1.writeFileSafe)(repoRoot, npmrcPath, originalNpmrc);
|
|
162
|
+
this.logger.info("Restored original project .npmrc");
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
await (0, quark_security_1.unlinkSafe)(repoRoot, npmrcPath).catch(() => { });
|
|
166
|
+
this.logger.info("Removed temporary project .npmrc");
|
|
167
|
+
}
|
|
168
|
+
await (0, quark_security_1.writeFileSafe)(repoRoot, pkgJsonPath, originalPkgJson);
|
|
169
|
+
this.logger.info("Restored original package.json");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
buildDevNpmrc() {
|
|
173
|
+
const nodeConfig = this.quarkConfig.publish?.node;
|
|
174
|
+
const registryUrl = (0, nodeRegistryEnv_1.effectiveDevRegistryUrl)(nodeConfig);
|
|
175
|
+
if (!registryUrl) {
|
|
176
|
+
throw new Error("Dev registry URL not configured. Set DEV_REGISTRY_URL in .env or publish.node.registryUrl (or publish.node.devRegistryUrl) in quark-config.json");
|
|
177
|
+
}
|
|
178
|
+
const scope = (0, nodeRegistryEnv_1.effectiveNodeScope)(nodeConfig);
|
|
179
|
+
if (!scope) {
|
|
180
|
+
throw new Error("Registry scope not configured. Set SCOPE in .env or publish.node.scope in quark-config.json");
|
|
181
|
+
}
|
|
182
|
+
const authToken = process.env.DEV_AUTH_TOKEN || process.env.NEXUS_DEV_AUTH;
|
|
183
|
+
if (!authToken) {
|
|
184
|
+
throw new Error("Auth token required for dev publishing: set DEV_AUTH_TOKEN or NEXUS_DEV_AUTH");
|
|
185
|
+
}
|
|
186
|
+
const registryPath = registryUrl.replace(/^https?:/, "");
|
|
187
|
+
return [
|
|
188
|
+
`${scope}:registry=${registryUrl}`,
|
|
189
|
+
`${registryPath}:_auth=${authToken}`,
|
|
190
|
+
`${registryPath}:always-auth=true`,
|
|
191
|
+
].join("\n") + "\n";
|
|
192
|
+
}
|
|
193
|
+
async readFileOrNull(repoRoot, filePath) {
|
|
194
|
+
try {
|
|
195
|
+
return await (0, quark_security_1.readFileSafe)(repoRoot, filePath);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.NodeReleaseAdapter = NodeReleaseAdapter;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadDotEnvFromRepoRoot = loadDotEnvFromRepoRoot;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
/**
|
|
10
|
+
* Loads `.env` from `cwd` into `process.env` for keys that are not already set.
|
|
11
|
+
* Tolerates spaces around `=` and optional single/double quotes on values.
|
|
12
|
+
*/
|
|
13
|
+
function loadDotEnvFromRepoRoot(cwd = process.cwd()) {
|
|
14
|
+
const envPath = path_1.default.resolve(cwd, ".env");
|
|
15
|
+
if (!fs_1.default.existsSync(envPath)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const text = fs_1.default.readFileSync(envPath, "utf8");
|
|
19
|
+
for (const line of text.split(/\r?\n/)) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const eq = trimmed.indexOf("=");
|
|
25
|
+
if (eq === -1) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const key = trimmed.slice(0, eq).trim();
|
|
29
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
30
|
+
if ((value.startsWith("'") && value.endsWith("'")) ||
|
|
31
|
+
(value.startsWith('"') && value.endsWith('"'))) {
|
|
32
|
+
value = value.slice(1, -1).trim();
|
|
33
|
+
}
|
|
34
|
+
if (key && process.env[key] === undefined) {
|
|
35
|
+
process.env[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeEnvString = normalizeEnvString;
|
|
4
|
+
exports.effectiveDevRegistryUrl = effectiveDevRegistryUrl;
|
|
5
|
+
exports.effectiveProdRegistryUrl = effectiveProdRegistryUrl;
|
|
6
|
+
exports.effectiveNodeScope = effectiveNodeScope;
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes a value from `process.env` or config (trim, strip surrounding quotes).
|
|
9
|
+
*/
|
|
10
|
+
function normalizeEnvString(value) {
|
|
11
|
+
if (value === undefined) {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
const t = value.trim();
|
|
15
|
+
if (!t) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
if ((t.startsWith("'") && t.endsWith("'")) ||
|
|
19
|
+
(t.startsWith('"') && t.endsWith('"'))) {
|
|
20
|
+
return t.slice(1, -1).trim();
|
|
21
|
+
}
|
|
22
|
+
return t;
|
|
23
|
+
}
|
|
24
|
+
/** Dev registry: `DEV_REGISTRY_URL`, then quark-config `devRegistryUrl` / `registryUrl`. */
|
|
25
|
+
function effectiveDevRegistryUrl(node) {
|
|
26
|
+
return (normalizeEnvString(process.env.DEV_REGISTRY_URL) ||
|
|
27
|
+
normalizeEnvString(node?.devRegistryUrl) ||
|
|
28
|
+
normalizeEnvString(node?.registryUrl));
|
|
29
|
+
}
|
|
30
|
+
/** Prod registry: `PROD_REGISTRY_URL`, then quark-config `prodRegistryUrl` / `registryUrl`. */
|
|
31
|
+
function effectiveProdRegistryUrl(node) {
|
|
32
|
+
return (normalizeEnvString(process.env.PROD_REGISTRY_URL) ||
|
|
33
|
+
normalizeEnvString(node?.prodRegistryUrl) ||
|
|
34
|
+
normalizeEnvString(node?.registryUrl));
|
|
35
|
+
}
|
|
36
|
+
/** Scope: `SCOPE`, then quark-config `publish.node.scope`. */
|
|
37
|
+
function effectiveNodeScope(node) {
|
|
38
|
+
return (normalizeEnvString(process.env.SCOPE) ||
|
|
39
|
+
normalizeEnvString(node?.scope) ||
|
|
40
|
+
"");
|
|
41
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.QuarkConfigProvider = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
release: {
|
|
11
|
+
masterBranch: "main",
|
|
12
|
+
autoCommit: false,
|
|
13
|
+
autoBump: true,
|
|
14
|
+
freeze: true,
|
|
15
|
+
},
|
|
16
|
+
publish: {
|
|
17
|
+
node: {
|
|
18
|
+
registryUrl: "",
|
|
19
|
+
scope: "",
|
|
20
|
+
},
|
|
21
|
+
maven: {
|
|
22
|
+
repositoryUrl: "",
|
|
23
|
+
repositoryId: "",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
class QuarkConfigProvider {
|
|
28
|
+
getConfig() {
|
|
29
|
+
const configPath = path_1.default.resolve(process.cwd(), "quark-config.json");
|
|
30
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
31
|
+
return DEFAULT_CONFIG;
|
|
32
|
+
}
|
|
33
|
+
const raw = fs_1.default.readFileSync(configPath, "utf-8");
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
return {
|
|
36
|
+
release: { ...DEFAULT_CONFIG.release, ...parsed.release },
|
|
37
|
+
publish: {
|
|
38
|
+
node: {
|
|
39
|
+
...DEFAULT_CONFIG.publish.node,
|
|
40
|
+
...parsed.publish?.node,
|
|
41
|
+
},
|
|
42
|
+
maven: {
|
|
43
|
+
...DEFAULT_CONFIG.publish.maven,
|
|
44
|
+
...parsed.publish?.maven,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.QuarkConfigProvider = QuarkConfigProvider;
|