forgedock 1.2.0 → 1.4.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/README.md +8 -2
- package/bin/batch-runner.mjs +31 -2
- package/bin/engine/admission.mjs +294 -0
- package/bin/engine/admission.test.mjs +265 -0
- package/bin/engine/invariants.mjs +17 -4
- package/bin/engine/phases.mjs +358 -42
- package/bin/engine/resolve.mjs +256 -0
- package/bin/engine/resolve.test.mjs +149 -0
- package/bin/engine/state.mjs +31 -7
- package/bin/engine-cli.mjs +138 -2
- package/bin/engine.mjs +414 -21
- package/bin/forgedock.mjs +552 -44
- package/bin/hooks/interactive-engine.mjs +50 -24
- package/bin/hooks/pre-tool-use.mjs +189 -19
- package/bin/journey.mjs +109 -11
- package/bin/labels.json +5 -0
- package/bin/runner.mjs +321 -10
- package/bin/watch-utils.mjs +255 -0
- package/commands/cleanup.md +7 -3
- package/commands/forge-stats.md +5 -4
- package/commands/issue.md +5 -1
- package/commands/orchestrate/config.md +41 -1
- package/commands/orchestrate/phase-1-resolve.md +234 -8
- package/commands/orchestrate/phase-3-dependency.md +27 -18
- package/commands/orchestrate/phase-4-execution.md +223 -22
- package/commands/qa-sweep.md +17 -0
- package/commands/quality-gate.md +6 -6
- package/commands/replay.md +16 -2
- package/commands/review-pr-agents/spec-cli.md +179 -0
- package/commands/review-pr.md +65 -0
- package/commands/upgrade-deps.md +523 -0
- package/commands/work-on/build/validate.md +61 -1
- package/commands/work-on/build.md +9 -1
- package/commands/work-on/decompose.md +10 -1
- package/commands/work-on/investigate.md +22 -4
- package/commands/work-on/remediate.md +2 -0
- package/commands/work-on/review.md +40 -1
- package/commands/work-on.md +19 -4
- package/docs/AIDER.md +264 -0
- package/docs/AUTOPILOT-CRON.md +130 -0
- package/docs/CONFIG.md +156 -0
- package/docs/CURSOR.md +135 -0
- package/docs/OPENCODE.md +312 -0
- package/docs/demo.gif +0 -0
- package/docs/eval/README.md +4 -4
- package/docs/launch/show-hn.md +3 -3
- package/docs/site/architecture.md +180 -0
- package/docs/site/command-reference.md +17 -0
- package/docs/site/configuration.md +243 -0
- package/docs/site/for-companies.md +4 -4
- package/docs/site/forge-annotation-protocol.md +1 -1
- package/docs/site/github-is-the-memory.md +2 -2
- package/docs/site/how-it-works.md +1 -1
- package/docs/site/public/llms-full.txt +380 -0
- package/docs/site/public/llms.txt +35 -0
- package/docs/spec/eval-run-result.md +8 -4
- package/docs/spec/label-state-machine.md +9 -0
- package/docs/spec-graph-schema.md +3 -3
- package/package.json +3 -3
- package/packages/protocol/README.md +75 -0
- package/packages/protocol/src/card.js +114 -0
- package/packages/protocol/src/cli.js +4 -77
- package/packages/protocol/src/emit.js +5 -11
- package/packages/protocol/src/html-comment-escape.js +68 -0
- package/packages/protocol/src/index.js +3 -1
- package/packages/protocol/src/parse.js +19 -11
- package/packages/protocol/src/phases.js +105 -0
- package/packages/protocol/src/types.js +18 -1
- package/packages/protocol/src/validate.js +11 -0
- package/scripts/check-admission-parity.mjs +167 -0
- package/scripts/check-command-side-effects.sh +376 -0
- package/scripts/check-phase-registry-drift.mjs +96 -0
- package/scripts/check-spec-markers.sh +274 -0
- package/scripts/classify-lane.sh +29 -7
- package/scripts/forge-run.sh +353 -0
- package/templates/cross-repo-trigger.yml +282 -0
- package/templates/cursor/forge.mdc +148 -0
- package/templates/github-actions/forgedock-autopilot.yml +113 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# check-spec-markers.sh — Validate FORGE: annotation markers in commands/**/*.md and
|
|
6
|
+
# detect unsubstituted {PLACEHOLDER} tokens in CI workflow files.
|
|
7
|
+
#
|
|
8
|
+
# Command specs contain two classes of spec-as-code marker defects:
|
|
9
|
+
#
|
|
10
|
+
# 1. Non-registry FORGE: markers — a typo or invented marker (e.g. FORGE:INVESTIAGOR,
|
|
11
|
+
# FORGE:NOTES) in a command spec will cause agents to look for a comment annotation
|
|
12
|
+
# that is never emitted, silently breaking pipeline state detection. (Ref: forge#633)
|
|
13
|
+
#
|
|
14
|
+
# 2. Unsubstituted {PLACEHOLDER} tokens in CI workflow YAML — a template variable like
|
|
15
|
+
# {GH_REPO} or {NUMBER} left unsubstituted in a workflow inline script means every
|
|
16
|
+
# CI run executes a malformed command that fails silently or produces wrong output.
|
|
17
|
+
# (Ref: forge#318; note: {PLACEHOLDER} tokens in commands/*.md are intentional
|
|
18
|
+
# spec notation and are NOT flagged by this check.)
|
|
19
|
+
#
|
|
20
|
+
# This script:
|
|
21
|
+
# 1. Scans all *.md files in commands/ for <!-- FORGE:XXXX --> annotations.
|
|
22
|
+
# 2. Validates XXXX against the known registry (see MARKER_REGISTRY below).
|
|
23
|
+
# 3. Scans .github/workflows/*.yml for unsubstituted {PLACEHOLDER} tokens in run: blocks.
|
|
24
|
+
#
|
|
25
|
+
# Registry: covers all reserved types from packages/protocol/src/types.js
|
|
26
|
+
# (RESERVED_TYPE_NAMES) plus all operational/pipeline markers used throughout the
|
|
27
|
+
# ForgeDock spec corpus. Compound markers (FORGE:TYPE-SUBTYPE, FORGE:TYPE:SUBTYPE)
|
|
28
|
+
# are validated by their primary type (the part before the first - or :).
|
|
29
|
+
# Update the registry when adding new marker types.
|
|
30
|
+
#
|
|
31
|
+
# Allowlist: add <!-- allowlist:check-spec-markers --> on the same line as the marker
|
|
32
|
+
# to suppress that specific hit.
|
|
33
|
+
#
|
|
34
|
+
# Usage:
|
|
35
|
+
# check-spec-markers.sh [<commands_dir> [<repo_root>]]
|
|
36
|
+
# commands_dir: path to the commands/ directory (default: ./commands)
|
|
37
|
+
# repo_root: path to repo root for workflow scanning (default: .)
|
|
38
|
+
#
|
|
39
|
+
# Exit codes:
|
|
40
|
+
# 0 no violations found
|
|
41
|
+
# 1 one or more violations found (listed to stderr)
|
|
42
|
+
# 2 usage / dependency error (commands_dir not found)
|
|
43
|
+
#
|
|
44
|
+
# <!-- Added: forge#1609 -->
|
|
45
|
+
|
|
46
|
+
set -euo pipefail
|
|
47
|
+
|
|
48
|
+
COMMANDS_DIR="${1:-./commands}"
|
|
49
|
+
REPO_ROOT="${2:-.}"
|
|
50
|
+
|
|
51
|
+
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
|
52
|
+
sed -n '2,/^set -/p' "$0" | grep '^#' | sed 's/^# *//'
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if [ ! -d "$COMMANDS_DIR" ]; then
|
|
57
|
+
echo "ERROR: commands directory not found: $COMMANDS_DIR" >&2
|
|
58
|
+
echo "Usage: $0 [<commands_dir> [<repo_root>]]" >&2
|
|
59
|
+
exit 2
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# FORGE: marker registry
|
|
64
|
+
#
|
|
65
|
+
# Sources:
|
|
66
|
+
# - packages/protocol/src/types.js — RESERVED_TYPE_NAMES (reserved lifecycle types)
|
|
67
|
+
# - Operational pipeline markers used in commands/**/*.md specs
|
|
68
|
+
#
|
|
69
|
+
# Compound markers (FORGE:REVIEW-AGENT:material-change, FORGE:PHASE:COMPLETE) are
|
|
70
|
+
# validated by their PRIMARY type only (the part before the first - or : after FORGE:).
|
|
71
|
+
# Add entries for each primary type; sub-types are unrestricted.
|
|
72
|
+
#
|
|
73
|
+
# Update this list when new marker types are added to the protocol or spec corpus.
|
|
74
|
+
# Keep sorted alphabetically for readability.
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
MARKER_REGISTRY="
|
|
77
|
+
ACCEPTANCE_GATE
|
|
78
|
+
ADR_EXTRACTED
|
|
79
|
+
ANCESTRY_FAILED
|
|
80
|
+
ARCHITECT
|
|
81
|
+
AUDIT
|
|
82
|
+
AUTOPILOT_CYCLE
|
|
83
|
+
AUTOPILOT_GATE
|
|
84
|
+
AUTOPILOT_IMPACT
|
|
85
|
+
BATCHABLE
|
|
86
|
+
BATCH_ID
|
|
87
|
+
BATCH_MEMBERS
|
|
88
|
+
BENCH_SCORECARD
|
|
89
|
+
BLOCKED_ON_HUMAN_MERGE
|
|
90
|
+
BUILDER
|
|
91
|
+
CALIBRATION_CHECK
|
|
92
|
+
CARD
|
|
93
|
+
CHECKPOINT
|
|
94
|
+
CLAIM
|
|
95
|
+
CLAIM_RELEASED
|
|
96
|
+
CONTEXT
|
|
97
|
+
CONTRACT
|
|
98
|
+
COORD_ISSUE
|
|
99
|
+
CRITIQUE
|
|
100
|
+
DECISION_RECORD
|
|
101
|
+
DECOMPOSED
|
|
102
|
+
DESIGN
|
|
103
|
+
DISPATCHER
|
|
104
|
+
DOSSIER_UPDATED
|
|
105
|
+
FAST_PATH
|
|
106
|
+
FINDING_SOURCE
|
|
107
|
+
FIX_CI
|
|
108
|
+
GATE_FAILED
|
|
109
|
+
GATE_FAILURE
|
|
110
|
+
GATE_PASS
|
|
111
|
+
HEARTBEAT
|
|
112
|
+
INDEP_VERIFY
|
|
113
|
+
INDEP_VERIFY_FAIL
|
|
114
|
+
INDEP_VERIFY_PASS
|
|
115
|
+
INVESTIGATOR
|
|
116
|
+
JSONL_PARSER_UTIL
|
|
117
|
+
KNOWLEDGE_GIST
|
|
118
|
+
LEARNED
|
|
119
|
+
LEARNED_RULES
|
|
120
|
+
LEDGER_INDEXED
|
|
121
|
+
MEMORY_INDEX
|
|
122
|
+
MEMORY_INDEXED
|
|
123
|
+
MILESTONE_INDEX
|
|
124
|
+
MODEL_TIER_NOTE
|
|
125
|
+
ORPHAN_ESCALATED
|
|
126
|
+
ORPHAN_RECOVERED
|
|
127
|
+
PATTERN
|
|
128
|
+
PHASE
|
|
129
|
+
PHASE_COMPLETE
|
|
130
|
+
PLAN_DAG
|
|
131
|
+
PRIOR_DECISIONS
|
|
132
|
+
PRIOR_GIST
|
|
133
|
+
PROTOCOL_SOURCE
|
|
134
|
+
PUSH_BLOCKED
|
|
135
|
+
PUSH_BLOCKED_EMPTY_BRANCH
|
|
136
|
+
PUSH_FAILED
|
|
137
|
+
REMEDIATION
|
|
138
|
+
REREVIEW_SKIPPED
|
|
139
|
+
REVIEW
|
|
140
|
+
REVIEWER
|
|
141
|
+
REVIEW_ROUTE
|
|
142
|
+
REVIEW_STARTED
|
|
143
|
+
SECURITY_AUDIT
|
|
144
|
+
SIGNAL_RESOLVED
|
|
145
|
+
SIGNAL_UNRESOLVED
|
|
146
|
+
SPAWN_POLICY
|
|
147
|
+
SPEC_DOCTOR_COMPLETE
|
|
148
|
+
SPEC_LOADED
|
|
149
|
+
STALL_DETECTED
|
|
150
|
+
STATE
|
|
151
|
+
SYNTHESIS_BRIEF
|
|
152
|
+
TEST_GATE
|
|
153
|
+
TRAIN_CANDIDATE
|
|
154
|
+
TRAJECTORY
|
|
155
|
+
UNBLOCKED
|
|
156
|
+
USER_FEEDBACK
|
|
157
|
+
"
|
|
158
|
+
|
|
159
|
+
# Build an alternation pattern for registry lookup.
|
|
160
|
+
# Words separated by | for use with grep -E: ^(WORD1|WORD2|...)$
|
|
161
|
+
REGISTRY_NAMES=$(echo "$MARKER_REGISTRY" | tr -s '[:space:]' '\n' | grep -vE '^\s*$' | sort -u | tr '\n' '|' | sed 's/|$//')
|
|
162
|
+
REGISTRY_PATTERN="^(${REGISTRY_NAMES})$"
|
|
163
|
+
|
|
164
|
+
# Allowlist token — a line containing this token is exempt
|
|
165
|
+
ALLOWLIST_TOKEN='allowlist:check-spec-markers'
|
|
166
|
+
|
|
167
|
+
VIOLATIONS=0
|
|
168
|
+
|
|
169
|
+
# ---------------------------------------------------------------------------
|
|
170
|
+
# Pass 1: Validate FORGE: markers in commands/**/*.md
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
find_md_files() {
|
|
174
|
+
find "$COMMANDS_DIR" -name '*.md' | sort
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
while IFS= read -r file; do
|
|
178
|
+
[ -f "$file" ] || continue
|
|
179
|
+
|
|
180
|
+
# Extract all lines containing <!-- FORGE:XXXX --> annotations with their line numbers.
|
|
181
|
+
while IFS= read -r markerline; do
|
|
182
|
+
[ -z "$markerline" ] && continue
|
|
183
|
+
lineno="${markerline%%:*}"
|
|
184
|
+
content="${markerline#*:}"
|
|
185
|
+
|
|
186
|
+
# Skip allowlisted lines
|
|
187
|
+
if echo "$content" | grep -qF "$ALLOWLIST_TOKEN"; then
|
|
188
|
+
continue
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Skip markers that are inside code spans (backtick-quoted) — those are docs examples
|
|
192
|
+
# Heuristic: if the FORGE:TYPE appears between backticks on the same line, skip it
|
|
193
|
+
# We check for backtick on either side of the FORGE: marker
|
|
194
|
+
if echo "$content" | grep -qE "\`[^\`]*FORGE:[A-Z_]"; then
|
|
195
|
+
continue
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# Extract the full marker string after FORGE: — everything up to a space, -->, or end
|
|
199
|
+
# This captures compound markers like REVIEW-AGENT:material-change
|
|
200
|
+
full_marker=$(echo "$content" | grep -oE 'FORGE:[A-Z][A-Z0-9_:-]*' | head -1 | sed 's/^FORGE://')
|
|
201
|
+
|
|
202
|
+
[ -z "$full_marker" ] && continue
|
|
203
|
+
|
|
204
|
+
# Extract PRIMARY type: part before the first - or : separator
|
|
205
|
+
primary_type=$(echo "$full_marker" | sed 's/[-:].*//')
|
|
206
|
+
|
|
207
|
+
# Validate primary type against registry
|
|
208
|
+
if ! echo "$primary_type" | grep -qE "$REGISTRY_PATTERN"; then
|
|
209
|
+
echo "HIGH | $file:$lineno | unknown FORGE: marker 'FORGE:${full_marker}' (primary type '${primary_type}') — not in registry" >&2
|
|
210
|
+
VIOLATIONS=$((VIOLATIONS + 1))
|
|
211
|
+
fi
|
|
212
|
+
done < <(grep -nE '<!--[[:space:]]*FORGE:[A-Z]' "$file" 2>/dev/null || true)
|
|
213
|
+
|
|
214
|
+
done < <(find_md_files)
|
|
215
|
+
|
|
216
|
+
# ---------------------------------------------------------------------------
|
|
217
|
+
# Pass 2: Detect unsubstituted {PLACEHOLDER} in .github/workflows/*.yml
|
|
218
|
+
#
|
|
219
|
+
# In workflow YAML, {PLACEHOLDER} tokens (not bash ${VAR} expansions) indicate
|
|
220
|
+
# a template variable that should have been resolved before committing.
|
|
221
|
+
#
|
|
222
|
+
# Bash ${VAR} references are excluded — we strip ${...} patterns before scanning.
|
|
223
|
+
# Pattern: {UPPERCASE_3+} — e.g. {GH_REPO}, {NUMBER}, {WORKTREE_PATH}
|
|
224
|
+
# ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
WORKFLOWS_DIR="${REPO_ROOT}/.github/workflows"
|
|
227
|
+
|
|
228
|
+
if [ -d "$WORKFLOWS_DIR" ]; then
|
|
229
|
+
# Pattern: { followed by uppercase letter, then 2+ uppercase/digit/underscore, then }
|
|
230
|
+
# Preceded by something other than $ (to exclude bash ${VAR} expansions)
|
|
231
|
+
PLACEHOLDER_PATTERN='\{[A-Z][A-Z0-9_]{2,}\}'
|
|
232
|
+
|
|
233
|
+
while IFS= read -r wf_file; do
|
|
234
|
+
[ -f "$wf_file" ] || continue
|
|
235
|
+
|
|
236
|
+
LINENO=0
|
|
237
|
+
|
|
238
|
+
while IFS= read -r line; do
|
|
239
|
+
LINENO=$((LINENO + 1))
|
|
240
|
+
|
|
241
|
+
# Skip allowlisted lines
|
|
242
|
+
if echo "$line" | grep -qF "$ALLOWLIST_TOKEN"; then
|
|
243
|
+
continue
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
# Skip comment lines
|
|
247
|
+
if echo "$line" | grep -qE '^[[:space:]]*#'; then
|
|
248
|
+
continue
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# Strip bash ${VAR} and $VAR patterns before checking for template placeholders
|
|
252
|
+
# so that ELAPSED in "${ELAPSED}s" doesn't false-positive as {ELAPSED}
|
|
253
|
+
stripped=$(echo "$line" | sed 's/\${\([^}]*\)}/BASH_VAR/g; s/\$[A-Za-z_][A-Za-z0-9_]*/BASH_VAR/g')
|
|
254
|
+
|
|
255
|
+
if echo "$stripped" | grep -qE "$PLACEHOLDER_PATTERN"; then
|
|
256
|
+
placeholder=$(echo "$stripped" | grep -oE "$PLACEHOLDER_PATTERN" | head -1)
|
|
257
|
+
echo "HIGH | $wf_file:$LINENO | unsubstituted placeholder '$placeholder' in workflow" >&2
|
|
258
|
+
VIOLATIONS=$((VIOLATIONS + 1))
|
|
259
|
+
fi
|
|
260
|
+
done < "$wf_file"
|
|
261
|
+
done < <(find "$WORKFLOWS_DIR" -name '*.yml' | sort)
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
# Summary
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
if [ "$VIOLATIONS" -gt 0 ]; then
|
|
269
|
+
echo "check-spec-markers: $VIOLATIONS violation(s) found. See stderr for details." >&2
|
|
270
|
+
exit 1
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
echo "OK: No marker or placeholder violations found"
|
|
274
|
+
exit 0
|
package/scripts/classify-lane.sh
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# classify-lane.sh — Deterministic lane routing from issue milestone
|
|
3
3
|
#
|
|
4
|
-
# Usage: classify-lane.sh <issue_number> [-R <owner/repo>]
|
|
4
|
+
# Usage: classify-lane.sh <issue_number> [-R <owner/repo>] [--json]
|
|
5
5
|
# issue_number: GitHub issue number (required)
|
|
6
6
|
# -R <owner/repo>: GitHub repository (optional, defaults to current repo)
|
|
7
|
+
# --json: emit structured JSON object instead of plain lane string (optional)
|
|
7
8
|
#
|
|
8
|
-
# Output: lane string written to stdout
|
|
9
|
+
# Output: lane string written to stdout (default)
|
|
9
10
|
# staging — issue has no milestone (fast lane)
|
|
10
11
|
# milestone/{slug} — issue has a milestone (feature lane, slug = lowercased, spaces→hyphens, git-invalid chars stripped)
|
|
11
12
|
#
|
|
13
|
+
# Output: JSON object when --json flag is passed
|
|
14
|
+
# {"lane":"staging","branch":"staging","source":"fast-lane","milestone":null}
|
|
15
|
+
# {"lane":"milestone/{slug}","branch":"milestone/{slug}","source":"feature-lane","milestone":"{title}","slug":"{slug}"}
|
|
16
|
+
#
|
|
12
17
|
# Exit codes: 0 = success, 1 = error (invalid issue, gh auth failure, branch missing, etc.)
|
|
13
18
|
#
|
|
14
19
|
# Branch existence validation:
|
|
@@ -23,11 +28,12 @@ set -euo pipefail
|
|
|
23
28
|
|
|
24
29
|
ISSUE_NUMBER="${1:-}"
|
|
25
30
|
GH_REPO_ARGS=()
|
|
31
|
+
JSON_OUTPUT=false
|
|
26
32
|
|
|
27
|
-
# Parse arguments: issue number + optional -R flag
|
|
33
|
+
# Parse arguments: issue number + optional -R flag + optional --json flag
|
|
28
34
|
if [ -z "$ISSUE_NUMBER" ]; then
|
|
29
35
|
echo "ERROR: issue number is required" >&2
|
|
30
|
-
echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>]" >&2
|
|
36
|
+
echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>] [--json]" >&2
|
|
31
37
|
exit 1
|
|
32
38
|
fi
|
|
33
39
|
|
|
@@ -37,7 +43,7 @@ while [ $# -gt 0 ]; do
|
|
|
37
43
|
-R)
|
|
38
44
|
if [ $# -lt 2 ]; then
|
|
39
45
|
echo "ERROR: -R requires a value <owner/repo>" >&2
|
|
40
|
-
echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>]" >&2
|
|
46
|
+
echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>] [--json]" >&2
|
|
41
47
|
exit 1
|
|
42
48
|
fi
|
|
43
49
|
if ! [[ "$2" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then
|
|
@@ -47,6 +53,10 @@ while [ $# -gt 0 ]; do
|
|
|
47
53
|
GH_REPO_ARGS=(-R "$2")
|
|
48
54
|
shift 2
|
|
49
55
|
;;
|
|
56
|
+
--json)
|
|
57
|
+
JSON_OUTPUT=true
|
|
58
|
+
shift
|
|
59
|
+
;;
|
|
50
60
|
*)
|
|
51
61
|
echo "ERROR: unknown argument: $1" >&2
|
|
52
62
|
exit 1
|
|
@@ -94,7 +104,12 @@ if [ -z "$MILESTONE_TITLE" ]; then
|
|
|
94
104
|
echo " Run: git push origin HEAD:$STAGING_BRANCH (from the default branch)" >&2
|
|
95
105
|
exit 1
|
|
96
106
|
fi
|
|
97
|
-
|
|
107
|
+
if [ "$JSON_OUTPUT" = "true" ]; then
|
|
108
|
+
printf '{"lane":"%s","branch":"%s","source":"fast-lane","milestone":null}\n' \
|
|
109
|
+
"$STAGING_BRANCH" "$STAGING_BRANCH"
|
|
110
|
+
else
|
|
111
|
+
echo "$STAGING_BRANCH"
|
|
112
|
+
fi
|
|
98
113
|
else
|
|
99
114
|
# Slugify: lowercase, spaces → hyphens, strip git-invalid chars, collapse multiple hyphens,
|
|
100
115
|
# strip leading/trailing hyphens.
|
|
@@ -134,5 +149,12 @@ else
|
|
|
134
149
|
exit 1
|
|
135
150
|
fi
|
|
136
151
|
|
|
137
|
-
|
|
152
|
+
if [ "$JSON_OUTPUT" = "true" ]; then
|
|
153
|
+
# Escape double quotes in milestone title for safe JSON embedding.
|
|
154
|
+
MILESTONE_ESCAPED="${MILESTONE_TITLE//\"/\\\"}"
|
|
155
|
+
printf '{"lane":"%s","branch":"%s","source":"feature-lane","milestone":"%s","slug":"%s"}\n' \
|
|
156
|
+
"$LANE" "$LANE" "$MILESTONE_ESCAPED" "$SLUG"
|
|
157
|
+
else
|
|
158
|
+
echo "$LANE"
|
|
159
|
+
fi
|
|
138
160
|
fi
|