@jenga-ai/agent 1.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/LICENSE +201 -0
- package/README.md +340 -0
- package/agents/ai_engineer.md +113 -0
- package/agents/developer.md +236 -0
- package/agents/scrum-master.md +349 -0
- package/agents/scrutiny-agent.md +137 -0
- package/agents/solution-assessor.md +185 -0
- package/agents/tester.md +339 -0
- package/bin/jenga.js +70 -0
- package/hooks/copilot_session_end.sh +29 -0
- package/hooks/on_session_end.sh +238 -0
- package/hooks/prompt_router.sh +11 -0
- package/hooks/prompt_router_helper.js +52 -0
- package/hooks/session_end_helper.js +29 -0
- package/hooks/session_end_watcher.sh +24 -0
- package/lib/commands/attach.js +47 -0
- package/lib/commands/init.js +207 -0
- package/lib/commands/start.js +16 -0
- package/lib/commands/status.js +53 -0
- package/lib/config-schema.js +72 -0
- package/lib/inject-settings.js +61 -0
- package/lib/mirror.js +244 -0
- package/lib/resolve-project-dir.sh +47 -0
- package/mcp/execute-ticket/index.js +10 -0
- package/mcp/execute-ticket/package.json +5 -0
- package/mcp/help/index.js +79 -0
- package/mcp/help/package.json +14 -0
- package/mcp/router/README.md +19 -0
- package/mcp/router/embedder.js +23 -0
- package/mcp/router/index.js +204 -0
- package/mcp/router/matcher.js +87 -0
- package/mcp/router/package-lock.json +1048 -0
- package/mcp/router/package.json +11 -0
- package/mcp/router/skill-index.js +104 -0
- package/package.json +47 -0
- package/scripts/board_resolver.sh +46 -0
- package/scripts/e25_s01_extract_board_graph.py +292 -0
- package/scripts/e25_s01_generate_synthetic_board.py +90 -0
- package/scripts/measurement-10x.json +50 -0
- package/scripts/measurement-10x.txt +4 -0
- package/scripts/measurement-real.json +50 -0
- package/scripts/measurement-real.txt +4 -0
- package/scripts/postinstall.js +165 -0
- package/scripts/todo_cleanup.sh +22 -0
- package/scripts/todo_manager.sh +86 -0
- package/scripts/validate-board.sh +190 -0
- package/scripts/validate-story-format.sh +53 -0
- package/skills/brainstorm/SKILL.md +47 -0
- package/skills/btw/SKILL.md +42 -0
- package/skills/commit/SKILL.md +29 -0
- package/skills/commit/assets/user_instructions_template.md +22 -0
- package/skills/continue/SKILL.md +29 -0
- package/skills/convert/SKILL.md +124 -0
- package/skills/convert/convert_cli.py +235 -0
- package/skills/convert/tests/sample.csv +4 -0
- package/skills/convert/tests/sample.json +5 -0
- package/skills/convert/tests/sample.jsonl +3 -0
- package/skills/convert/tests/sample.yaml +18 -0
- package/skills/convert/tests/sample_obj.csv +2 -0
- package/skills/convert/tests/sample_obj.json +9 -0
- package/skills/deep-dive/SKILL.md +167 -0
- package/skills/do/SKILL.md +88 -0
- package/skills/do/assets/sender_template.json +12 -0
- package/skills/doc/SKILL.md +314 -0
- package/skills/doc/assets/path-objectives.yaml +38 -0
- package/skills/doc-sync/SKILL.md +167 -0
- package/skills/doc-sync/assets/default_excludes.txt +21 -0
- package/skills/doc-sync/assets/doc_targets.md +14 -0
- package/skills/dooo/SKILL.md +60 -0
- package/skills/error/SKILL.md +29 -0
- package/skills/evaluate/SKILL.md +45 -0
- package/skills/evaluate/assets/evaluation_invokation_template.yml +3 -0
- package/skills/evaluate/assets/evaluation_rapport_template.md +24 -0
- package/skills/examplify/SKILL.md +42 -0
- package/skills/help/SKILL.md +36 -0
- package/skills/improve/SKILL.md +55 -0
- package/skills/index/scripts/board-index +4 -0
- package/skills/index/scripts/board_index.py +615 -0
- package/skills/index/scripts/smoke_test.sh +86 -0
- package/skills/init/SKILL.md +44 -0
- package/skills/init/assets/.gitignore_template +15 -0
- package/skills/init/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/init/assets/directory_structure.txt +13 -0
- package/skills/init/assets/test-config_template.json +4 -0
- package/skills/init/assets/workflow_template.json +30 -0
- package/skills/init/scripts/init.sh +48 -0
- package/skills/jbp/SKILL.md +25 -0
- package/skills/jenga/SKILL.md +68 -0
- package/skills/lgtm/SKILL.md +21 -0
- package/skills/mirror-public/SKILL.md +237 -0
- package/skills/mirror-public/assets/config.json +5 -0
- package/skills/mirror-public/scripts/mirror.sh +374 -0
- package/skills/pi-plan/SKILL.md +62 -0
- package/skills/pi-plan/assets/epic.json +7 -0
- package/skills/pi-plan/assets/story_template.md +18 -0
- package/skills/proceed/SKILL.md +29 -0
- package/skills/publish/SKILL.md +351 -0
- package/skills/publish/adapters/droplet.md +200 -0
- package/skills/publish/adapters/mobile-ios.md +114 -0
- package/skills/publish/adapters/npm-ci.md +223 -0
- package/skills/publish/adapters/npm.md +121 -0
- package/skills/publish/assets/ExportOptions.plist.template +19 -0
- package/skills/publish/assets/ci-contract.md +111 -0
- package/skills/publish/assets/ownership-matrix.md +17 -0
- package/skills/publish/assets/publish.example.json +85 -0
- package/skills/publish/assets/publish.example.npm-ci.json +40 -0
- package/skills/publish/assets/publish.example.npm.json +41 -0
- package/skills/publish/assets/secrets-guide.md +104 -0
- package/skills/publish/schemas/fixtures/npm-ci-minimal.json +17 -0
- package/skills/publish/schemas/fixtures/npm-ci-with-empty-secrets.json +18 -0
- package/skills/publish/schemas/fixtures/npm-ci-with-workflow-path.json +18 -0
- package/skills/publish/schemas/publish.schema.json +428 -0
- package/skills/publish/scripts/check_target_config.sh +96 -0
- package/skills/publish/scripts/droplet_pipeline.sh +208 -0
- package/skills/publish/scripts/generate_release_notes.sh +200 -0
- package/skills/publish/scripts/ios_pipeline.sh +486 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +225 -0
- package/skills/publish/scripts/npm_pipeline.sh +249 -0
- package/skills/publish/scripts/publish_common.sh +253 -0
- package/skills/publish/scripts/publish_deploy.sh +538 -0
- package/skills/publish/scripts/reconcile_tags.sh +135 -0
- package/skills/publish/scripts/run_gates.sh +616 -0
- package/skills/publish/scripts/setup_wizard.sh +394 -0
- package/skills/publish/scripts/show_history.sh +95 -0
- package/skills/publish/scripts/suggest_semver_bump.sh +105 -0
- package/skills/publish/scripts/validate_config.sh +163 -0
- package/skills/publish/scripts/validate_droplet_env.sh +45 -0
- package/skills/publish/scripts/validate_ios_env.sh +68 -0
- package/skills/publish/scripts/validate_npm_ci_env.sh +71 -0
- package/skills/publish/scripts/validate_npm_env.sh +22 -0
- package/skills/publish/scripts/write_ledger_entry.sh +126 -0
- package/skills/publish/wizards/droplet.md +275 -0
- package/skills/publish/wizards/mobile-ios.md +157 -0
- package/skills/publish/wizards/npm-ci.md +240 -0
- package/skills/publish/wizards/npm.md +224 -0
- package/skills/reconcile/SKILL.md +93 -0
- package/skills/reconcile/assets/report_format.md +44 -0
- package/skills/reconcile-origin/SKILL.md +75 -0
- package/skills/reconcile-origin/scripts/reconcile-origin.sh +372 -0
- package/skills/redo/SKILL.md +70 -0
- package/skills/route/SKILL.md +180 -0
- package/skills/self-sync/SKILL.md +73 -0
- package/skills/self-sync/scripts/run.js +136 -0
- package/skills/skillify/SKILL.md +68 -0
- package/skills/skillify/assets/init-new/SKILL.md +35 -0
- package/skills/skillify/assets/init-new/assets/.gitignore_template +15 -0
- package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/skillify/assets/init-new/assets/directory_structure.txt +10 -0
- package/skills/skillify/assets/init-new/assets/test-config_template.json +4 -0
- package/skills/skillify/assets/init-new/assets/workflow_template.json +17 -0
- package/skills/skillify/assets/init-new/scripts/init.sh +48 -0
- package/skills/skillify/assets/init-old/SKILL.md +124 -0
- package/skills/spinoff/SKILL.md +48 -0
- package/skills/status/SKILL.md +33 -0
- package/skills/status/assets/output_format.md +41 -0
- package/skills/todo/SKILL.md +46 -0
- package/skills/todo/assets/todo_handoff_template.md +22 -0
- package/skills/todo/assets/todo_template.md +3 -0
- package/skills/train/SKILL.md +116 -0
- package/skills/train/assets/dashboard-templates/classifiers.html +106 -0
- package/skills/train/assets/dashboard-templates/nlp.html +102 -0
- package/skills/train/assets/dashboard-templates/transformers.html +98 -0
- package/skills/train/assets/results-parsers/__init__.py +9 -0
- package/skills/train/assets/results-parsers/classifiers.py +84 -0
- package/skills/train/assets/results-parsers/nlp.py +88 -0
- package/skills/train/assets/results-parsers/reporter.py +154 -0
- package/skills/train/assets/results-parsers/transformers.py +120 -0
- package/skills/train/train_cli.py +786 -0
- package/templates/EXECUTION_PLAN_TEMPLATE.md +43 -0
- package/templates/EXECUTION_SUMMARY_TEMPLATE.md +50 -0
- package/templates/JENGA_CONFIG_TEMPLATE.json +23 -0
- package/templates/PROBLEM_RAPPORT_TEMPLATE.md +88 -0
- package/templates/SCRUM_BOARD_SCHEMA.md +311 -0
- package/templates/SKILL.md +16 -0
- package/templates/SKILL_TEMPLATE.md +28 -0
- package/templates/USER_INSTRUCTIONS_TEMPLATE.md +22 -0
- package/templates/copilot-instructions.md.tpl +55 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# droplet_pipeline.sh — Generate (or update) .github/workflows/publish-droplet.yml
|
|
3
|
+
# and commit it. With --dry-run, print the YAML to stdout only.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# droplet_pipeline.sh --target <name> [--config <path>] [--dry-run] [--non-interactive]
|
|
7
|
+
#
|
|
8
|
+
# Exit codes:
|
|
9
|
+
# 0 success
|
|
10
|
+
# 2 pre-flight failure (bad args, missing tools)
|
|
11
|
+
# 4 validation failure (missing required config fields)
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
EXIT_PRE_FLIGHT=2
|
|
16
|
+
EXIT_ENV_INVALID=4
|
|
17
|
+
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# Resolve repo root
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
if REPO_ROOT="$(git -C "${SCRIPT_DIR}" rev-parse --show-toplevel 2>/dev/null)"; then
|
|
24
|
+
:
|
|
25
|
+
else
|
|
26
|
+
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Source shared helpers
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# shellcheck source=publish_common.sh
|
|
33
|
+
source "${SCRIPT_DIR}/publish_common.sh"
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Arg parsing
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
TARGET_NAME=""
|
|
39
|
+
CONFIG_PATH=""
|
|
40
|
+
DRY_RUN=0
|
|
41
|
+
NON_INTERACTIVE=0
|
|
42
|
+
|
|
43
|
+
usage() {
|
|
44
|
+
cat <<'USAGE'
|
|
45
|
+
Usage: droplet_pipeline.sh --target <name> [--config <path>] [--dry-run] [--non-interactive]
|
|
46
|
+
USAGE
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fail_pre() {
|
|
50
|
+
printf 'droplet pipeline pre-flight failure: %s\n' "$1" >&2
|
|
51
|
+
exit "${EXIT_PRE_FLIGHT}"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
while [[ $# -gt 0 ]]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--target)
|
|
57
|
+
[[ $# -ge 2 ]] || fail_pre "--target requires a value"
|
|
58
|
+
TARGET_NAME="$2"
|
|
59
|
+
shift 2
|
|
60
|
+
;;
|
|
61
|
+
--target=*)
|
|
62
|
+
TARGET_NAME="${1#*=}"
|
|
63
|
+
shift
|
|
64
|
+
;;
|
|
65
|
+
--config)
|
|
66
|
+
[[ $# -ge 2 ]] || fail_pre "--config requires a value"
|
|
67
|
+
CONFIG_PATH="$2"
|
|
68
|
+
shift 2
|
|
69
|
+
;;
|
|
70
|
+
--config=*)
|
|
71
|
+
CONFIG_PATH="${1#*=}"
|
|
72
|
+
shift
|
|
73
|
+
;;
|
|
74
|
+
--dry-run)
|
|
75
|
+
DRY_RUN=1
|
|
76
|
+
shift
|
|
77
|
+
;;
|
|
78
|
+
--non-interactive)
|
|
79
|
+
NON_INTERACTIVE=1
|
|
80
|
+
shift
|
|
81
|
+
;;
|
|
82
|
+
-h|--help)
|
|
83
|
+
usage
|
|
84
|
+
exit 0
|
|
85
|
+
;;
|
|
86
|
+
*)
|
|
87
|
+
usage >&2
|
|
88
|
+
fail_pre "unknown option: $1"
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
# Silence unused-variable warning; flag is reserved for CI callers.
|
|
94
|
+
: "${NON_INTERACTIVE:=0}"
|
|
95
|
+
|
|
96
|
+
[[ -n "${TARGET_NAME}" ]] || fail_pre "--target <name> is required"
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# Resolve config path
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
if [[ -z "${CONFIG_PATH}" ]]; then
|
|
102
|
+
if [[ -f "${REPO_ROOT}/publish.json" ]]; then
|
|
103
|
+
CONFIG_PATH="${REPO_ROOT}/publish.json"
|
|
104
|
+
elif [[ -f "${REPO_ROOT}/project/configs/publish.json" ]]; then
|
|
105
|
+
CONFIG_PATH="${REPO_ROOT}/project/configs/publish.json"
|
|
106
|
+
else
|
|
107
|
+
fail_pre "publish.json not found at '${REPO_ROOT}/publish.json' or '${REPO_ROOT}/project/configs/publish.json'"
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
[[ -f "${CONFIG_PATH}" ]] || fail_pre "publish config not found at '${CONFIG_PATH}'"
|
|
112
|
+
command -v jq >/dev/null 2>&1 || fail_pre "jq is required but not found on PATH"
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
# Validate
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
if ! bash "${SCRIPT_DIR}/validate_droplet_env.sh" "${CONFIG_PATH}" "${TARGET_NAME}"; then
|
|
118
|
+
exit "${EXIT_ENV_INVALID}"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# Extract config values
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
TARGET_JSON="$(jq -c --arg t "${TARGET_NAME}" \
|
|
125
|
+
'.targets[]? | select(.name == $t and .type == "droplet")' \
|
|
126
|
+
"${CONFIG_PATH}")"
|
|
127
|
+
|
|
128
|
+
DEPLOY_PATH="$(printf '%s' "${TARGET_JSON}" | jq -r '.droplet.deploy_path')"
|
|
129
|
+
DEPLOY_BRANCH="$(printf '%s' "${TARGET_JSON}" | jq -r '.droplet.deploy_branch')"
|
|
130
|
+
BUILD_CMD="$(printf '%s' "${TARGET_JSON}" | jq -r '.droplet.build_cmd // empty')"
|
|
131
|
+
START_CMD="$(printf '%s' "${TARGET_JSON}" | jq -r '.droplet.start_cmd')"
|
|
132
|
+
GITHUB_REPO="$(printf '%s' "${TARGET_JSON}" | jq -r '.droplet.github_repo')"
|
|
133
|
+
SSH_KEY_SECRET="$(printf '%s' "${TARGET_JSON}" | jq -r '.secrets.ssh_key_secret')"
|
|
134
|
+
KNOWN_HOSTS_SECRET="$(printf '%s' "${TARGET_JSON}" | jq -r '.secrets.known_hosts_secret')"
|
|
135
|
+
|
|
136
|
+
# Conventional GH Actions secret names for host/user/port
|
|
137
|
+
SSH_HOST_SECRET="DROPLET_SSH_HOST"
|
|
138
|
+
SSH_USER_SECRET="DROPLET_SSH_USER"
|
|
139
|
+
SSH_PORT_SECRET="DROPLET_SSH_PORT"
|
|
140
|
+
|
|
141
|
+
# Keep GITHUB_REPO available for callers / future gh-trigger use.
|
|
142
|
+
: "${GITHUB_REPO}"
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
# Generate workflow YAML
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
WORKFLOW_HEADER="$(cat <<EOF
|
|
148
|
+
# Generated by /publish droplet pipeline -- do not edit manually
|
|
149
|
+
name: "Publish -- ${TARGET_NAME} (Droplet)"
|
|
150
|
+
on:
|
|
151
|
+
workflow_dispatch:
|
|
152
|
+
|
|
153
|
+
jobs:
|
|
154
|
+
deploy:
|
|
155
|
+
runs-on: ubuntu-latest
|
|
156
|
+
steps:
|
|
157
|
+
- name: Deploy via SSH
|
|
158
|
+
# appleboy/ssh-action pinned SHA: 4a13f365c4cb2c5a7b0a2e0a54db7a4a7b5f6fd1
|
|
159
|
+
uses: appleboy/ssh-action@4a13f365c4cb2c5a7b0a2e0a54db7a4a7b5f6fd1
|
|
160
|
+
with:
|
|
161
|
+
host: \${{ secrets.${SSH_HOST_SECRET} }}
|
|
162
|
+
username: \${{ secrets.${SSH_USER_SECRET} }}
|
|
163
|
+
port: \${{ secrets.${SSH_PORT_SECRET} }}
|
|
164
|
+
key: \${{ secrets.${SSH_KEY_SECRET} }}
|
|
165
|
+
known_hosts: \${{ secrets.${KNOWN_HOSTS_SECRET} }}
|
|
166
|
+
script: |
|
|
167
|
+
cd ${DEPLOY_PATH}
|
|
168
|
+
git pull origin ${DEPLOY_BRANCH}
|
|
169
|
+
EOF
|
|
170
|
+
)"
|
|
171
|
+
|
|
172
|
+
# Build the script block lines separately so newlines are always present
|
|
173
|
+
SCRIPT_LINES=""
|
|
174
|
+
if [[ -n "${BUILD_CMD}" ]]; then
|
|
175
|
+
SCRIPT_LINES+=" ${BUILD_CMD}"$'\n'
|
|
176
|
+
fi
|
|
177
|
+
SCRIPT_LINES+=" ${START_CMD}"$'\n'
|
|
178
|
+
|
|
179
|
+
WORKFLOW_YAML="${WORKFLOW_HEADER}"$'\n'"${SCRIPT_LINES}"
|
|
180
|
+
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
# Dry-run: print and exit
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
if (( DRY_RUN )); then
|
|
185
|
+
printf '%s' "${WORKFLOW_YAML}"
|
|
186
|
+
exit 0
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------------------
|
|
190
|
+
# Write workflow file
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
WORKFLOW_DIR="${REPO_ROOT}/.github/workflows"
|
|
193
|
+
WORKFLOW_FILE="${WORKFLOW_DIR}/publish-droplet.yml"
|
|
194
|
+
|
|
195
|
+
mkdir -p "${WORKFLOW_DIR}"
|
|
196
|
+
printf '%s' "${WORKFLOW_YAML}" > "${WORKFLOW_FILE}"
|
|
197
|
+
|
|
198
|
+
# Commit only when the file actually changed
|
|
199
|
+
if git -C "${REPO_ROOT}" diff --quiet -- ".github/workflows/publish-droplet.yml" 2>/dev/null && \
|
|
200
|
+
git -C "${REPO_ROOT}" diff --cached --quiet -- ".github/workflows/publish-droplet.yml" 2>/dev/null && \
|
|
201
|
+
git -C "${REPO_ROOT}" ls-files --error-unmatch ".github/workflows/publish-droplet.yml" >/dev/null 2>&1; then
|
|
202
|
+
printf 'Workflow unchanged, skipping commit.\n'
|
|
203
|
+
exit 0
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
git -C "${REPO_ROOT}" add ".github/workflows/publish-droplet.yml"
|
|
207
|
+
git -C "${REPO_ROOT}" commit -m "chore(publish): update droplet workflow for target ${TARGET_NAME}"
|
|
208
|
+
printf 'Workflow committed: %s\n' "${WORKFLOW_FILE}"
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
source "$SCRIPT_DIR/publish_common.sh"
|
|
6
|
+
|
|
7
|
+
usage() {
|
|
8
|
+
cat <<'USAGE'
|
|
9
|
+
Usage: generate_release_notes.sh [--target <name>] [--from-tag <tag>] [--to-ref <git-ref>] [--output <path>] [<publish_json_path>]
|
|
10
|
+
USAGE
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
TARGET_NAME=""
|
|
14
|
+
FROM_TAG=""
|
|
15
|
+
TO_REF="HEAD"
|
|
16
|
+
OUTPUT_PATH=""
|
|
17
|
+
CONFIG_PATH=""
|
|
18
|
+
POSITIONAL_COUNT=0
|
|
19
|
+
|
|
20
|
+
while [[ $# -gt 0 ]]; do
|
|
21
|
+
case "$1" in
|
|
22
|
+
--target)
|
|
23
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
24
|
+
TARGET_NAME="$2"
|
|
25
|
+
shift 2
|
|
26
|
+
;;
|
|
27
|
+
--from-tag)
|
|
28
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
29
|
+
FROM_TAG="$2"
|
|
30
|
+
shift 2
|
|
31
|
+
;;
|
|
32
|
+
--to-ref)
|
|
33
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
34
|
+
TO_REF="$2"
|
|
35
|
+
shift 2
|
|
36
|
+
;;
|
|
37
|
+
--output)
|
|
38
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
39
|
+
OUTPUT_PATH="$2"
|
|
40
|
+
shift 2
|
|
41
|
+
;;
|
|
42
|
+
--help|-h)
|
|
43
|
+
usage
|
|
44
|
+
exit 0
|
|
45
|
+
;;
|
|
46
|
+
--*)
|
|
47
|
+
usage >&2
|
|
48
|
+
exit 1
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
POSITIONAL_COUNT=$((POSITIONAL_COUNT + 1))
|
|
52
|
+
if (( POSITIONAL_COUNT > 1 )); then
|
|
53
|
+
usage >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
CONFIG_PATH="$1"
|
|
57
|
+
shift
|
|
58
|
+
;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
command -v jq >/dev/null 2>&1 || { echo 'jq is required.' >&2; exit 1; }
|
|
63
|
+
|
|
64
|
+
git rev-parse --verify "$TO_REF" >/dev/null 2>&1 || {
|
|
65
|
+
echo "Unknown git ref: $TO_REF" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
HISTORY_FILE="$(publish_resolve_history_file "$CONFIG_PATH")"
|
|
70
|
+
LAST_TAG="$FROM_TAG"
|
|
71
|
+
FIRST_RELEASE=0
|
|
72
|
+
CUTOFF_DATE=""
|
|
73
|
+
|
|
74
|
+
if [[ -z "$LAST_TAG" ]]; then
|
|
75
|
+
if LAST_TAG="$(publish_resolve_last_publish_tag "$HISTORY_FILE" "$TO_REF" 2>/dev/null || true)" && [[ -n "$LAST_TAG" ]]; then
|
|
76
|
+
:
|
|
77
|
+
else
|
|
78
|
+
LAST_TAG=""
|
|
79
|
+
FIRST_RELEASE=1
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [[ -n "$LAST_TAG" ]]; then
|
|
84
|
+
git rev-parse --verify "$LAST_TAG" >/dev/null 2>&1 || {
|
|
85
|
+
echo "Unknown git tag: $LAST_TAG" >&2
|
|
86
|
+
exit 1
|
|
87
|
+
}
|
|
88
|
+
CUTOFF_DATE="$(git log -1 --format=%cI "$LAST_TAG" 2>/dev/null | cut -dT -f1)"
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
FEATURES=()
|
|
92
|
+
BUG_FIXES=()
|
|
93
|
+
OTHER=()
|
|
94
|
+
|
|
95
|
+
if [[ -n "$LAST_TAG" ]]; then
|
|
96
|
+
LOG_RANGE=("$LAST_TAG..$TO_REF")
|
|
97
|
+
else
|
|
98
|
+
LOG_RANGE=("$TO_REF")
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
while IFS=$'\t' read -r subject short_sha || [[ -n "${subject:-}${short_sha:-}" ]]; do
|
|
102
|
+
[[ -z "${subject:-}" ]] && continue
|
|
103
|
+
line="- $subject ($short_sha)"
|
|
104
|
+
if printf '%s\n' "$subject" | grep -Eq '^feat(\([^)]+\))?!?: '; then
|
|
105
|
+
FEATURES+=("$line")
|
|
106
|
+
elif printf '%s\n' "$subject" | grep -Eq '^fix(\([^)]+\))?!?: '; then
|
|
107
|
+
BUG_FIXES+=("$line")
|
|
108
|
+
else
|
|
109
|
+
OTHER+=("$line")
|
|
110
|
+
fi
|
|
111
|
+
done < <(git log "${LOG_RANGE[@]}" --pretty=format:'%s%x09%h')
|
|
112
|
+
|
|
113
|
+
collect_completed_tasks() {
|
|
114
|
+
local cutoff_date="$1"
|
|
115
|
+
local tasks_dir="$PUBLISH_REPO_ROOT/project/board/tasks"
|
|
116
|
+
local collected=()
|
|
117
|
+
local task_file status completed_date task_id task_title
|
|
118
|
+
|
|
119
|
+
[[ -d "$tasks_dir" ]] || return 0
|
|
120
|
+
|
|
121
|
+
while IFS= read -r -d '' task_file; do
|
|
122
|
+
case "$task_file" in
|
|
123
|
+
*_INSTRUCTIONS.md) continue ;;
|
|
124
|
+
esac
|
|
125
|
+
|
|
126
|
+
status="$(awk '/^status: /{sub(/^status: /, ""); print; exit}' "$task_file" 2>/dev/null || true)"
|
|
127
|
+
[[ "$status" == "Passed" ]] || continue
|
|
128
|
+
|
|
129
|
+
completed_date="$(awk '/^date_completed: /{sub(/^date_completed: /, ""); print; exit}' "$task_file" 2>/dev/null || true)"
|
|
130
|
+
[[ -n "$completed_date" ]] || continue
|
|
131
|
+
|
|
132
|
+
if [[ -n "$cutoff_date" && "$completed_date" < "$cutoff_date" ]]; then
|
|
133
|
+
continue
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
task_id="$(awk '/^id: /{sub(/^id: /, ""); print; exit}' "$task_file" 2>/dev/null || true)"
|
|
137
|
+
task_title="$(awk '/^title: /{sub(/^title: /, ""); print; exit}' "$task_file" 2>/dev/null || true)"
|
|
138
|
+
[[ -n "$task_id" ]] || continue
|
|
139
|
+
[[ -n "$task_title" ]] || task_title="$task_id"
|
|
140
|
+
collected+=("- $task_id — $task_title")
|
|
141
|
+
done < <(find "$tasks_dir" -maxdepth 1 -type f -name '*.md' -print0 2>/dev/null)
|
|
142
|
+
|
|
143
|
+
if (( ${#collected[@]} > 0 )); then
|
|
144
|
+
printf '%s\n' "${collected[@]}" | LC_ALL=C sort -u
|
|
145
|
+
fi
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if [[ -z "$OUTPUT_PATH" ]]; then
|
|
149
|
+
OUTPUT_PATH="$(publish_temp_release_notes_path)"
|
|
150
|
+
fi
|
|
151
|
+
mkdir -p "$(dirname "$OUTPUT_PATH")"
|
|
152
|
+
|
|
153
|
+
write_section() {
|
|
154
|
+
local heading="$1"
|
|
155
|
+
local array_name="$2"
|
|
156
|
+
local item_count
|
|
157
|
+
|
|
158
|
+
eval "item_count=\${#${array_name}[@]}"
|
|
159
|
+
{
|
|
160
|
+
printf '### %s\n' "$heading"
|
|
161
|
+
if (( item_count > 0 )); then
|
|
162
|
+
eval "printf '%s\\n' \"\${${array_name}[@]}\""
|
|
163
|
+
else
|
|
164
|
+
printf -- '- None\n'
|
|
165
|
+
fi
|
|
166
|
+
printf '\n'
|
|
167
|
+
} >> "$OUTPUT_PATH"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
GENERATED_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
171
|
+
{
|
|
172
|
+
printf '# Release notes\n\n'
|
|
173
|
+
printf -- '- Generated: %s\n' "$GENERATED_AT"
|
|
174
|
+
if [[ -n "$TARGET_NAME" ]]; then
|
|
175
|
+
printf -- '- Target: %s\n' "$TARGET_NAME"
|
|
176
|
+
fi
|
|
177
|
+
if [[ -n "$LAST_TAG" ]]; then
|
|
178
|
+
printf -- '- Changes: %s..%s\n\n' "$LAST_TAG" "$TO_REF"
|
|
179
|
+
else
|
|
180
|
+
printf -- '- Changes: full history through %s\n\n' "$TO_REF"
|
|
181
|
+
if (( FIRST_RELEASE )); then
|
|
182
|
+
printf '> First release — full history included\n\n'
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
} > "$OUTPUT_PATH"
|
|
186
|
+
|
|
187
|
+
write_section 'Features' FEATURES
|
|
188
|
+
write_section 'Bug Fixes' BUG_FIXES
|
|
189
|
+
write_section 'Other' OTHER
|
|
190
|
+
|
|
191
|
+
COMPLETED_TASKS="$(collect_completed_tasks "$CUTOFF_DATE" || true)"
|
|
192
|
+
if [[ -n "$COMPLETED_TASKS" ]]; then
|
|
193
|
+
{
|
|
194
|
+
printf '### Completed tasks\n'
|
|
195
|
+
printf '%s\n' "$COMPLETED_TASKS"
|
|
196
|
+
printf '\n'
|
|
197
|
+
} >> "$OUTPUT_PATH"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
printf '📝 Release notes draft written to %s\n' "$OUTPUT_PATH"
|