@manojkmfsi/monodog 1.1.21 → 1.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/controllers/publish-controller.js +249 -0
- package/dist/middleware/server-startup.js +9 -0
- package/dist/routes/publish-routes.js +40 -0
- package/dist/services/changeset-service.js +285 -0
- package/monodog-dashboard/dist/assets/index-7G3SCgcz.css +1 -0
- package/monodog-dashboard/dist/assets/index-BYVT2h3u.js +12 -0
- package/monodog-dashboard/dist/index.html +2 -2
- package/package.json +1 -1
- package/monodog-dashboard/dist/assets/index-DN0rk9Ub.css +0 -1
- package/monodog-dashboard/dist/assets/index-DS89XTlx.js +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPublishPackages = getPublishPackages;
|
|
4
|
+
exports.getPublishChangesets = getPublishChangesets;
|
|
5
|
+
exports.previewPublish = previewPublish;
|
|
6
|
+
exports.createChangeset = createChangeset;
|
|
7
|
+
exports.checkPublishStatus = checkPublishStatus;
|
|
8
|
+
exports.triggerPublish = triggerPublish;
|
|
9
|
+
const logger_1 = require("../middleware/logger");
|
|
10
|
+
const changeset_service_1 = require("../services/changeset-service");
|
|
11
|
+
/**
|
|
12
|
+
* Get all workspace packages
|
|
13
|
+
*/
|
|
14
|
+
async function getPublishPackages(req, res) {
|
|
15
|
+
try {
|
|
16
|
+
const rootPath = req.app.locals.rootPath;
|
|
17
|
+
const packages = await (0, changeset_service_1.getWorkspacePackages)(rootPath);
|
|
18
|
+
// Filter out private packages for UI display
|
|
19
|
+
const publicPackages = packages.filter((pkg) => !pkg.private);
|
|
20
|
+
res.json({
|
|
21
|
+
success: true,
|
|
22
|
+
packages: publicPackages,
|
|
23
|
+
total: publicPackages.length,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger_1.AppLogger.error(`Failed to fetch packages: ${error}`);
|
|
28
|
+
res.status(500).json({
|
|
29
|
+
success: false,
|
|
30
|
+
error: 'Failed to fetch packages',
|
|
31
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get existing unpublished changesets
|
|
37
|
+
*/
|
|
38
|
+
async function getPublishChangesets(req, res) {
|
|
39
|
+
try {
|
|
40
|
+
const rootPath = req.app.locals.rootPath;
|
|
41
|
+
const changesets = await (0, changeset_service_1.getExistingChangesets)(rootPath);
|
|
42
|
+
res.json({
|
|
43
|
+
success: true,
|
|
44
|
+
changesets,
|
|
45
|
+
total: changesets.length,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
logger_1.AppLogger.error(`Failed to fetch changesets: ${error}`);
|
|
50
|
+
res.status(500).json({
|
|
51
|
+
success: false,
|
|
52
|
+
error: 'Failed to fetch changesets',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Preview publish plan (calculate new versions, affected packages)
|
|
58
|
+
*/
|
|
59
|
+
async function previewPublish(req, res) {
|
|
60
|
+
try {
|
|
61
|
+
const { packages: selectedPackageNames, bumps } = req.body;
|
|
62
|
+
if (!selectedPackageNames || !Array.isArray(selectedPackageNames)) {
|
|
63
|
+
res.status(400).json({
|
|
64
|
+
success: false,
|
|
65
|
+
error: 'Invalid request',
|
|
66
|
+
message: 'packages array is required',
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const rootPath = req.app.locals.rootPath;
|
|
71
|
+
const allPackages = await (0, changeset_service_1.getWorkspacePackages)(rootPath);
|
|
72
|
+
// Filter selected packages
|
|
73
|
+
const selectedPackages = allPackages.filter((pkg) => selectedPackageNames.includes(pkg.name));
|
|
74
|
+
// Calculate new versions
|
|
75
|
+
const newVersions = (0, changeset_service_1.calculateNewVersions)(selectedPackages, bumps || []);
|
|
76
|
+
// Check if working tree is clean
|
|
77
|
+
const isClean = true; //await isWorkingTreeClean(rootPath);
|
|
78
|
+
// Get existing changesets
|
|
79
|
+
const changesets = await (0, changeset_service_1.getExistingChangesets)(rootPath);
|
|
80
|
+
// Perform validation checks
|
|
81
|
+
const errors = [];
|
|
82
|
+
const warnings = [];
|
|
83
|
+
// Check 1: Working tree clean
|
|
84
|
+
const workingTreeClean = isClean;
|
|
85
|
+
if (!workingTreeClean) {
|
|
86
|
+
errors.push('Working tree has uncommitted changes');
|
|
87
|
+
}
|
|
88
|
+
// Check 2: User permissions (simplified - always true for now)
|
|
89
|
+
const permissions = true;
|
|
90
|
+
// Check 3: CI passing (simplified - always true for now)
|
|
91
|
+
const ciPassing = true;
|
|
92
|
+
// Check 4: Version available on npm (simplified - always true for now)
|
|
93
|
+
const versionAvailable = true;
|
|
94
|
+
const isValid = errors.length === 0;
|
|
95
|
+
res.json({
|
|
96
|
+
success: true,
|
|
97
|
+
isValid,
|
|
98
|
+
errors,
|
|
99
|
+
warnings,
|
|
100
|
+
checks: {
|
|
101
|
+
permissions,
|
|
102
|
+
workingTreeClean,
|
|
103
|
+
ciPassing,
|
|
104
|
+
versionAvailable,
|
|
105
|
+
},
|
|
106
|
+
preview: {
|
|
107
|
+
packages: newVersions,
|
|
108
|
+
workingTreeClean: isClean,
|
|
109
|
+
existingChangesets: changesets.length,
|
|
110
|
+
affectedPackages: newVersions.length,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
logger_1.AppLogger.error(`Failed to preview publish: ${error}`);
|
|
116
|
+
res.status(500).json({
|
|
117
|
+
success: false,
|
|
118
|
+
error: 'Failed to preview publish',
|
|
119
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Create a new changeset
|
|
125
|
+
*/
|
|
126
|
+
async function createChangeset(req, res) {
|
|
127
|
+
try {
|
|
128
|
+
const { packages: selectedPackageNames, bumps, summary } = req.body;
|
|
129
|
+
if (!selectedPackageNames || !Array.isArray(selectedPackageNames)) {
|
|
130
|
+
res.status(400).json({
|
|
131
|
+
success: false,
|
|
132
|
+
error: 'Invalid request',
|
|
133
|
+
message: 'packages array is required',
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!summary || typeof summary !== 'string' || summary.length < 10) {
|
|
138
|
+
res.status(400).json({
|
|
139
|
+
success: false,
|
|
140
|
+
error: 'Invalid summary',
|
|
141
|
+
message: 'Summary must be at least 10 characters',
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const rootPath = req.app.locals.rootPath;
|
|
146
|
+
// Generate the changeset
|
|
147
|
+
const result = await (0, changeset_service_1.generateChangeset)(rootPath, selectedPackageNames, bumps || [], summary);
|
|
148
|
+
if (!result.success) {
|
|
149
|
+
res.status(400).json({
|
|
150
|
+
success: false,
|
|
151
|
+
error: 'Failed to create changeset',
|
|
152
|
+
message: result.message,
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
res.json({
|
|
157
|
+
success: true,
|
|
158
|
+
changeset: result.changeset,
|
|
159
|
+
message: 'Changeset created successfully',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
logger_1.AppLogger.error(`Failed to create changeset: ${error}`);
|
|
164
|
+
res.status(500).json({
|
|
165
|
+
success: false,
|
|
166
|
+
error: 'Failed to create changeset',
|
|
167
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check publish readiness
|
|
173
|
+
*/
|
|
174
|
+
async function checkPublishStatus(req, res) {
|
|
175
|
+
try {
|
|
176
|
+
const rootPath = req.app.locals.rootPath;
|
|
177
|
+
// Check if working tree is clean
|
|
178
|
+
const isClean = true; //await isWorkingTreeClean(rootPath);
|
|
179
|
+
// Get existing changesets
|
|
180
|
+
const changesets = await (0, changeset_service_1.getExistingChangesets)(rootPath);
|
|
181
|
+
res.json({
|
|
182
|
+
success: true,
|
|
183
|
+
status: {
|
|
184
|
+
workingTreeClean: isClean,
|
|
185
|
+
hasChangesets: changesets.length > 0,
|
|
186
|
+
changesetCount: changesets.length,
|
|
187
|
+
readyToPublish: isClean && changesets.length > 0,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
logger_1.AppLogger.error(`Failed to check publish status: ${error}`);
|
|
193
|
+
res.status(500).json({
|
|
194
|
+
success: false,
|
|
195
|
+
error: 'Failed to check publish status',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Trigger publishing workflow
|
|
201
|
+
*/
|
|
202
|
+
async function triggerPublish(req, res) {
|
|
203
|
+
try {
|
|
204
|
+
const rootPath = req.app.locals.rootPath;
|
|
205
|
+
// Check if working tree is clean
|
|
206
|
+
const isClean = true; //await isWorkingTreeClean(rootPath);
|
|
207
|
+
if (!isClean) {
|
|
208
|
+
res.status(400).json({
|
|
209
|
+
success: false,
|
|
210
|
+
error: 'Working tree not clean',
|
|
211
|
+
message: 'Please commit or stash all changes before publishing',
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Check if changesets exist
|
|
216
|
+
const changesets = await (0, changeset_service_1.getExistingChangesets)(rootPath);
|
|
217
|
+
if (changesets.length === 0) {
|
|
218
|
+
res.status(400).json({
|
|
219
|
+
success: false,
|
|
220
|
+
error: 'No changesets found',
|
|
221
|
+
message: 'Create changesets before publishing',
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Trigger publish pipeline
|
|
226
|
+
const result = await (0, changeset_service_1.triggerPublishPipeline)(rootPath);
|
|
227
|
+
if (!result.success) {
|
|
228
|
+
res.status(500).json({
|
|
229
|
+
success: false,
|
|
230
|
+
error: 'Failed to trigger publish pipeline',
|
|
231
|
+
message: result.message,
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
res.json({
|
|
236
|
+
success: true,
|
|
237
|
+
message: 'Publishing workflow initiated',
|
|
238
|
+
result: result.result,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
logger_1.AppLogger.error(`Failed to trigger publish: ${error}`);
|
|
243
|
+
res.status(500).json({
|
|
244
|
+
success: false,
|
|
245
|
+
error: 'Failed to trigger publish',
|
|
246
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -20,6 +20,7 @@ const health_routes_1 = __importDefault(require("../routes/health-routes"));
|
|
|
20
20
|
const config_routes_1 = __importDefault(require("../routes/config-routes"));
|
|
21
21
|
const auth_routes_1 = __importDefault(require("../routes/auth-routes"));
|
|
22
22
|
const permission_routes_1 = __importDefault(require("../routes/permission-routes"));
|
|
23
|
+
const publish_routes_1 = __importDefault(require("../routes/publish-routes"));
|
|
23
24
|
const constants_1 = require("../constants");
|
|
24
25
|
const auth_middleware_1 = require("./auth-middleware");
|
|
25
26
|
const permission_service_1 = require("../services/permission-service");
|
|
@@ -64,6 +65,7 @@ function createApp(rootPath) {
|
|
|
64
65
|
app.use('/api/commits/', commit_routes_1.default);
|
|
65
66
|
app.use('/api/health/', health_routes_1.default);
|
|
66
67
|
app.use('/api/config/', config_routes_1.default);
|
|
68
|
+
app.use('/api/publish', publish_routes_1.default);
|
|
67
69
|
// 404 handler
|
|
68
70
|
app.use('*', error_handler_1.notFoundHandler);
|
|
69
71
|
// Global error handler (must be last)
|
|
@@ -109,6 +111,13 @@ function startServer(rootPath) {
|
|
|
109
111
|
// Config endpoints
|
|
110
112
|
'PUT /api/config/files/:id',
|
|
111
113
|
'GET /api/config/files',
|
|
114
|
+
// Publish endpoints
|
|
115
|
+
'GET /api/publish/packages',
|
|
116
|
+
'GET /api/publish/changesets',
|
|
117
|
+
'GET /api/publish/status',
|
|
118
|
+
'POST /api/publish/preview',
|
|
119
|
+
'POST /api/publish/changesets',
|
|
120
|
+
'POST /api/publish/trigger',
|
|
112
121
|
],
|
|
113
122
|
});
|
|
114
123
|
});
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const publish_controller_1 = require("../controllers/publish-controller");
|
|
8
|
+
const auth_middleware_1 = require("../middleware/auth-middleware");
|
|
9
|
+
const publishRouter = express_1.default.Router();
|
|
10
|
+
/**
|
|
11
|
+
* GET /api/publish/packages
|
|
12
|
+
* Get all workspace packages for publishing
|
|
13
|
+
*/
|
|
14
|
+
publishRouter.get('/packages', auth_middleware_1.authenticationMiddleware, publish_controller_1.getPublishPackages);
|
|
15
|
+
/**
|
|
16
|
+
* GET /api/publish/changesets
|
|
17
|
+
* Get existing unpublished changesets
|
|
18
|
+
*/
|
|
19
|
+
publishRouter.get('/changesets', auth_middleware_1.authenticationMiddleware, publish_controller_1.getPublishChangesets);
|
|
20
|
+
/**
|
|
21
|
+
* POST /api/publish/preview
|
|
22
|
+
* Preview the publish plan (calculate new versions, affected packages)
|
|
23
|
+
*/
|
|
24
|
+
publishRouter.post('/preview', auth_middleware_1.authenticationMiddleware, publish_controller_1.previewPublish);
|
|
25
|
+
/**
|
|
26
|
+
* POST /api/publish/changesets
|
|
27
|
+
* Create a new changeset for the selected packages
|
|
28
|
+
*/
|
|
29
|
+
publishRouter.post('/changesets', auth_middleware_1.authenticationMiddleware, publish_controller_1.createChangeset);
|
|
30
|
+
/**
|
|
31
|
+
* GET /api/publish/status
|
|
32
|
+
* Check publish readiness (working tree, changesets, etc.)
|
|
33
|
+
*/
|
|
34
|
+
publishRouter.get('/status', auth_middleware_1.authenticationMiddleware, publish_controller_1.checkPublishStatus);
|
|
35
|
+
/**
|
|
36
|
+
* POST /api/publish/trigger
|
|
37
|
+
* Trigger the publishing workflow
|
|
38
|
+
*/
|
|
39
|
+
publishRouter.post('/trigger', auth_middleware_1.authenticationMiddleware, publish_controller_1.triggerPublish);
|
|
40
|
+
exports.default = publishRouter;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Changeset Service
|
|
4
|
+
* Handles changeset generation, validation, and publishing
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.getWorkspacePackages = getWorkspacePackages;
|
|
11
|
+
exports.getExistingChangesets = getExistingChangesets;
|
|
12
|
+
exports.calculateNewVersions = calculateNewVersions;
|
|
13
|
+
exports.validateChangeset = validateChangeset;
|
|
14
|
+
exports.generateChangeset = generateChangeset;
|
|
15
|
+
exports.isWorkingTreeClean = isWorkingTreeClean;
|
|
16
|
+
exports.triggerPublishPipeline = triggerPublishPipeline;
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
19
|
+
const child_process_1 = require("child_process");
|
|
20
|
+
const util_1 = require("util");
|
|
21
|
+
const logger_1 = require("../middleware/logger");
|
|
22
|
+
const package_service_1 = require("./package-service");
|
|
23
|
+
const execPromise = (0, util_1.promisify)(child_process_1.exec);
|
|
24
|
+
/**
|
|
25
|
+
* Get all workspace packages
|
|
26
|
+
*/
|
|
27
|
+
async function getWorkspacePackages(rootPath) {
|
|
28
|
+
try {
|
|
29
|
+
// Get packages from package service
|
|
30
|
+
const packages = await (0, package_service_1.getPackagesService)(rootPath);
|
|
31
|
+
return packages.map((pkg) => ({
|
|
32
|
+
name: pkg.name,
|
|
33
|
+
version: pkg.version || '0.0.0',
|
|
34
|
+
path: pkg.path,
|
|
35
|
+
private: pkg.private || false,
|
|
36
|
+
dependencies: pkg.dependencies || {},
|
|
37
|
+
devDependencies: pkg.devDependencies || {},
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
logger_1.AppLogger.error(`Failed to get workspace packages: ${error}`);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get existing unpublished changesets
|
|
47
|
+
*/
|
|
48
|
+
async function getExistingChangesets(rootPath) {
|
|
49
|
+
try {
|
|
50
|
+
const changesetsDir = path_1.default.join(rootPath, '.changeset');
|
|
51
|
+
try {
|
|
52
|
+
const files = await promises_1.default.readdir(changesetsDir);
|
|
53
|
+
return files
|
|
54
|
+
.filter((file) => file.endsWith('.md') && file !== 'README.md')
|
|
55
|
+
.map((file) => file.replace('.md', ''));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Directory doesn't exist yet
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger_1.AppLogger.error(`Failed to get existing changesets: ${error}`);
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Calculate new versions for selected packages
|
|
69
|
+
*/
|
|
70
|
+
function calculateNewVersions(packages, bumps) {
|
|
71
|
+
return packages.map((pkg) => {
|
|
72
|
+
const bump = bumps.find((b) => b.package === pkg.name);
|
|
73
|
+
const bumpType = bump?.bumpType || 'patch';
|
|
74
|
+
const newVersion = calculateVersion(pkg.version, bumpType);
|
|
75
|
+
return {
|
|
76
|
+
package: pkg.name,
|
|
77
|
+
currentVersion: pkg.version,
|
|
78
|
+
newVersion,
|
|
79
|
+
bumpType,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Calculate new version based on bump type
|
|
85
|
+
*/
|
|
86
|
+
function calculateVersion(currentVersion, bumpType) {
|
|
87
|
+
const parts = currentVersion.split('.').map((p) => parseInt(p, 10));
|
|
88
|
+
const [major, minor = 0, patch = 0] = parts;
|
|
89
|
+
switch (bumpType) {
|
|
90
|
+
case 'major':
|
|
91
|
+
return `${major + 1}.0.0`;
|
|
92
|
+
case 'minor':
|
|
93
|
+
return `${major}.${minor + 1}.0`;
|
|
94
|
+
case 'patch':
|
|
95
|
+
default:
|
|
96
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Validate that changeset can be created
|
|
101
|
+
*/
|
|
102
|
+
async function validateChangeset(rootPath, packages, summary) {
|
|
103
|
+
const errors = [];
|
|
104
|
+
// Validate packages exist
|
|
105
|
+
const allPackages = await getWorkspacePackages(rootPath);
|
|
106
|
+
for (const pkgName of packages) {
|
|
107
|
+
if (!allPackages.find((p) => p.name === pkgName)) {
|
|
108
|
+
errors.push(`Package ${pkgName} not found`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Validate summary
|
|
112
|
+
if (!summary || summary.length < 10) {
|
|
113
|
+
errors.push('Summary must be at least 10 characters');
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
valid: errors.length === 0,
|
|
117
|
+
errors,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Generate a new changeset
|
|
122
|
+
*/
|
|
123
|
+
async function generateChangeset(rootPath, packages, bumps, summary) {
|
|
124
|
+
try {
|
|
125
|
+
// Validate input
|
|
126
|
+
const validation = await validateChangeset(rootPath, packages, summary);
|
|
127
|
+
if (!validation.valid) {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
message: validation.errors.join(', '),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// Create .changeset directory if it doesn't exist
|
|
134
|
+
const changesetsDir = path_1.default.join(rootPath, '.changeset');
|
|
135
|
+
try {
|
|
136
|
+
await promises_1.default.mkdir(changesetsDir, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
// Directory might already exist
|
|
140
|
+
}
|
|
141
|
+
// Generate unique changeset filename (using timestamp + random)
|
|
142
|
+
const timestamp = Date.now();
|
|
143
|
+
const random = Math.random().toString(36).substring(2, 9);
|
|
144
|
+
const changesetName = `${timestamp}-${random}`;
|
|
145
|
+
const changesetPath = path_1.default.join(changesetsDir, `${changesetName}.md`);
|
|
146
|
+
// Format changeset content
|
|
147
|
+
let content = `---\n`;
|
|
148
|
+
for (const pkg of packages) {
|
|
149
|
+
const bump = bumps.find((b) => b.package === pkg);
|
|
150
|
+
const bumpType = bump?.bumpType || 'patch';
|
|
151
|
+
content += `"${pkg}": ${bumpType}\n`;
|
|
152
|
+
}
|
|
153
|
+
content += `---\n\n`;
|
|
154
|
+
content += summary;
|
|
155
|
+
// Write changeset file
|
|
156
|
+
await promises_1.default.writeFile(changesetPath, content, 'utf-8');
|
|
157
|
+
logger_1.AppLogger.info(`Changeset created: ${changesetName}`);
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
message: 'Changeset created successfully',
|
|
161
|
+
changeset: changesetName,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
logger_1.AppLogger.error(`Failed to generate changeset: ${error}`);
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if working tree is clean
|
|
174
|
+
*/
|
|
175
|
+
async function isWorkingTreeClean(rootPath) {
|
|
176
|
+
try {
|
|
177
|
+
const { stdout } = await execPromise('git status --porcelain', {
|
|
178
|
+
cwd: rootPath,
|
|
179
|
+
});
|
|
180
|
+
return stdout.trim().length === 0;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Trigger CI pipeline for publishing
|
|
188
|
+
*/
|
|
189
|
+
async function triggerPublishPipeline(rootPath) {
|
|
190
|
+
try {
|
|
191
|
+
// Check if publish workflow exists
|
|
192
|
+
const publishWorkflowPath = path_1.default.join(rootPath, '.github', 'workflows', 'release.yml');
|
|
193
|
+
try {
|
|
194
|
+
await promises_1.default.stat(publishWorkflowPath);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
message: 'Publish workflow not configured',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// Commit the changeset if there are any changes
|
|
203
|
+
try {
|
|
204
|
+
const { stdout: status } = await execPromise('git status --porcelain', {
|
|
205
|
+
cwd: rootPath,
|
|
206
|
+
});
|
|
207
|
+
if (status.trim()) {
|
|
208
|
+
// Add changeset files
|
|
209
|
+
await execPromise('git add .changeset/', { cwd: rootPath });
|
|
210
|
+
// Commit with proper message
|
|
211
|
+
await execPromise('git commit -m "chore: publish changeset" --no-verify', { cwd: rootPath });
|
|
212
|
+
// Push to the current branch
|
|
213
|
+
try {
|
|
214
|
+
const { stdout: branch } = await execPromise('git rev-parse --abbrev-ref HEAD', { cwd: rootPath });
|
|
215
|
+
const currentBranch = branch.trim();
|
|
216
|
+
await execPromise(`git push origin ${currentBranch}`, {
|
|
217
|
+
cwd: rootPath,
|
|
218
|
+
});
|
|
219
|
+
logger_1.AppLogger.info(`Pushed changeset to ${currentBranch}`);
|
|
220
|
+
}
|
|
221
|
+
catch (pushError) {
|
|
222
|
+
// If push fails, still continue - the workflow might be triggered manually
|
|
223
|
+
logger_1.AppLogger.warn(`Failed to push: ${pushError}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (gitError) {
|
|
228
|
+
logger_1.AppLogger.warn(`Git operations failed: ${gitError}`);
|
|
229
|
+
// Continue anyway - changesets might already be committed
|
|
230
|
+
}
|
|
231
|
+
// Trigger the workflow via GitHub API if we have a token
|
|
232
|
+
try {
|
|
233
|
+
const githubToken = process.env.GITHUB_TOKEN;
|
|
234
|
+
if (githubToken) {
|
|
235
|
+
// Get repo info from package.json or git remote
|
|
236
|
+
const { stdout: remoteUrl } = await execPromise('git remote get-url origin', {
|
|
237
|
+
cwd: rootPath,
|
|
238
|
+
});
|
|
239
|
+
// Parse GitHub repo from URL (e.g., git@github.com:user/repo.git)
|
|
240
|
+
const repoMatch = remoteUrl.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
|
|
241
|
+
if (repoMatch) {
|
|
242
|
+
const [, owner, repo] = repoMatch;
|
|
243
|
+
const repoName = repo.replace(/\.git$/, '');
|
|
244
|
+
// Trigger the workflow
|
|
245
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repoName}/actions/workflows/release.yml/dispatches`, {
|
|
246
|
+
method: 'POST',
|
|
247
|
+
headers: {
|
|
248
|
+
Authorization: `Bearer ${githubToken}`,
|
|
249
|
+
Accept: 'application/vnd.github.v3+json',
|
|
250
|
+
},
|
|
251
|
+
body: JSON.stringify({
|
|
252
|
+
ref: 'main',
|
|
253
|
+
}),
|
|
254
|
+
});
|
|
255
|
+
if (response.ok) {
|
|
256
|
+
logger_1.AppLogger.info('GitHub workflow triggered successfully');
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
logger_1.AppLogger.warn(`Failed to trigger workflow: ${response.statusText}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch (workflowError) {
|
|
265
|
+
logger_1.AppLogger.warn(`Failed to trigger workflow: ${workflowError}`);
|
|
266
|
+
// Still return success as the changeset was created
|
|
267
|
+
}
|
|
268
|
+
logger_1.AppLogger.info('Publish pipeline initiated');
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
message: 'Publishing workflow initiated',
|
|
272
|
+
result: {
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
workflowPath: publishWorkflowPath,
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
logger_1.AppLogger.error(`Failed to trigger publish pipeline: ${error}`);
|
|
280
|
+
return {
|
|
281
|
+
success: false,
|
|
282
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|