@workrail/cli 0.5.10 → 0.6.0
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 +20 -12
- package/bin/workrail.js +36 -38
- package/lib/integrity.cjs +59 -0
- package/package.json +4 -1
- package/release-checksums.txt +10 -0
- package/scripts/postinstall.mjs +61 -60
- package/scripts/prepare-release.mjs +53 -0
package/README.md
CHANGED
|
@@ -36,43 +36,43 @@ The CLI automatically downloads the correct binary for your platform during inst
|
|
|
36
36
|
|
|
37
37
|
## Quick Start
|
|
38
38
|
|
|
39
|
-
1. **
|
|
39
|
+
1. **Run guided setup**:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
workrail
|
|
42
|
+
workrail setup
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
On a new installation, the same guided flow starts automatically in an
|
|
46
|
+
interactive terminal. It connects your account, links repositories to your
|
|
47
|
+
Default Project, offers optional GitHub context and autosync, and runs your
|
|
48
|
+
first sync.
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
workrail link
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
3. **Sync your work** (captures commit metadata, not source code):
|
|
50
|
+
2. **Sync your work** (captures metadata, not source code):
|
|
52
51
|
|
|
53
52
|
```bash
|
|
54
53
|
workrail sync
|
|
55
54
|
```
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
3. **Create an entry manually** (for non-code achievements):
|
|
58
57
|
|
|
59
58
|
```bash
|
|
60
59
|
workrail entry add
|
|
61
60
|
```
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
4. **Enable autosync** (optional - runs sync on a schedule):
|
|
64
63
|
|
|
65
64
|
```bash
|
|
66
65
|
workrail autosync enable
|
|
67
66
|
```
|
|
68
67
|
|
|
69
|
-
|
|
68
|
+
5. **View your dashboard** at [workrail.dev](https://workrail.dev)
|
|
70
69
|
|
|
71
70
|
## Common Commands
|
|
72
71
|
|
|
73
72
|
| Command | Description |
|
|
74
73
|
| --------------------------- | --------------------------------------- |
|
|
75
74
|
| `workrail auth login` | Authenticate via browser OAuth |
|
|
75
|
+
| `workrail setup` | Run or revisit guided onboarding |
|
|
76
76
|
| `workrail auth status` | Check authentication status |
|
|
77
77
|
| `workrail link` | Link current repo to a Workrail project |
|
|
78
78
|
| `workrail sync` | Sync commits and create entries |
|
|
@@ -86,7 +86,15 @@ The CLI automatically downloads the correct binary for your platform during inst
|
|
|
86
86
|
|
|
87
87
|
## Privacy
|
|
88
88
|
|
|
89
|
-
Workrail syncs **metadata only** (commit hashes, timestamps, messages). Your
|
|
89
|
+
Workrail syncs **metadata only** (commit hashes, timestamps, messages). Your
|
|
90
|
+
source code never leaves your machine.
|
|
91
|
+
|
|
92
|
+
Optional GitHub context uses your locally authenticated GitHub CLI to add
|
|
93
|
+
repository-scoped PR and issue titles, descriptions, labels, dates, links, and
|
|
94
|
+
deterministic commit relationships. It does not request source, diffs, comments,
|
|
95
|
+
reviews, checks, or authentication tokens.
|
|
96
|
+
|
|
97
|
+
[Read more about our privacy practices](https://workrail.dev/privacy).
|
|
90
98
|
|
|
91
99
|
## Updating
|
|
92
100
|
|
package/bin/workrail.js
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
const fs = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const https = require("node:https");
|
|
6
|
-
const crypto = require("node:crypto");
|
|
7
6
|
const { spawn } = require("node:child_process");
|
|
7
|
+
const {
|
|
8
|
+
loadPackagedChecksums,
|
|
9
|
+
verifyArtifact,
|
|
10
|
+
} = require("../lib/integrity.cjs");
|
|
8
11
|
|
|
9
12
|
function resolveBinaryName() {
|
|
10
13
|
const platform = process.platform;
|
|
@@ -19,6 +22,10 @@ function resolveBinaryName() {
|
|
|
19
22
|
return null;
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
function resolveHelperBinaryName(binaryName) {
|
|
26
|
+
return binaryName.replace(/^workrail-/, "workrail-gh-collector-");
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
const binName = resolveBinaryName();
|
|
23
30
|
if (!binName) {
|
|
24
31
|
console.error(`Unsupported platform: ${process.platform} ${process.arch}`);
|
|
@@ -26,6 +33,14 @@ if (!binName) {
|
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
const binPath = path.join(__dirname, "native", binName);
|
|
36
|
+
const helperName = resolveHelperBinaryName(binName);
|
|
37
|
+
const helperPath = path.join(
|
|
38
|
+
__dirname,
|
|
39
|
+
"native",
|
|
40
|
+
process.platform === "win32"
|
|
41
|
+
? "workrail-gh-collector.exe"
|
|
42
|
+
: "workrail-gh-collector",
|
|
43
|
+
);
|
|
29
44
|
|
|
30
45
|
function getPackageVersion() {
|
|
31
46
|
const pkgPath = path.join(__dirname, "..", "package.json");
|
|
@@ -39,24 +54,6 @@ function getPackageVersion() {
|
|
|
39
54
|
return null;
|
|
40
55
|
}
|
|
41
56
|
|
|
42
|
-
function sha256File(filePath) {
|
|
43
|
-
const hash = crypto.createHash("sha256");
|
|
44
|
-
hash.update(fs.readFileSync(filePath));
|
|
45
|
-
return hash.digest("hex");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function parseChecksums(text) {
|
|
49
|
-
const map = new Map();
|
|
50
|
-
for (const line of text.split("\n")) {
|
|
51
|
-
const trimmed = line.trim();
|
|
52
|
-
if (!trimmed) continue;
|
|
53
|
-
const parts = trimmed.split(/\s+/);
|
|
54
|
-
if (parts.length < 2) continue;
|
|
55
|
-
map.set(parts[parts.length - 1], parts[0]);
|
|
56
|
-
}
|
|
57
|
-
return map;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
57
|
function download(url) {
|
|
61
58
|
return new Promise((resolve, reject) => {
|
|
62
59
|
https
|
|
@@ -82,8 +79,11 @@ function download(url) {
|
|
|
82
79
|
});
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
async function ensureBinaryInstalled(binaryPath, binaryName) {
|
|
86
|
-
if (fs.existsSync(binaryPath))
|
|
82
|
+
async function ensureBinaryInstalled(binaryPath, binaryName, checksums) {
|
|
83
|
+
if (fs.existsSync(binaryPath)) {
|
|
84
|
+
verifyArtifact(binaryPath, binaryName, checksums);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
87
|
|
|
88
88
|
const version = getPackageVersion();
|
|
89
89
|
if (!version) {
|
|
@@ -96,7 +96,6 @@ async function ensureBinaryInstalled(binaryPath, binaryName) {
|
|
|
96
96
|
process.env.WORKRAIL_BINARY_BASE_URL ||
|
|
97
97
|
`https://cli.workrail.dev/workrail/v${version}`;
|
|
98
98
|
const binUrl = `${baseUrl}/${binaryName}`;
|
|
99
|
-
const checksumsUrl = `${baseUrl}/checksums.txt`;
|
|
100
99
|
|
|
101
100
|
fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
|
|
102
101
|
console.error(
|
|
@@ -104,27 +103,18 @@ async function ensureBinaryInstalled(binaryPath, binaryName) {
|
|
|
104
103
|
);
|
|
105
104
|
|
|
106
105
|
const binaryBytes = await download(binUrl);
|
|
107
|
-
|
|
106
|
+
const temporaryPath = `${binaryPath}.download`;
|
|
107
|
+
fs.writeFileSync(temporaryPath, binaryBytes, { mode: 0o600 });
|
|
108
108
|
|
|
109
109
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const expected = checksums.get(binaryName);
|
|
113
|
-
if (!expected) {
|
|
114
|
-
throw new Error(`No checksum entry found for ${binaryName}`);
|
|
115
|
-
}
|
|
116
|
-
const actual = sha256File(binaryPath);
|
|
117
|
-
if (actual !== expected) {
|
|
118
|
-
throw new Error(
|
|
119
|
-
`Checksum mismatch for ${binaryName}. expected=${expected} actual=${actual}`,
|
|
120
|
-
);
|
|
121
|
-
}
|
|
110
|
+
verifyArtifact(temporaryPath, binaryName, checksums);
|
|
111
|
+
fs.renameSync(temporaryPath, binaryPath);
|
|
122
112
|
if (process.platform !== "win32") {
|
|
123
113
|
fs.chmodSync(binaryPath, 0o755);
|
|
124
114
|
}
|
|
125
115
|
} catch (error) {
|
|
126
116
|
try {
|
|
127
|
-
fs.unlinkSync(
|
|
117
|
+
fs.unlinkSync(temporaryPath);
|
|
128
118
|
} catch {}
|
|
129
119
|
throw error;
|
|
130
120
|
}
|
|
@@ -140,7 +130,9 @@ function printInstallHelp() {
|
|
|
140
130
|
|
|
141
131
|
async function main() {
|
|
142
132
|
try {
|
|
143
|
-
|
|
133
|
+
const checksums = loadPackagedChecksums(path.join(__dirname, ".."));
|
|
134
|
+
await ensureBinaryInstalled(binPath, binName, checksums);
|
|
135
|
+
await ensureBinaryInstalled(helperPath, helperName, checksums);
|
|
144
136
|
} catch (error) {
|
|
145
137
|
console.error(
|
|
146
138
|
`[workrail] Failed to install native binary: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -154,7 +146,13 @@ async function main() {
|
|
|
154
146
|
process.exit(1);
|
|
155
147
|
}
|
|
156
148
|
|
|
157
|
-
const child = spawn(binPath, process.argv.slice(2), {
|
|
149
|
+
const child = spawn(binPath, process.argv.slice(2), {
|
|
150
|
+
stdio: "inherit",
|
|
151
|
+
env: {
|
|
152
|
+
...process.env,
|
|
153
|
+
PATH: `${path.dirname(helperPath)}${path.delimiter}${process.env.PATH || ""}`,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
158
156
|
|
|
159
157
|
child.on("error", (err) => {
|
|
160
158
|
console.error(`Failed to run Workrail binary: ${err.message}`);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const crypto = require("node:crypto");
|
|
2
|
+
const fs = require("node:fs");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
function parseChecksums(text) {
|
|
6
|
+
const checksums = new Map();
|
|
7
|
+
for (const [index, line] of text.split("\n").entries()) {
|
|
8
|
+
const trimmed = line.trim();
|
|
9
|
+
if (!trimmed) continue;
|
|
10
|
+
const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?([^/\\]+)$/);
|
|
11
|
+
if (!match) {
|
|
12
|
+
throw new Error(`Invalid checksum manifest line ${index + 1}`);
|
|
13
|
+
}
|
|
14
|
+
checksums.set(match[2], match[1].toLowerCase());
|
|
15
|
+
}
|
|
16
|
+
if (checksums.size === 0) throw new Error("Checksum manifest is empty");
|
|
17
|
+
return checksums;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadPackagedChecksums(packageRoot) {
|
|
21
|
+
const manifestPath = path.join(packageRoot, "release-checksums.txt");
|
|
22
|
+
try {
|
|
23
|
+
return parseChecksums(fs.readFileSync(manifestPath, "utf8"));
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Trusted release checksum manifest is unavailable: ${error instanceof Error ? error.message : String(error)}`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function sha256File(filePath) {
|
|
32
|
+
const hash = crypto.createHash("sha256");
|
|
33
|
+
hash.update(fs.readFileSync(filePath));
|
|
34
|
+
return hash.digest("hex");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function verifyArtifact(filePath, artifactName, checksums) {
|
|
38
|
+
const expected = checksums.get(artifactName);
|
|
39
|
+
if (!expected) throw new Error(`No trusted checksum for ${artifactName}`);
|
|
40
|
+
const actual = sha256File(filePath);
|
|
41
|
+
if (actual !== expected) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Checksum mismatch for ${artifactName}. expected=${expected} actual=${actual}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function verifyRequiredArtifacts(distRoot, artifactNames, checksums) {
|
|
49
|
+
for (const artifactName of artifactNames) {
|
|
50
|
+
verifyArtifact(path.join(distRoot, artifactName), artifactName, checksums);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
loadPackagedChecksums,
|
|
56
|
+
parseChecksums,
|
|
57
|
+
verifyArtifact,
|
|
58
|
+
verifyRequiredArtifacts,
|
|
59
|
+
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workrail/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Workrail CLI - Track your engineering work and build a career record",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|
|
7
7
|
"workrail": "bin/workrail.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
+
"prepack": "node ./scripts/prepare-release.mjs",
|
|
10
11
|
"postinstall": "node ./scripts/postinstall.mjs"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"bin",
|
|
15
|
+
"lib",
|
|
14
16
|
"scripts",
|
|
17
|
+
"release-checksums.txt",
|
|
15
18
|
"README.md"
|
|
16
19
|
],
|
|
17
20
|
"publishConfig": {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
6b5aa61e2bb1733a3b5c8a66684586c015c32c565f77ea51b6273c7d66d89c25 workrail-darwin-arm64
|
|
2
|
+
20325c936a2032d5ee35fa02357e232ecd67c246e670319f78e980d3ea6ab0ac workrail-darwin-x64
|
|
3
|
+
2a51c9e382cb0c184dd42fabcc72e7e6d6589657934b73f8154191d19afcb0c3 workrail-gh-collector-darwin-arm64
|
|
4
|
+
3393e24280097c2430d088d8c4c0407f0d7c912cdade416652b0e55b3c617737 workrail-gh-collector-darwin-x64
|
|
5
|
+
cd98378b00252c1a3337a402961ddb80f76a4c826b7b40c45b5e76dde32e58cb workrail-gh-collector-linux-arm64
|
|
6
|
+
4f2d59e4e95d59d22cfb4ea6fc1533d9fde9b5964fa6eae91d0ab1b5857b5c14 workrail-gh-collector-linux-x64
|
|
7
|
+
315139c4108cb6ae327fb57fecc6f1a0f4fdc5317c92674f5c189cef4ba03ee5 workrail-gh-collector-windows-x64.exe
|
|
8
|
+
9b039c7d533c722f1230a9391015ec9daed98649d59bb85365003edbcccaadb0 workrail-linux-arm64
|
|
9
|
+
d10d8af7660c2f92c5a69669023685086d431f0fabb95c5ba6e375f9b8748be9 workrail-linux-x64
|
|
10
|
+
2402bafae25d263bf1a3aa667a124b07fe35a1857f2d45b0550b9e6ea8d35584 workrail-windows-x64.exe
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import https from "node:https";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const {
|
|
11
|
+
loadPackagedChecksums,
|
|
12
|
+
verifyArtifact,
|
|
13
|
+
} = require("../lib/integrity.cjs");
|
|
9
14
|
|
|
10
15
|
const pkgPath = path.join(__dirname, "..", "package.json");
|
|
11
16
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
12
17
|
const version = pkg.version;
|
|
13
18
|
|
|
19
|
+
function isMonorepoSourceCheckout() {
|
|
20
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
21
|
+
const repositoryRoot = path.resolve(packageRoot, "../..");
|
|
22
|
+
try {
|
|
23
|
+
const repositoryPackage = JSON.parse(
|
|
24
|
+
fs.readFileSync(path.join(repositoryRoot, "package.json"), "utf8"),
|
|
25
|
+
);
|
|
26
|
+
return (
|
|
27
|
+
repositoryPackage.private === true &&
|
|
28
|
+
Array.isArray(repositoryPackage.workspaces) &&
|
|
29
|
+
repositoryPackage.workspaces.includes("packages/*") &&
|
|
30
|
+
fs.realpathSync(packageRoot) ===
|
|
31
|
+
path.join(fs.realpathSync(repositoryRoot), "packages", "cli-wrapper")
|
|
32
|
+
);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
14
38
|
const baseUrl =
|
|
15
39
|
process.env.WORKRAIL_BINARY_BASE_URL ||
|
|
16
40
|
`https://cli.workrail.dev/workrail/v${version}`;
|
|
@@ -28,6 +52,10 @@ function resolveBinaryName() {
|
|
|
28
52
|
return null;
|
|
29
53
|
}
|
|
30
54
|
|
|
55
|
+
function resolveHelperBinaryName(binaryName) {
|
|
56
|
+
return binaryName.replace(/^workrail-/, "workrail-gh-collector-");
|
|
57
|
+
}
|
|
58
|
+
|
|
31
59
|
function downloadToFile(url, dest) {
|
|
32
60
|
return new Promise((resolve, reject) => {
|
|
33
61
|
const file = fs.createWriteStream(dest);
|
|
@@ -44,45 +72,14 @@ function downloadToFile(url, dest) {
|
|
|
44
72
|
});
|
|
45
73
|
}
|
|
46
74
|
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
reject(new Error(`Download failed (${res.statusCode}) for ${url}`));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
res.setEncoding("utf8");
|
|
57
|
-
res.on("data", (chunk) => (data += chunk));
|
|
58
|
-
res.on("end", () => resolve(data));
|
|
59
|
-
})
|
|
60
|
-
.on("error", (err) => reject(err));
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function sha256File(filePath) {
|
|
65
|
-
const hash = crypto.createHash("sha256");
|
|
66
|
-
const data = fs.readFileSync(filePath);
|
|
67
|
-
hash.update(data);
|
|
68
|
-
return hash.digest("hex");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function parseChecksums(text) {
|
|
72
|
-
const map = new Map();
|
|
73
|
-
for (const line of text.split("\n")) {
|
|
74
|
-
const trimmed = line.trim();
|
|
75
|
-
if (!trimmed) continue;
|
|
76
|
-
const parts = trimmed.split(/\s+/);
|
|
77
|
-
if (parts.length < 2) continue;
|
|
78
|
-
const hash = parts[0];
|
|
79
|
-
const file = parts[parts.length - 1];
|
|
80
|
-
map.set(file, hash);
|
|
75
|
+
async function main() {
|
|
76
|
+
if (isMonorepoSourceCheckout()) {
|
|
77
|
+
console.log(
|
|
78
|
+
"[workrail] Source checkout detected; skipping native binary installation.",
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
81
|
}
|
|
82
|
-
return map;
|
|
83
|
-
}
|
|
84
82
|
|
|
85
|
-
async function main() {
|
|
86
83
|
const binaryName = resolveBinaryName();
|
|
87
84
|
if (!binaryName) {
|
|
88
85
|
console.warn(
|
|
@@ -94,32 +91,36 @@ async function main() {
|
|
|
94
91
|
const nativeDir = path.join(__dirname, "..", "bin", "native");
|
|
95
92
|
fs.mkdirSync(nativeDir, { recursive: true });
|
|
96
93
|
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const
|
|
94
|
+
const checksums = loadPackagedChecksums(path.join(__dirname, ".."));
|
|
95
|
+
const helperBinaryName = resolveHelperBinaryName(binaryName);
|
|
96
|
+
const artifacts = [
|
|
97
|
+
{ remoteName: binaryName, targetName: binaryName },
|
|
98
|
+
{
|
|
99
|
+
remoteName: helperBinaryName,
|
|
100
|
+
targetName:
|
|
101
|
+
process.platform === "win32"
|
|
102
|
+
? "workrail-gh-collector.exe"
|
|
103
|
+
: "workrail-gh-collector",
|
|
104
|
+
},
|
|
105
|
+
];
|
|
100
106
|
|
|
101
107
|
console.log(
|
|
102
108
|
`📦 Installing Workrail CLI for ${process.platform} (${process.arch})...`,
|
|
103
109
|
);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (process.platform !== "win32") {
|
|
122
|
-
fs.chmodSync(targetPath, 0o755);
|
|
110
|
+
for (const artifact of artifacts) {
|
|
111
|
+
const targetPath = path.join(nativeDir, artifact.targetName);
|
|
112
|
+
const temporaryPath = `${targetPath}.download`;
|
|
113
|
+
try {
|
|
114
|
+
await downloadToFile(`${baseUrl}/${artifact.remoteName}`, temporaryPath);
|
|
115
|
+
console.log(`🔒 Verifying ${artifact.remoteName}...`);
|
|
116
|
+
verifyArtifact(temporaryPath, artifact.remoteName, checksums);
|
|
117
|
+
fs.renameSync(temporaryPath, targetPath);
|
|
118
|
+
} finally {
|
|
119
|
+
if (fs.existsSync(temporaryPath)) fs.unlinkSync(temporaryPath);
|
|
120
|
+
}
|
|
121
|
+
if (process.platform !== "win32") {
|
|
122
|
+
fs.chmodSync(targetPath, 0o755);
|
|
123
|
+
}
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
console.log("✅ Workrail CLI installed successfully!");
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const {
|
|
8
|
+
parseChecksums,
|
|
9
|
+
verifyRequiredArtifacts,
|
|
10
|
+
} = require("../lib/integrity.cjs");
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
13
|
+
const repoRoot = path.resolve(packageRoot, "../..");
|
|
14
|
+
|
|
15
|
+
const packageJson = JSON.parse(
|
|
16
|
+
fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"),
|
|
17
|
+
);
|
|
18
|
+
const denoJson = JSON.parse(
|
|
19
|
+
fs.readFileSync(path.join(repoRoot, "apps/cli/deno.json"), "utf8"),
|
|
20
|
+
);
|
|
21
|
+
if (packageJson.version !== denoJson.version) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`CLI version mismatch: npm=${packageJson.version} deno=${denoJson.version}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const source = path.join(repoRoot, "apps/cli/dist/checksums.txt");
|
|
28
|
+
const contents = fs.readFileSync(source, "utf8");
|
|
29
|
+
const checksums = parseChecksums(contents);
|
|
30
|
+
const required = [
|
|
31
|
+
"workrail-darwin-arm64",
|
|
32
|
+
"workrail-darwin-x64",
|
|
33
|
+
"workrail-linux-arm64",
|
|
34
|
+
"workrail-linux-x64",
|
|
35
|
+
"workrail-windows-x64.exe",
|
|
36
|
+
"workrail-gh-collector-darwin-arm64",
|
|
37
|
+
"workrail-gh-collector-darwin-x64",
|
|
38
|
+
"workrail-gh-collector-linux-arm64",
|
|
39
|
+
"workrail-gh-collector-linux-x64",
|
|
40
|
+
"workrail-gh-collector-windows-x64.exe",
|
|
41
|
+
];
|
|
42
|
+
for (const artifact of required) {
|
|
43
|
+
if (!checksums.has(artifact))
|
|
44
|
+
throw new Error(`Missing checksum for ${artifact}`);
|
|
45
|
+
}
|
|
46
|
+
verifyRequiredArtifacts(path.dirname(source), required, checksums);
|
|
47
|
+
|
|
48
|
+
fs.writeFileSync(path.join(packageRoot, "release-checksums.txt"), contents, {
|
|
49
|
+
mode: 0o644,
|
|
50
|
+
});
|
|
51
|
+
console.log(
|
|
52
|
+
`Embedded trusted checksums for Workrail CLI v${packageJson.version}`,
|
|
53
|
+
);
|