@agentify/cli 0.2.8 → 0.2.9
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 +56 -2
- package/bin/darwin-arm64/agentify +0 -0
- package/bin/darwin-x64/agentify +0 -0
- package/bin/linux-arm64/agentify +0 -0
- package/bin/linux-x64/agentify +0 -0
- package/bin/win32-arm64/agentify.exe +0 -0
- package/bin/win32-x64/agentify.exe +0 -0
- package/package.json +3 -1
- package/scripts/release-all.sh +614 -0
package/README.md
CHANGED
|
@@ -17,13 +17,13 @@ The hosted setup flow at `https://chat.agentify.sh` can also install the CLI for
|
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
agentify chat pair --pairing-ticket <ticket> --workspace "$PWD" --label "$(basename "$PWD")"
|
|
20
|
-
agentify chat daemon --runner
|
|
20
|
+
agentify chat daemon --runner codex
|
|
21
21
|
agentify chat daemon --runner-command claude
|
|
22
22
|
agentify chat daemon --runner-command gemini
|
|
23
23
|
agentify doctor
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
`agentify chat pair` registers this machine as the local decrypting device for a workspace. `agentify chat daemon --runner
|
|
26
|
+
`agentify chat pair` registers this machine as the local decrypting device for a workspace. `agentify chat daemon --runner codex` runs Codex through structured JSON events for reliable encrypted output streaming. `agentify chat daemon --runner-command ...` is available for other local CLIs and custom runner commands.
|
|
27
27
|
|
|
28
28
|
## Security Model
|
|
29
29
|
|
|
@@ -56,6 +56,60 @@ To verify release readiness:
|
|
|
56
56
|
npm run release:check
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### Release automation
|
|
60
|
+
|
|
61
|
+
For one-command release prep across all supported platforms:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run release:all
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
From the workspace root, you can run the same flow with:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
bash ../scripts/release-agentify-cli.sh
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The release pipeline is non-publishing by default and runs:
|
|
74
|
+
|
|
75
|
+
- cross-platform Rust builds
|
|
76
|
+
- multi-target npm wrapper staging
|
|
77
|
+
- local version/release checks
|
|
78
|
+
- package verification
|
|
79
|
+
- release payload leak scan
|
|
80
|
+
|
|
81
|
+
Use explicit flags to publish:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm run release:all:publish -- --npm-tag latest
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
or:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bash ./scripts/release-all.sh --publish-npm --publish-git --auto-commit --create-tag --push-git
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Useful options:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
--targets darwin-arm64,linux-x64
|
|
97
|
+
--publish-npm
|
|
98
|
+
--publish-git
|
|
99
|
+
--auto-commit
|
|
100
|
+
--create-tag
|
|
101
|
+
--push-git
|
|
102
|
+
--skip-leak-scan
|
|
103
|
+
--skip-cargo-test
|
|
104
|
+
--skip-npm-tests
|
|
105
|
+
--dry-run
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Identity checks are enforced on publish:
|
|
109
|
+
|
|
110
|
+
- npm account must be `agentify`
|
|
111
|
+
- GitHub account must be `waml`
|
|
112
|
+
|
|
59
113
|
During local incubation, when only some platform binaries are staged, use:
|
|
60
114
|
|
|
61
115
|
```bash
|
|
Binary file
|
package/bin/darwin-x64/agentify
CHANGED
|
Binary file
|
package/bin/linux-arm64/agentify
CHANGED
|
Binary file
|
package/bin/linux-x64/agentify
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentify/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Agentify CLI for secure local Agentify Chat runners",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"homepage": "https://agentify.sh",
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"postinstall": "node ./scripts/postinstall.js",
|
|
31
31
|
"prepublishOnly": "npm run release:check",
|
|
32
32
|
"release:check": "node ./scripts/release-check.js",
|
|
33
|
+
"release:all": "bash ./scripts/release-all.sh",
|
|
34
|
+
"release:all:publish": "bash ./scripts/release-all.sh --publish-npm --publish-git --auto-commit --create-tag --push-git",
|
|
33
35
|
"release:manifest": "node ./scripts/build-release-manifest.js",
|
|
34
36
|
"stage:bin": "node ./scripts/stage-binary.js",
|
|
35
37
|
"test:npm": "node --test ./test/npm/*.test.js",
|
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
|
|
6
|
+
NPM_PUBLISH=false
|
|
7
|
+
GIT_PUBLISH=false
|
|
8
|
+
AUTO_COMMIT=false
|
|
9
|
+
CREATE_TAG=false
|
|
10
|
+
PUSH_GIT=false
|
|
11
|
+
ALLOW_MISSING_TARGETS=false
|
|
12
|
+
ALLOW_MISSING_TOOLCHAIN=false
|
|
13
|
+
AUTO_INSTALL_TARGETS=false
|
|
14
|
+
SKIP_LEAK_SCAN=false
|
|
15
|
+
SKIP_CARGO_TEST=false
|
|
16
|
+
SKIP_NPM_TESTS=false
|
|
17
|
+
SKIP_RELEASE_CHECK=false
|
|
18
|
+
SKIP_VERIFY_PACKAGE=false
|
|
19
|
+
SKIP_GIT=false
|
|
20
|
+
DRY_RUN=false
|
|
21
|
+
CHECK_IDENTITY=false
|
|
22
|
+
REQUIRE_CLEAN_WORKTREE=false
|
|
23
|
+
|
|
24
|
+
NPM_TAG="latest"
|
|
25
|
+
GIT_REMOTE="origin"
|
|
26
|
+
GIT_BRANCH=""
|
|
27
|
+
COMMIT_MESSAGE=""
|
|
28
|
+
TAG_NAME=""
|
|
29
|
+
|
|
30
|
+
REQUIRED_NPM_ACCOUNT="agentify"
|
|
31
|
+
REQUIRED_GH_ACCOUNT="waml"
|
|
32
|
+
ALL_TARGETS="darwin-arm64,darwin-x64,linux-arm64,linux-x64,win32-arm64,win32-x64"
|
|
33
|
+
FILTER_TARGETS=""
|
|
34
|
+
|
|
35
|
+
declare -A TARGET_INFO=(
|
|
36
|
+
["darwin-arm64"]="aarch64-apple-darwin:agentify"
|
|
37
|
+
["darwin-x64"]="x86_64-apple-darwin:agentify"
|
|
38
|
+
["linux-arm64"]="aarch64-unknown-linux-gnu:agentify"
|
|
39
|
+
["linux-x64"]="x86_64-unknown-linux-gnu:agentify"
|
|
40
|
+
["win32-arm64"]="aarch64-pc-windows-gnullvm:agentify.exe"
|
|
41
|
+
["win32-x64"]="x86_64-pc-windows-gnu:agentify.exe"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
log() {
|
|
45
|
+
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
abort() {
|
|
49
|
+
log "ERROR: $*"
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
usage() {
|
|
54
|
+
cat <<'EOF'
|
|
55
|
+
Usage:
|
|
56
|
+
bash scripts/release-all.sh [options]
|
|
57
|
+
|
|
58
|
+
Release automation (safe by default: no publish unless explicitly enabled):
|
|
59
|
+
--publish-npm Publish npm package (explicit opt-in)
|
|
60
|
+
--publish-git Push release git changes/tags (explicit opt-in)
|
|
61
|
+
--auto-commit Commit staged release artifacts before git push
|
|
62
|
+
--create-tag Create an annotated git tag
|
|
63
|
+
--push-git Push branch/tags (requires --publish-git)
|
|
64
|
+
--publish-all Publish npm + git together (implies --publish-npm --publish-git)
|
|
65
|
+
--skip-missing-targets Continue if a requested target cannot be built (strict by default)
|
|
66
|
+
--allow-missing-toolchain Continue if requested target is blocked by missing toolchain
|
|
67
|
+
--auto-install-targets Install missing rust targets via rustup
|
|
68
|
+
|
|
69
|
+
Build / checks:
|
|
70
|
+
--targets <list> Comma-separated targets (default: all supported)
|
|
71
|
+
--skip-leak-scan Skip leak scan
|
|
72
|
+
--skip-cargo-test Skip cargo test
|
|
73
|
+
--skip-npm-tests Skip npm wrapper tests
|
|
74
|
+
--skip-release-check Skip npm run release:check -- --skip-cargo
|
|
75
|
+
--skip-verify-package Skip npm run verify:package
|
|
76
|
+
--dry-run Print actions only
|
|
77
|
+
--require-clean-worktree Require clean worktree for git publish
|
|
78
|
+
|
|
79
|
+
Git:
|
|
80
|
+
--git-remote <name> Git remote (default: origin)
|
|
81
|
+
--git-branch <name> Git branch for push (default: current branch)
|
|
82
|
+
--commit-message <text> Commit message for auto commit
|
|
83
|
+
--tag <name> Tag name (default: v<version>)
|
|
84
|
+
--skip-git Skip all git publish checks
|
|
85
|
+
|
|
86
|
+
Publish:
|
|
87
|
+
--npm-tag <tag> npm tag (default: latest)
|
|
88
|
+
--check-identity Verify npm + GitHub identities before publish
|
|
89
|
+
--help
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
bash scripts/release-all.sh
|
|
93
|
+
bash scripts/release-all.sh --targets darwin-arm64,linux-x64
|
|
94
|
+
bash scripts/release-all.sh --publish-npm --publish-git --auto-commit --create-tag --push-git
|
|
95
|
+
EOF
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
run_cmd() {
|
|
99
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
100
|
+
log "DRY RUN: $*" >&2
|
|
101
|
+
return 0
|
|
102
|
+
fi
|
|
103
|
+
log "RUN: $*" >&2
|
|
104
|
+
"$@"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
run_in_root() {
|
|
108
|
+
(cd "$ROOT_DIR" && "$@")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
log_target_status() {
|
|
112
|
+
local target=$1
|
|
113
|
+
local status=$2
|
|
114
|
+
if [[ "$status" == "ok" ]]; then
|
|
115
|
+
log "Target ${target} built and staged."
|
|
116
|
+
elif [[ "$status" == "skipped" ]]; then
|
|
117
|
+
log "Target ${target} skipped."
|
|
118
|
+
fi
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
trim() {
|
|
122
|
+
local value=$1
|
|
123
|
+
value="${value#"${value%%[![:space:]]*}"}"
|
|
124
|
+
value="${value%"${value##*[![:space:]]}"}"
|
|
125
|
+
if [[ -z "$value" ]]; then
|
|
126
|
+
printf ''
|
|
127
|
+
return
|
|
128
|
+
fi
|
|
129
|
+
printf '%s' "$value"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
is_installed_target() {
|
|
133
|
+
local rust_target=$1
|
|
134
|
+
if ! command -v rustup >/dev/null 2>&1; then
|
|
135
|
+
return 1
|
|
136
|
+
fi
|
|
137
|
+
rustup target list --installed | awk '{print $1}' | grep -Fxq "$rust_target"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ensure_target_toolchain() {
|
|
141
|
+
local rust_target=$1
|
|
142
|
+
if is_installed_target "$rust_target"; then
|
|
143
|
+
return 0
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
if [[ "$AUTO_INSTALL_TARGETS" == "true" ]]; then
|
|
147
|
+
if ! command -v rustup >/dev/null 2>&1; then
|
|
148
|
+
log "WARN: rustup is required to install missing target ${rust_target}, but it is unavailable."
|
|
149
|
+
return 1
|
|
150
|
+
fi
|
|
151
|
+
log "Installing rust target ${rust_target}"
|
|
152
|
+
run_cmd rustup target add "$rust_target"
|
|
153
|
+
if is_installed_target "$rust_target"; then
|
|
154
|
+
return 0
|
|
155
|
+
fi
|
|
156
|
+
return 1
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
if [[ "$ALLOW_MISSING_TOOLCHAIN" == "true" ]]; then
|
|
160
|
+
log "WARN: missing rust target ${rust_target}; skipping (allow-missing-toolchain=true)"
|
|
161
|
+
return 1
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
return 1
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
cleanup_temp_dir() {
|
|
168
|
+
local dir=$1
|
|
169
|
+
if [[ -d "$dir" ]]; then
|
|
170
|
+
python3 - "$dir" <<'PY'
|
|
171
|
+
import shutil
|
|
172
|
+
import sys
|
|
173
|
+
shutil.rmtree(sys.argv[1], ignore_errors=True)
|
|
174
|
+
PY
|
|
175
|
+
fi
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
require_cmd() {
|
|
179
|
+
local cmd_name=$1
|
|
180
|
+
command -v "$cmd_name" >/dev/null 2>&1 || abort "${cmd_name} is required"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
while [[ "${#}" -gt 0 ]]; do
|
|
184
|
+
case "$1" in
|
|
185
|
+
--publish-npm)
|
|
186
|
+
NPM_PUBLISH=true
|
|
187
|
+
;;
|
|
188
|
+
--publish-git)
|
|
189
|
+
GIT_PUBLISH=true
|
|
190
|
+
;;
|
|
191
|
+
--publish-all)
|
|
192
|
+
NPM_PUBLISH=true
|
|
193
|
+
GIT_PUBLISH=true
|
|
194
|
+
;;
|
|
195
|
+
--auto-commit)
|
|
196
|
+
AUTO_COMMIT=true
|
|
197
|
+
;;
|
|
198
|
+
--create-tag)
|
|
199
|
+
CREATE_TAG=true
|
|
200
|
+
;;
|
|
201
|
+
--push-git)
|
|
202
|
+
PUSH_GIT=true
|
|
203
|
+
;;
|
|
204
|
+
--skip-leak-scan)
|
|
205
|
+
SKIP_LEAK_SCAN=true
|
|
206
|
+
;;
|
|
207
|
+
--allow-missing-toolchain)
|
|
208
|
+
ALLOW_MISSING_TOOLCHAIN=true
|
|
209
|
+
;;
|
|
210
|
+
--auto-install-targets)
|
|
211
|
+
AUTO_INSTALL_TARGETS=true
|
|
212
|
+
;;
|
|
213
|
+
--skip-cargo-test)
|
|
214
|
+
SKIP_CARGO_TEST=true
|
|
215
|
+
;;
|
|
216
|
+
--skip-npm-tests)
|
|
217
|
+
SKIP_NPM_TESTS=true
|
|
218
|
+
;;
|
|
219
|
+
--skip-missing-targets)
|
|
220
|
+
ALLOW_MISSING_TARGETS=true
|
|
221
|
+
;;
|
|
222
|
+
--skip-release-check)
|
|
223
|
+
SKIP_RELEASE_CHECK=true
|
|
224
|
+
;;
|
|
225
|
+
--skip-verify-package)
|
|
226
|
+
SKIP_VERIFY_PACKAGE=true
|
|
227
|
+
;;
|
|
228
|
+
--skip-git)
|
|
229
|
+
SKIP_GIT=true
|
|
230
|
+
;;
|
|
231
|
+
--require-clean-worktree)
|
|
232
|
+
REQUIRE_CLEAN_WORKTREE=true
|
|
233
|
+
;;
|
|
234
|
+
--check-identity)
|
|
235
|
+
CHECK_IDENTITY=true
|
|
236
|
+
;;
|
|
237
|
+
--dry-run)
|
|
238
|
+
DRY_RUN=true
|
|
239
|
+
;;
|
|
240
|
+
--git-remote)
|
|
241
|
+
shift
|
|
242
|
+
GIT_REMOTE=${1:-}
|
|
243
|
+
[[ -n "$GIT_REMOTE" ]] || abort "--git-remote requires a value"
|
|
244
|
+
;;
|
|
245
|
+
--git-branch)
|
|
246
|
+
shift
|
|
247
|
+
GIT_BRANCH=${1:-}
|
|
248
|
+
[[ -n "$GIT_BRANCH" ]] || abort "--git-branch requires a value"
|
|
249
|
+
;;
|
|
250
|
+
--commit-message)
|
|
251
|
+
shift
|
|
252
|
+
COMMIT_MESSAGE=${1:-}
|
|
253
|
+
[[ -n "$COMMIT_MESSAGE" ]] || abort "--commit-message requires a value"
|
|
254
|
+
;;
|
|
255
|
+
--tag)
|
|
256
|
+
shift
|
|
257
|
+
TAG_NAME=${1:-}
|
|
258
|
+
[[ -n "$TAG_NAME" ]] || abort "--tag requires a value"
|
|
259
|
+
;;
|
|
260
|
+
--targets)
|
|
261
|
+
shift
|
|
262
|
+
FILTER_TARGETS=${1:-}
|
|
263
|
+
[[ -n "$FILTER_TARGETS" ]] || abort "--targets requires a value"
|
|
264
|
+
;;
|
|
265
|
+
--npm-tag)
|
|
266
|
+
shift
|
|
267
|
+
NPM_TAG=${1:-}
|
|
268
|
+
[[ -n "$NPM_TAG" ]] || abort "--npm-tag requires a value"
|
|
269
|
+
;;
|
|
270
|
+
-h|--help)
|
|
271
|
+
usage
|
|
272
|
+
exit 0
|
|
273
|
+
;;
|
|
274
|
+
*)
|
|
275
|
+
abort "Unknown argument: $1"
|
|
276
|
+
;;
|
|
277
|
+
esac
|
|
278
|
+
shift
|
|
279
|
+
done
|
|
280
|
+
|
|
281
|
+
if [[ "$NPM_PUBLISH" == "true" && "$SKIP_GIT" == "true" ]]; then
|
|
282
|
+
log "WARN: --skip-git only disables git publish checks, not npm publish"
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
if [[ "$GIT_PUBLISH" == "true" && "$SKIP_GIT" == "true" ]]; then
|
|
286
|
+
abort "--publish-git requires git publish actions; remove --skip-git."
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
require_cmd node
|
|
290
|
+
require_cmd npm
|
|
291
|
+
require_cmd cargo
|
|
292
|
+
|
|
293
|
+
get_workspace_version() {
|
|
294
|
+
node -p "require('./package.json').version"
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
VERSION="$(cd "$ROOT_DIR" && get_workspace_version)"
|
|
298
|
+
VERSION="${VERSION:-0.2.9}"
|
|
299
|
+
COMMIT_MESSAGE="${COMMIT_MESSAGE:-chore(release): release Agentify CLI ${VERSION}}"
|
|
300
|
+
TAG_NAME="${TAG_NAME:-v${VERSION}}"
|
|
301
|
+
|
|
302
|
+
if [[ -z "$FILTER_TARGETS" ]]; then
|
|
303
|
+
FILTER_TARGETS="$ALL_TARGETS"
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
declare -a TARGETS=()
|
|
307
|
+
declare -a RAW_TARGETS=()
|
|
308
|
+
declare -A SEEN_TARGETS=()
|
|
309
|
+
IFS=',' read -r -a RAW_TARGETS <<< "$FILTER_TARGETS"
|
|
310
|
+
|
|
311
|
+
for raw_target in "${RAW_TARGETS[@]}"; do
|
|
312
|
+
target="$(trim "$raw_target")"
|
|
313
|
+
[[ -z "$target" ]] && continue
|
|
314
|
+
if [[ -n "${SEEN_TARGETS[$target]+x}" ]]; then
|
|
315
|
+
continue
|
|
316
|
+
fi
|
|
317
|
+
SEEN_TARGETS["$target"]=1
|
|
318
|
+
TARGETS+=("$target")
|
|
319
|
+
done
|
|
320
|
+
|
|
321
|
+
if [[ "${#TARGETS[@]}" -eq 0 ]]; then
|
|
322
|
+
abort "No valid targets provided."
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
validate_target() {
|
|
326
|
+
local target=$1
|
|
327
|
+
if [[ -z "${TARGET_INFO[$target]+x}" ]]; then
|
|
328
|
+
abort "Unsupported target: ${target}"
|
|
329
|
+
fi
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
for target in "${TARGETS[@]}"; do
|
|
333
|
+
validate_target "$target"
|
|
334
|
+
done
|
|
335
|
+
|
|
336
|
+
cargo_build_target() {
|
|
337
|
+
local rust_target=$1
|
|
338
|
+
if [[ "$rust_target" != *apple-darwin && -x "${HOME}/.cargo/bin/cargo-zigbuild" ]]; then
|
|
339
|
+
run_in_root run_cmd cargo zigbuild --locked --release --target "$rust_target"
|
|
340
|
+
return
|
|
341
|
+
fi
|
|
342
|
+
run_in_root run_cmd cargo build --locked --release --target "$rust_target"
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
run_build_and_stage() {
|
|
346
|
+
local target=$1
|
|
347
|
+
local info=${TARGET_INFO[$target]}
|
|
348
|
+
IFS=':' read -r rust_target binary_name <<< "$info"
|
|
349
|
+
local platform="${target%%-*}"
|
|
350
|
+
local arch="${target#*-}"
|
|
351
|
+
local source_path="${ROOT_DIR}/target/${rust_target}/release/${binary_name}"
|
|
352
|
+
|
|
353
|
+
log "Building ${target} -> ${rust_target}"
|
|
354
|
+
|
|
355
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
356
|
+
log "DRY RUN: skipping build + stage for ${target}"
|
|
357
|
+
return 0
|
|
358
|
+
fi
|
|
359
|
+
|
|
360
|
+
if ! ensure_target_toolchain "$rust_target"; then
|
|
361
|
+
return 1
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
if ! cargo_build_target "$rust_target"; then
|
|
365
|
+
if [[ "$ALLOW_MISSING_TARGETS" == "true" ]]; then
|
|
366
|
+
log "WARN: failed to build ${target}; likely missing cross toolchain."
|
|
367
|
+
return 1
|
|
368
|
+
fi
|
|
369
|
+
return 1
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
[[ -f "$source_path" ]] || abort "build output missing: ${source_path}"
|
|
373
|
+
|
|
374
|
+
log "Staging ${target} from ${source_path}"
|
|
375
|
+
(cd "$ROOT_DIR" && run_cmd npm run stage:bin -- --platform "$platform" --arch "$arch" --source "$source_path" --force)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
parse_pack_name() {
|
|
379
|
+
local pack_json=$1
|
|
380
|
+
node -e "const input=require('fs').readFileSync(process.argv[1], 'utf8'); const data=JSON.parse(input); const first=Array.isArray(data) ? data[0] : data; process.stdout.write(first && first.filename ? first.filename : '');" "$pack_json"
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
run_leak_scan() {
|
|
384
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
385
|
+
log "Leak scan skipped in dry run."
|
|
386
|
+
return 0
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
if [[ "$SKIP_LEAK_SCAN" == "true" ]]; then
|
|
390
|
+
log "Leak scan skipped."
|
|
391
|
+
return 0
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
log "Running release leak scan"
|
|
395
|
+
local temp_dir
|
|
396
|
+
temp_dir="$(mktemp -d)"
|
|
397
|
+
local pack_json="${temp_dir}/pack.json"
|
|
398
|
+
local pack_name
|
|
399
|
+
local gitleaks_log="${temp_dir}/gitleaks.log"
|
|
400
|
+
|
|
401
|
+
(cd "$ROOT_DIR" &&
|
|
402
|
+
run_cmd npm pack --silent --json --pack-destination "$temp_dir" > "$pack_json")
|
|
403
|
+
|
|
404
|
+
pack_name="$(parse_pack_name "$pack_json")"
|
|
405
|
+
if [[ -z "$pack_name" ]]; then
|
|
406
|
+
cleanup_temp_dir "$temp_dir"
|
|
407
|
+
abort "Could not determine npm pack filename"
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
local archive="${temp_dir}/${pack_name}"
|
|
411
|
+
if [[ ! -f "$archive" ]]; then
|
|
412
|
+
cleanup_temp_dir "$temp_dir"
|
|
413
|
+
abort "npm pack artifact not found: ${archive}"
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
log "Unpacking ${archive}"
|
|
417
|
+
tar -xzf "$archive" -C "$temp_dir"
|
|
418
|
+
local package_payload="${temp_dir}/package"
|
|
419
|
+
if [[ ! -d "$package_payload" ]]; then
|
|
420
|
+
cleanup_temp_dir "$temp_dir"
|
|
421
|
+
abort "Expected package payload directory missing: ${package_payload}"
|
|
422
|
+
fi
|
|
423
|
+
|
|
424
|
+
if command -v gitleaks >/dev/null 2>&1; then
|
|
425
|
+
log "Running gitleaks on packed package"
|
|
426
|
+
local gitleaks_report="${temp_dir}/gitleaks-report.json"
|
|
427
|
+
set +e
|
|
428
|
+
gitleaks detect --no-git --redact --source "$package_payload" --report-format json --report-path "$gitleaks_report" >"$gitleaks_log" 2>&1
|
|
429
|
+
local gitleaks_code=$?
|
|
430
|
+
set -e
|
|
431
|
+
if [[ "$gitleaks_code" -ne 0 ]]; then
|
|
432
|
+
if [[ -s "$gitleaks_report" ]]; then
|
|
433
|
+
cleanup_temp_dir "$temp_dir"
|
|
434
|
+
abort "Gitleaks detected possible secrets in release payload. Report: $gitleaks_report"
|
|
435
|
+
fi
|
|
436
|
+
if rg -q "Found" "$gitleaks_log"; then
|
|
437
|
+
cleanup_temp_dir "$temp_dir"
|
|
438
|
+
abort "Gitleaks failed to complete for release payload"
|
|
439
|
+
fi
|
|
440
|
+
fi
|
|
441
|
+
cleanup_temp_dir "$temp_dir"
|
|
442
|
+
return 0
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
log "Gitleaks unavailable; running fallback regex scan"
|
|
446
|
+
local pattern='(?i)-----BEGIN [A-Z ]+ PRIVATE KEY-----|(?i)AKIA[0-9A-Z]{16}|(?i)(?:ghp|gho|ghu|ghr)_[A-Za-z0-9]{36,}|(?i)github_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}|(?i)BEGIN OPENSSH PRIVATE KEY|(?i)secret\\s*[:=]|(?i)api[_-]?key\\s*[:=]|(?i)password\\s*[:=]'
|
|
447
|
+
if rg -n --hidden --pcre2 -S "$pattern" "$package_payload"; then
|
|
448
|
+
cleanup_temp_dir "$temp_dir"
|
|
449
|
+
abort "Fallback leak scan found suspicious strings in packed package."
|
|
450
|
+
fi
|
|
451
|
+
|
|
452
|
+
log "Leak scan passed"
|
|
453
|
+
cleanup_temp_dir "$temp_dir"
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
maybe_check_npm_identity() {
|
|
457
|
+
local actual
|
|
458
|
+
actual="$(npm whoami 2>/dev/null || true)"
|
|
459
|
+
if [[ -z "$actual" ]]; then
|
|
460
|
+
abort "npm auth missing. Run npm login as ${REQUIRED_NPM_ACCOUNT}."
|
|
461
|
+
fi
|
|
462
|
+
if [[ "$actual" != "$REQUIRED_NPM_ACCOUNT" ]]; then
|
|
463
|
+
abort "npm identity mismatch. Expected ${REQUIRED_NPM_ACCOUNT}, got ${actual}."
|
|
464
|
+
fi
|
|
465
|
+
log "npm identity: ${actual}"
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
maybe_check_github_identity() {
|
|
469
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
470
|
+
abort "gh CLI required for GitHub identity checks."
|
|
471
|
+
fi
|
|
472
|
+
local user
|
|
473
|
+
user="$(gh api user --jq .login 2>/dev/null || true)"
|
|
474
|
+
if [[ -z "$user" ]]; then
|
|
475
|
+
abort "GitHub auth missing or gh not authorized."
|
|
476
|
+
fi
|
|
477
|
+
if [[ "$user" != "$REQUIRED_GH_ACCOUNT" ]]; then
|
|
478
|
+
abort "GitHub identity mismatch. Expected ${REQUIRED_GH_ACCOUNT}, got ${user}."
|
|
479
|
+
fi
|
|
480
|
+
log "GitHub identity: ${user}"
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
maybe_publish_npm() {
|
|
484
|
+
[[ "$NPM_PUBLISH" == "true" ]] || return 0
|
|
485
|
+
|
|
486
|
+
maybe_check_npm_identity
|
|
487
|
+
log "Publishing npm package @agentify/cli@${VERSION} (${NPM_TAG})"
|
|
488
|
+
(cd "$ROOT_DIR" && run_cmd npm publish --access public --tag "$NPM_TAG")
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
current_branch() {
|
|
492
|
+
(cd "$ROOT_DIR" && git rev-parse --abbrev-ref HEAD)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
maybe_publish_git() {
|
|
496
|
+
[[ "$GIT_PUBLISH" == "true" ]] || return 0
|
|
497
|
+
|
|
498
|
+
if [[ "$REQUIRE_CLEAN_WORKTREE" == "true" ]]; then
|
|
499
|
+
if [[ -n "$(cd "$ROOT_DIR" && git status --porcelain)" ]]; then
|
|
500
|
+
abort "git publish requested with --require-clean-worktree, but workspace has changes."
|
|
501
|
+
fi
|
|
502
|
+
fi
|
|
503
|
+
|
|
504
|
+
maybe_check_github_identity
|
|
505
|
+
local branch
|
|
506
|
+
branch="$(current_branch)"
|
|
507
|
+
if [[ -z "$GIT_BRANCH" ]]; then
|
|
508
|
+
GIT_BRANCH="$branch"
|
|
509
|
+
fi
|
|
510
|
+
if [[ "$GIT_BRANCH" != "$branch" ]]; then
|
|
511
|
+
log "WARN: target push branch is ${GIT_BRANCH}; current branch is ${branch}"
|
|
512
|
+
fi
|
|
513
|
+
|
|
514
|
+
(cd "$ROOT_DIR" && git status --short)
|
|
515
|
+
|
|
516
|
+
if [[ "$AUTO_COMMIT" == "true" ]]; then
|
|
517
|
+
local commit_paths=(".")
|
|
518
|
+
local repo_root
|
|
519
|
+
repo_root="$(cd "$ROOT_DIR" && git rev-parse --show-toplevel)"
|
|
520
|
+
local wrapper_path="${repo_root}/scripts/release-agentify-cli.sh"
|
|
521
|
+
if [[ -f "$wrapper_path" ]]; then
|
|
522
|
+
commit_paths+=("$wrapper_path")
|
|
523
|
+
fi
|
|
524
|
+
(cd "$ROOT_DIR" && run_cmd git add "${commit_paths[@]}")
|
|
525
|
+
if (cd "$ROOT_DIR" && git diff --cached --quiet); then
|
|
526
|
+
log "No release artifacts changed; skipping commit."
|
|
527
|
+
else
|
|
528
|
+
(cd "$ROOT_DIR" && run_cmd git commit -m "$COMMIT_MESSAGE")
|
|
529
|
+
fi
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
if [[ "$CREATE_TAG" == "true" ]]; then
|
|
533
|
+
if (cd "$ROOT_DIR" && git rev-parse "$TAG_NAME" >/dev/null 2>&1); then
|
|
534
|
+
log "Tag exists already: ${TAG_NAME}"
|
|
535
|
+
else
|
|
536
|
+
(cd "$ROOT_DIR" && run_cmd git tag -a "${TAG_NAME}" -m "${COMMIT_MESSAGE}")
|
|
537
|
+
fi
|
|
538
|
+
fi
|
|
539
|
+
|
|
540
|
+
if [[ "$PUSH_GIT" == "true" ]]; then
|
|
541
|
+
(cd "$ROOT_DIR" && run_cmd git push "$GIT_REMOTE" "$GIT_BRANCH")
|
|
542
|
+
if [[ "$CREATE_TAG" == "true" ]]; then
|
|
543
|
+
(cd "$ROOT_DIR" && run_cmd git push "$GIT_REMOTE" "${TAG_NAME}")
|
|
544
|
+
fi
|
|
545
|
+
fi
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
run_checks() {
|
|
549
|
+
if [[ "$SKIP_RELEASE_CHECK" != "true" ]]; then
|
|
550
|
+
log "Running release checks"
|
|
551
|
+
run_in_root run_cmd npm run check:versions
|
|
552
|
+
|
|
553
|
+
if [[ "$SKIP_CARGO_TEST" != "true" ]]; then
|
|
554
|
+
run_in_root run_cmd cargo test --locked
|
|
555
|
+
fi
|
|
556
|
+
if [[ "$SKIP_NPM_TESTS" != "true" ]]; then
|
|
557
|
+
run_in_root run_cmd npm run test:npm
|
|
558
|
+
fi
|
|
559
|
+
if [[ "$ALLOW_MISSING_TARGETS" == "true" ]]; then
|
|
560
|
+
run_in_root run_cmd npm run release:check -- --skip-cargo --allow-missing-targets
|
|
561
|
+
else
|
|
562
|
+
run_in_root run_cmd npm run release:check -- --skip-cargo
|
|
563
|
+
fi
|
|
564
|
+
fi
|
|
565
|
+
|
|
566
|
+
if [[ "$SKIP_VERIFY_PACKAGE" != "true" ]]; then
|
|
567
|
+
run_in_root run_cmd npm run verify:package
|
|
568
|
+
fi
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if [[ "$CHECK_IDENTITY" == "true" ]]; then
|
|
572
|
+
maybe_check_npm_identity
|
|
573
|
+
maybe_check_github_identity
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
log "Starting release pipeline"
|
|
577
|
+
log "Version: ${VERSION}"
|
|
578
|
+
log "Targets: ${TARGETS[*]}"
|
|
579
|
+
log "Dry run: ${DRY_RUN}"
|
|
580
|
+
|
|
581
|
+
declare -a COMPLETED_TARGETS=()
|
|
582
|
+
declare -a FAILED_TARGETS=()
|
|
583
|
+
|
|
584
|
+
for target in "${TARGETS[@]}"; do
|
|
585
|
+
if run_build_and_stage "$target"; then
|
|
586
|
+
COMPLETED_TARGETS+=("$target")
|
|
587
|
+
log_target_status "$target" "ok"
|
|
588
|
+
else
|
|
589
|
+
FAILED_TARGETS+=("$target")
|
|
590
|
+
log_target_status "$target" "skipped"
|
|
591
|
+
if [[ "$ALLOW_MISSING_TARGETS" != "true" ]]; then
|
|
592
|
+
abort "Build failed for requested target: ${target}"
|
|
593
|
+
fi
|
|
594
|
+
fi
|
|
595
|
+
done
|
|
596
|
+
|
|
597
|
+
if [[ "${#COMPLETED_TARGETS[@]}" -eq 0 ]]; then
|
|
598
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
599
|
+
log "No targets would be built in dry-run (check requested target list and allow-missing settings)."
|
|
600
|
+
else
|
|
601
|
+
abort "No targets were built successfully."
|
|
602
|
+
fi
|
|
603
|
+
fi
|
|
604
|
+
|
|
605
|
+
run_checks
|
|
606
|
+
run_leak_scan
|
|
607
|
+
maybe_publish_npm
|
|
608
|
+
maybe_publish_git
|
|
609
|
+
|
|
610
|
+
if [[ "${#FAILED_TARGETS[@]}" -gt 0 ]]; then
|
|
611
|
+
log "Skipped targets: ${FAILED_TARGETS[*]}"
|
|
612
|
+
fi
|
|
613
|
+
|
|
614
|
+
log "Release pipeline complete"
|