@aware-aeco/cli 0.90.0 → 0.91.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/package.json +1 -1
- package/scripts/bin/aware.js +37 -1
- package/scripts/payload.js +23 -0
- package/scripts/postinstall.js +58 -6
package/package.json
CHANGED
package/scripts/bin/aware.js
CHANGED
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
2
3
|
const path = require('path');
|
|
3
4
|
const { spawnSync } = require('child_process');
|
|
4
5
|
|
|
6
|
+
const PKG_VERSION = require('../../package.json').version;
|
|
7
|
+
const { requiredFiles } = require('../payload');
|
|
5
8
|
const binaryName = process.platform === 'win32' ? 'aware.exe' : 'aware';
|
|
6
|
-
const
|
|
9
|
+
const binariesDir = path.join(__dirname, '..', '..', 'binaries');
|
|
10
|
+
let binary = path.join(binariesDir, binaryName);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binary)) {
|
|
13
|
+
// Rescue a partial install: postinstall ≤0.90.0 could leave the extracted
|
|
14
|
+
// versioned folder unpromoted (#287) — run the binary from there. Pinned to
|
|
15
|
+
// this package's version so a leftover from another release never runs, and
|
|
16
|
+
// only a COMPLETE payload qualifies — the CLI resolves its sidecar/roslyn
|
|
17
|
+
// companions next to the executable, so a half-extracted folder would run
|
|
18
|
+
// but fail confusingly later.
|
|
19
|
+
const exeSuffix = process.platform === 'win32' ? '.exe' : '';
|
|
20
|
+
const complete = (dir) =>
|
|
21
|
+
requiredFiles(exeSuffix).every((f) => fs.existsSync(path.join(dir, f)));
|
|
22
|
+
const candidate = fs.existsSync(binariesDir)
|
|
23
|
+
? fs.readdirSync(binariesDir)
|
|
24
|
+
.filter((name) => name.startsWith(`aware-${PKG_VERSION}-`))
|
|
25
|
+
.map((name) => path.join(binariesDir, name))
|
|
26
|
+
.filter(complete)
|
|
27
|
+
.map((dir) => path.join(dir, binaryName))[0]
|
|
28
|
+
: undefined;
|
|
29
|
+
if (candidate) {
|
|
30
|
+
binary = candidate;
|
|
31
|
+
} else {
|
|
32
|
+
// Never fail silently (#287) — say what is missing and how to repair it.
|
|
33
|
+
console.error(`aware: binary not found at ${binary}`);
|
|
34
|
+
console.error('The npm postinstall step did not complete on this machine. To repair, run:');
|
|
35
|
+
console.error(` node "${path.join(__dirname, '..', 'postinstall.js')}"`);
|
|
36
|
+
console.error('or download a release manually: https://github.com/aware-aeco/aware/releases');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
7
40
|
|
|
8
41
|
const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
|
|
42
|
+
if (result.error) {
|
|
43
|
+
console.error(`aware: failed to run ${binary}: ${result.error.message}`);
|
|
44
|
+
}
|
|
9
45
|
process.exit(result.status ?? 1);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Shared payload contract for postinstall.js and bin/aware.js (#287).
|
|
2
|
+
//
|
|
3
|
+
// The files without which the CLI (or its aware-roslyn companion host) cannot
|
|
4
|
+
// start. aware-roslyn ships as a self-contained FOLDER (hundreds of framework
|
|
5
|
+
// DLLs); exhaustively manifesting it is disproportionate here — these four are
|
|
6
|
+
// the startup-critical set (apphost, app dll, runtime config, deps graph), and
|
|
7
|
+
// the extraction paths themselves fail loudly on any entry error, so a payload
|
|
8
|
+
// passing this check with an arbitrary missing middle DLL requires the
|
|
9
|
+
// extractor to have lied about its exit code.
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
function requiredFiles(exeSuffix) {
|
|
13
|
+
return [
|
|
14
|
+
`aware${exeSuffix}`,
|
|
15
|
+
`aware-sidecar${exeSuffix}`,
|
|
16
|
+
path.join('aware-roslyn', `aware-roslyn${exeSuffix}`),
|
|
17
|
+
path.join('aware-roslyn', 'aware-roslyn.dll'),
|
|
18
|
+
path.join('aware-roslyn', 'aware-roslyn.runtimeconfig.json'),
|
|
19
|
+
path.join('aware-roslyn', 'aware-roslyn.deps.json'),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { requiredFiles };
|
package/scripts/postinstall.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const https = require('https');
|
|
5
|
-
const {
|
|
5
|
+
const { execFileSync } = require('child_process');
|
|
6
|
+
const { requiredFiles } = require('./payload');
|
|
6
7
|
|
|
7
8
|
const PKG_VERSION = require('../package.json').version;
|
|
8
9
|
const REPO = 'aware-aeco/aware';
|
|
@@ -61,22 +62,73 @@ function download(srcUrl, dest, depth = 0) {
|
|
|
61
62
|
await download(url, tmpFile);
|
|
62
63
|
|
|
63
64
|
console.log('[aware-npm] extracting...');
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
// bsdtar handles both .zip and .tar.gz, exits non-zero on real failures, and
|
|
66
|
+
// (unlike PS 5.1 Expand-Archive) never half-extracts silently (#287). Use the
|
|
67
|
+
// System32 copy on Windows — a GNU tar earlier on PATH (Git Bash) can't read zip.
|
|
68
|
+
const winTar = path.join(process.env.WINDIR || 'C:\\Windows', 'System32', 'tar.exe');
|
|
69
|
+
if (process.platform === 'win32' && !fs.existsSync(winTar)) {
|
|
70
|
+
// Server 2016 / Win10 <1803 ship no inbox bsdtar — fall back to
|
|
71
|
+
// Expand-Archive. Its silent half-extraction mode is defused by the
|
|
72
|
+
// shim-target check below, which turns it into a loud failure (#287).
|
|
73
|
+
// $ErrorActionPreference='Stop' promotes Expand-Archive's non-terminating
|
|
74
|
+
// extraction errors to a non-zero exit, so a partial payload throws here
|
|
75
|
+
// instead of surfacing later.
|
|
76
|
+
execFileSync('powershell', [
|
|
77
|
+
'-NoProfile', '-Command',
|
|
78
|
+
`$ErrorActionPreference='Stop'; Expand-Archive -LiteralPath '${tmpFile}' -DestinationPath '${binariesDir}' -Force`,
|
|
79
|
+
], { stdio: 'inherit' });
|
|
66
80
|
} else {
|
|
67
|
-
|
|
81
|
+
const tar = process.platform === 'win32' ? winTar : 'tar';
|
|
82
|
+
// Explicit -z for tar.gz: minimal POSIX tar builds don't autodetect gzip.
|
|
83
|
+
const flags = target.archive === 'zip' ? '-xf' : '-xzf';
|
|
84
|
+
execFileSync(tar, [flags, tmpFile, '-C', binariesDir], { stdio: 'inherit' });
|
|
68
85
|
}
|
|
69
|
-
|
|
86
|
+
// Cleanup is cosmetic — an AV scan holding the fresh archive must not abort
|
|
87
|
+
// an install that already extracted successfully (#287).
|
|
88
|
+
try { fs.unlinkSync(tmpFile); } catch { /* locked archive; leave it */ }
|
|
70
89
|
|
|
71
90
|
// Promote binaries from <binariesDir>/aware-<version>-<rid>/ to <binariesDir>/
|
|
72
91
|
const extractedDir = path.join(binariesDir, `aware-${PKG_VERSION}-${target.rid}`);
|
|
73
92
|
if (fs.existsSync(extractedDir)) {
|
|
74
93
|
for (const file of fs.readdirSync(extractedDir)) {
|
|
75
|
-
|
|
94
|
+
const src = path.join(extractedDir, file);
|
|
95
|
+
const dest = path.join(binariesDir, file);
|
|
96
|
+
// renameSync replaces existing FILES atomically (MOVEFILE_REPLACE_EXISTING
|
|
97
|
+
// on Windows); only a directory dest (aware-roslyn) must be moved aside
|
|
98
|
+
// first. Keep it until the replacement lands so an interrupted repair
|
|
99
|
+
// can't destroy a working install (#287 review).
|
|
100
|
+
let backup = null;
|
|
101
|
+
if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) {
|
|
102
|
+
backup = `${dest}.old-${process.pid}`;
|
|
103
|
+
fs.rmSync(backup, { recursive: true, force: true });
|
|
104
|
+
fs.renameSync(dest, backup);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
fs.renameSync(src, dest);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (backup) fs.renameSync(backup, dest); // restore the old payload
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
// The replacement already landed — a locked obsolete backup (AV handle)
|
|
113
|
+
// must not abort the remaining promotions.
|
|
114
|
+
if (backup) {
|
|
115
|
+
try { fs.rmSync(backup, { recursive: true, force: true }); } catch { /* best-effort */ }
|
|
116
|
+
}
|
|
76
117
|
}
|
|
77
118
|
fs.rmdirSync(extractedDir);
|
|
78
119
|
}
|
|
79
120
|
|
|
121
|
+
// The shim resolves <binariesDir>/aware(.exe), and the CLI needs its
|
|
122
|
+
// sidecar + roslyn companions next to it. Anything missing is a broken
|
|
123
|
+
// install — fail loudly here instead of letting every later `aware` call
|
|
124
|
+
// exit 1 in silence (#287). Also catches Expand-Archive's silent
|
|
125
|
+
// half-extraction on the no-inbox-tar fallback path.
|
|
126
|
+
const required = requiredFiles(target.ext);
|
|
127
|
+
const missing = required.filter((f) => !fs.existsSync(path.join(binariesDir, f)));
|
|
128
|
+
if (missing.length) {
|
|
129
|
+
throw new Error(`extraction finished but ${missing.join(', ')} missing from ${binariesDir}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
80
132
|
if (process.platform !== 'win32') {
|
|
81
133
|
fs.chmodSync(path.join(binariesDir, `aware${target.ext}`), 0o755);
|
|
82
134
|
fs.chmodSync(path.join(binariesDir, `aware-sidecar${target.ext}`), 0o755);
|