Skip to content

Commit 0878d0c

Browse files
committed
Add GitHub token support for private repo installs
1 parent 7abfb5a commit 0878d0c

File tree

2 files changed

+92
-15
lines changed

2 files changed

+92
-15
lines changed

bin/relay-dedup

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ const fs = require("fs");
77
const binaryPath = path.join(__dirname, "relay-dedup-binary");
88

99
if (!fs.existsSync(binaryPath)) {
10-
console.error(
11-
"relay-dedup binary not found. Please run: npm run postinstall"
12-
);
10+
console.error("Error: relay-dedup binary not found.\n");
11+
console.error("This is likely because the postinstall script failed to download it.");
12+
console.error("This is a private repo - you need GitHub authentication.\n");
13+
console.error("Fix:");
14+
console.error(" 1. Install GitHub CLI and run: gh auth login");
15+
console.error(" 2. Reinstall: pnpm install relay-dedup\n");
16+
console.error("Or set GITHUB_TOKEN environment variable before installing.");
1317
process.exit(1);
1418
}
1519

scripts/postinstall.js

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,52 @@ function getVersion() {
4040
return pkg.version;
4141
}
4242

43-
function httpsGet(url) {
43+
function getGitHubToken() {
44+
// Check environment variables first
45+
if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
46+
if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
47+
48+
// Try gh CLI (GitHub's official CLI)
49+
try {
50+
const token = execSync("gh auth token", {
51+
encoding: "utf8",
52+
stdio: ["pipe", "pipe", "pipe"],
53+
}).trim();
54+
if (token && token.startsWith("gh")) {
55+
return token;
56+
}
57+
} catch {
58+
// gh CLI not installed or not logged in
59+
}
60+
61+
return null;
62+
}
63+
64+
function httpsGet(url, token = null, isBinaryDownload = false) {
4465
return new Promise((resolve, reject) => {
66+
const headers = { "User-Agent": "relay-dedup-installer" };
67+
68+
// Add auth header for private repos
69+
if (token) {
70+
headers["Authorization"] = `token ${token}`;
71+
}
72+
73+
// For downloading binary assets from API URL
74+
if (isBinaryDownload) {
75+
headers["Accept"] = "application/octet-stream";
76+
}
77+
4578
https
46-
.get(url, { headers: { "User-Agent": "relay-dedup-installer" } }, (res) => {
47-
// Follow redirects
79+
.get(url, { headers }, (res) => {
80+
// Follow redirects (don't pass token to external domains like S3)
4881
if (res.statusCode === 301 || res.statusCode === 302) {
49-
return httpsGet(res.headers.location).then(resolve).catch(reject);
82+
const redirectUrl = res.headers.location;
83+
const sameOrigin =
84+
redirectUrl.includes("github.com") ||
85+
redirectUrl.includes("api.github.com");
86+
return httpsGet(redirectUrl, sameOrigin ? token : null, isBinaryDownload)
87+
.then(resolve)
88+
.catch(reject);
5089
}
5190
if (res.statusCode !== 200) {
5291
reject(new Error(`HTTP ${res.statusCode}: ${url}`));
@@ -61,17 +100,52 @@ function httpsGet(url) {
61100
});
62101
}
63102

103+
async function getAssetUrl(version, assetName, token) {
104+
// For private repos, we need to use the API to get the asset download URL
105+
const apiUrl = `https://api.github.com/repos/${REPO}/releases/tags/v${version}`;
106+
107+
const response = await httpsGet(apiUrl, token);
108+
const release = JSON.parse(response.toString());
109+
110+
if (!release.assets) {
111+
throw new Error(`No assets found in release v${version}`);
112+
}
113+
114+
const asset = release.assets.find((a) => a.name === assetName);
115+
if (!asset) {
116+
const available = release.assets.map((a) => a.name).join(", ");
117+
throw new Error(`Asset ${assetName} not found. Available: ${available}`);
118+
}
119+
120+
// Return the API URL for downloading (works for private repos)
121+
return asset.url;
122+
}
123+
64124
async function downloadBinary() {
65125
const target = getTarget();
66126
const version = getVersion();
127+
const token = getGitHubToken();
67128
const assetName = `${BINARY_NAME}-${target}.tar.gz`;
68-
const url = `https://github.com/${REPO}/releases/download/v${version}/${assetName}`;
69129

70130
console.log(`Downloading ${BINARY_NAME} v${version} for ${getPlatformKey()}...`);
71-
console.log(` ${url}`);
131+
132+
if (!token) {
133+
console.error(`\nError: No GitHub token found.`);
134+
console.error(`\nThis is a private repo. You need either:`);
135+
console.error(` 1. GitHub CLI: Install and run 'gh auth login'`);
136+
console.error(` 2. Environment variable: export GITHUB_TOKEN=ghp_your_token`);
137+
console.error(`\nThen reinstall: pnpm install`);
138+
process.exit(1);
139+
}
140+
141+
console.log(` (using GitHub token for authentication)`);
72142

73143
try {
74-
const tarGz = await httpsGet(url);
144+
// Get the asset download URL from the API
145+
const assetUrl = await getAssetUrl(version, assetName, token);
146+
console.log(` ${assetUrl}`);
147+
148+
const tarGz = await httpsGet(assetUrl, token, true);
75149

76150
// Extract tar.gz
77151
const tar = zlib.gunzipSync(tarGz);
@@ -90,13 +164,12 @@ async function downloadBinary() {
90164

91165
console.log(`✓ Installed ${BINARY_NAME} to ${binaryPath}`);
92166
} catch (error) {
167+
console.error(`\nError: Failed to download binary for ${getPlatformKey()}`);
168+
console.error(` ${error.message}`);
93169
if (error.message.includes("404")) {
94-
console.error(`\nError: No prebuilt binary found for ${getPlatformKey()}`);
95-
console.error(`Release v${version} may not exist or may not have binaries yet.`);
96-
console.error(`\nYou can build from source with: cargo build --release`);
97-
} else {
98-
console.error(`\nError downloading binary: ${error.message}`);
170+
console.error(`\nRelease v${version} may not exist or may not have binaries for your platform.`);
99171
}
172+
console.error(`\nYou can build from source with: cargo build --release`);
100173
process.exit(1);
101174
}
102175
}

0 commit comments

Comments
 (0)