Skip to content

Commit 4ab8dc3

Browse files
committed
Complete migration to optionalDependencies approach
- Replace binary-install with optionalDependencies for platform packages - Add platform-specific packages with fallback to GitHub releases - Update GitHub Actions workflow to build and publish platform packages - Maintain backward compatibility with GitHub releases download
1 parent ce8070d commit 4ab8dc3

File tree

8 files changed

+330
-27
lines changed

8 files changed

+330
-27
lines changed

.github/workflows/release-npm.yml

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,73 @@ jobs:
133133
echo "" >> $GITHUB_STEP_SUMMARY
134134
echo "# Or install globally" >> $GITHUB_STEP_SUMMARY
135135
echo "npm install -g @pulseengine/studio-mcp-server" >> $GITHUB_STEP_SUMMARY
136-
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
136+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
137+
138+
build-platform-packages:
139+
name: Build Platform Packages
140+
needs: build-binaries
141+
runs-on: ubuntu-latest
142+
strategy:
143+
matrix:
144+
include:
145+
- target: aarch64-apple-darwin
146+
platform: darwin-arm64
147+
binary: studio-mcp-server
148+
- target: x86_64-apple-darwin
149+
platform: darwin-x64
150+
binary: studio-mcp-server
151+
- target: x86_64-unknown-linux-gnu
152+
platform: linux-x64
153+
binary: studio-mcp-server
154+
- target: x86_64-pc-windows-msvc
155+
platform: win32-x64
156+
binary: studio-mcp-server.exe
157+
158+
steps:
159+
- name: Checkout repository
160+
uses: actions/checkout@v4
161+
162+
- name: Setup Node.js
163+
uses: actions/setup-node@v4
164+
with:
165+
node-version: '18'
166+
registry-url: 'https://registry.npmjs.org'
167+
168+
- name: Download release asset
169+
env:
170+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
171+
run: |
172+
VERSION=${{ github.event.release.tag_name || github.event.inputs.version }}
173+
ARCHIVE_EXT=${{ matrix.target == 'x86_64-pc-windows-msvc' && 'zip' || 'tar.gz' }}
174+
ASSET_NAME="studio-mcp-server-${VERSION}-${{ matrix.target }}.${ARCHIVE_EXT}"
175+
176+
# Download the release asset
177+
gh release download "${VERSION}" --pattern "${ASSET_NAME}" --dir ./temp
178+
179+
# Extract the binary
180+
cd temp
181+
if [[ "${ARCHIVE_EXT}" == "zip" ]]; then
182+
unzip "${ASSET_NAME}"
183+
else
184+
tar -xzf "${ASSET_NAME}"
185+
fi
186+
187+
# Move binary to platform package directory
188+
mv "${{ matrix.binary }}" "../studio-mcp-server/platform-packages/${{ matrix.platform }}/"
189+
190+
- name: Update platform package version
191+
working-directory: studio-mcp-server/platform-packages/${{ matrix.platform }}
192+
run: |
193+
VERSION=${{ github.event.release.tag_name || github.event.inputs.version }}
194+
# Remove 'v' prefix if present
195+
VERSION=${VERSION#v}
196+
npm version $VERSION --no-git-tag-version
197+
198+
- name: Publish platform package
199+
working-directory: studio-mcp-server/platform-packages/${{ matrix.platform }}
200+
run: npm publish --access public
201+
env:
202+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
203+
204+
- name: Clean up
205+
run: rm -rf temp

studio-mcp-server/npm/index.js

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,90 @@
11
#!/usr/bin/env node
22

3-
// Main entry point for programmatic usage
4-
const { Binary } = require("binary-install");
53
const os = require("os");
4+
const path = require("path");
5+
const { existsSync } = require("fs");
66

77
function getBinaryName() {
88
const platform = os.type();
99
return platform === "Windows_NT" ? "studio-mcp-server.exe" : "studio-mcp-server";
1010
}
1111

12-
function createBinary() {
13-
return new Binary(getBinaryName(), "");
12+
function getPlatformPackageName() {
13+
const platform = os.platform();
14+
const arch = os.arch();
15+
16+
if (platform === "darwin") {
17+
return arch === "arm64" ? "@pulseengine/studio-mcp-server-darwin-arm64" : "@pulseengine/studio-mcp-server-darwin-x64";
18+
} else if (platform === "linux") {
19+
return "@pulseengine/studio-mcp-server-linux-x64";
20+
} else if (platform === "win32") {
21+
return "@pulseengine/studio-mcp-server-win32-x64";
22+
}
23+
24+
throw new Error(`Unsupported platform: ${platform}-${arch}`);
25+
}
26+
27+
function getBinaryPath() {
28+
try {
29+
const platformPackage = getPlatformPackageName();
30+
const binaryName = getBinaryName();
31+
32+
// Try to find the platform-specific package
33+
try {
34+
const platformPackagePath = require.resolve(platformPackage);
35+
const binaryPath = path.join(path.dirname(platformPackagePath), binaryName);
36+
if (existsSync(binaryPath)) {
37+
return binaryPath;
38+
}
39+
} catch (err) {
40+
// Platform package not found, continue to fallback
41+
}
42+
43+
// Fallback to local bin directory (for GitHub releases fallback)
44+
const fallbackBinaryPath = path.join(__dirname, "bin", binaryName);
45+
if (existsSync(fallbackBinaryPath)) {
46+
return fallbackBinaryPath;
47+
}
48+
49+
throw new Error(`Binary not found. Install with: npm install ${platformPackage}`);
50+
} catch (err) {
51+
throw new Error(`${err.message}
52+
53+
🔧 Installation options:
54+
1. npm install -g @pulseengine/studio-mcp-server
55+
2. Manual download: https://github.com/pulseengine/studio-mcp/releases
56+
3. Build from source: cargo install --git https://github.com/pulseengine/studio-mcp.git studio-mcp-server`);
57+
}
1458
}
1559

1660
module.exports = {
17-
createBinary,
18-
Binary,
19-
getBinaryName
61+
getBinaryPath,
62+
getBinaryName,
63+
getPlatformPackageName
2064
};
2165

2266
// If called directly, run the binary
2367
if (require.main === module) {
24-
const binary = createBinary();
25-
binary.run().catch(err => {
26-
console.error("Failed to run studio-mcp-server:", err.message);
68+
try {
69+
const { spawn } = require("child_process");
70+
const binaryPath = getBinaryPath();
71+
const [, , ...args] = process.argv;
72+
73+
const child = spawn(binaryPath, args, {
74+
stdio: "inherit",
75+
cwd: process.cwd()
76+
});
77+
78+
child.on("close", (code) => {
79+
process.exit(code || 0);
80+
});
81+
82+
child.on("error", (err) => {
83+
console.error("Failed to run studio-mcp-server:", err.message);
84+
process.exit(1);
85+
});
86+
} catch (err) {
87+
console.error(err.message);
2788
process.exit(1);
28-
});
89+
}
2990
}

studio-mcp-server/npm/install.js

Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
#!/usr/bin/env node
22

3-
const { Binary } = require("binary-install");
43
const os = require("os");
54
const path = require("path");
5+
const fs = require("fs");
6+
const https = require("https");
7+
const { execSync } = require("child_process");
68

9+
// Check if platform packages are available first
10+
const { getPlatformPackageName } = require("./index.js");
11+
12+
try {
13+
const platformPackage = getPlatformPackageName();
14+
try {
15+
require.resolve(platformPackage);
16+
console.log(`✅ Platform package ${platformPackage} is already available`);
17+
process.exit(0);
18+
} catch (err) {
19+
console.log(`⚠️ Platform package ${platformPackage} not found, falling back to GitHub release download`);
20+
}
21+
} catch (err) {
22+
console.log("⚠️ Platform not supported for platform packages, using GitHub release download");
23+
}
24+
25+
// Fallback to GitHub releases download
726
function getPlatform() {
827
const type = os.type();
928
const arch = os.arch();
@@ -42,7 +61,6 @@ function getBinaryName() {
4261
function getDownloadUrl() {
4362
const version = require("./package.json").version;
4463
const platform = getPlatform();
45-
const binaryName = getBinaryName();
4664

4765
// Determine archive format based on platform
4866
const archiveExtension = os.type() === "Windows_NT" ? "zip" : "tar.gz";
@@ -51,19 +69,99 @@ function getDownloadUrl() {
5169
return `https://github.com/pulseengine/studio-mcp/releases/download/v${version}/studio-mcp-server-v${version}-${platform}.${archiveExtension}`;
5270
}
5371

54-
const binaryName = getBinaryName();
55-
const downloadUrl = getDownloadUrl();
56-
57-
// Debug logging
58-
console.log(`Platform detected: ${os.type()} ${os.arch()}`);
59-
console.log(`Target: ${getPlatform()}`);
60-
console.log(`Binary: ${binaryName}`);
61-
console.log(`Download URL: ${downloadUrl}`);
72+
function downloadFile(url, destination) {
73+
return new Promise((resolve, reject) => {
74+
const file = fs.createWriteStream(destination);
75+
76+
https.get(url, (response) => {
77+
if (response.statusCode === 302 || response.statusCode === 301) {
78+
// Handle redirect
79+
return downloadFile(response.headers.location, destination)
80+
.then(resolve)
81+
.catch(reject);
82+
}
83+
84+
if (response.statusCode !== 200) {
85+
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
86+
return;
87+
}
88+
89+
response.pipe(file);
90+
91+
file.on('finish', () => {
92+
file.close();
93+
resolve();
94+
});
95+
96+
file.on('error', (err) => {
97+
fs.unlink(destination, () => {});
98+
reject(err);
99+
});
100+
}).on('error', (err) => {
101+
reject(err);
102+
});
103+
});
104+
}
62105

63-
const binary = new Binary(binaryName, downloadUrl);
106+
async function installBinary() {
107+
const binaryName = getBinaryName();
108+
const downloadUrl = getDownloadUrl();
109+
const platform = getPlatform();
110+
111+
console.log(`Platform detected: ${os.type()} ${os.arch()}`);
112+
console.log(`Target: ${platform}`);
113+
console.log(`Binary: ${binaryName}`);
114+
console.log(`Download URL: ${downloadUrl}`);
115+
116+
// Create bin directory
117+
const binDir = path.join(__dirname, "bin");
118+
if (!fs.existsSync(binDir)) {
119+
fs.mkdirSync(binDir, { recursive: true });
120+
}
121+
122+
// Download archive
123+
const archiveExtension = os.type() === "Windows_NT" ? "zip" : "tar.gz";
124+
const archivePath = path.join(binDir, `studio-mcp-server.${archiveExtension}`);
125+
126+
console.log("📥 Downloading binary...");
127+
try {
128+
await downloadFile(downloadUrl, archivePath);
129+
console.log("✅ Download completed");
130+
} catch (err) {
131+
throw new Error(`Download failed: ${err.message}`);
132+
}
133+
134+
// Extract archive
135+
console.log("📦 Extracting binary...");
136+
try {
137+
if (archiveExtension === "zip") {
138+
// Extract zip (Windows)
139+
execSync(`cd "${binDir}" && powershell -command "Expand-Archive -Path '${archivePath}' -DestinationPath '.' -Force"`, { stdio: 'inherit' });
140+
} else {
141+
// Extract tar.gz (Unix)
142+
execSync(`cd "${binDir}" && tar -xzf "${path.basename(archivePath)}"`, { stdio: 'inherit' });
143+
}
144+
145+
// Clean up archive
146+
fs.unlinkSync(archivePath);
147+
148+
// Make binary executable on Unix
149+
if (os.type() !== "Windows_NT") {
150+
const binaryPath = path.join(binDir, binaryName);
151+
if (fs.existsSync(binaryPath)) {
152+
fs.chmodSync(binaryPath, 0o755);
153+
}
154+
}
155+
156+
console.log("✅ Binary installed successfully");
157+
} catch (err) {
158+
throw new Error(`Extraction failed: ${err.message}`);
159+
}
160+
}
64161

65-
binary.install().catch(err => {
66-
console.error("Failed to install studio-mcp-server binary:", err.message);
162+
// Run installation
163+
installBinary().catch(err => {
164+
console.error("❌ Failed to install studio-mcp-server binary:", err.message);
67165

68166
// Provide helpful error message
69167
console.error("\n📋 Installation failed. You can:");

studio-mcp-server/npm/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
"bugs": {
3030
"url": "https://github.com/pulseengine/studio-mcp/issues"
3131
},
32-
"dependencies": {
33-
"binary-install": "^1.1.0"
32+
"optionalDependencies": {
33+
"@pulseengine/studio-mcp-server-darwin-arm64": "0.2.7",
34+
"@pulseengine/studio-mcp-server-darwin-x64": "0.2.7",
35+
"@pulseengine/studio-mcp-server-linux-x64": "0.2.7",
36+
"@pulseengine/studio-mcp-server-win32-x64": "0.2.7"
3437
},
3538
"engines": {
3639
"node": ">=14"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@pulseengine/studio-mcp-server-darwin-arm64",
3+
"version": "0.2.7",
4+
"description": "Darwin ARM64 binary for studio-mcp-server",
5+
"main": "studio-mcp-server",
6+
"os": ["darwin"],
7+
"cpu": ["arm64"],
8+
"keywords": ["mcp", "model-context-protocol", "studio", "windriver"],
9+
"author": "PulseEngine",
10+
"license": "MIT",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/pulseengine/studio-mcp.git"
14+
},
15+
"publishConfig": {
16+
"access": "public"
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@pulseengine/studio-mcp-server-darwin-x64",
3+
"version": "0.2.7",
4+
"description": "Darwin x64 binary for studio-mcp-server",
5+
"main": "studio-mcp-server",
6+
"os": ["darwin"],
7+
"cpu": ["x64"],
8+
"keywords": ["mcp", "model-context-protocol", "studio", "windriver"],
9+
"author": "PulseEngine",
10+
"license": "MIT",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/pulseengine/studio-mcp.git"
14+
},
15+
"publishConfig": {
16+
"access": "public"
17+
}
18+
}

0 commit comments

Comments
 (0)