com.jimuwd.xian.registry-proxy 1.0.132 → 1.0.133

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/README.MD CHANGED
@@ -70,142 +70,6 @@ unsafeHttpWhitelist:
70
70
 
71
71
  在 `scripts/` 目录下创建 `install-from-proxy-registries.sh` 脚本,用于自动化代理设置和依赖安装:
72
72
 
73
- ```bash
74
- #!/bin/bash
75
-
76
- # 启用严格模式,但移除 set -e,手动处理错误
77
- set -u # 未定义变量时退出
78
- set -o pipefail # 管道中任一命令失败时退出
79
-
80
- # 动态确定项目根目录(假设 package.json 所在目录为根目录)
81
- find_project_root() {
82
- local dir="$PWD"
83
- while [ "$dir" != "/" ]; do
84
- if [ -f "$dir/package.json" ]; then
85
- echo "$dir"
86
- return 0
87
- fi
88
- dir=$(dirname "$dir")
89
- done
90
- echo "Error: Could not find project root (package.json not found)" >&2
91
- exit 1
92
- }
93
-
94
- PROJECT_ROOT=$(find_project_root)
95
-
96
- # 定义锁文件和端口文件路径(固定在项目根目录)
97
- LOCK_FILE="$PROJECT_ROOT/.registry-proxy-install.lock"
98
-
99
- # 检查是否已经在运行(通过锁文件)
100
- if [ -f "$LOCK_FILE" ]; then
101
- echo "Custom install script is already running (lock file $LOCK_FILE exists)."
102
- echo "If this is unexpected, please remove $LOCK_FILE and try again."
103
- exit 0 # 内层脚本直接退出,表示任务已由外层脚本完成
104
- fi
105
-
106
- # 创建锁文件
107
- touch "$LOCK_FILE"
108
-
109
- # 清理函数,支持不同的退出状态
110
- # 参数 $1:退出状态(0 表示正常退出,1 表示异常退出)
111
- cleanup() {
112
- local exit_code=${1:-1} # 默认退出码为 1(异常退出)
113
-
114
- # 显式清除 EXIT 信号的 trap,避免潜在的误解
115
- trap - EXIT
116
-
117
- if [ "$exit_code" -eq 0 ]; then
118
- echo "Cleaning up after successful execution..."
119
- else
120
- echo "Caught interrupt signal or error, cleaning up..."
121
- fi
122
-
123
- # 清理临时文件
124
- rm -f "$LOCK_FILE" 2>/dev/null
125
- # PORT_FILE端口临时文件是registry-proxy服务器管理的文件这里不负责清理,服务器退出时会自动清理
126
- #rm -f "$PORT_FILE" 2>/dev/null
127
-
128
- # 停止代理服务器
129
- if [ -n "${PROXY_PID:-}" ]; then
130
- echo "Stopping proxy server (PID: $PROXY_PID)..."
131
- kill -TERM "$PROXY_PID" 2>/dev/null
132
- wait "$PROXY_PID" 2>/dev/null || true
133
- echo "Proxy server stopped."
134
- fi
135
-
136
- # 切换到项目根目录
137
- # shellcheck disable=SC2164
138
- cd "$PROJECT_ROOT"
139
-
140
- # 清理 npmRegistryServer 配置
141
- yarn config unset npmRegistryServer 2>/dev/null || true
142
- echo "Cleared npmRegistryServer configuration"
143
-
144
- # 根据退出状态退出
145
- exit "$exit_code"
146
- }
147
-
148
- # 注册信号处理
149
- trap 'cleanup 1' SIGINT SIGTERM EXIT # 异常退出时调用 cleanup,退出码为 1
150
-
151
- # 切换到项目根目录
152
- # shellcheck disable=SC2164
153
- cd "$PROJECT_ROOT"
154
-
155
- # 使用 yarn dlx 直接运行 registry-proxy,可通过环境变量指定registry-proxy版本号,默认是latest,registry-proxy将会被放入后台运行,并在安装结束后自动退出。
156
- REGISTRY_PROXY_VERSION="${REGISTRY_PROXY_VERSION:-latest}"
157
- echo "Starting registry-proxy@$REGISTRY_PROXY_VERSION in the background (logs will be displayed below)..."
158
- # 下载registry-proxy临时可执行程序并运行 因yarn可能会缓存tarball url的缘故(yarn.lock内<package>.resolution值),这里不得已只能写死本地代理端口地址,以便无论是从缓存获取tarball url还是从代理服务提供的元数据获取tarball url地址都能成功下载tarball文件
159
- # 但是注意 这个端口不能暴露到外部使用,只允许本地使用,避免不必要的安全隐患 事实上registry-proxy server也是只监听着::1本机端口的。
160
- yarn dlx com.jimuwd.xian.registry-proxy@"$REGISTRY_PROXY_VERSION" .registry-proxy.yml .yarnrc.yml ~/.yarnrc.yml 40061 &
161
- PROXY_PID=$!
162
-
163
- # 等待代理服务器启动并写入端口,最多 30 秒
164
- echo "Waiting for proxy server to start (up to 30 seconds)..."
165
- PORT_FILE="$PROJECT_ROOT/.registry-proxy-port"
166
- # shellcheck disable=SC2034
167
- for i in {1..300}; do # 300 次循环,每次 0.1 秒,总共 30 秒
168
- if [ -f "$PORT_FILE" ]; then
169
- PROXY_PORT=$(cat "$PORT_FILE")
170
- if [ -z "$PROXY_PORT" ]; then
171
- echo "Error: Port file $PORT_FILE is empty"
172
- cleanup 1
173
- fi
174
- if nc -z localhost "$PROXY_PORT" 2>/dev/null; then
175
- echo "Proxy server is ready on port $PROXY_PORT!"
176
- break
177
- else
178
- # 检查端口是否被占用
179
- if netstat -tuln 2>/dev/null | grep -q ":$PROXY_PORT "; then
180
- echo "Error: Port $PROXY_PORT is already in use by another process"
181
- cleanup 1
182
- fi
183
- fi
184
- fi
185
- sleep 0.1
186
- done
187
-
188
- # 检查是否成功启动
189
- if [ -z "${PROXY_PORT:-}" ] || ! nc -z localhost "$PROXY_PORT" 2>/dev/null; then
190
- echo "Error: Proxy server failed to start after 30 seconds"
191
- echo "Please check the registry-proxy logs above for more details."
192
- cleanup 1
193
- fi
194
-
195
- # 动态设置 npmRegistryServer 为代理地址 注意:yarn对“localhost”域名不友好,请直接使用 [::1]
196
- yarn config set npmRegistryServer "http://[::1]:$PROXY_PORT"
197
- echo "Set npmRegistryServer to http://[::1]:$PROXY_PORT"
198
-
199
- # 使用动态代理端口运行 yarn install,并捕获错误
200
- if ! yarn install; then
201
- echo "Error: yarn install failed"
202
- cleanup 1
203
- fi
204
-
205
- # 正常执行完成,调用 cleanup 并传入退出码 0
206
- cleanup 0
207
- ```
208
-
209
73
  ### 5. 设置脚本权限
210
74
 
211
75
  确保脚本具有可执行权限,并将权限状态提交到版本控制:
@@ -3,9 +3,8 @@
3
3
  import fs from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import { execa } from 'execa';
6
- import net from 'node:net';
7
- import { fileURLToPath } from 'node:url';
8
6
  import findProjectRoot from "../utils/findProjectRoot.js";
7
+ import { isPortConnectable } from "../utils/portTester.js";
9
8
  // Constants
10
9
  const REGISTRY_PROXY_VERSION = process.env.REGISTRY_PROXY_VERSION || 'latest';
11
10
  const LOCK_FILE_NAME = '.registry-proxy-install.lock';
@@ -16,18 +15,6 @@ const CHECK_INTERVAL_MS = 100; // 0.1 seconds
16
15
  let proxyProcess = null;
17
16
  let cleanupHandlers = [];
18
17
  let signalHandlers = [];
19
- // Helper functions
20
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
- async function isPortAvailable(port) {
22
- return new Promise(resolve => {
23
- const server = net.createServer();
24
- server.unref();
25
- server.on('error', () => resolve(false));
26
- server.listen({ port }, () => {
27
- server.close(() => resolve(true));
28
- });
29
- });
30
- }
31
18
  async function waitForFile(filePath, timeoutMs) {
32
19
  const startTime = Date.now();
33
20
  while (Date.now() - startTime < timeoutMs) {
@@ -53,17 +40,6 @@ async function readPortFile(filePath) {
53
40
  }
54
41
  return port;
55
42
  }
56
- async function checkPortListening(port) {
57
- return new Promise(resolve => {
58
- const socket = new net.Socket();
59
- socket.on('error', () => resolve(false));
60
- socket.on('connect', () => {
61
- socket.destroy();
62
- resolve(true);
63
- });
64
- socket.connect({ port, host: '::1' });
65
- });
66
- }
67
43
  // Cleanup management
68
44
  async function cleanup(exitCode = 1) {
69
45
  // Run all cleanup handlers in reverse order
@@ -113,8 +89,7 @@ async function main() {
113
89
  // Start registry proxy
114
90
  console.log(`Starting registry-proxy@${REGISTRY_PROXY_VERSION} in the background...`);
115
91
  proxyProcess = execa('yarn', [
116
- 'dlx',
117
- `com.jimuwd.xian.registry-proxy@${REGISTRY_PROXY_VERSION}`,
92
+ 'dlx', '-p', `com.jimuwd.xian.registry-proxy@${REGISTRY_PROXY_VERSION}`,
118
93
  'registry-proxy',
119
94
  '.registry-proxy.yml',
120
95
  '.yarnrc.yml',
@@ -144,15 +119,10 @@ async function main() {
144
119
  throw new Error(`Proxy server failed to create port file after ${MAX_WAIT_TIME_MS / 1000} seconds`);
145
120
  }
146
121
  const PROXY_PORT = await readPortFile(PORT_FILE);
147
- const portAvailable = await isPortAvailable(PROXY_PORT);
148
- if (!portAvailable) {
149
- throw new Error(`Port ${PROXY_PORT} is already in use by another process`);
150
- }
151
- const isListening = await checkPortListening(PROXY_PORT);
152
- if (!isListening) {
122
+ const portConnectable = await isPortConnectable(PROXY_PORT);
123
+ if (!portConnectable) {
153
124
  throw new Error(`Proxy server not listening on port ${PROXY_PORT}`);
154
125
  }
155
- console.log(`Proxy server is ready on port ${PROXY_PORT}!`);
156
126
  // Configure yarn
157
127
  await execa('yarn', ['config', 'set', 'npmRegistryServer', `http://[::1]:${PROXY_PORT}`]);
158
128
  console.log(`Set npmRegistryServer to http://[::1]:${PROXY_PORT}`);
@@ -180,23 +150,26 @@ async function main() {
180
150
  await cleanup(1);
181
151
  }
182
152
  }
183
- // Signal handling
184
- ['SIGINT', 'SIGTERM', 'SIGHUP'].forEach(signal => {
185
- process.on(signal, async () => {
186
- console.log(`Received ${signal}, cleaning up...`);
187
- for (const handler of signalHandlers) {
188
- try {
189
- await handler();
190
- }
191
- catch (err) {
192
- console.error('Signal handler error:', err);
153
+ // 当前模块是否是直接运行的入口文件,而不是被其他模块导入的
154
+ if (import.meta.url === `file://${process.argv[1]}`) {
155
+ // Signal handling
156
+ ['SIGINT', 'SIGTERM', 'SIGHUP'].forEach(signal => {
157
+ process.on(signal, async () => {
158
+ console.log(`Received ${signal}, cleaning up...`);
159
+ for (const handler of signalHandlers) {
160
+ try {
161
+ await handler();
162
+ }
163
+ catch (err) {
164
+ console.error('Signal handler error:', err);
165
+ }
193
166
  }
194
- }
195
- await cleanup(1);
167
+ await cleanup(1);
168
+ });
196
169
  });
197
- });
198
- // Start the program
199
- main().catch(err => {
200
- console.error('Unhandled error:', err);
201
- cleanup(1);
202
- });
170
+ // Start the program
171
+ main().catch(err => {
172
+ console.error('Unhandled error:', err);
173
+ cleanup(1);
174
+ });
175
+ }
@@ -0,0 +1,18 @@
1
+ export declare function isPortFree(port: number): Promise<boolean>;
2
+ /**
3
+ * 检查指定端口是否可连接(有服务正在监听)
4
+ * @param port 要检查的端口号
5
+ * @param host 目标主机(默认本地IPv6 ::1)
6
+ * @param timeout 超时时间(毫秒,默认1000ms)
7
+ */
8
+ export declare function isPortConnectable(port: number, host?: string, timeout?: number): Promise<boolean>;
9
+ /**
10
+ * 简单的检查,若需要更高级的检查,请使用{@link isPortConnectable}
11
+ */
12
+ declare function checkPortListening(port: number): Promise<boolean>;
13
+ declare const _default: {
14
+ isPortFree: typeof isPortFree;
15
+ isPortConnectable: typeof isPortConnectable;
16
+ checkPortListening: typeof checkPortListening;
17
+ };
18
+ export default _default;
@@ -0,0 +1,52 @@
1
+ import net from "node:net";
2
+ export async function isPortFree(port) {
3
+ return new Promise(resolve => {
4
+ const server = net.createServer();
5
+ server.unref();
6
+ server.on('error', () => resolve(false));
7
+ server.listen({ port }, () => {
8
+ server.close(() => resolve(true));
9
+ });
10
+ });
11
+ }
12
+ /**
13
+ * 检查指定端口是否可连接(有服务正在监听)
14
+ * @param port 要检查的端口号
15
+ * @param host 目标主机(默认本地IPv6 ::1)
16
+ * @param timeout 超时时间(毫秒,默认1000ms)
17
+ */
18
+ export async function isPortConnectable(port, host = '::1', timeout = 1000) {
19
+ return new Promise((resolve) => {
20
+ const socket = new net.Socket();
21
+ // 设置超时
22
+ socket.setTimeout(timeout);
23
+ socket.on('connect', () => {
24
+ socket.destroy();
25
+ resolve(true);
26
+ });
27
+ socket.on('timeout', () => {
28
+ socket.destroy();
29
+ resolve(false);
30
+ });
31
+ socket.on('error', () => {
32
+ socket.destroy();
33
+ resolve(false);
34
+ });
35
+ socket.connect(port, host);
36
+ });
37
+ }
38
+ /**
39
+ * 简单的检查,若需要更高级的检查,请使用{@link isPortConnectable}
40
+ */
41
+ async function checkPortListening(port) {
42
+ return new Promise(resolve => {
43
+ const socket = new net.Socket();
44
+ socket.on('error', () => resolve(false));
45
+ socket.on('connect', () => {
46
+ socket.destroy();
47
+ resolve(true);
48
+ });
49
+ socket.connect({ port, host: '::1' });
50
+ });
51
+ }
52
+ export default { isPortFree, isPortConnectable, checkPortListening };
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.132",
3
+ "version": "1.0.133",
4
4
  "description": "A lightweight npm registry proxy with fallback support",
5
5
  "type": "module",
6
6
  "main": "dist/server/index.js",
7
7
  "bin": {
8
8
  "registry-proxy": "dist/server/index.js",
9
- "yarn-install": "dist/client/yarn-install.js"
9
+ "yarn-install": "dist/client/yarn-install.js",
10
+ "yarn-install-bash": "src/client/yarn-install.sh"
10
11
  },
11
12
  "files": [
12
- "dist"
13
+ "dist",
14
+ "src/client/yarn-install.sh"
13
15
  ],
14
16
  "scripts": {
15
17
  "build": "tsc",
@@ -0,0 +1,133 @@
1
+ #!/bin/bash
2
+
3
+ # 启用严格模式,但移除 set -e,手动处理错误
4
+ set -u # 未定义变量时退出
5
+ set -o pipefail # 管道中任一命令失败时退出
6
+
7
+ # 动态确定项目根目录(假设 package.json 所在目录为根目录)
8
+ find_project_root() {
9
+ local dir="$PWD"
10
+ while [ "$dir" != "/" ]; do
11
+ if [ -f "$dir/package.json" ]; then
12
+ echo "$dir"
13
+ return 0
14
+ fi
15
+ dir=$(dirname "$dir")
16
+ done
17
+ echo "Error: Could not find project root (package.json not found)" >&2
18
+ exit 1
19
+ }
20
+
21
+ PROJECT_ROOT=$(find_project_root)
22
+
23
+ # 定义锁文件和端口文件路径(固定在项目根目录)
24
+ LOCK_FILE="$PROJECT_ROOT/.registry-proxy-install.lock"
25
+
26
+ # 检查是否已经在运行(通过锁文件)
27
+ if [ -f "$LOCK_FILE" ]; then
28
+ echo "Custom install script is already running (lock file $LOCK_FILE exists)."
29
+ echo "If this is unexpected, please remove $LOCK_FILE and try again."
30
+ exit 0 # 内层脚本直接退出,表示任务已由外层脚本完成
31
+ fi
32
+
33
+ # 创建锁文件
34
+ touch "$LOCK_FILE"
35
+
36
+ # 清理函数,支持不同的退出状态
37
+ # 参数 $1:退出状态(0 表示正常退出,1 表示异常退出)
38
+ cleanup() {
39
+ local exit_code=${1:-1} # 默认退出码为 1(异常退出)
40
+
41
+ # 显式清除 EXIT 信号的 trap,避免潜在的误解
42
+ trap - EXIT
43
+
44
+ if [ "$exit_code" -eq 0 ]; then
45
+ echo "Cleaning up after successful execution..."
46
+ else
47
+ echo "Caught interrupt signal or error, cleaning up..."
48
+ fi
49
+
50
+ # 清理临时文件
51
+ rm -f "$LOCK_FILE" 2>/dev/null
52
+ # PORT_FILE端口临时文件是registry-proxy服务器管理的文件这里不负责清理,服务器退出时会自动清理
53
+ #rm -f "$PORT_FILE" 2>/dev/null
54
+
55
+ # 停止代理服务器
56
+ if [ -n "${PROXY_PID:-}" ]; then
57
+ echo "Stopping proxy server (PID: $PROXY_PID)..."
58
+ kill -TERM "$PROXY_PID" 2>/dev/null
59
+ wait "$PROXY_PID" 2>/dev/null || true
60
+ echo "Proxy server stopped."
61
+ fi
62
+
63
+ # 切换到项目根目录
64
+ # shellcheck disable=SC2164
65
+ cd "$PROJECT_ROOT"
66
+
67
+ # 清理 npmRegistryServer 配置
68
+ yarn config unset npmRegistryServer 2>/dev/null || true
69
+ echo "Cleared npmRegistryServer configuration"
70
+
71
+ # 根据退出状态退出
72
+ exit "$exit_code"
73
+ }
74
+
75
+ # 注册信号处理
76
+ trap 'cleanup 1' SIGINT SIGTERM EXIT # 异常退出时调用 cleanup,退出码为 1
77
+
78
+ # 切换到项目根目录
79
+ # shellcheck disable=SC2164
80
+ cd "$PROJECT_ROOT"
81
+
82
+ # 使用 yarn dlx 直接运行 registry-proxy,可通过环境变量指定registry-proxy版本号,默认是latest,registry-proxy将会被放入后台运行,并在安装结束后自动退出。
83
+ REGISTRY_PROXY_VERSION="${REGISTRY_PROXY_VERSION:-latest}"
84
+ echo "Starting registry-proxy@$REGISTRY_PROXY_VERSION in the background (logs will be displayed below)..."
85
+ # 下载registry-proxy临时可执行程序并运行 因yarn可能会缓存tarball url的缘故(yarn.lock内<package>.resolution值),这里不得已只能写死本地代理端口地址,以便无论是从缓存获取tarball url还是从代理服务提供的元数据获取tarball url地址都能成功下载tarball文件
86
+ # 但是注意 这个端口不能暴露到外部使用,只允许本地使用,避免不必要的安全隐患 事实上registry-proxy server也是只监听着::1本机端口的。
87
+ yarn dlx -p com.jimuwd.xian.registry-proxy@"$REGISTRY_PROXY_VERSION" registry-proxy .registry-proxy.yml .yarnrc.yml ~/.yarnrc.yml 40061 &
88
+ PROXY_PID=$!
89
+
90
+ # 等待代理服务器启动并写入端口,最多 30 秒
91
+ echo "Waiting for proxy server to start (up to 30 seconds)..."
92
+ PORT_FILE="$PROJECT_ROOT/.registry-proxy-port"
93
+ # shellcheck disable=SC2034
94
+ for i in {1..300}; do # 300 次循环,每次 0.1 秒,总共 30 秒
95
+ if [ -f "$PORT_FILE" ]; then
96
+ PROXY_PORT=$(cat "$PORT_FILE")
97
+ if [ -z "$PROXY_PORT" ]; then
98
+ echo "Error: Port file $PORT_FILE is empty"
99
+ cleanup 1
100
+ fi
101
+ if nc -z localhost "$PROXY_PORT" 2>/dev/null; then
102
+ echo "Proxy server is ready on port $PROXY_PORT!"
103
+ break
104
+ else
105
+ # 检查端口是否被占用
106
+ if netstat -tuln 2>/dev/null | grep -q ":$PROXY_PORT "; then
107
+ echo "Error: Port $PROXY_PORT is already in use by another process"
108
+ cleanup 1
109
+ fi
110
+ fi
111
+ fi
112
+ sleep 0.1
113
+ done
114
+
115
+ # 检查是否成功启动
116
+ if [ -z "${PROXY_PORT:-}" ] || ! nc -z localhost "$PROXY_PORT" 2>/dev/null; then
117
+ echo "Error: Proxy server failed to start after 30 seconds"
118
+ echo "Please check the registry-proxy logs above for more details."
119
+ cleanup 1
120
+ fi
121
+
122
+ # 动态设置 npmRegistryServer 为代理地址 注意:yarn对“localhost”域名不友好,请直接使用 [::1]
123
+ yarn config set npmRegistryServer "http://[::1]:$PROXY_PORT"
124
+ echo "Set npmRegistryServer to http://[::1]:$PROXY_PORT"
125
+
126
+ # 使用动态代理端口运行 yarn install,并捕获错误
127
+ if ! yarn install; then
128
+ echo "Error: yarn install failed"
129
+ cleanup 1
130
+ fi
131
+
132
+ # 正常执行完成,调用 cleanup 并传入退出码 0
133
+ cleanup 0