@automattic/vip 3.25.3-dev.0 → 4.0.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/AGENTS.md +6 -0
- package/CLAUDE.md +1 -0
- package/dist/bin/vip-dev-env-exec.js +18 -1
- package/dist/bin/vip-dev-env-start.js +3 -8
- package/dist/bin/vip-sea.js +19 -0
- package/dist/bin/vip.js +111 -69
- package/dist/lib/cli/command.js +224 -53
- package/dist/lib/cli/config.js +13 -5
- package/dist/lib/cli/exit.js +2 -1
- package/dist/lib/cli/internal-bin-loader.js +81 -0
- package/dist/lib/cli/runtime-mode.js +21 -0
- package/dist/lib/cli/sea-dispatch.js +88 -0
- package/dist/lib/cli/sea-runtime.js +75 -0
- package/dist/lib/dev-environment/dev-environment-cli.js +9 -2
- package/dist/lib/dev-environment/dev-environment-core.js +64 -6
- package/dist/lib/dev-environment/dev-environment-lando.js +53 -95
- package/dist/lib/dev-environment/lando-loader.js +48 -0
- package/docs/COMMANDER-MIGRATION.md +55 -0
- package/docs/SEA-BUILD-SIGNING.md +171 -0
- package/helpers/build-sea.js +167 -0
- package/npm-shrinkwrap.json +499 -121
- package/package.json +6 -3
- package/dist/lib/dev-environment/hosts-updater.js +0 -184
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# SEA Build and Signing Runbook
|
|
2
|
+
|
|
3
|
+
Purpose: build and sign the standalone VIP CLI executable (`dist/sea/vip` or `dist/sea/vip.exe`) for each supported platform.
|
|
4
|
+
|
|
5
|
+
This repo uses `helpers/build-sea.js` and the `npm run build:sea` script. SEA build is pinned to Node 22.
|
|
6
|
+
|
|
7
|
+
## Shared Prerequisites
|
|
8
|
+
|
|
9
|
+
- Use Node 22.x exactly for SEA builds.
|
|
10
|
+
- Install dependencies before building: `npm ci`.
|
|
11
|
+
- Build from repo root.
|
|
12
|
+
- The build script embeds:
|
|
13
|
+
- Node runtime
|
|
14
|
+
- bundled CLI code (`src/bin/vip-sea.js`)
|
|
15
|
+
- SEA assets and runtime dependency archive (`sea.node_modules.tgz`)
|
|
16
|
+
- Output paths:
|
|
17
|
+
- Unix: `dist/sea/vip`
|
|
18
|
+
- Windows: `dist/sea/vip.exe`
|
|
19
|
+
|
|
20
|
+
## Shared Build Steps
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm ci
|
|
24
|
+
npm run build
|
|
25
|
+
npm run build:sea
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Quick smoke checks after every build:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
dist/sea/vip --version
|
|
32
|
+
dist/sea/vip whoami --help
|
|
33
|
+
dist/sea/vip dev-env info --help
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## macOS (native)
|
|
37
|
+
|
|
38
|
+
Node/tool setup:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
export NVM_DIR="$HOME/.nvm"
|
|
42
|
+
. "$NVM_DIR/nvm.sh"
|
|
43
|
+
nvm use 22
|
|
44
|
+
node -v
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Build:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm ci
|
|
51
|
+
npm run build
|
|
52
|
+
npm run build:sea
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Notes:
|
|
56
|
+
|
|
57
|
+
- `helpers/build-sea.js` already does ad-hoc signing (`codesign --sign -`) after blob injection so local execution works.
|
|
58
|
+
- For distribution, replace ad-hoc signature with a real Developer ID certificate.
|
|
59
|
+
|
|
60
|
+
Distribution signing:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
codesign --remove-signature dist/sea/vip
|
|
64
|
+
codesign --sign "Developer ID Application: <TEAM/ORG>" --force --options runtime dist/sea/vip
|
|
65
|
+
codesign --verify --strict --verbose=2 dist/sea/vip
|
|
66
|
+
spctl -a -t exec -vv dist/sea/vip
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Linux (native)
|
|
70
|
+
|
|
71
|
+
Node/tool setup:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
node -v
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
(Use Node 22 before build.)
|
|
78
|
+
|
|
79
|
+
Build:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm ci
|
|
83
|
+
npm run build
|
|
84
|
+
npm run build:sea
|
|
85
|
+
chmod +x dist/sea/vip
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Signing guidance:
|
|
89
|
+
|
|
90
|
+
- Linux does not have a universal OS-enforced Authenticode-style executable signature.
|
|
91
|
+
- Recommended: publish checksums and detached signatures.
|
|
92
|
+
|
|
93
|
+
Checksum + GPG example:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
sha256sum dist/sea/vip > dist/sea/vip.sha256
|
|
97
|
+
gpg --armor --detach-sign dist/sea/vip
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Cosign blob example:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cosign sign-blob --yes --output-signature dist/sea/vip.sig dist/sea/vip
|
|
104
|
+
cosign verify-blob --signature dist/sea/vip.sig dist/sea/vip
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Windows (native)
|
|
108
|
+
|
|
109
|
+
Use PowerShell or `cmd.exe` on Windows (not WSL) when producing Windows artifacts.
|
|
110
|
+
|
|
111
|
+
Build:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
npm ci
|
|
115
|
+
npm run build
|
|
116
|
+
npm run build:sea
|
|
117
|
+
.\dist\sea\vip.exe --version
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Authenticode signing (SignTool):
|
|
121
|
+
|
|
122
|
+
```powershell
|
|
123
|
+
signtool sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /a .\dist\sea\vip.exe
|
|
124
|
+
signtool verify /pa /v .\dist\sea\vip.exe
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
If your cert is in a PFX file:
|
|
128
|
+
|
|
129
|
+
```powershell
|
|
130
|
+
signtool sign /f C:\path\cert.pfx /p <PFX_PASSWORD> /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com .\dist\sea\vip.exe
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Windows from WSL
|
|
134
|
+
|
|
135
|
+
Important: WSL builds Linux binaries by default.
|
|
136
|
+
|
|
137
|
+
- If target is Linux binary: build/sign inside WSL using the Linux flow.
|
|
138
|
+
- If target is Windows `.exe`: run the build and signing commands in Windows context.
|
|
139
|
+
|
|
140
|
+
From WSL, invoke Windows PowerShell for a Windows-target build:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
WIN_REPO_PATH="$(wslpath -w "$PWD")"
|
|
144
|
+
powershell.exe -NoProfile -Command "Set-Location '$WIN_REPO_PATH'; npm ci; npm run build; npm run build:sea"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then sign in Windows context:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
powershell.exe -NoProfile -Command "Set-Location '$WIN_REPO_PATH'; signtool sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /a .\\dist\\sea\\vip.exe; signtool verify /pa /v .\\dist\\sea\\vip.exe"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Release Checklist for Agents
|
|
154
|
+
|
|
155
|
+
- Confirm Node 22 before SEA build.
|
|
156
|
+
- Confirm artifact type matches target OS (`vip` vs `vip.exe`).
|
|
157
|
+
- Run smoke checks on the produced executable.
|
|
158
|
+
- Apply platform-appropriate signature method.
|
|
159
|
+
- Verify signature/checksum before publishing.
|
|
160
|
+
- Record signing method and timestamp authority in release notes.
|
|
161
|
+
|
|
162
|
+
## GitHub Actions Automation
|
|
163
|
+
|
|
164
|
+
- Workflow: `.github/workflows/sea-build-sign.yml`
|
|
165
|
+
- Trigger: manual `workflow_dispatch`
|
|
166
|
+
- Jobs: native macOS/Linux/Windows SEA builds plus a Windows WSL SEA build
|
|
167
|
+
- Optional signing: set `sign_artifacts=true` and provide signing secrets/vars:
|
|
168
|
+
- macOS: `MACOS_CERTIFICATE_P12_BASE64`, `MACOS_CERTIFICATE_PASSWORD`, `MACOS_SIGNING_IDENTITY`
|
|
169
|
+
- Windows: `WINDOWS_CERTIFICATE_PFX_BASE64`, `WINDOWS_CERTIFICATE_PASSWORD`
|
|
170
|
+
- optional variable: `WINDOWS_TIMESTAMP_URL`
|
|
171
|
+
- Output: uploaded SEA binary + SHA256 artifact per job
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { buildSync } = require( 'esbuild' );
|
|
4
|
+
const { spawnSync } = require( 'node:child_process' );
|
|
5
|
+
const { chmodSync, copyFileSync, mkdirSync, readFileSync, writeFileSync } = require( 'node:fs' );
|
|
6
|
+
const path = require( 'node:path' );
|
|
7
|
+
const tar = require( 'tar' );
|
|
8
|
+
|
|
9
|
+
const SEA_FUSE = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2';
|
|
10
|
+
|
|
11
|
+
const projectRoot = path.resolve( __dirname, '..' );
|
|
12
|
+
const seaDir = path.join( projectRoot, 'dist', 'sea' );
|
|
13
|
+
const bundlePath = path.join( seaDir, 'vip.bundle.cjs' );
|
|
14
|
+
const blobPath = path.join( seaDir, 'vip.blob' );
|
|
15
|
+
const seaConfigPath = path.join( seaDir, 'sea-config.json' );
|
|
16
|
+
const executablePath = path.join( seaDir, process.platform === 'win32' ? 'vip.exe' : 'vip' );
|
|
17
|
+
const nodeModulesArchivePath = path.join( seaDir, 'node_modules.tgz' );
|
|
18
|
+
|
|
19
|
+
function run( command, args, options = {} ) {
|
|
20
|
+
const result = spawnSync( command, args, {
|
|
21
|
+
cwd: projectRoot,
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
...options,
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
if ( result.status !== 0 ) {
|
|
27
|
+
process.exit( result.status || 1 );
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ensureNode22() {
|
|
32
|
+
const major = Number( process.versions.node.split( '.' )[ 0 ] );
|
|
33
|
+
if ( major !== 22 ) {
|
|
34
|
+
console.error(
|
|
35
|
+
`Error: SEA build requires Node 22.x. Current version is ${ process.versions.node }.`
|
|
36
|
+
);
|
|
37
|
+
process.exit( 1 );
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function createRuntimeArchive() {
|
|
42
|
+
await tar.c(
|
|
43
|
+
{
|
|
44
|
+
cwd: projectRoot,
|
|
45
|
+
file: nodeModulesArchivePath,
|
|
46
|
+
gzip: true,
|
|
47
|
+
portable: true,
|
|
48
|
+
noMtime: true,
|
|
49
|
+
},
|
|
50
|
+
[ 'node_modules' ]
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function writeSeaConfig() {
|
|
55
|
+
const config = {
|
|
56
|
+
main: bundlePath,
|
|
57
|
+
output: blobPath,
|
|
58
|
+
disableExperimentalSEAWarning: true,
|
|
59
|
+
assets: {
|
|
60
|
+
'dev-env.lando.template.yml.ejs': path.join(
|
|
61
|
+
projectRoot,
|
|
62
|
+
'assets',
|
|
63
|
+
'dev-env.lando.template.yml.ejs'
|
|
64
|
+
),
|
|
65
|
+
'dev-env.nginx.template.conf.ejs': path.join(
|
|
66
|
+
projectRoot,
|
|
67
|
+
'assets',
|
|
68
|
+
'dev-env.nginx.template.conf.ejs'
|
|
69
|
+
),
|
|
70
|
+
'sea.node_modules.tgz': nodeModulesArchivePath,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
writeFileSync( seaConfigPath, `${ JSON.stringify( config, null, 2 ) }\n`, 'utf8' );
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function buildBundle() {
|
|
78
|
+
buildSync( {
|
|
79
|
+
entryPoints: [ path.join( projectRoot, 'src', 'bin', 'vip-sea.js' ) ],
|
|
80
|
+
bundle: true,
|
|
81
|
+
platform: 'node',
|
|
82
|
+
target: 'node22',
|
|
83
|
+
format: 'cjs',
|
|
84
|
+
outfile: bundlePath,
|
|
85
|
+
external: [
|
|
86
|
+
'@postman/node-keytar',
|
|
87
|
+
'@postman/node-keytar/*',
|
|
88
|
+
'cpu-features',
|
|
89
|
+
'cpu-features/*',
|
|
90
|
+
'lando',
|
|
91
|
+
'lando/*',
|
|
92
|
+
'ssh2',
|
|
93
|
+
'ssh2/*',
|
|
94
|
+
'*.node',
|
|
95
|
+
],
|
|
96
|
+
} );
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function stripBundleShebang() {
|
|
100
|
+
const bundleContent = readFileSync( bundlePath, 'utf8' );
|
|
101
|
+
if ( ! bundleContent.startsWith( '#!' ) ) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
writeFileSync( bundlePath, bundleContent.replace( /^#![^\n]*\n/, '' ), 'utf8' );
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildBlob() {
|
|
109
|
+
run( process.execPath, [ '--experimental-sea-config', seaConfigPath ] );
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function prepareExecutable() {
|
|
113
|
+
copyFileSync( process.execPath, executablePath );
|
|
114
|
+
|
|
115
|
+
if ( process.platform === 'darwin' ) {
|
|
116
|
+
run( 'codesign', [ '--remove-signature', executablePath ] );
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function injectBlob() {
|
|
121
|
+
const postjectCli = require.resolve( 'postject/dist/cli.js' );
|
|
122
|
+
const args = [
|
|
123
|
+
postjectCli,
|
|
124
|
+
executablePath,
|
|
125
|
+
'NODE_SEA_BLOB',
|
|
126
|
+
blobPath,
|
|
127
|
+
'--sentinel-fuse',
|
|
128
|
+
SEA_FUSE,
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if ( process.platform === 'darwin' ) {
|
|
132
|
+
args.push( '--macho-segment-name', 'NODE_SEA' );
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if ( process.platform === 'win32' ) {
|
|
136
|
+
args.push( '--overwrite' );
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
run( process.execPath, args );
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function finalizeExecutable() {
|
|
143
|
+
if ( process.platform === 'darwin' ) {
|
|
144
|
+
run( 'codesign', [ '--sign', '-', '--force', executablePath ] );
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if ( process.platform !== 'win32' ) {
|
|
148
|
+
chmodSync( executablePath, 0o755 );
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function main() {
|
|
153
|
+
ensureNode22();
|
|
154
|
+
mkdirSync( seaDir, { recursive: true } );
|
|
155
|
+
await createRuntimeArchive();
|
|
156
|
+
writeSeaConfig();
|
|
157
|
+
buildBundle();
|
|
158
|
+
stripBundleShebang();
|
|
159
|
+
buildBlob();
|
|
160
|
+
prepareExecutable();
|
|
161
|
+
injectBlob();
|
|
162
|
+
finalizeExecutable();
|
|
163
|
+
|
|
164
|
+
console.log( `SEA executable written to ${ executablePath }` );
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
void main();
|