@kody-ade/kody-engine 0.4.224 → 0.4.226

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.
Files changed (114) hide show
  1. package/README.md +2 -2
  2. package/dist/bin/kody.js +825 -832
  3. package/dist/executables/run/profile.json +1 -1
  4. package/dist/executables/types.ts +3 -1
  5. package/package.json +1 -1
  6. package/dist/duties/duty-scheduler/duty.md +0 -3
  7. package/dist/duties/duty-scheduler/profile.json +0 -6
  8. package/dist/duties/duty-tick/duty.md +0 -3
  9. package/dist/duties/duty-tick/profile.json +0 -6
  10. package/dist/duties/duty-tick-scripted/duty.md +0 -3
  11. package/dist/duties/duty-tick-scripted/profile.json +0 -6
  12. package/dist/duties/fix/duty.md +0 -10
  13. package/dist/duties/fix/profile.json +0 -6
  14. package/dist/duties/fix-ci/duty.md +0 -10
  15. package/dist/duties/fix-ci/profile.json +0 -6
  16. package/dist/duties/goal-scheduler/duty.md +0 -3
  17. package/dist/duties/goal-scheduler/profile.json +0 -6
  18. package/dist/duties/goal-tick/duty.md +0 -3
  19. package/dist/duties/goal-tick/profile.json +0 -6
  20. package/dist/duties/init/duty.md +0 -10
  21. package/dist/duties/init/profile.json +0 -6
  22. package/dist/duties/job-live-verify/duty.md +0 -3
  23. package/dist/duties/job-live-verify/profile.json +0 -6
  24. package/dist/duties/merge/duty.md +0 -10
  25. package/dist/duties/merge/profile.json +0 -6
  26. package/dist/duties/npm-publish/duty.md +0 -17
  27. package/dist/duties/npm-publish/profile.json +0 -6
  28. package/dist/duties/plan-verify/duty.md +0 -3
  29. package/dist/duties/plan-verify/profile.json +0 -6
  30. package/dist/duties/preview-build/duty.md +0 -10
  31. package/dist/duties/preview-build/profile.json +0 -6
  32. package/dist/duties/probe-skill/duty.md +0 -3
  33. package/dist/duties/probe-skill/profile.json +0 -6
  34. package/dist/duties/qa-goal/duty.md +0 -3
  35. package/dist/duties/qa-goal/profile.json +0 -6
  36. package/dist/duties/release/duty.md +0 -10
  37. package/dist/duties/release/profile.json +0 -6
  38. package/dist/duties/release-deploy/duty.md +0 -10
  39. package/dist/duties/release-deploy/profile.json +0 -6
  40. package/dist/duties/release-prepare/duty.md +0 -10
  41. package/dist/duties/release-prepare/profile.json +0 -6
  42. package/dist/duties/release-publish/duty.md +0 -10
  43. package/dist/duties/release-publish/profile.json +0 -6
  44. package/dist/duties/resolve/duty.md +0 -10
  45. package/dist/duties/resolve/profile.json +0 -6
  46. package/dist/duties/revert/duty.md +0 -10
  47. package/dist/duties/revert/profile.json +0 -6
  48. package/dist/duties/sync/duty.md +0 -10
  49. package/dist/duties/sync/profile.json +0 -6
  50. package/dist/duties/task-job-fail-once/duty.md +0 -3
  51. package/dist/duties/task-job-fail-once/profile.json +0 -6
  52. package/dist/duties/task-job-pass-a/duty.md +0 -3
  53. package/dist/duties/task-job-pass-a/profile.json +0 -6
  54. package/dist/duties/task-job-pass-b/duty.md +0 -3
  55. package/dist/duties/task-job-pass-b/profile.json +0 -6
  56. package/dist/duties/task-jobs/duty.md +0 -3
  57. package/dist/duties/task-jobs/profile.json +0 -6
  58. package/dist/duties/worker-ask/duty.md +0 -10
  59. package/dist/duties/worker-ask/profile.json +0 -6
  60. package/dist/executables/duty-scheduler/profile.json +0 -57
  61. package/dist/executables/duty-tick/profile.json +0 -78
  62. package/dist/executables/duty-tick/prompt.md +0 -77
  63. package/dist/executables/duty-tick/prompts/locked.md +0 -50
  64. package/dist/executables/duty-tick-scripted/profile.json +0 -72
  65. package/dist/executables/fix/profile.json +0 -91
  66. package/dist/executables/fix/prompt.md +0 -90
  67. package/dist/executables/fix-ci/profile.json +0 -71
  68. package/dist/executables/fix-ci/prompt.md +0 -78
  69. package/dist/executables/goal-scheduler/profile.json +0 -53
  70. package/dist/executables/goal-scheduler/scheduler.sh +0 -82
  71. package/dist/executables/goal-tick/profile.json +0 -69
  72. package/dist/executables/init/profile.json +0 -36
  73. package/dist/executables/job-live-verify/profile.json +0 -36
  74. package/dist/executables/job-live-verify/prompt.md +0 -42
  75. package/dist/executables/merge/profile.json +0 -54
  76. package/dist/executables/npm-publish/profile.json +0 -73
  77. package/dist/executables/npm-publish/publish.sh +0 -93
  78. package/dist/executables/plan-verify/profile.json +0 -38
  79. package/dist/executables/plan-verify/prompt.md +0 -72
  80. package/dist/executables/preview-build/profile.json +0 -61
  81. package/dist/executables/preview-health/profile.json +0 -48
  82. package/dist/executables/preview-health/tick.sh +0 -319
  83. package/dist/executables/probe-skill/profile.json +0 -35
  84. package/dist/executables/probe-skill/prompt.md +0 -31
  85. package/dist/executables/probe-skill/skills/probe-skill-marker/SKILL.md +0 -18
  86. package/dist/executables/qa-goal/profile.json +0 -65
  87. package/dist/executables/release/deploy.sh +0 -157
  88. package/dist/executables/release/prepare.sh +0 -353
  89. package/dist/executables/release/profile.json +0 -95
  90. package/dist/executables/release/publish.sh +0 -88
  91. package/dist/executables/release/release.sh +0 -230
  92. package/dist/executables/release/wait.sh +0 -88
  93. package/dist/executables/release-deploy/deploy.sh +0 -222
  94. package/dist/executables/release-deploy/profile.json +0 -53
  95. package/dist/executables/release-prepare/prepare.sh +0 -368
  96. package/dist/executables/release-prepare/profile.json +0 -70
  97. package/dist/executables/release-publish/profile.json +0 -53
  98. package/dist/executables/release-publish/publish.sh +0 -137
  99. package/dist/executables/resolve/apply-prefer.sh +0 -54
  100. package/dist/executables/resolve/profile.json +0 -107
  101. package/dist/executables/resolve/prompt.md +0 -51
  102. package/dist/executables/revert/profile.json +0 -82
  103. package/dist/executables/revert/revert.sh +0 -33
  104. package/dist/executables/sync/profile.json +0 -48
  105. package/dist/executables/task-job-fail-once/profile.json +0 -44
  106. package/dist/executables/task-job-fail-once/prompt.md +0 -1
  107. package/dist/executables/task-job-pass-a/profile.json +0 -43
  108. package/dist/executables/task-job-pass-a/prompt.md +0 -1
  109. package/dist/executables/task-job-pass-b/profile.json +0 -43
  110. package/dist/executables/task-job-pass-b/prompt.md +0 -1
  111. package/dist/executables/task-jobs/profile.json +0 -41
  112. package/dist/executables/task-jobs/prompt.md +0 -1
  113. package/dist/executables/worker-ask/profile.json +0 -70
  114. 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
- }