Skip to content

Commit d85d750

Browse files
committed
Release v0.2.17
Automated release commit with version bump from 0.2.16 to 0.2.17
1 parent d40fbcb commit d85d750

File tree

5 files changed

+507
-4
lines changed

5 files changed

+507
-4
lines changed

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@wonderwhy-er/desktop-commander",
3-
"version": "0.2.16",
3+
"version": "0.2.17",
44
"description": "MCP server for terminal operations and file editing",
55
"mcpName": "io.github.wonderwhy-er/desktop-commander",
66
"license": "MIT",
@@ -43,6 +43,10 @@
4343
"unlink:local": "npm unlink",
4444
"inspector": "npx @modelcontextprotocol/inspector dist/index.js",
4545
"build:mcpb": "node scripts/build-mcpb.cjs",
46+
"release": "node scripts/publish-release.cjs",
47+
"release:minor": "node scripts/publish-release.cjs --minor",
48+
"release:major": "node scripts/publish-release.cjs --major",
49+
"release:dry": "node scripts/publish-release.cjs --dry-run",
4650
"logs:view": "npm run build && node scripts/view-fuzzy-logs.js",
4751
"logs:analyze": "npm run build && node scripts/analyze-fuzzy-logs.js",
4852
"logs:clear": "npm run build && node scripts/clear-fuzzy-logs.js",

scripts/publish-release.cjs

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Desktop Commander - Complete Release Publishing Script
5+
*
6+
* This script handles the entire release process:
7+
* 1. Version bump
8+
* 2. Build project and MCPB bundle
9+
* 3. Commit and tag
10+
* 4. Publish to NPM
11+
* 5. Publish to MCP Registry
12+
* 6. Verify publications
13+
*/
14+
15+
const { execSync } = require('child_process');
16+
const fs = require('fs');
17+
const path = require('path');
18+
19+
// Colors for output
20+
const colors = {
21+
reset: '\x1b[0m',
22+
red: '\x1b[31m',
23+
green: '\x1b[32m',
24+
yellow: '\x1b[33m',
25+
blue: '\x1b[34m',
26+
};
27+
28+
// Helper functions for colored output
29+
function printStep(message) {
30+
console.log(`${colors.blue}==>${colors.reset} ${message}`);
31+
}
32+
33+
function printSuccess(message) {
34+
console.log(`${colors.green}${colors.reset} ${message}`);
35+
}
36+
37+
function printError(message) {
38+
console.error(`${colors.red}${colors.reset} ${message}`);
39+
}
40+
41+
function printWarning(message) {
42+
console.log(`${colors.yellow}${colors.reset} ${message}`);
43+
}
44+
45+
// Execute command with error handling
46+
function exec(command, options = {}) {
47+
try {
48+
return execSync(command, {
49+
encoding: 'utf8',
50+
stdio: options.silent ? 'pipe' : 'inherit',
51+
...options
52+
});
53+
} catch (error) {
54+
if (options.ignoreError) {
55+
return options.silent ? '' : null;
56+
}
57+
throw error;
58+
}
59+
}
60+
61+
// Execute command silently and return output
62+
function execSilent(command, options = {}) {
63+
return exec(command, { silent: true, ...options });
64+
}
65+
66+
// Parse command line arguments
67+
function parseArgs() {
68+
const args = process.argv.slice(2);
69+
const options = {
70+
bumpType: 'patch',
71+
skipTests: false,
72+
dryRun: false,
73+
help: false,
74+
};
75+
76+
for (const arg of args) {
77+
switch (arg) {
78+
case '--minor':
79+
options.bumpType = 'minor';
80+
break;
81+
case '--major':
82+
options.bumpType = 'major';
83+
break;
84+
case '--skip-tests':
85+
options.skipTests = true;
86+
break;
87+
case '--dry-run':
88+
options.dryRun = true;
89+
break;
90+
case '--help':
91+
case '-h':
92+
options.help = true;
93+
break;
94+
default:
95+
printError(`Unknown option: ${arg}`);
96+
console.log("Run 'node scripts/publish-release.js --help' for usage information.");
97+
process.exit(1);
98+
}
99+
}
100+
101+
return options;
102+
}
103+
104+
// Show help message
105+
function showHelp() {
106+
console.log('Usage: node scripts/publish-release.cjs [OPTIONS]');
107+
console.log('');
108+
console.log('Options:');
109+
console.log(' --minor Bump minor version (default: patch)');
110+
console.log(' --major Bump major version (default: patch)');
111+
console.log(' --skip-tests Skip running tests');
112+
console.log(' --dry-run Simulate the release without publishing');
113+
console.log(' --help, -h Show this help message');
114+
console.log('');
115+
console.log('Examples:');
116+
console.log(' node scripts/publish-release.cjs # Patch release (0.2.16 -> 0.2.17)');
117+
console.log(' node scripts/publish-release.cjs --minor # Minor release (0.2.16 -> 0.3.0)');
118+
console.log(' node scripts/publish-release.cjs --major # Major release (0.2.16 -> 1.0.0)');
119+
console.log(' node scripts/publish-release.cjs --dry-run # Test without publishing');
120+
}
121+
122+
// Main release function
123+
async function publishRelease() {
124+
const options = parseArgs();
125+
126+
if (options.help) {
127+
showHelp();
128+
return;
129+
}
130+
131+
// Check if we're in the right directory
132+
const packageJsonPath = path.join(process.cwd(), 'package.json');
133+
if (!fs.existsSync(packageJsonPath)) {
134+
printError('package.json not found. Please run this script from the project root.');
135+
process.exit(1);
136+
}
137+
138+
console.log('');
139+
console.log('╔══════════════════════════════════════════════════════════╗');
140+
console.log('║ Desktop Commander Release Publisher ║');
141+
console.log('╚══════════════════════════════════════════════════════════╝');
142+
console.log('');
143+
144+
// Get current version
145+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
146+
const currentVersion = packageJson.version;
147+
printStep(`Current version: ${currentVersion}`);
148+
printStep(`Bump type: ${options.bumpType}`);
149+
150+
if (options.dryRun) {
151+
printWarning('DRY RUN MODE - No changes will be published');
152+
console.log('');
153+
}
154+
155+
try {
156+
// Step 1: Bump version
157+
printStep('Step 1/7: Bumping version...');
158+
const bumpCommand = options.bumpType === 'minor' ? 'npm run bump:minor' :
159+
options.bumpType === 'major' ? 'npm run bump:major' :
160+
'npm run bump';
161+
exec(bumpCommand);
162+
163+
const newPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
164+
const newVersion = newPackageJson.version;
165+
printSuccess(`Version bumped: ${currentVersion}${newVersion}`);
166+
console.log('');
167+
168+
// Step 2: Build project
169+
printStep('Step 2/7: Building project...');
170+
exec('npm run build');
171+
printSuccess('Project built successfully');
172+
console.log('');
173+
174+
// Step 3: Run tests (unless skipped)
175+
if (!options.skipTests) {
176+
printStep('Step 3/7: Running tests...');
177+
exec('npm test');
178+
printSuccess('All tests passed');
179+
} else {
180+
printWarning('Step 3/7: Tests skipped');
181+
}
182+
console.log('');
183+
184+
// Step 4: Build MCPB bundle
185+
printStep('Step 4/7: Building MCPB bundle...');
186+
exec('npm run build:mcpb');
187+
printSuccess('MCPB bundle created');
188+
console.log('');
189+
190+
// Step 5: Commit and tag
191+
printStep('Step 5/7: Creating git commit and tag...');
192+
193+
// Check if there are changes to commit
194+
const gitStatus = execSilent('git status --porcelain', { ignoreError: true });
195+
const hasChanges = gitStatus.includes('package.json') ||
196+
gitStatus.includes('server.json') ||
197+
gitStatus.includes('src/version.ts');
198+
199+
if (!hasChanges) {
200+
printWarning('No changes to commit (version files already committed)');
201+
} else {
202+
exec('git add package.json server.json src/version.ts');
203+
204+
const commitMsg = `Release v${newVersion}
205+
206+
Automated release commit with version bump from ${currentVersion} to ${newVersion}`;
207+
208+
if (options.dryRun) {
209+
printWarning(`Would commit: ${commitMsg.split('\n')[0]}`);
210+
} else {
211+
exec(`git commit -m "${commitMsg}"`);
212+
printSuccess('Changes committed');
213+
}
214+
}
215+
216+
// Create and push tag
217+
const tagName = `v${newVersion}`;
218+
219+
if (options.dryRun) {
220+
printWarning(`Would create tag: ${tagName}`);
221+
printWarning(`Would push to origin: main and ${tagName}`);
222+
} else {
223+
exec(`git tag ${tagName}`);
224+
exec('git push origin main');
225+
exec(`git push origin ${tagName}`);
226+
printSuccess(`Tag ${tagName} created and pushed`);
227+
}
228+
console.log('');
229+
230+
// Step 6: Publish to NPM
231+
printStep('Step 6/7: Publishing to NPM...');
232+
233+
// Check NPM authentication
234+
const npmUser = execSilent('npm whoami', { ignoreError: true }).trim();
235+
if (!npmUser) {
236+
printError('Not logged into NPM. Please run "npm login" first.');
237+
process.exit(1);
238+
}
239+
printSuccess(`NPM user: ${npmUser}`);
240+
241+
if (options.dryRun) {
242+
printWarning('Would publish to NPM: npm publish');
243+
printWarning('Skipping NPM publish (dry run)');
244+
} else {
245+
exec('npm publish');
246+
printSuccess('Published to NPM');
247+
248+
// Verify NPM publication
249+
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3 seconds
250+
const npmVersion = execSilent('npm view @wonderwhy-er/desktop-commander version', { ignoreError: true }).trim();
251+
if (npmVersion === newVersion) {
252+
printSuccess(`NPM publication verified: v${npmVersion}`);
253+
} else {
254+
printWarning(`NPM version mismatch: expected ${newVersion}, got ${npmVersion} (may take a moment to propagate)`);
255+
}
256+
}
257+
console.log('');
258+
259+
// Step 7: Publish to MCP Registry
260+
printStep('Step 7/7: Publishing to MCP Registry...');
261+
262+
// Check if mcp-publisher is installed
263+
const hasMcpPublisher = execSilent('mcp-publisher --version', { ignoreError: true });
264+
if (!hasMcpPublisher) {
265+
printError('mcp-publisher not found. Install it with: brew install mcp-publisher');
266+
process.exit(1);
267+
}
268+
269+
if (options.dryRun) {
270+
printWarning('Would publish to MCP Registry: mcp-publisher publish');
271+
printWarning('Skipping MCP Registry publish (dry run)');
272+
} else {
273+
exec('mcp-publisher publish');
274+
printSuccess('Published to MCP Registry');
275+
276+
// Verify MCP Registry publication
277+
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3 seconds
278+
try {
279+
const mcpResponse = execSilent('curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=io.github.wonderwhy-er/desktop-commander"');
280+
const mcpData = JSON.parse(mcpResponse);
281+
const mcpVersion = mcpData.servers?.[0]?.version || 'unknown';
282+
283+
if (mcpVersion === newVersion) {
284+
printSuccess(`MCP Registry publication verified: v${mcpVersion}`);
285+
} else {
286+
printWarning(`MCP Registry version: ${mcpVersion} (expected ${newVersion}, may take a moment to propagate)`);
287+
}
288+
} catch (error) {
289+
printWarning('Could not verify MCP Registry publication');
290+
}
291+
}
292+
console.log('');
293+
294+
// Success summary
295+
console.log('╔══════════════════════════════════════════════════════════╗');
296+
console.log('║ 🎉 Release Complete! 🎉 ║');
297+
console.log('╚══════════════════════════════════════════════════════════╝');
298+
console.log('');
299+
printSuccess(`Version: ${newVersion}`);
300+
printSuccess('NPM: https://www.npmjs.com/package/@wonderwhy-er/desktop-commander');
301+
printSuccess('MCP Registry: https://registry.modelcontextprotocol.io/');
302+
printSuccess(`GitHub Tag: https://github.com/wonderwhy-er/DesktopCommanderMCP/releases/tag/${tagName}`);
303+
console.log('');
304+
console.log('Next steps:');
305+
console.log(` 1. Create GitHub release at: https://github.com/wonderwhy-er/DesktopCommanderMCP/releases/new?tag=${tagName}`);
306+
console.log(' 2. Add release notes with features and fixes');
307+
console.log(' 3. Announce on Discord');
308+
console.log('');
309+
310+
if (options.dryRun) {
311+
console.log('');
312+
printWarning('This was a DRY RUN - no changes were published');
313+
printWarning('Run without --dry-run to perform the actual release');
314+
console.log('');
315+
}
316+
317+
} catch (error) {
318+
console.log('');
319+
printError('Release failed!');
320+
printError(error.message);
321+
process.exit(1);
322+
}
323+
}
324+
325+
// Run the script
326+
publishRelease().catch(error => {
327+
printError('Unexpected error:');
328+
console.error(error);
329+
process.exit(1);
330+
});

0 commit comments

Comments
 (0)