Skip to content

Commit b26ff32

Browse files
committed
chore: make npm installer respect env proxy
1 parent 06ac8be commit b26ff32

File tree

5 files changed

+152
-6
lines changed

5 files changed

+152
-6
lines changed

.github/workflows/release.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ jobs:
5252
- os: ubuntu-latest
5353
target: x86_64-unknown-linux-gnu
5454
exeSuffix: ''
55+
- os: ubuntu-latest
56+
target: aarch64-unknown-linux-gnu
57+
exeSuffix: ''
5558
- os: macos-13
5659
target: x86_64-apple-darwin
5760
exeSuffix: ''
@@ -68,25 +71,36 @@ jobs:
6871
- uses: actions/checkout@v4
6972
- name: Install Rust
7073
uses: dtolnay/rust-toolchain@stable
74+
with:
75+
targets: ${{ matrix.target }}
7176
- name: Cache cargo
7277
uses: swatinem/rust-cache@v2
7378
with:
7479
workspaces: .
80+
- name: Install cross linker (Linux aarch64)
81+
if: matrix.target == 'aarch64-unknown-linux-gnu'
82+
run: |
83+
sudo apt-get update
84+
sudo apt-get install -y gcc-aarch64-linux-gnu
85+
7586
- name: Build release
76-
run: cargo build --release --locked
87+
env:
88+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
89+
run: |
90+
cargo build --release --locked --target ${{ matrix.target }}
7791
7892
- name: Prepare asset (unix)
7993
if: runner.os != 'Windows'
8094
run: |
8195
ASSET="loctok-v${VERSION}-${{ matrix.target }}"
82-
cp target/release/loctok "$ASSET"
96+
cp "target/${{ matrix.target }}/release/loctok" "$ASSET"
8397
echo "ASSET=$ASSET" >> $GITHUB_ENV
8498
- name: Prepare asset (windows)
8599
if: runner.os == 'Windows'
86100
shell: powershell
87101
run: |
88102
$env:ASSET = "loctok-v$env:VERSION-${{ matrix.target }}.exe"
89-
Copy-Item target\release\loctok.exe $env:ASSET
103+
Copy-Item "target\${{ matrix.target }}\release\loctok.exe" $env:ASSET
90104
echo "ASSET=$env:ASSET" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
91105
92106
- name: Upload asset to release

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ counts.json
1717

1818
# Node/npm leftovers (if present)
1919
node_modules/
20-
20+
vendor/

npm/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
"scripts": {
1010
"postinstall": "node scripts/install.js"
1111
},
12+
"dependencies": {
13+
"http-proxy-agent": "^7.0.2",
14+
"https-proxy-agent": "^7.0.2",
15+
"proxy-from-env": "^1.1.0"
16+
},
1217
"files": [
1318
"bin/",
1419
"scripts/",

npm/pnpm-lock.yaml

Lines changed: 74 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

npm/scripts/install.js

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,20 @@
55
*/
66
const fs = require('fs');
77
const path = require('path');
8+
const http = require('http');
89
const https = require('https');
10+
const { URL } = require('url');
11+
// Respect proxy settings from environment (HTTP(S)_PROXY, NO_PROXY) and npm configs
12+
let getProxyForUrl;
13+
let HttpsProxyAgent;
14+
let HttpProxyAgent;
15+
try {
16+
({ getProxyForUrl } = require('proxy-from-env'));
17+
({ HttpsProxyAgent } = require('https-proxy-agent'));
18+
({ HttpProxyAgent } = require('http-proxy-agent'));
19+
} catch (_) {
20+
// Dependencies may not resolve if a user vendors this; fall back to no-proxy mode.
21+
}
922

1023
const REPO = 'zxch3n/loctok';
1124

@@ -41,13 +54,53 @@ function ensureDir(p) {
4154
fs.mkdirSync(p, { recursive: true });
4255
}
4356

57+
function chooseAgent(u) {
58+
try {
59+
if (!getProxyForUrl || (!HttpsProxyAgent && !HttpProxyAgent)) return undefined;
60+
const proxy = getProxyForUrl(u) ||
61+
// fallback to npm-specific envs if set
62+
(new URL(u).protocol === 'https:'
63+
? (process.env.npm_config_https_proxy || process.env.npm_config_proxy)
64+
: (process.env.npm_config_http_proxy || process.env.npm_config_proxy));
65+
if (!proxy) return undefined;
66+
const isHttps = new URL(u).protocol === 'https:';
67+
if (isHttps && HttpsProxyAgent) return new HttpsProxyAgent(proxy);
68+
if (!isHttps && HttpProxyAgent) return new HttpProxyAgent(proxy);
69+
} catch (_) {
70+
return undefined;
71+
}
72+
return undefined;
73+
}
74+
75+
function getLib(u) {
76+
const proto = new URL(u).protocol;
77+
return proto === 'http:' ? http : https;
78+
}
79+
80+
function toAbsoluteLocation(currentUrl, location) {
81+
try {
82+
return new URL(location, currentUrl).toString();
83+
} catch (_) {
84+
return location; // best effort
85+
}
86+
}
87+
4488
function download(url, dest) {
4589
return new Promise((resolve, reject) => {
4690
const doReq = (u, redirectsLeft = 5) => {
47-
const req = https.get(u, { headers: { 'User-Agent': 'loctok-installer' } }, (res) => {
91+
const lib = getLib(u);
92+
const opts = {
93+
headers: { 'User-Agent': 'loctok-installer' },
94+
};
95+
const agent = chooseAgent(u);
96+
if (agent) opts.agent = agent;
97+
98+
const req = lib.get(u, opts, (res) => {
4899
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
49100
if (redirectsLeft === 0) return reject(new Error('Too many redirects'));
50-
return doReq(res.headers.location, redirectsLeft - 1);
101+
const next = toAbsoluteLocation(u, res.headers.location);
102+
res.resume(); // drain
103+
return doReq(next, redirectsLeft - 1);
51104
}
52105
if (res.statusCode !== 200) {
53106
return reject(new Error(`HTTP ${res.statusCode} for ${u}`));

0 commit comments

Comments
 (0)