@ddtcorex/govard 1.71.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 +37 -0
- package/install.js +162 -0
- package/package.json +21 -0
- package/run.js +20 -0
- package/test-install.mjs +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @ddtcorex/govard
|
|
2
|
+
|
|
3
|
+
Thin binary installer for the [Govard](https://github.com/ddtcorex/govard)
|
|
4
|
+
local development orchestrator CLI. The package downloads the matching
|
|
5
|
+
prebuilt binary from GitHub Releases on `postinstall`, verifies its sha256
|
|
6
|
+
checksum, and exposes it as the `govard` bin.
|
|
7
|
+
|
|
8
|
+
CLI only — the Desktop app is distributed via `.deb` / `.pkg` releases.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npx @ddtcorex/govard --version
|
|
14
|
+
npm i -g @ddtcorex/govard
|
|
15
|
+
govard --help
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Pin a version for CI:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm i -g @ddtcorex/govard@1.70.3
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> npm 11+ holds `postinstall` scripts for approval: after install, run
|
|
25
|
+
> `npm approve-scripts @ddtcorex/govard` (or pre-approve in CI config)
|
|
26
|
+
> so the binary download step is allowed to run.
|
|
27
|
+
|
|
28
|
+
## Environment
|
|
29
|
+
|
|
30
|
+
- `GOVARD_MIRROR` — base URL override for release assets (e.g. an internal
|
|
31
|
+
mirror) instead of `github.com/ddtcorex/govard/releases/download`.
|
|
32
|
+
- `GOVARD_SKIP_POSTINSTALL=1` — skip the binary download (for pre-seeded
|
|
33
|
+
environments).
|
|
34
|
+
|
|
35
|
+
Installs via this package report install source `npm`, so
|
|
36
|
+
`govard self-update` defers to the package manager (`npm i -g
|
|
37
|
+
@ddtcorex/govard@latest`) instead of overwriting the binary.
|
package/install.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Postinstall downloader for @ddtcorex/govard.
|
|
2
|
+
// Downloads the matching GoReleaser asset from GitHub Releases, verifies its
|
|
3
|
+
// sha256 against checksums.txt, extracts it to ./bin, and records the npm
|
|
4
|
+
// install-source marker. Stdlib only, no dependencies.
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const { execFileSync } = require('node:child_process');
|
|
8
|
+
const crypto = require('node:crypto');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const https = require('node:https');
|
|
11
|
+
const os = require('node:os');
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
|
|
14
|
+
const REPO = 'ddtcorex/govard';
|
|
15
|
+
const BIN_DIR = path.join(__dirname, 'bin');
|
|
16
|
+
const BINARY_NAME = process.platform === 'win32' ? 'govard.exe' : 'govard';
|
|
17
|
+
|
|
18
|
+
// Maps Node platform/arch to the GoReleaser asset infix
|
|
19
|
+
// (name_template: "{{ .ProjectName }}_{{ .Version }}_{{- title .Os }}_{{ .Arch }}").
|
|
20
|
+
function platformAsset(platform, arch) {
|
|
21
|
+
const normalizedArch = arch === 'x64' ? 'amd64' : arch === 'arm64' ? 'arm64' : null;
|
|
22
|
+
if (normalizedArch === null) {
|
|
23
|
+
throw new Error(`Unsupported architecture for @ddtcorex/govard: ${arch}. See https://github.com/${REPO} for manual install via install.sh.`);
|
|
24
|
+
}
|
|
25
|
+
switch (platform) {
|
|
26
|
+
case 'linux':
|
|
27
|
+
return `Linux_${normalizedArch}.tar.gz`;
|
|
28
|
+
case 'darwin':
|
|
29
|
+
return `Darwin_${normalizedArch}.tar.gz`;
|
|
30
|
+
case 'win32':
|
|
31
|
+
return `Windows_${normalizedArch}.zip`;
|
|
32
|
+
default:
|
|
33
|
+
throw new Error(`Unsupported platform for @ddtcorex/govard: ${platform}/${arch}. See https://github.com/${REPO} for manual install via install.sh.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function packageVersion() {
|
|
38
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
39
|
+
const version = String(pkg.version || '').trim();
|
|
40
|
+
if (version === '0.0.0-dev') {
|
|
41
|
+
throw new Error('Refusing to download: wrapper version is still the unstamped 0.0.0-dev placeholder (the release job stamps it from the git tag).');
|
|
42
|
+
}
|
|
43
|
+
if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(version)) {
|
|
44
|
+
throw new Error(`Refusing to download: package version ${JSON.stringify(version)} is not a release version (the release job stamps it from the git tag).`);
|
|
45
|
+
}
|
|
46
|
+
return version;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function downloadWithCurl(url, dest) {
|
|
50
|
+
execFileSync('curl', ['-fsSL', '--retry', '3', '-o', dest, url], { stdio: 'inherit' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function downloadWithNode(url, dest, redirects = 5) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
https
|
|
56
|
+
.get(url, { headers: { 'User-Agent': '@ddtcorex/govard-installer' } }, (res) => {
|
|
57
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
58
|
+
if (redirects <= 0) {
|
|
59
|
+
reject(new Error(`Too many redirects downloading ${url}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
res.resume();
|
|
63
|
+
resolve(downloadWithNode(res.headers.location, dest, redirects - 1));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (res.statusCode !== 200) {
|
|
67
|
+
reject(new Error(`Download failed (${res.statusCode}): ${url}`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const out = fs.createWriteStream(dest);
|
|
71
|
+
res.pipe(out);
|
|
72
|
+
out.on('finish', () => out.close(resolve));
|
|
73
|
+
out.on('error', reject);
|
|
74
|
+
})
|
|
75
|
+
.on('error', reject);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function download(url, dest) {
|
|
80
|
+
try {
|
|
81
|
+
execFileSync('curl', ['--version'], { stdio: 'ignore' });
|
|
82
|
+
downloadWithCurl(url, dest);
|
|
83
|
+
} catch {
|
|
84
|
+
await downloadWithNode(url, dest);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sha256File(filePath) {
|
|
89
|
+
const hash = crypto.createHash('sha256');
|
|
90
|
+
hash.update(fs.readFileSync(filePath));
|
|
91
|
+
return hash.digest('hex');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function extract(archivePath, destDir) {
|
|
95
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
96
|
+
if (archivePath.endsWith('.zip')) {
|
|
97
|
+
execFileSync(
|
|
98
|
+
'powershell',
|
|
99
|
+
['-NoProfile', '-Command', `Expand-Archive -Force '${archivePath}' '${destDir}'`],
|
|
100
|
+
{ stdio: 'inherit' },
|
|
101
|
+
);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
execFileSync('tar', ['-xzf', archivePath, '-C', destDir], { stdio: 'inherit' });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function main() {
|
|
108
|
+
if (process.env.GOVARD_SKIP_POSTINSTALL) {
|
|
109
|
+
console.log('@ddtcorex/govard: GOVARD_SKIP_POSTINSTALL set, skipping binary download.');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const version = packageVersion();
|
|
113
|
+
const assetInfix = platformAsset(process.platform, process.arch);
|
|
114
|
+
const mirror = (process.env.GOVARD_MIRROR || '').replace(/\/+$/, '');
|
|
115
|
+
const base = mirror || `https://github.com/${REPO}/releases/download/v${version}`;
|
|
116
|
+
const archiveName = `govard_${version}_${assetInfix}`;
|
|
117
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'govard-npm-'));
|
|
118
|
+
try {
|
|
119
|
+
const archivePath = path.join(tmpDir, archiveName);
|
|
120
|
+
console.log(`@ddtcorex/govard: downloading ${archiveName} ...`);
|
|
121
|
+
await download(`${base}/${archiveName}`, archivePath);
|
|
122
|
+
|
|
123
|
+
console.log('@ddtcorex/govard: verifying checksum ...');
|
|
124
|
+
const checksumsPath = path.join(tmpDir, 'checksums.txt');
|
|
125
|
+
await download(`${base}/checksums.txt`, checksumsPath);
|
|
126
|
+
const line = fs
|
|
127
|
+
.readFileSync(checksumsPath, 'utf8')
|
|
128
|
+
.split('\n')
|
|
129
|
+
.find((l) => l.trim().endsWith(` ${archiveName}`) || l.trim().endsWith(` *${archiveName}`));
|
|
130
|
+
if (!line) {
|
|
131
|
+
throw new Error(`Checksum entry for ${archiveName} not found in checksums.txt.`);
|
|
132
|
+
}
|
|
133
|
+
const expected = line.trim().split(/\s+/)[0];
|
|
134
|
+
const actual = sha256File(archivePath);
|
|
135
|
+
if (expected !== actual) {
|
|
136
|
+
throw new Error(`Checksum mismatch for ${archiveName}: expected ${expected}, got ${actual}.`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log('@ddtcorex/govard: extracting ...');
|
|
140
|
+
extract(archivePath, BIN_DIR);
|
|
141
|
+
const binaryPath = path.join(BIN_DIR, BINARY_NAME);
|
|
142
|
+
if (!fs.existsSync(binaryPath)) {
|
|
143
|
+
throw new Error(`Archive ${archiveName} does not contain ${BINARY_NAME}.`);
|
|
144
|
+
}
|
|
145
|
+
if (process.platform !== 'win32') {
|
|
146
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
147
|
+
}
|
|
148
|
+
fs.writeFileSync(path.join(BIN_DIR, '.install-source'), 'npm\n');
|
|
149
|
+
console.log(`@ddtcorex/govard: installed ${BINARY_NAME} ${version}.`);
|
|
150
|
+
} finally {
|
|
151
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (require.main === module) {
|
|
156
|
+
main().catch((err) => {
|
|
157
|
+
console.error(`@ddtcorex/govard postinstall failed: ${err.message}`);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = { platformAsset };
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ddtcorex/govard",
|
|
3
|
+
"version": "1.71.2",
|
|
4
|
+
"description": "Govard local development orchestrator CLI (binary installer)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/ddtcorex/govard",
|
|
9
|
+
"directory": "npm"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"govard": "./run.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"postinstall": "node ./install.js",
|
|
16
|
+
"test": "node ./test-install.mjs"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Bin shim for @ddtcorex/govard: forwards to the downloaded binary.
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require('node:child_process');
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
|
|
9
|
+
const binaryName = process.platform === 'win32' ? 'govard.exe' : 'govard';
|
|
10
|
+
const binaryPath = path.join(__dirname, 'bin', binaryName);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binaryPath)) {
|
|
13
|
+
console.error(
|
|
14
|
+
`@ddtcorex/govard: binary not found at ${binaryPath}. The postinstall download may have been skipped or failed — reinstall with: npm i -g @ddtcorex/govard@latest`,
|
|
15
|
+
);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
20
|
+
process.exit(result.status === null ? 1 : result.status);
|
package/test-install.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { platformAsset } from './install.js';
|
|
3
|
+
|
|
4
|
+
const asset = platformAsset(process.platform, process.arch);
|
|
5
|
+
assert.match(
|
|
6
|
+
asset,
|
|
7
|
+
/^(Linux|Darwin|Windows)_(amd64|arm64)\.(tar\.gz|zip)$/,
|
|
8
|
+
`unexpected asset: ${asset}`,
|
|
9
|
+
);
|
|
10
|
+
console.log(`OK: ${process.platform}/${process.arch} -> ${asset}`);
|