@kody-ade/kody-engine 0.4.224 → 0.4.225
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 +2 -2
- package/dist/bin/kody.js +779 -831
- package/dist/executables/run/profile.json +1 -1
- package/dist/executables/types.ts +3 -1
- package/package.json +1 -1
- package/dist/duties/duty-scheduler/duty.md +0 -3
- package/dist/duties/duty-scheduler/profile.json +0 -6
- package/dist/duties/duty-tick/duty.md +0 -3
- package/dist/duties/duty-tick/profile.json +0 -6
- package/dist/duties/duty-tick-scripted/duty.md +0 -3
- package/dist/duties/duty-tick-scripted/profile.json +0 -6
- package/dist/duties/fix/duty.md +0 -10
- package/dist/duties/fix/profile.json +0 -6
- package/dist/duties/fix-ci/duty.md +0 -10
- package/dist/duties/fix-ci/profile.json +0 -6
- package/dist/duties/goal-scheduler/duty.md +0 -3
- package/dist/duties/goal-scheduler/profile.json +0 -6
- package/dist/duties/goal-tick/duty.md +0 -3
- package/dist/duties/goal-tick/profile.json +0 -6
- package/dist/duties/init/duty.md +0 -10
- package/dist/duties/init/profile.json +0 -6
- package/dist/duties/job-live-verify/duty.md +0 -3
- package/dist/duties/job-live-verify/profile.json +0 -6
- package/dist/duties/merge/duty.md +0 -10
- package/dist/duties/merge/profile.json +0 -6
- package/dist/duties/npm-publish/duty.md +0 -17
- package/dist/duties/npm-publish/profile.json +0 -6
- package/dist/duties/plan-verify/duty.md +0 -3
- package/dist/duties/plan-verify/profile.json +0 -6
- package/dist/duties/preview-build/duty.md +0 -10
- package/dist/duties/preview-build/profile.json +0 -6
- package/dist/duties/probe-skill/duty.md +0 -3
- package/dist/duties/probe-skill/profile.json +0 -6
- package/dist/duties/qa-goal/duty.md +0 -3
- package/dist/duties/qa-goal/profile.json +0 -6
- package/dist/duties/release/duty.md +0 -10
- package/dist/duties/release/profile.json +0 -6
- package/dist/duties/release-deploy/duty.md +0 -10
- package/dist/duties/release-deploy/profile.json +0 -6
- package/dist/duties/release-prepare/duty.md +0 -10
- package/dist/duties/release-prepare/profile.json +0 -6
- package/dist/duties/release-publish/duty.md +0 -10
- package/dist/duties/release-publish/profile.json +0 -6
- package/dist/duties/resolve/duty.md +0 -10
- package/dist/duties/resolve/profile.json +0 -6
- package/dist/duties/revert/duty.md +0 -10
- package/dist/duties/revert/profile.json +0 -6
- package/dist/duties/sync/duty.md +0 -10
- package/dist/duties/sync/profile.json +0 -6
- package/dist/duties/task-job-fail-once/duty.md +0 -3
- package/dist/duties/task-job-fail-once/profile.json +0 -6
- package/dist/duties/task-job-pass-a/duty.md +0 -3
- package/dist/duties/task-job-pass-a/profile.json +0 -6
- package/dist/duties/task-job-pass-b/duty.md +0 -3
- package/dist/duties/task-job-pass-b/profile.json +0 -6
- package/dist/duties/task-jobs/duty.md +0 -3
- package/dist/duties/task-jobs/profile.json +0 -6
- package/dist/duties/worker-ask/duty.md +0 -10
- package/dist/duties/worker-ask/profile.json +0 -6
- package/dist/executables/duty-scheduler/profile.json +0 -57
- package/dist/executables/duty-tick/profile.json +0 -78
- package/dist/executables/duty-tick/prompt.md +0 -77
- package/dist/executables/duty-tick/prompts/locked.md +0 -50
- package/dist/executables/duty-tick-scripted/profile.json +0 -72
- package/dist/executables/fix/profile.json +0 -91
- package/dist/executables/fix/prompt.md +0 -90
- package/dist/executables/fix-ci/profile.json +0 -71
- package/dist/executables/fix-ci/prompt.md +0 -78
- package/dist/executables/goal-scheduler/profile.json +0 -53
- package/dist/executables/goal-scheduler/scheduler.sh +0 -82
- package/dist/executables/goal-tick/profile.json +0 -69
- package/dist/executables/init/profile.json +0 -36
- package/dist/executables/job-live-verify/profile.json +0 -36
- package/dist/executables/job-live-verify/prompt.md +0 -42
- package/dist/executables/merge/profile.json +0 -54
- package/dist/executables/npm-publish/profile.json +0 -73
- package/dist/executables/npm-publish/publish.sh +0 -93
- package/dist/executables/plan-verify/profile.json +0 -38
- package/dist/executables/plan-verify/prompt.md +0 -72
- package/dist/executables/preview-build/profile.json +0 -61
- package/dist/executables/preview-health/profile.json +0 -48
- package/dist/executables/preview-health/tick.sh +0 -319
- package/dist/executables/probe-skill/profile.json +0 -35
- package/dist/executables/probe-skill/prompt.md +0 -31
- package/dist/executables/probe-skill/skills/probe-skill-marker/SKILL.md +0 -18
- package/dist/executables/qa-goal/profile.json +0 -65
- package/dist/executables/release/deploy.sh +0 -157
- package/dist/executables/release/prepare.sh +0 -353
- package/dist/executables/release/profile.json +0 -95
- package/dist/executables/release/publish.sh +0 -88
- package/dist/executables/release/release.sh +0 -230
- package/dist/executables/release/wait.sh +0 -88
- package/dist/executables/release-deploy/deploy.sh +0 -222
- package/dist/executables/release-deploy/profile.json +0 -53
- package/dist/executables/release-prepare/prepare.sh +0 -368
- package/dist/executables/release-prepare/profile.json +0 -70
- package/dist/executables/release-publish/profile.json +0 -53
- package/dist/executables/release-publish/publish.sh +0 -137
- package/dist/executables/resolve/apply-prefer.sh +0 -54
- package/dist/executables/resolve/profile.json +0 -107
- package/dist/executables/resolve/prompt.md +0 -51
- package/dist/executables/revert/profile.json +0 -82
- package/dist/executables/revert/revert.sh +0 -33
- package/dist/executables/sync/profile.json +0 -48
- package/dist/executables/task-job-fail-once/profile.json +0 -44
- package/dist/executables/task-job-fail-once/prompt.md +0 -1
- package/dist/executables/task-job-pass-a/profile.json +0 -43
- package/dist/executables/task-job-pass-a/prompt.md +0 -1
- package/dist/executables/task-job-pass-b/profile.json +0 -43
- package/dist/executables/task-job-pass-b/prompt.md +0 -1
- package/dist/executables/task-jobs/profile.json +0 -41
- package/dist/executables/task-jobs/prompt.md +0 -1
- package/dist/executables/worker-ask/profile.json +0 -70
- package/dist/executables/worker-ask/prompt.md +0 -60
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# release/prepare.sh — function library for the prepare phase.
|
|
4
|
-
#
|
|
5
|
-
# Functions exported:
|
|
6
|
-
# read_pkg_version
|
|
7
|
-
# bump_version <cur> <patch|minor|major>
|
|
8
|
-
# write_pkg_version <file> <new>
|
|
9
|
-
# resolve_version_files -> prints \n-separated paths
|
|
10
|
-
# generate_changelog -> prints raw `subject||sha` lines
|
|
11
|
-
# format_changelog <new_version> -> reads stdin, prints markdown entry
|
|
12
|
-
# prepend_changelog <entry>
|
|
13
|
-
# remote_branch_exists <branch>
|
|
14
|
-
# find_open_pr <branch> -> prints url or empty
|
|
15
|
-
# open_prepare_pr <new_version> <issue_number> <prefer>
|
|
16
|
-
# -> echoes PR url; sets globals
|
|
17
|
-
# set_kody_release_pr_marker <issue_number> <pr_url>
|
|
18
|
-
#
|
|
19
|
-
# Side-effects: bumps version files, generates CHANGELOG.md, commits +
|
|
20
|
-
# pushes a release branch, opens the prepare PR.
|
|
21
|
-
|
|
22
|
-
# shellcheck disable=SC2148
|
|
23
|
-
|
|
24
|
-
read_pkg_version() {
|
|
25
|
-
python3 -c "import json,sys; print(json.load(open('package.json'))['version'])"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
bump_version() {
|
|
29
|
-
local cur="$1" kind="$2"
|
|
30
|
-
local core="${cur%%-*}"
|
|
31
|
-
if ! [[ "$core" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
|
32
|
-
echo "[prepare] cannot parse version '$cur'" >&2
|
|
33
|
-
return 1
|
|
34
|
-
fi
|
|
35
|
-
local maj="${BASH_REMATCH[1]}" min="${BASH_REMATCH[2]}" pat="${BASH_REMATCH[3]}"
|
|
36
|
-
case "$kind" in
|
|
37
|
-
major) maj=$((maj + 1)); min=0; pat=0 ;;
|
|
38
|
-
minor) min=$((min + 1)); pat=0 ;;
|
|
39
|
-
patch|*) pat=$((pat + 1)) ;;
|
|
40
|
-
esac
|
|
41
|
-
echo "${maj}.${min}.${pat}"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
write_pkg_version() {
|
|
45
|
-
local file="$1" new="$2"
|
|
46
|
-
python3 - "$file" "$new" <<'PY'
|
|
47
|
-
import json, sys
|
|
48
|
-
path, new = sys.argv[1], sys.argv[2]
|
|
49
|
-
try:
|
|
50
|
-
with open(path) as f:
|
|
51
|
-
text = f.read()
|
|
52
|
-
except FileNotFoundError:
|
|
53
|
-
print("MISSING")
|
|
54
|
-
sys.exit(0)
|
|
55
|
-
try:
|
|
56
|
-
data = json.loads(text)
|
|
57
|
-
except Exception:
|
|
58
|
-
print("UNCHANGED")
|
|
59
|
-
sys.exit(0)
|
|
60
|
-
if data.get("version") == new:
|
|
61
|
-
print("UNCHANGED")
|
|
62
|
-
sys.exit(0)
|
|
63
|
-
data["version"] = new
|
|
64
|
-
with open(path, "w") as f:
|
|
65
|
-
f.write(json.dumps(data, indent=2) + "\n")
|
|
66
|
-
print("WROTE")
|
|
67
|
-
PY
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
resolve_version_files() {
|
|
71
|
-
local raw="${KODY_CFG_RELEASE_VERSIONFILES:-}"
|
|
72
|
-
if [[ -z "$raw" ]]; then
|
|
73
|
-
echo "package.json"
|
|
74
|
-
return
|
|
75
|
-
fi
|
|
76
|
-
python3 - <<PY
|
|
77
|
-
import json, os
|
|
78
|
-
raw = os.environ.get("KODY_CFG_RELEASE_VERSIONFILES", "")
|
|
79
|
-
try:
|
|
80
|
-
arr = json.loads(raw)
|
|
81
|
-
except Exception:
|
|
82
|
-
print("package.json"); raise SystemExit
|
|
83
|
-
if isinstance(arr, list) and arr:
|
|
84
|
-
for f in arr:
|
|
85
|
-
if isinstance(f, str) and f:
|
|
86
|
-
print(f)
|
|
87
|
-
else:
|
|
88
|
-
print("package.json")
|
|
89
|
-
PY
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
generate_changelog() {
|
|
93
|
-
local last_tag count
|
|
94
|
-
if ! last_tag=$(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null); then
|
|
95
|
-
git fetch --tags --quiet 2>/dev/null || true
|
|
96
|
-
last_tag=$(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null || true)
|
|
97
|
-
fi
|
|
98
|
-
if [[ -n "$last_tag" ]]; then
|
|
99
|
-
count=$(git rev-list --count "${last_tag}..HEAD" --no-merges 2>/dev/null || echo "?")
|
|
100
|
-
echo " changelog: ${count} commits since ${last_tag}" >&2
|
|
101
|
-
git log "${last_tag}..HEAD" --pretty=format:'%s||%h' --no-merges 2>/dev/null || true
|
|
102
|
-
else
|
|
103
|
-
echo " changelog: no previous v* tag found — using last 100 commits" >&2
|
|
104
|
-
git log -n100 HEAD --pretty=format:'%s||%h' --no-merges 2>/dev/null || true
|
|
105
|
-
fi
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
format_changelog() {
|
|
109
|
-
local new_version="$1"
|
|
110
|
-
local date_str raw
|
|
111
|
-
date_str=$(date -u +%Y-%m-%d)
|
|
112
|
-
# Capture stdin BEFORE invoking python — the python heredoc below
|
|
113
|
-
# redirects sys.stdin to the heredoc text itself, so we can't rely on
|
|
114
|
-
# `sys.stdin.read()` to see the piped `subject||sha` lines. Pass the
|
|
115
|
-
# raw log through an env var instead.
|
|
116
|
-
raw=$(cat)
|
|
117
|
-
RAW_CHANGELOG="$raw" NEW_VER="$new_version" DATE_STR="$date_str" python3 - <<'PY'
|
|
118
|
-
import os, re, sys
|
|
119
|
-
new_version = os.environ["NEW_VER"]
|
|
120
|
-
date_str = os.environ["DATE_STR"]
|
|
121
|
-
raw = os.environ.get("RAW_CHANGELOG", "")
|
|
122
|
-
buckets = {k: [] for k in ("feat", "fix", "perf", "refactor", "docs", "chore", "other")}
|
|
123
|
-
for line in raw.splitlines():
|
|
124
|
-
line = line.strip()
|
|
125
|
-
if not line or "||" not in line:
|
|
126
|
-
continue
|
|
127
|
-
subject, sha = line.split("||", 1)
|
|
128
|
-
if re.match(r"(?i)^chore:\s*release\s+v\d", subject):
|
|
129
|
-
continue
|
|
130
|
-
m = re.match(r"^(\w+)(?:\(.*?\))?\s*:\s*(.+)$", subject)
|
|
131
|
-
if m:
|
|
132
|
-
kind = m.group(1).lower()
|
|
133
|
-
msg = m.group(2)
|
|
134
|
-
else:
|
|
135
|
-
kind = "other"
|
|
136
|
-
msg = subject
|
|
137
|
-
buckets.setdefault(kind, buckets["other"]).append(f"- {msg} ({sha})")
|
|
138
|
-
labels = [
|
|
139
|
-
("feat", "Features"),
|
|
140
|
-
("fix", "Fixes"),
|
|
141
|
-
("perf", "Performance"),
|
|
142
|
-
("refactor", "Refactoring"),
|
|
143
|
-
("docs", "Docs"),
|
|
144
|
-
("chore", "Chores"),
|
|
145
|
-
("other", "Other"),
|
|
146
|
-
]
|
|
147
|
-
parts = [f"## v{new_version} — {date_str}", ""]
|
|
148
|
-
emitted = False
|
|
149
|
-
for key, label in labels:
|
|
150
|
-
items = buckets.get(key) or []
|
|
151
|
-
if not items:
|
|
152
|
-
continue
|
|
153
|
-
parts.append(f"### {label}")
|
|
154
|
-
parts.extend(items)
|
|
155
|
-
parts.append("")
|
|
156
|
-
emitted = True
|
|
157
|
-
if not emitted:
|
|
158
|
-
parts.append("_No notable commits since the last release._")
|
|
159
|
-
parts.append("")
|
|
160
|
-
sys.stdout.write("\n".join(parts))
|
|
161
|
-
PY
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
prepend_changelog() {
|
|
165
|
-
local entry="$1"
|
|
166
|
-
local header='# Changelog
|
|
167
|
-
|
|
168
|
-
All notable changes to this project will be documented in this file.
|
|
169
|
-
|
|
170
|
-
'
|
|
171
|
-
if [[ -f CHANGELOG.md ]]; then
|
|
172
|
-
if grep -qE '^#\s*Changelog\b' CHANGELOG.md; then
|
|
173
|
-
python3 - "$entry" <<'PY'
|
|
174
|
-
import sys
|
|
175
|
-
entry = sys.argv[1]
|
|
176
|
-
with open("CHANGELOG.md") as f:
|
|
177
|
-
prior = f.read()
|
|
178
|
-
idx = prior.index("\n", prior.index("# Changelog"))
|
|
179
|
-
new = prior[: idx + 1] + "\n" + entry + prior[idx + 1 :]
|
|
180
|
-
with open("CHANGELOG.md", "w") as f:
|
|
181
|
-
f.write(new)
|
|
182
|
-
PY
|
|
183
|
-
else
|
|
184
|
-
python3 - "$entry" <<PY
|
|
185
|
-
import sys
|
|
186
|
-
entry = sys.argv[1]
|
|
187
|
-
header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n"
|
|
188
|
-
with open("CHANGELOG.md") as f:
|
|
189
|
-
prior = f.read()
|
|
190
|
-
with open("CHANGELOG.md", "w") as f:
|
|
191
|
-
f.write(header + entry + prior)
|
|
192
|
-
PY
|
|
193
|
-
fi
|
|
194
|
-
else
|
|
195
|
-
python3 - "$entry" <<PY
|
|
196
|
-
import sys
|
|
197
|
-
entry = sys.argv[1]
|
|
198
|
-
header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n"
|
|
199
|
-
with open("CHANGELOG.md", "w") as f:
|
|
200
|
-
f.write(header + entry)
|
|
201
|
-
PY
|
|
202
|
-
fi
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
remote_branch_exists() {
|
|
206
|
-
local branch="$1"
|
|
207
|
-
git ls-remote --heads origin "$branch" 2>/dev/null | grep -q .
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
find_open_pr() {
|
|
211
|
-
local branch="$1"
|
|
212
|
-
gh pr list --head "$branch" --state open --json url --limit 1 2>/dev/null \
|
|
213
|
-
| python3 -c 'import json,sys; data=json.load(sys.stdin); print(data[0]["url"] if data else "")' 2>/dev/null \
|
|
214
|
-
|| echo ""
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
# Returns the PREPARE PR url on stdout. Bumps version files, generates
|
|
218
|
-
# CHANGELOG, commits on a release branch, pushes, opens (or reuses) the PR.
|
|
219
|
-
# Globals it sets via export so release.sh can pass them to deploy/publish:
|
|
220
|
-
# PREPARE_NEW_VERSION
|
|
221
|
-
# PREPARE_TAG
|
|
222
|
-
# PREPARE_RELEASE_BRANCH
|
|
223
|
-
# PREPARE_CHANGELOG_ENTRY
|
|
224
|
-
open_prepare_pr() {
|
|
225
|
-
local new_version="$1"
|
|
226
|
-
local issue_arg="$2"
|
|
227
|
-
local prefer="$3"
|
|
228
|
-
local default_branch="${KODY_CFG_GIT_DEFAULTBRANCH:-main}"
|
|
229
|
-
|
|
230
|
-
local tag="v${new_version}"
|
|
231
|
-
local release_branch="release/${tag}"
|
|
232
|
-
|
|
233
|
-
export PREPARE_NEW_VERSION="$new_version"
|
|
234
|
-
export PREPARE_TAG="$tag"
|
|
235
|
-
export PREPARE_RELEASE_BRANCH="$release_branch"
|
|
236
|
-
|
|
237
|
-
# Branch-collision gate.
|
|
238
|
-
local collides=false
|
|
239
|
-
if remote_branch_exists "$release_branch"; then
|
|
240
|
-
collides=true
|
|
241
|
-
case "$prefer" in
|
|
242
|
-
theirs)
|
|
243
|
-
local existing
|
|
244
|
-
existing=$(find_open_pr "$release_branch")
|
|
245
|
-
if [[ -n "$existing" ]]; then
|
|
246
|
-
echo " reusing existing PR (--prefer theirs): ${existing}" >&2
|
|
247
|
-
echo "$existing"
|
|
248
|
-
return 0
|
|
249
|
-
fi
|
|
250
|
-
echo "[prepare] --prefer theirs: ${release_branch} exists but no open PR" >&2
|
|
251
|
-
return 1
|
|
252
|
-
;;
|
|
253
|
-
ours)
|
|
254
|
-
echo " branch ${release_branch} exists — will force-push (--prefer ours)" >&2
|
|
255
|
-
;;
|
|
256
|
-
*)
|
|
257
|
-
echo "[prepare] branch ${release_branch} already exists. Use --prefer ours/theirs." >&2
|
|
258
|
-
return 1
|
|
259
|
-
;;
|
|
260
|
-
esac
|
|
261
|
-
fi
|
|
262
|
-
|
|
263
|
-
# Bump version files.
|
|
264
|
-
local files=()
|
|
265
|
-
while IFS= read -r f; do
|
|
266
|
-
files+=("$f")
|
|
267
|
-
done < <(resolve_version_files)
|
|
268
|
-
local touched=()
|
|
269
|
-
for f in "${files[@]}"; do
|
|
270
|
-
local res
|
|
271
|
-
res=$(write_pkg_version "$f" "$new_version")
|
|
272
|
-
if [[ "$res" == "WROTE" ]]; then
|
|
273
|
-
touched+=("$f")
|
|
274
|
-
fi
|
|
275
|
-
done
|
|
276
|
-
if [[ ${#touched[@]} -eq 0 ]]; then
|
|
277
|
-
echo "[prepare] no version strings updated (files: ${files[*]})" >&2
|
|
278
|
-
return 1
|
|
279
|
-
fi
|
|
280
|
-
echo " wrote ${touched[*]}" >&2
|
|
281
|
-
|
|
282
|
-
# Changelog.
|
|
283
|
-
local raw_log entry
|
|
284
|
-
raw_log=$(generate_changelog) || raw_log=""
|
|
285
|
-
entry=$(printf '%s' "$raw_log" | format_changelog "$new_version")
|
|
286
|
-
prepend_changelog "$entry"
|
|
287
|
-
echo " wrote CHANGELOG.md" >&2
|
|
288
|
-
export PREPARE_CHANGELOG_ENTRY="$entry"
|
|
289
|
-
|
|
290
|
-
# Commit + push.
|
|
291
|
-
export HUSKY=0 SKIP_HOOKS=1
|
|
292
|
-
git checkout -b "$release_branch"
|
|
293
|
-
for f in "${touched[@]}" CHANGELOG.md; do
|
|
294
|
-
git add -- "$f"
|
|
295
|
-
done
|
|
296
|
-
git -c commit.gpgsign=false commit -m "chore: release ${tag}"
|
|
297
|
-
if [[ "$collides" == "true" && "$prefer" == "ours" ]]; then
|
|
298
|
-
git push -u --force-with-lease origin "$release_branch"
|
|
299
|
-
else
|
|
300
|
-
git push -u origin "$release_branch"
|
|
301
|
-
fi
|
|
302
|
-
|
|
303
|
-
# Open PR.
|
|
304
|
-
local pr_url=""
|
|
305
|
-
if [[ "$collides" == "true" && "$prefer" == "ours" ]]; then
|
|
306
|
-
pr_url=$(find_open_pr "$release_branch")
|
|
307
|
-
fi
|
|
308
|
-
|
|
309
|
-
if [[ -z "$pr_url" ]]; then
|
|
310
|
-
local body_max=60000 body_entry
|
|
311
|
-
if [[ ${#entry} -gt $body_max ]]; then
|
|
312
|
-
body_entry="${entry:0:$body_max}
|
|
313
|
-
|
|
314
|
-
_… truncated; see CHANGELOG.md_"
|
|
315
|
-
else
|
|
316
|
-
body_entry="$entry"
|
|
317
|
-
fi
|
|
318
|
-
local tracking_line=""
|
|
319
|
-
if [[ "$issue_arg" =~ ^[0-9]+$ && "$issue_arg" != "0" ]]; then
|
|
320
|
-
tracking_line=$'\n\nTracking-Issue: #'"${issue_arg}"
|
|
321
|
-
fi
|
|
322
|
-
local body
|
|
323
|
-
body=$'Automated release PR opened by kody.\n\n'"$body_entry"$'\n\nThe release flow will merge this into `'"${default_branch}"$'` and continue to publish + deploy.'"${tracking_line}"
|
|
324
|
-
pr_url=$(printf '%s' "$body" | gh pr create --head "$release_branch" --base "$default_branch" --title "chore: release ${tag}" --body-file -)
|
|
325
|
-
fi
|
|
326
|
-
|
|
327
|
-
if [[ -z "$pr_url" ]]; then
|
|
328
|
-
echo "[prepare] gh pr create returned empty URL" >&2
|
|
329
|
-
return 1
|
|
330
|
-
fi
|
|
331
|
-
|
|
332
|
-
echo "$pr_url"
|
|
333
|
-
return 0
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
set_kody_release_pr_marker() {
|
|
337
|
-
local issue_arg="$1" pr_url="$2"
|
|
338
|
-
if [[ ! "$issue_arg" =~ ^[0-9]+$ ]] || [[ "$issue_arg" == "0" ]]; then
|
|
339
|
-
return 0
|
|
340
|
-
fi
|
|
341
|
-
local pr_number="${pr_url##*/}"
|
|
342
|
-
if [[ ! "$pr_number" =~ ^[0-9]+$ ]]; then
|
|
343
|
-
return 0
|
|
344
|
-
fi
|
|
345
|
-
local cur_body cleaned_body
|
|
346
|
-
cur_body=$(gh issue view "$issue_arg" --json body -q .body 2>/dev/null || echo "")
|
|
347
|
-
cleaned_body=$(printf '%s' "$cur_body" | sed -E '/<!-- kody-release-pr:[^>]*-->/d')
|
|
348
|
-
{
|
|
349
|
-
printf '%s' "$cleaned_body"
|
|
350
|
-
printf '\n\n<!-- kody-release-pr: #%s -->\n' "$pr_number"
|
|
351
|
-
} | gh issue edit "$issue_arg" --body-file - >/dev/null 2>&1 || \
|
|
352
|
-
echo "[prepare] WARN: failed to write kody-release-pr marker to issue #${issue_arg}" >&2
|
|
353
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "release",
|
|
3
|
-
"role": "utility",
|
|
4
|
-
"phase": "shipped",
|
|
5
|
-
"describe": "Single-job release flow: prepare → wait CI → merge → publish → deploy → notify. No orchestrator chain — runs end-to-end inside one workflow run.",
|
|
6
|
-
"inputs": [
|
|
7
|
-
{
|
|
8
|
-
"name": "issue",
|
|
9
|
-
"flag": "--issue",
|
|
10
|
-
"type": "int",
|
|
11
|
-
"required": true,
|
|
12
|
-
"describe": "GitHub issue number that triggered the release."
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"name": "bump",
|
|
16
|
-
"flag": "--bump",
|
|
17
|
-
"type": "enum",
|
|
18
|
-
"values": ["patch", "minor", "major"],
|
|
19
|
-
"required": false,
|
|
20
|
-
"describe": "Version bump increment. Default patch."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"name": "dry-run",
|
|
24
|
-
"flag": "--dry-run",
|
|
25
|
-
"type": "bool",
|
|
26
|
-
"required": false,
|
|
27
|
-
"describe": "Print plan without writing files, committing, or opening a PR."
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"name": "prefer",
|
|
31
|
-
"flag": "--prefer",
|
|
32
|
-
"type": "enum",
|
|
33
|
-
"values": ["ours", "theirs"],
|
|
34
|
-
"required": false,
|
|
35
|
-
"describe": "On release/vX.Y.Z branch collision: 'ours' force-pushes; 'theirs' reuses the existing PR."
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
"claudeCode": {
|
|
39
|
-
"model": "inherit",
|
|
40
|
-
"permissionMode": "default",
|
|
41
|
-
"maxTurns": 0,
|
|
42
|
-
"maxThinkingTokens": null,
|
|
43
|
-
"systemPromptAppend": null,
|
|
44
|
-
"tools": [],
|
|
45
|
-
"hooks": [],
|
|
46
|
-
"skills": [],
|
|
47
|
-
"commands": [],
|
|
48
|
-
"subagents": [],
|
|
49
|
-
"plugins": [],
|
|
50
|
-
"mcpServers": []
|
|
51
|
-
},
|
|
52
|
-
"cliTools": [],
|
|
53
|
-
"inputArtifacts": [],
|
|
54
|
-
"outputArtifacts": [],
|
|
55
|
-
"scripts": {
|
|
56
|
-
"preflight": [
|
|
57
|
-
{
|
|
58
|
-
"script": "setLifecycleLabel",
|
|
59
|
-
"with": {
|
|
60
|
-
"label": "kody-flow:release",
|
|
61
|
-
"color": "5319e7",
|
|
62
|
-
"description": "kody flow: release"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
{ "script": "loadIssueContext" },
|
|
66
|
-
{ "script": "loadTaskState" },
|
|
67
|
-
{ "shell": "release.sh", "timeoutSec": 5400 },
|
|
68
|
-
{ "script": "skipAgent" }
|
|
69
|
-
],
|
|
70
|
-
"postflight": [
|
|
71
|
-
{ "script": "recordOutcome" },
|
|
72
|
-
{ "script": "saveTaskState" },
|
|
73
|
-
{
|
|
74
|
-
"script": "finishFlow",
|
|
75
|
-
"with": {
|
|
76
|
-
"reason": "release-completed",
|
|
77
|
-
"label": "kody:done",
|
|
78
|
-
"color": "0e8a16",
|
|
79
|
-
"description": "kody: release complete"
|
|
80
|
-
},
|
|
81
|
-
"runWhen": { "data.action.type": "RELEASE_COMPLETED" }
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"script": "finishFlow",
|
|
85
|
-
"with": {
|
|
86
|
-
"reason": "release-failed",
|
|
87
|
-
"label": "kody:failed",
|
|
88
|
-
"color": "e11d21",
|
|
89
|
-
"description": "kody: release flow failed"
|
|
90
|
-
},
|
|
91
|
-
"runWhen": { "data.action.type": "RELEASE_FAILED" }
|
|
92
|
-
}
|
|
93
|
-
]
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# release/publish.sh — function library for the publish phase.
|
|
4
|
-
#
|
|
5
|
-
# Functions:
|
|
6
|
-
# tag_and_publish <new_version> -> creates tag locally, pushes, runs publishCommand
|
|
7
|
-
# create_gh_release <tag> -> echoes release URL or empty
|
|
8
|
-
|
|
9
|
-
# shellcheck disable=SC2148
|
|
10
|
-
|
|
11
|
-
tag_and_publish() {
|
|
12
|
-
local new_version="$1"
|
|
13
|
-
local publish_cmd="${KODY_CFG_RELEASE_PUBLISHCOMMAND:-}"
|
|
14
|
-
local timeout_ms="${KODY_CFG_RELEASE_TIMEOUTMS:-600000}"
|
|
15
|
-
local timeout_s=$((timeout_ms / 1000))
|
|
16
|
-
local tag="v${new_version}"
|
|
17
|
-
|
|
18
|
-
# Idempotent tagging: if the tag already exists and points at HEAD,
|
|
19
|
-
# treat it as already-published. If it points elsewhere, fail loudly —
|
|
20
|
-
# something is inconsistent and a human should look.
|
|
21
|
-
local remote_sha local_sha head_sha
|
|
22
|
-
head_sha=$(git rev-parse HEAD)
|
|
23
|
-
if local_sha=$(git rev-parse --verify "$tag" 2>/dev/null); then
|
|
24
|
-
if [[ "$local_sha" == "$head_sha" ]]; then
|
|
25
|
-
echo " tag ${tag} already exists locally at HEAD — skipping create" >&2
|
|
26
|
-
else
|
|
27
|
-
echo "[publish] tag ${tag} exists locally at ${local_sha} but HEAD is ${head_sha}" >&2
|
|
28
|
-
return 1
|
|
29
|
-
fi
|
|
30
|
-
else
|
|
31
|
-
git tag -a "$tag" -m "Release ${tag}"
|
|
32
|
-
fi
|
|
33
|
-
# Push the tag if it isn't already on origin (or push always; gh will
|
|
34
|
-
# no-op on existing remote tag at the same sha).
|
|
35
|
-
if remote_sha=$(git ls-remote --tags origin "refs/tags/${tag}" 2>/dev/null | awk '{print $1}'); then
|
|
36
|
-
if [[ -z "$remote_sha" ]]; then
|
|
37
|
-
git push origin "$tag"
|
|
38
|
-
elif [[ "$remote_sha" != "$head_sha" ]]; then
|
|
39
|
-
echo " WARN: remote tag ${tag} points at ${remote_sha}, HEAD is ${head_sha}" >&2
|
|
40
|
-
fi
|
|
41
|
-
else
|
|
42
|
-
git push origin "$tag" || true
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
# publishCommand (optional). Failure here is recorded but does not abort —
|
|
46
|
-
# we still want the GH release entry so the tag is discoverable.
|
|
47
|
-
local publish_status="skipped"
|
|
48
|
-
if [[ -n "$publish_cmd" ]]; then
|
|
49
|
-
local cmd="${publish_cmd//\$VERSION/$new_version}"
|
|
50
|
-
echo " publish: ${cmd}" >&2
|
|
51
|
-
if timeout "${timeout_s}" bash -c "$cmd"; then
|
|
52
|
-
publish_status="ok"
|
|
53
|
-
else
|
|
54
|
-
publish_status="failed"
|
|
55
|
-
echo "[publish] publishCommand failed (continuing to create GH release)" >&2
|
|
56
|
-
fi
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
echo "$publish_status"
|
|
60
|
-
return 0
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
create_gh_release() {
|
|
64
|
-
local tag="$1"
|
|
65
|
-
local draft="${KODY_CFG_RELEASE_DRAFTRELEASE:-false}"
|
|
66
|
-
local draft_flag=""
|
|
67
|
-
[[ "$draft" == "true" ]] && draft_flag="--draft"
|
|
68
|
-
|
|
69
|
-
# Idempotent: if a release for this tag already exists, reuse it.
|
|
70
|
-
local existing_url
|
|
71
|
-
if existing_url=$(gh release view "$tag" --json url -q .url 2>/dev/null); then
|
|
72
|
-
if [[ -n "$existing_url" ]]; then
|
|
73
|
-
echo " GH release for ${tag} already exists: ${existing_url}" >&2
|
|
74
|
-
echo "$existing_url"
|
|
75
|
-
return 0
|
|
76
|
-
fi
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
local release_url=""
|
|
80
|
-
if release_url=$(gh release create "$tag" --title "$tag" --notes "Release ${tag} — automated by kody." $draft_flag 2>&1); then
|
|
81
|
-
echo "$release_url"
|
|
82
|
-
return 0
|
|
83
|
-
else
|
|
84
|
-
echo "[publish] gh release create failed: $release_url" >&2
|
|
85
|
-
echo ""
|
|
86
|
-
return 1
|
|
87
|
-
fi
|
|
88
|
-
}
|