@osvaldoandrade/codeq 0.2.2

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 ADDED
@@ -0,0 +1,28 @@
1
+ # codeq (npm)
2
+
3
+ This package installs the `codeq` native CLI from GitHub Releases and exposes it on your PATH via npm.
4
+
5
+ ## Install (npmjs)
6
+
7
+ ```bash
8
+ npm i -g @osvaldoandrade/codeq
9
+ codeq --help
10
+ ```
11
+
12
+ ## Upgrade
13
+
14
+ ```bash
15
+ npm i -g @osvaldoandrade/codeq@latest
16
+ ```
17
+
18
+ or:
19
+
20
+ ```bash
21
+ npm update -g @osvaldoandrade/codeq
22
+ ```
23
+
24
+ ## Overrides
25
+
26
+ - `CODEQ_GITHUB_REPO` (default `osvaldoandrade/codeq`): download binaries from another repo/fork.
27
+ - `CODEQ_RELEASE_TAG` (default `v<packageVersion>`): download from a specific tag.
28
+ - `CODEQ_GITHUB_TOKEN`: required when downloading binaries from a private repo release.
package/bin/codeq.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { spawnSync } = require("child_process");
7
+
8
+ const exe = process.platform === "win32" ? ".exe" : "";
9
+ const binPath = path.join(__dirname, "..", "native", `codeq${exe}`);
10
+
11
+ if (!fs.existsSync(binPath)) {
12
+ console.error("[codeq] Native binary not found.");
13
+ console.error("[codeq] Reinstall the package to trigger download:");
14
+ console.error(" npm i -g @osvaldoandrade/codeq@latest");
15
+ process.exit(1);
16
+ }
17
+
18
+ const res = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
19
+ if (res.error) {
20
+ console.error(`[codeq] Failed to run native binary: ${res.error.message}`);
21
+ process.exit(1);
22
+ }
23
+ process.exit(typeof res.status === "number" ? res.status : 1);
24
+
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@osvaldoandrade/codeq",
3
+ "version": "0.2.2",
4
+ "description": "codeQ CLI (installs a native binary from GitHub Releases)",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/osvaldoandrade/codeq",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/osvaldoandrade/codeq.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/osvaldoandrade/codeq/issues"
13
+ },
14
+ "bin": {
15
+ "codeq": "bin/codeq.js"
16
+ },
17
+ "scripts": {
18
+ "postinstall": "node scripts/postinstall.js"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "files": [
24
+ "bin/",
25
+ "scripts/",
26
+ "README.md"
27
+ ]
28
+ }
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const https = require("https");
6
+ const path = require("path");
7
+
8
+ const pkg = require("../package.json");
9
+
10
+ function goosFromPlatform(platform) {
11
+ switch (platform) {
12
+ case "darwin":
13
+ return "darwin";
14
+ case "linux":
15
+ return "linux";
16
+ case "win32":
17
+ return "windows";
18
+ default:
19
+ return null;
20
+ }
21
+ }
22
+
23
+ function goarchFromArch(arch) {
24
+ switch (arch) {
25
+ case "x64":
26
+ return "amd64";
27
+ case "arm64":
28
+ return "arm64";
29
+ default:
30
+ return null;
31
+ }
32
+ }
33
+
34
+ function githubToken() {
35
+ const t = (process.env.CODEQ_GITHUB_TOKEN || process.env.GITHUB_TOKEN || "").trim();
36
+ return t || null;
37
+ }
38
+
39
+ function githubAPIHeaders(extra) {
40
+ const headers = {
41
+ "User-Agent": "codeq-npm-installer",
42
+ ...extra,
43
+ };
44
+ const tok = githubToken();
45
+ if (tok) {
46
+ headers.Authorization = `Bearer ${tok}`;
47
+ }
48
+ return headers;
49
+ }
50
+
51
+ function getJSON(url, redirectsLeft) {
52
+ return new Promise((resolve, reject) => {
53
+ const req = https.get(
54
+ url,
55
+ {
56
+ headers: githubAPIHeaders({ Accept: "application/vnd.github+json" }),
57
+ },
58
+ (res) => {
59
+ const code = res.statusCode || 0;
60
+ if (code >= 300 && code < 400 && res.headers.location) {
61
+ if (redirectsLeft <= 0) {
62
+ reject(new Error(`too many redirects while fetching ${url}`));
63
+ res.resume();
64
+ return;
65
+ }
66
+ const next = res.headers.location.startsWith("http")
67
+ ? res.headers.location
68
+ : new URL(res.headers.location, url).toString();
69
+ res.resume();
70
+ getJSON(next, redirectsLeft - 1).then(resolve, reject);
71
+ return;
72
+ }
73
+ if (code !== 200) {
74
+ let body = "";
75
+ res.setEncoding("utf8");
76
+ res.on("data", (d) => {
77
+ body += d;
78
+ });
79
+ res.on("end", () => {
80
+ const msg = body.trim() ? `: ${body.trim()}` : "";
81
+ reject(new Error(`HTTP ${code} fetching ${url}${msg}`));
82
+ });
83
+ return;
84
+ }
85
+
86
+ let body = "";
87
+ res.setEncoding("utf8");
88
+ res.on("data", (d) => {
89
+ body += d;
90
+ });
91
+ res.on("end", () => {
92
+ try {
93
+ resolve(JSON.parse(body));
94
+ } catch (err) {
95
+ reject(new Error(`invalid JSON from ${url}`));
96
+ }
97
+ });
98
+ }
99
+ );
100
+ req.on("error", reject);
101
+ });
102
+ }
103
+
104
+ function downloadToFile(url, filePath, redirectsLeft) {
105
+ return new Promise((resolve, reject) => {
106
+ const req = https.get(
107
+ url,
108
+ {
109
+ headers: githubAPIHeaders({ Accept: "application/octet-stream" }),
110
+ },
111
+ (res) => {
112
+ const code = res.statusCode || 0;
113
+ if (code >= 300 && code < 400 && res.headers.location) {
114
+ if (redirectsLeft <= 0) {
115
+ reject(new Error(`too many redirects while downloading ${url}`));
116
+ res.resume();
117
+ return;
118
+ }
119
+ const next = res.headers.location.startsWith("http")
120
+ ? res.headers.location
121
+ : new URL(res.headers.location, url).toString();
122
+ res.resume();
123
+ downloadToFile(next, filePath, redirectsLeft - 1).then(resolve, reject);
124
+ return;
125
+ }
126
+ if (code !== 200) {
127
+ let body = "";
128
+ res.setEncoding("utf8");
129
+ res.on("data", (d) => {
130
+ body += d;
131
+ });
132
+ res.on("end", () => {
133
+ const msg = body.trim() ? `: ${body.trim()}` : "";
134
+ reject(new Error(`HTTP ${code} downloading ${url}${msg}`));
135
+ });
136
+ return;
137
+ }
138
+
139
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
140
+ const f = fs.createWriteStream(filePath, { mode: 0o755 });
141
+ res.pipe(f);
142
+ f.on("finish", () => f.close(resolve));
143
+ f.on("error", (err) => {
144
+ try {
145
+ fs.unlinkSync(filePath);
146
+ } catch (_) {}
147
+ reject(err);
148
+ });
149
+ }
150
+ );
151
+ req.on("error", reject);
152
+ });
153
+ }
154
+
155
+ async function main() {
156
+ const repo = process.env.CODEQ_GITHUB_REPO || "osvaldoandrade/codeq";
157
+ const tag = process.env.CODEQ_RELEASE_TAG || `v${pkg.version}`;
158
+
159
+ const goos = goosFromPlatform(process.platform);
160
+ const goarch = goarchFromArch(process.arch);
161
+ if (!goos || !goarch) {
162
+ console.error(`[codeq] Unsupported platform for prebuilt binaries: ${process.platform}/${process.arch}`);
163
+ console.error("[codeq] Fallback: install from source using install.sh:");
164
+ console.error(" curl -fsSL https://raw.githubusercontent.com/osvaldoandrade/codeq/main/install.sh | sh");
165
+ process.exit(1);
166
+ }
167
+
168
+ const exe = goos === "windows" ? ".exe" : "";
169
+ const assetName = `codeq-${goos}-${goarch}${exe}`;
170
+ const url = `https://github.com/${repo}/releases/download/${tag}/${assetName}`;
171
+
172
+ const outPath = path.join(__dirname, "..", "native", `codeq${exe}`);
173
+ const tmpPath = `${outPath}.tmp`;
174
+
175
+ console.log(`[codeq] Installing ${assetName} (${tag})`);
176
+
177
+ // Prefer GitHub API when token is available (required for private repos).
178
+ const tok = githubToken();
179
+ if (tok) {
180
+ const release = await getJSON(`https://api.github.com/repos/${repo}/releases/tags/${tag}`, 5);
181
+ const assets = Array.isArray(release.assets) ? release.assets : [];
182
+ const asset = assets.find((a) => a && a.name === assetName);
183
+ if (!asset || !asset.url) {
184
+ throw new Error(`asset not found in release ${tag}: ${assetName}`);
185
+ }
186
+ await downloadToFile(asset.url, tmpPath, 5);
187
+ } else {
188
+ console.log(`[codeq] Downloading from ${url}`);
189
+ await downloadToFile(url, tmpPath, 5);
190
+ }
191
+
192
+ try {
193
+ fs.renameSync(tmpPath, outPath);
194
+ } catch (err) {
195
+ // Windows can fail to replace an existing file in use; try to remove and retry.
196
+ try {
197
+ fs.unlinkSync(outPath);
198
+ } catch (_) {}
199
+ fs.renameSync(tmpPath, outPath);
200
+ }
201
+
202
+ if (goos !== "windows") {
203
+ try {
204
+ fs.chmodSync(outPath, 0o755);
205
+ } catch (_) {}
206
+ }
207
+
208
+ console.log("[codeq] Installed native binary");
209
+ }
210
+
211
+ main().catch((err) => {
212
+ console.error(`[codeq] Install failed: ${err && err.message ? err.message : String(err)}`);
213
+ process.exit(1);
214
+ });