forgedock 1.18.0 → 1.20.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 +2 -0
- package/bin/engine/outcome-codes.mjs +13 -0
- package/bin/engine/phases.mjs +54 -4
- package/bin/engine/runlog.mjs +10 -2
- package/bin/engine/telemetry-seam.mjs +3 -0
- package/bin/engine/telemetry.test.mjs +3 -2
- package/bin/engine-cli.mjs +32 -2
- package/bin/engine.mjs +187 -10
- package/bin/runner.mjs +89 -13
- package/bin/stats/aggregate.mjs +7 -3
- package/bin/stats/cache.mjs +7 -3
- package/bin/stats/github-source.mjs +136 -28
- package/bin/stats/index.mjs +46 -9
- package/bin/stats/render.mjs +1 -1
- package/bin/stats/stats.test.mjs +201 -40
- package/commands/orchestrate/phase-4-execution.md +1 -1
- package/commands/review-pr-agents/protocols.md +19 -0
- package/commands/review-pr-staging.md +4 -4
- package/commands/review-pr.md +31 -20
- package/commands/work-on/build/architect.md +54 -1
- package/commands/work-on/build/implement.md +28 -1
- package/commands/work-on/build/validate.md +321 -29
- package/commands/work-on/build.md +184 -17
- package/commands/work-on/close.md +9 -4
- package/commands/work-on/investigate.md +7 -0
- package/commands/work-on/remediate.md +43 -17
- package/commands/work-on/review.md +43 -22
- package/commands/work-on.md +4 -4
- package/docs/CONFIG.md +93 -2
- package/docs/OUTCOME-CODES.md +5 -1
- package/docs/RELEASE-NOTES.md +15 -0
- package/docs/STATS.md +11 -4
- package/docs/TELEMETRY.md +5 -2
- package/docs/WORK-ON-RUNTIME.md +29 -3
- package/package.json +2 -2
- package/packages/protocol/src/types.js +1 -1
- package/scripts/acceptance-red-green.sh +90 -0
- package/scripts/acceptance-red-green.test.sh +74 -0
- package/scripts/architect-resume-match.test.sh +12 -3
- package/scripts/build-knowledge-index.mjs +8 -5
- package/scripts/builder-resume-match.test.sh +12 -3
- package/scripts/calibration.mjs +5 -2
- package/scripts/cap-findings.test.sh +2 -0
- package/scripts/check-contract-scope.test.sh +2 -0
- package/scripts/check-edge-cases.sh +143 -0
- package/scripts/check-edge-cases.test.sh +175 -0
- package/scripts/check-spec-markers.sh +4 -0
- package/scripts/check-test-tmp-cleanup.sh +56 -0
- package/scripts/check-test-tmp-cleanup.test.sh +69 -0
- package/scripts/ci-local/adapters/azure-pipelines.mjs +113 -0
- package/scripts/ci-local/adapters/bitbucket.mjs +64 -0
- package/scripts/ci-local/adapters/buildkite.mjs +58 -0
- package/scripts/ci-local/adapters/circleci.mjs +101 -0
- package/scripts/ci-local/adapters/common.mjs +115 -0
- package/scripts/ci-local/adapters/explicit.mjs +21 -0
- package/scripts/ci-local/adapters/github-actions.mjs +349 -0
- package/scripts/ci-local/adapters/gitlab-ci.mjs +184 -0
- package/scripts/ci-local/adapters/portability.mjs +104 -0
- package/scripts/ci-local/adapters/task-runners.mjs +67 -0
- package/scripts/ci-local/cli.mjs +50 -0
- package/scripts/ci-local/lib.mjs +379 -0
- package/scripts/ci-local/plan.mjs +186 -0
- package/scripts/ci-local/run.mjs +451 -0
- package/scripts/ci-local/scoping.mjs +531 -0
- package/scripts/ci-local.sh +98 -0
- package/scripts/ci-local.test.sh +1108 -0
- package/scripts/classify-finding.sh +33 -7
- package/scripts/classify-finding.test.sh +23 -0
- package/scripts/classify-test-run.sh +69 -0
- package/scripts/classify-test-run.test.sh +90 -0
- package/scripts/close-parent-c3.test.sh +2 -0
- package/scripts/consumer-sweep.sh +569 -0
- package/scripts/consumer-sweep.test.sh +357 -0
- package/scripts/detect-append-registries.sh +106 -0
- package/scripts/detect-append-registries.test.sh +64 -0
- package/scripts/file-deferred-followups.sh +91 -30
- package/scripts/file-deferred-followups.test.sh +101 -8
- package/scripts/fixtures/ci-local/azure/azure-pipelines.yml +11 -0
- package/scripts/fixtures/ci-local/bitbucket/bitbucket-pipelines.yml +12 -0
- package/scripts/fixtures/ci-local/buildkite/.buildkite/pipeline.yml +10 -0
- package/scripts/fixtures/ci-local/circleci/.circleci/config.yml +29 -0
- package/scripts/fixtures/ci-local/gha-basic/.github/workflows/ci.yml +59 -0
- package/scripts/fixtures/ci-local/gha-basic/.github/workflows/docs.yml +10 -0
- package/scripts/fixtures/ci-local/gha-basic/.github/workflows/nightly.yml +10 -0
- package/scripts/fixtures/ci-local/gha-basic/docs/index.md +1 -0
- package/scripts/fixtures/ci-local/gha-basic/src/greet.sh +2 -0
- package/scripts/fixtures/ci-local/gha-basic/tests/greet.test.sh +3 -0
- package/scripts/fixtures/ci-local/gha-dorny/.github/workflows/ci.yml +51 -0
- package/scripts/fixtures/ci-local/gha-dorny/backend/app.py +1 -0
- package/scripts/fixtures/ci-local/gha-dorny/docs/a.md +1 -0
- package/scripts/fixtures/ci-local/gha-dorny/frontend/app.js +1 -0
- package/scripts/fixtures/ci-local/gha-run/.github/workflows/ci.yml +39 -0
- package/scripts/fixtures/ci-local/gha-run/src/x.txt +1 -0
- package/scripts/fixtures/ci-local/gitlab/.gitlab-ci.yml +43 -0
- package/scripts/fixtures/ci-local/gitlab/docs/d.md +1 -0
- package/scripts/fixtures/ci-local/gitlab/src/a.txt +1 -0
- package/scripts/fixtures/ci-local/monorepo/.github/workflows/ci.yml +13 -0
- package/scripts/fixtures/ci-local/monorepo/package-lock.json +1 -0
- package/scripts/fixtures/ci-local/monorepo/packages/a/src/a.sh +2 -0
- package/scripts/fixtures/ci-local/monorepo/packages/a/tests/a.test.sh +3 -0
- package/scripts/fixtures/ci-local/monorepo/packages/b/src/b.sh +2 -0
- package/scripts/fixtures/ci-local/monorepo/packages/b/tests/b.test.sh +3 -0
- package/scripts/fixtures/ci-local/no-ci/Makefile +6 -0
- package/scripts/fixtures/ci-local/no-ci/package.json +1 -0
- package/scripts/fixtures/ci-local/no-ci/src/a.js +1 -0
- package/scripts/fixtures/ci-local/no-ci/src/b.js +1 -0
- package/scripts/fixtures/ci-local/no-ci/tests/a.test.js +2 -0
- package/scripts/fixtures/ci-local/no-ci/tests/b.test.js +2 -0
- package/scripts/forge-root.test.sh +1 -1
- package/scripts/lib/telemetry.sh +3 -0
- package/scripts/remediate-approved-head.test.sh +2 -0
- package/scripts/remediate-basesync.test.sh +1 -1
- package/scripts/review-acceptance-gate.sh +2 -2
- package/scripts/review-acceptance-gate.test.sh +20 -0
- package/scripts/review-delta.sh +8 -1
- package/scripts/review-delta.test.sh +19 -0
- package/scripts/review-finalize.sh +294 -53
- package/scripts/review-finalize.test.sh +347 -9
- package/scripts/review-pr-delta-wiring.test.sh +13 -0
- package/scripts/review-prepare.sh +5 -3
- package/scripts/review-prepare.test.sh +18 -2
- package/scripts/review-roster.sh +18 -5
- package/scripts/review-roster.test.sh +57 -1
- package/scripts/run-shell-tests.sh +5 -0
- package/scripts/selfreview-plan.sh +86 -0
- package/scripts/selfreview-plan.test.sh +90 -0
- package/scripts/spawn-depth-check.sh +5 -4
- package/scripts/spawn-depth-check.test.sh +3 -0
- package/scripts/telemetry.test.sh +10 -0
- package/scripts/transition-label.sh +26 -0
- package/scripts/trusted-comments.test.sh +2 -1
- package/scripts/verify-phase-trail.test.sh +14 -0
- package/scripts/wait-ci-green.sh +61 -9
- package/scripts/wait-ci-green.test.sh +41 -3
- package/scripts/with-run-tmpdir.sh +20 -0
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
#
|
|
6
6
|
# The builder decides fold-vs-defer (judgment, build.md B2.0); this script does the mechanics for the items
|
|
7
|
-
# that stay deferred: trusted lookup
|
|
8
|
-
#
|
|
7
|
+
# that stay deferred: trusted lookup (claim marker <!-- FORGE:DEFERRED_ISSUE: #M --> on the parent, then a state-all search) of a follow-up carrying
|
|
8
|
+
# <!-- FORGE:DEFERRED_FROM: #N -->, append the missing items (exact path match; a closed one is reopened), or create ONE issue
|
|
9
|
+
# with a checklist, claim it on the parent and re-check once for a concurrent lower-numbered duplicate.
|
|
9
10
|
#
|
|
10
11
|
# Usage: file-deferred-followups.sh --parent N --repo OWNER/REPO [--items FILE|-] [--folded N]
|
|
11
12
|
# items TSV, one per line: <path> TAB <why> TAB <known-flaw flag: known-flaw|1|yes|true, else empty>
|
|
@@ -71,27 +72,60 @@ finish() { # $1 = batched_into number or empty, $2 = filed count
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
# --- Trusted lookup (author predicate applied before path match), one retry ---------------------------------
|
|
74
|
-
|
|
75
|
+
# Sources, in order: (1) claim markers `<!-- FORGE:DEFERRED_ISSUE: #M -->` on the parent's REST comments (read-after-write
|
|
76
|
+
# consistent), (2) a search over ALL issue states (eventually consistent, so only a fallback). Every candidate is then
|
|
77
|
+
# verified by number over REST (trusted author, DEFERRED_FROM marker, not a PR); the lowest number is canonical.
|
|
78
|
+
JQ_PRE='def lst: split(",") | map(gsub("^\\s+|\\s+$"; "")) | map(select(length > 0));
|
|
79
|
+
def trusted: ((.author_association // "") as $x | ($assoc | lst) | index($x) != null)
|
|
80
|
+
or ((.user.type // "") == "Bot")
|
|
81
|
+
or ((.user.login // "") as $x | ($logins | lst) | index($x) != null);'
|
|
82
|
+
CLAIM_RE='^\s*<!-- FORGE:DEFERRED_ISSUE: #(?<n>[0-9]+) -->'
|
|
83
|
+
claim_nums() {
|
|
84
|
+
gh api --paginate "repos/${REPO}/issues/${PARENT}/comments" 2>/dev/null \
|
|
85
|
+
| jq -s -r --arg assoc "$TRUSTED_ASSOC" --arg logins "$TRUSTED_LOGINS" --arg re "$CLAIM_RE" "$JQ_PRE"'
|
|
86
|
+
[.[][] | select(trusted) | (.body // "") | capture($re) | .n | tonumber] | unique | .[]' 2>/dev/null
|
|
87
|
+
}
|
|
88
|
+
search_nums() {
|
|
75
89
|
gh api -X GET search/issues -f per_page=50 \
|
|
76
|
-
-f q="\"FORGE:DEFERRED_FROM: #${PARENT}\" in:body repo:${REPO} is:issue
|
|
77
|
-
| jq -
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
|
90
|
+
-f q="\"FORGE:DEFERRED_FROM: #${PARENT}\" in:body repo:${REPO} is:issue" 2>/dev/null \
|
|
91
|
+
| jq -r --arg assoc "$TRUSTED_ASSOC" --arg logins "$TRUSTED_LOGINS" --arg m "$MARKER" "$JQ_PRE"'
|
|
92
|
+
[.items[] | select(trusted) | select((.body // "") | contains($m)) | .number] | sort | .[]' 2>/dev/null
|
|
93
|
+
}
|
|
94
|
+
verify_num() { # number -> `number<TAB>state` when a trusted, non-PR issue carries the marker; empty when not (a 404 is "not it")
|
|
95
|
+
local out rc
|
|
96
|
+
out=$(gh api "repos/${REPO}/issues/$1" 2>"$TMP/verr"); rc=$?
|
|
97
|
+
if [ "$rc" -ne 0 ]; then grep -qE '404|Not Found|410' "$TMP/verr" 2>/dev/null && return 0; return 1; fi
|
|
98
|
+
printf '%s' "$out" | jq -r --arg assoc "$TRUSTED_ASSOC" --arg logins "$TRUSTED_LOGINS" --arg m "$MARKER" "$JQ_PRE"'
|
|
99
|
+
select(.pull_request == null) | select(trusted) | select((.body // "") | contains($m))
|
|
100
|
+
| "\(.number)\t\(.state)"' 2>/dev/null
|
|
101
|
+
}
|
|
102
|
+
CANON=""; CSTATE=""
|
|
103
|
+
pick_verified() { # numbers... ; sets CANON/CSTATE from the first verified one; non-zero only when a fetch failed
|
|
104
|
+
local n r
|
|
105
|
+
for n in "$@"; do
|
|
106
|
+
r=$(verify_num "$n") || return 1
|
|
107
|
+
if [ -n "$r" ]; then CANON="${r%%$'\t'*}"; CSTATE="${r#*$'\t'}"; return 0; fi
|
|
108
|
+
done
|
|
109
|
+
return 0
|
|
87
110
|
}
|
|
88
|
-
|
|
111
|
+
find_canonical() {
|
|
112
|
+
local cands
|
|
113
|
+
CANON=""; CSTATE=""
|
|
114
|
+
cands=$(claim_nums) || return 1
|
|
115
|
+
# shellcheck disable=SC2086
|
|
116
|
+
pick_verified $cands || return 1
|
|
117
|
+
[ -n "$CANON" ] && return 0
|
|
118
|
+
cands=$(search_nums) || return 1
|
|
119
|
+
# shellcheck disable=SC2086
|
|
120
|
+
pick_verified $cands || return 1
|
|
121
|
+
return 0
|
|
122
|
+
}
|
|
123
|
+
LOOKUP_OK=""
|
|
89
124
|
for attempt in 1 2; do
|
|
90
|
-
|
|
91
|
-
LOOKUP=""
|
|
125
|
+
if find_canonical; then LOOKUP_OK=1; break; fi
|
|
92
126
|
[ "$attempt" = 1 ] && sleep "${FORGE_DEFERRED_RETRY_SLEEP:-2}"
|
|
93
127
|
done
|
|
94
|
-
if [ -z "$
|
|
128
|
+
if [ -z "$LOOKUP_OK" ]; then
|
|
95
129
|
echo "DEFERRED_LOOKUP=FAILED"
|
|
96
130
|
exit 3
|
|
97
131
|
fi
|
|
@@ -108,27 +142,35 @@ has_entry() { # body-file path : exact checklist-line match, not substring
|
|
|
108
142
|
grep -F -- "\`$2\`" "$1" | grep -qE "^- \[[ xX]\] \`$(printf '%s' "$2" | sed 's/[][\.*^$+?(){}|]/\\&/g')\`( |\$)"
|
|
109
143
|
}
|
|
110
144
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
145
|
+
# Append the missing items to follow-up $1 (state $2), then reopen it when closed (review-finalize needs it open).
|
|
146
|
+
append_items() { # number state ; non-zero = failure
|
|
147
|
+
local num="$1" state="$2" note=""
|
|
114
148
|
# Re-read the body immediately before editing (parallel builds of the same parent), then append what is missing.
|
|
115
|
-
|
|
116
|
-
echo "DEFERRED_FILE=FAILED"; exit 4
|
|
117
|
-
fi
|
|
149
|
+
gh api "repos/${REPO}/issues/${num}" --jq '.body // ""' > "$TMP/body" 2>/dev/null || return 1
|
|
118
150
|
: > "$TMP/add"
|
|
119
151
|
while IFS="$SEP" read -r p w k; do
|
|
120
152
|
has_entry "$TMP/body" "$p" || entry "$p" "$w" "$k" >> "$TMP/add"
|
|
121
153
|
done < "$TMP/items"
|
|
154
|
+
[ "$state" = "closed" ] && note=" (follow-up was closed; reopened)"
|
|
122
155
|
if [ -s "$TMP/add" ]; then
|
|
123
|
-
{ cat "$TMP/body"; printf '\n### Added by a later build of #%s\n\n' "$PARENT"; cat "$TMP/add"; } > "$TMP/newbody"
|
|
156
|
+
{ cat "$TMP/body"; printf '\n### Added by a later build of #%s%s\n\n' "$PARENT" "$note"; cat "$TMP/add"; } > "$TMP/newbody"
|
|
124
157
|
if [ -n "${DRY_RUN:-}" ]; then
|
|
125
|
-
echo "DRY_RUN: gh issue edit $
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
echo "DRY_RUN: gh issue edit $num (append $(wc -l < "$TMP/add" | tr -d ' ') item(s))" >&2
|
|
159
|
+
else
|
|
160
|
+
gh issue edit "$num" -R "$REPO" --body-file "$TMP/newbody" >/dev/null 2>&1 || return 1
|
|
128
161
|
fi
|
|
129
162
|
fi
|
|
130
|
-
|
|
131
|
-
|
|
163
|
+
if [ "$state" = "closed" ]; then
|
|
164
|
+
if [ -n "${DRY_RUN:-}" ]; then echo "DRY_RUN: gh issue reopen $num" >&2
|
|
165
|
+
else gh issue reopen "$num" -R "$REPO" >/dev/null 2>&1 || return 1; fi
|
|
166
|
+
fi
|
|
167
|
+
return 0
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if [ -n "$CANON" ]; then
|
|
171
|
+
append_items "$CANON" "$CSTATE" || { echo "DEFERRED_FILE=FAILED"; exit 4; }
|
|
172
|
+
while IFS="$SEP" read -r p w k; do printf '%s\t%s\n' "$p" "$CANON"; done < "$TMP/items"
|
|
173
|
+
finish "$CANON" 0
|
|
132
174
|
exit 0
|
|
133
175
|
fi
|
|
134
176
|
|
|
@@ -154,6 +196,25 @@ else
|
|
|
154
196
|
NUM=$(printf '%s' "$URL" | grep -oE '[0-9]+$' | tail -n 1)
|
|
155
197
|
[ -n "$NUM" ] || { echo "DEFERRED_FILE=FAILED"; exit 4; }
|
|
156
198
|
fi
|
|
199
|
+
if [ -z "${DRY_RUN:-}" ]; then
|
|
200
|
+
# Claim the follow-up on the parent (REST comments are read-after-write consistent; best effort, the search still finds it).
|
|
201
|
+
gh issue comment "$PARENT" -R "$REPO" --body "<!-- FORGE:DEFERRED_ISSUE: #${NUM} -->" >/dev/null 2>&1 \
|
|
202
|
+
|| echo "file-deferred-followups: warning: could not post claim marker for #${NUM} on #${PARENT}" >&2
|
|
203
|
+
# Post-create re-check: a concurrent build may have filed first. The lowest trusted number is canonical; the newer one is closed.
|
|
204
|
+
if find_canonical; then
|
|
205
|
+
if [ -n "$CANON" ] && [ "$CANON" -lt "$NUM" ]; then
|
|
206
|
+
if append_items "$CANON" "$CSTATE"; then
|
|
207
|
+
gh issue close "$NUM" -R "$REPO" --reason "not planned" --comment "Duplicate of #${CANON}: deferred items of #${PARENT} are tracked there." >/dev/null 2>&1 \
|
|
208
|
+
|| echo "file-deferred-followups: warning: could not close duplicate #${NUM}" >&2
|
|
209
|
+
NUM="$CANON"
|
|
210
|
+
else
|
|
211
|
+
echo "file-deferred-followups: warning: could not move items to #${CANON}; keeping #${NUM}" >&2
|
|
212
|
+
fi
|
|
213
|
+
fi
|
|
214
|
+
else
|
|
215
|
+
echo "file-deferred-followups: warning: post-create re-check failed; keeping #${NUM}" >&2
|
|
216
|
+
fi
|
|
217
|
+
fi
|
|
157
218
|
while IFS="$SEP" read -r p w k; do printf '%s\t%s\n' "$p" "$NUM"; done < "$TMP/items"
|
|
158
219
|
finish "$NUM" 1
|
|
159
220
|
exit 0
|
|
@@ -8,19 +8,27 @@ S="$HERE/file-deferred-followups.sh"
|
|
|
8
8
|
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
9
9
|
mkdir "$T/bin"
|
|
10
10
|
export FORGE_DEFERRED_RETRY_SLEEP=0
|
|
11
|
-
export GH_LOG="$T/gh.log" SEARCH_JSON="$T/search.json" ISSUE_BODY="$T/issue.body"
|
|
11
|
+
export GH_LOG="$T/gh.log" SEARCH_JSON="$T/search.json" ISSUE_BODY="$T/issue.body" COMMENTS_JSON="$T/comments.json" T_DIR="$T"
|
|
12
12
|
|
|
13
13
|
cat > "$T/bin/gh" <<'MOCK'
|
|
14
14
|
#!/usr/bin/env bash
|
|
15
15
|
echo "$*" >> "$GH_LOG"
|
|
16
16
|
case "$1 $2" in
|
|
17
17
|
"api -X") [ -n "${LOOKUP_FAIL:-}" ] && exit 1; cat "$SEARCH_JSON"; exit 0 ;;
|
|
18
|
+
"api --paginate") [ -n "${CLAIM_FAIL:-}" ] && exit 1; cat "$COMMENTS_JSON"; exit 0 ;;
|
|
18
19
|
"issue create") [ -n "${CREATE_FAIL:-}" ] && exit 1; echo "https://github.com/o/r/issues/901"; exit 0 ;;
|
|
20
|
+
"issue comment") [ -n "${COMMENT_FAIL:-}" ] && exit 1; exit 0 ;;
|
|
21
|
+
"issue reopen") [ -n "${REOPEN_FAIL:-}" ] && exit 1; exit 0 ;;
|
|
22
|
+
"issue close") [ -n "${CLOSE_FAIL:-}" ] && exit 1; exit 0 ;;
|
|
19
23
|
"issue edit")
|
|
20
24
|
[ -n "${EDIT_FAIL:-}" ] && exit 1
|
|
21
25
|
while [ $# -gt 0 ]; do [ "$1" = "--body-file" ] && cp "$2" "$ISSUE_BODY.edited"; shift; done; exit 0 ;;
|
|
22
26
|
esac
|
|
23
|
-
|
|
27
|
+
if [ "$1" = api ]; then
|
|
28
|
+
case "$*" in *--jq*) cat "$ISSUE_BODY"; exit 0 ;; esac
|
|
29
|
+
n="${2##*/}"; [ -f "$T_DIR/issue.$n.json" ] && { cat "$T_DIR/issue.$n.json"; exit 0; }
|
|
30
|
+
echo "HTTP 404: Not Found" >&2; exit 1
|
|
31
|
+
fi
|
|
24
32
|
exit 1
|
|
25
33
|
MOCK
|
|
26
34
|
chmod +x "$T/bin/gh"; export PATH="$T/bin:$PATH"
|
|
@@ -29,7 +37,12 @@ PASS=0; FAIL=0
|
|
|
29
37
|
ok() { PASS=$((PASS+1)); echo "PASS: $1"; }
|
|
30
38
|
bad() { FAIL=$((FAIL+1)); echo "FAIL: $1"; }
|
|
31
39
|
check() { if eval "$2"; then ok "$1"; else bad "$1"; fi; }
|
|
32
|
-
reset() { : > "$GH_LOG"; rm -f "$ISSUE_BODY.edited"; unset LOOKUP_FAIL CREATE_FAIL EDIT_FAIL
|
|
40
|
+
reset() { : > "$GH_LOG"; rm -f "$ISSUE_BODY.edited" "$T"/issue.*.json; unset LOOKUP_FAIL CREATE_FAIL EDIT_FAIL CLAIM_FAIL COMMENT_FAIL REOPEN_FAIL CLOSE_FAIL DRY_RUN
|
|
41
|
+
echo '{"items":[]}' > "$SEARCH_JSON"; echo '[]' > "$COMMENTS_JSON"; }
|
|
42
|
+
# issue_json NUMBER STATE ASSOCIATION [PARENT]: REST view of a follow-up carrying the DEFERRED_FROM marker
|
|
43
|
+
issue_json() { printf '{"number":%s,"state":"%s","author_association":"%s","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #%s -->"}\n' "$1" "$2" "$3" "${4:-10}" > "$T/issue.$1.json"; }
|
|
44
|
+
# claim_json ASSOCIATION NUMBER...: parent comments carrying claim markers
|
|
45
|
+
claim_json() { local a="$1" out="" n; shift; for n in "$@"; do out="${out:+$out,}{\"author_association\":\"$a\",\"user\":{\"type\":\"User\"},\"body\":\"<!-- FORGE:DEFERRED_ISSUE: #$n -->\"}"; done; echo "[$out]" > "$COMMENTS_JSON"; }
|
|
33
46
|
printf 'a/x.sh\twhy a\t\nb/y.sh\twhy b\tknown-flaw\nc/z.sh\twhy c\t\n' > "$T/items.tsv"
|
|
34
47
|
|
|
35
48
|
# 1. N items -> exactly one create
|
|
@@ -43,7 +56,7 @@ check "create: telemetry" "printf '%s' \"\$OUT\" | grep -q 'DEFERRED_TELEMETRY d
|
|
|
43
56
|
# 2. append to existing trusted open issue; exact path match
|
|
44
57
|
reset
|
|
45
58
|
printf '## Affected Files\n- `a/x.sh`\n\n- [ ] `a/x.sh.bak` — other\n<!-- FORGE:DEFERRED_FROM: #10 -->\n' > "$ISSUE_BODY"
|
|
46
|
-
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"
|
|
59
|
+
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"; issue_json 77 open OWNER
|
|
47
60
|
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
48
61
|
check "append: rc 0, no create" "[ $RC -eq 0 ] && ! grep -q '^issue create' '$GH_LOG'"
|
|
49
62
|
check "append: telemetry batched into 77 filed=0" "printf '%s' \"\$OUT\" | grep -q 'batched_into=#77 filed=0'"
|
|
@@ -55,7 +68,7 @@ reset
|
|
|
55
68
|
printf 'd/w.sh\t\tknown-flaw\n' > "$T/emptywhy.tsv"
|
|
56
69
|
bash "$S" --parent 10 --repo o/r --items "$T/emptywhy.tsv" >/dev/null; RC=$?
|
|
57
70
|
rm -f "$ISSUE_BODY.edited"; printf 'x\n' > "$ISSUE_BODY"
|
|
58
|
-
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"
|
|
71
|
+
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"; issue_json 77 open OWNER
|
|
59
72
|
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/emptywhy.tsv"); RC=$?
|
|
60
73
|
check "empty-why: append keeps known-flaw flag" "[ $RC -eq 0 ] && grep -q 'd/w.sh. \*\*known-flaw\*\*' '$ISSUE_BODY.edited'"
|
|
61
74
|
|
|
@@ -66,13 +79,13 @@ check "missing option value: rc 2" "[ $RC -eq 2 ]"
|
|
|
66
79
|
# 3. already-listed path is not duplicated; nothing to add -> no edit
|
|
67
80
|
reset
|
|
68
81
|
printf -- '- [ ] `a/x.sh` — w\n- [x] `b/y.sh`\n- [ ] `c/z.sh`\n' > "$ISSUE_BODY"
|
|
69
|
-
echo '{"items":[{"number":77,"author_association":"MEMBER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"
|
|
82
|
+
echo '{"items":[{"number":77,"author_association":"MEMBER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"; issue_json 77 open OWNER
|
|
70
83
|
bash "$S" --parent 10 --repo o/r --items "$T/items.tsv" >/dev/null; RC=$?
|
|
71
84
|
check "idempotent: no edit, no create on resume" "[ $RC -eq 0 ] && ! grep -qE '^issue (edit|create)' '$GH_LOG'"
|
|
72
85
|
|
|
73
86
|
# 4. untrusted match ignored -> new issue
|
|
74
87
|
reset
|
|
75
|
-
echo '{"items":[{"number":66,"author_association":"NONE","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"
|
|
88
|
+
echo '{"items":[{"number":66,"author_association":"NONE","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"; issue_json 66 open NONE; issue_json 77 open OWNER
|
|
76
89
|
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv")
|
|
77
90
|
check "untrusted: ignored, create filed" "grep -q '^issue create' '$GH_LOG' && printf '%s' \"\$OUT\" | grep -q 'filed=1'"
|
|
78
91
|
|
|
@@ -87,7 +100,7 @@ OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
|
87
100
|
check "create failure: rc 4, no telemetry" "[ $RC -eq 4 ] && ! printf '%s' \"\$OUT\" | grep -q DEFERRED_TELEMETRY"
|
|
88
101
|
reset; export EDIT_FAIL=1
|
|
89
102
|
printf 'x\n' > "$ISSUE_BODY"
|
|
90
|
-
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"
|
|
103
|
+
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"; issue_json 77 open OWNER
|
|
91
104
|
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
92
105
|
check "edit failure: rc 4, no telemetry" "[ $RC -eq 4 ] && ! printf '%s' \"\$OUT\" | grep -q DEFERRED_TELEMETRY"
|
|
93
106
|
|
|
@@ -102,5 +115,85 @@ OUT=$(printf '' | bash "$S" --parent 10 --repo o/r)
|
|
|
102
115
|
check "empty: no create, deferred_items=0" "! grep -q '^issue create' '$GH_LOG' && printf '%s' \"\$OUT\" | grep -q 'deferred_items=0'"
|
|
103
116
|
bash "$S" --repo o/r >/dev/null 2>&1; check "usage: missing parent rc 2" "[ $? -eq 2 ]"
|
|
104
117
|
|
|
118
|
+
# 9. e1: search index lags (returns nothing) but the claim marker on the parent finds the follow-up
|
|
119
|
+
reset
|
|
120
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 77 open OWNER; claim_json OWNER 77
|
|
121
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
122
|
+
check "index-lag: claim found, no duplicate create" "[ $RC -eq 0 ] && ! grep -q '^issue create' '$GH_LOG' && printf '%s' \"\$OUT\" | grep -q 'batched_into=#77 filed=0'"
|
|
123
|
+
check "index-lag: search never needed" "! grep -q search/issues '$GH_LOG'"
|
|
124
|
+
|
|
125
|
+
# 10. e2: closed follow-up (found only by a state-all search) is reused and reopened
|
|
126
|
+
reset
|
|
127
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 77 closed OWNER
|
|
128
|
+
echo '{"items":[{"number":77,"author_association":"OWNER","user":{"type":"User"},"body":"<!-- FORGE:DEFERRED_FROM: #10 -->"}]}' > "$SEARCH_JSON"
|
|
129
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
130
|
+
check "closed: reused, reopened, no create" "[ $RC -eq 0 ] && grep -q '^issue reopen 77' '$GH_LOG' && ! grep -q '^issue create' '$GH_LOG'"
|
|
131
|
+
check "closed: search query covers all states" "grep -q 'is:issue' '$GH_LOG' && ! grep 'api -X' '$GH_LOG' | grep -q 'is:open'"
|
|
132
|
+
reset; export REOPEN_FAIL=1
|
|
133
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 77 closed OWNER; claim_json OWNER 77
|
|
134
|
+
bash "$S" --parent 10 --repo o/r --items "$T/items.tsv" >/dev/null; RC=$?
|
|
135
|
+
check "closed: reopen failure is rc 4" "[ $RC -eq 4 ]"
|
|
136
|
+
|
|
137
|
+
# 11. create posts the claim marker on the parent
|
|
138
|
+
reset
|
|
139
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
140
|
+
check "claim: posted on parent after create" "[ $RC -eq 0 ] && grep -q '^issue comment 10 .*FORGE:DEFERRED_ISSUE: #901' '$GH_LOG'"
|
|
141
|
+
reset; export COMMENT_FAIL=1
|
|
142
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv" 2>/dev/null); RC=$?
|
|
143
|
+
check "claim: post failure only warns" "[ $RC -eq 0 ] && printf '%s' \"\$OUT\" | grep -q 'batched_into=#901 filed=1'"
|
|
144
|
+
|
|
145
|
+
# 12. e3: a concurrent build filed #800 first; the first claim read (before our create) misses it, the post-create re-check sees it
|
|
146
|
+
race_setup() { # first comments read returns [] (the other build has not claimed yet), later reads return the real listing
|
|
147
|
+
CALLS="$T/claim.calls"; : > "$CALLS"; export CALLS
|
|
148
|
+
mv "$T/bin/gh" "$T/bin/gh.real"
|
|
149
|
+
cat > "$T/bin/gh" <<'WRAP'
|
|
150
|
+
#!/usr/bin/env bash
|
|
151
|
+
if [ "$1 $2" = "api --paginate" ]; then
|
|
152
|
+
echo x >> "$CALLS"
|
|
153
|
+
if [ "$(wc -l < "$CALLS" | tr -d ' ')" -le 1 ]; then echo "$*" >> "$GH_LOG"; echo '[]'; exit 0; fi
|
|
154
|
+
fi
|
|
155
|
+
exec "$(dirname "$0")/gh.real" "$@"
|
|
156
|
+
WRAP
|
|
157
|
+
chmod +x "$T/bin/gh"
|
|
158
|
+
}
|
|
159
|
+
race_teardown() { mv "$T/bin/gh.real" "$T/bin/gh"; }
|
|
160
|
+
reset
|
|
161
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 800 open OWNER; claim_json OWNER 800
|
|
162
|
+
race_setup
|
|
163
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
164
|
+
race_teardown
|
|
165
|
+
check "race: newer duplicate closed, items moved to lowest" "[ $RC -eq 0 ] && grep -q '^issue close 901' '$GH_LOG' && grep -q '^issue edit 800' '$GH_LOG' && printf '%s' \"\$OUT\" | grep -q 'batched_into=#800 filed=1'"
|
|
166
|
+
check "race: rows point at canonical issue" "[ \$(printf '%s\n' \"\$OUT\" | grep -c '800\$') -ge 3 ]"
|
|
167
|
+
# e5: duplicate-close failure only warns
|
|
168
|
+
reset; export CLOSE_FAIL=1
|
|
169
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 800 open OWNER; claim_json OWNER 800
|
|
170
|
+
race_setup
|
|
171
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv" 2>/dev/null); RC=$?
|
|
172
|
+
race_teardown
|
|
173
|
+
check "race: duplicate-close failure still rc 0" "[ $RC -eq 0 ] && printf '%s' \"\$OUT\" | grep -q 'batched_into=#800'"
|
|
174
|
+
|
|
175
|
+
# 13. e4: claim listing failure is a failed lookup, never "none found"
|
|
176
|
+
reset; export CLAIM_FAIL=1
|
|
177
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
178
|
+
check "claim-failure: rc 3, no create" "[ $RC -eq 3 ] && printf '%s' \"\$OUT\" | grep -q DEFERRED_LOOKUP=FAILED && ! grep -q '^issue create' '$GH_LOG'"
|
|
179
|
+
|
|
180
|
+
# 15. e6: untrusted claim comment is ignored (falls through to create)
|
|
181
|
+
reset
|
|
182
|
+
issue_json 77 open OWNER; claim_json NONE 77
|
|
183
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv" 2>/dev/null); RC=$?
|
|
184
|
+
check "untrusted-claim: ignored, create filed" "[ $RC -eq 0 ] && grep -q '^issue create' '$GH_LOG'"
|
|
185
|
+
|
|
186
|
+
# 16. e7: several claims, lowest valid wins; a claim to a missing issue falls through
|
|
187
|
+
reset
|
|
188
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 80 open OWNER; issue_json 90 open OWNER; claim_json OWNER 70 90 80
|
|
189
|
+
OUT=$(bash "$S" --parent 10 --repo o/r --items "$T/items.tsv"); RC=$?
|
|
190
|
+
check "multi-claim: lowest valid (80) wins, missing 70 skipped" "[ $RC -eq 0 ] && printf '%s' \"\$OUT\" | grep -q 'batched_into=#80 filed=0'"
|
|
191
|
+
|
|
192
|
+
# 17. DRY_RUN: no claim, reopen or close
|
|
193
|
+
reset; export DRY_RUN=1
|
|
194
|
+
printf 'x\n' > "$ISSUE_BODY"; issue_json 77 closed OWNER; claim_json OWNER 77
|
|
195
|
+
bash "$S" --parent 10 --repo o/r --items "$T/items.tsv" >/dev/null 2>&1
|
|
196
|
+
check "dry-run: no comment/reopen/close/edit" "! grep -qE '^issue (comment|reopen|close|edit|create)' '$GH_LOG'"
|
|
197
|
+
|
|
105
198
|
echo "passed=$PASS failed=$FAIL"
|
|
106
199
|
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
version: 2.1
|
|
2
|
+
jobs:
|
|
3
|
+
test:
|
|
4
|
+
docker:
|
|
5
|
+
- image: cimg/base:stable
|
|
6
|
+
steps:
|
|
7
|
+
- checkout
|
|
8
|
+
- run:
|
|
9
|
+
name: unit
|
|
10
|
+
command: echo circle-unit
|
|
11
|
+
secured:
|
|
12
|
+
docker:
|
|
13
|
+
- image: cimg/base:stable
|
|
14
|
+
steps:
|
|
15
|
+
- run: echo secured
|
|
16
|
+
deploy-prod:
|
|
17
|
+
docker:
|
|
18
|
+
- image: cimg/base:stable
|
|
19
|
+
steps:
|
|
20
|
+
- run: echo deploy
|
|
21
|
+
workflows:
|
|
22
|
+
main:
|
|
23
|
+
jobs:
|
|
24
|
+
- test
|
|
25
|
+
- secured:
|
|
26
|
+
context: org-secrets
|
|
27
|
+
requires: [test]
|
|
28
|
+
- deploy-prod:
|
|
29
|
+
requires: [test]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
branches: [main]
|
|
5
|
+
paths:
|
|
6
|
+
- 'src/**'
|
|
7
|
+
- 'tests/**'
|
|
8
|
+
- '.github/workflows/ci.yml'
|
|
9
|
+
jobs:
|
|
10
|
+
unit:
|
|
11
|
+
name: Unit
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
- name: unit tests
|
|
19
|
+
run: bash tests/greet.test.sh
|
|
20
|
+
matrix:
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
strategy:
|
|
23
|
+
matrix:
|
|
24
|
+
os: [ubuntu-latest, macos-14, windows-latest]
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- run: echo matrix-ok
|
|
28
|
+
withservice:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
services:
|
|
31
|
+
postgres:
|
|
32
|
+
image: postgres:16
|
|
33
|
+
env:
|
|
34
|
+
POSTGRES_PASSWORD: example
|
|
35
|
+
ports:
|
|
36
|
+
- 5432:5432
|
|
37
|
+
options: --health-cmd pg_isready --health-interval 5s
|
|
38
|
+
steps:
|
|
39
|
+
- run: echo service-ok
|
|
40
|
+
needsecret:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
env:
|
|
43
|
+
API_TOKEN: ${{ secrets.API_TOKEN }}
|
|
44
|
+
steps:
|
|
45
|
+
- run: echo uses-secret
|
|
46
|
+
unknownaction:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: some-org/mystery-action@v9
|
|
50
|
+
deployprod:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
environment: production
|
|
53
|
+
steps:
|
|
54
|
+
- run: echo deploy
|
|
55
|
+
conditional:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
if: github.event_name == 'push'
|
|
58
|
+
steps:
|
|
59
|
+
- run: echo push-only
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# docs
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
branches: [staging, main]
|
|
5
|
+
jobs:
|
|
6
|
+
changes:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
outputs:
|
|
9
|
+
backend: ${{ steps.filter.outputs.backend }}
|
|
10
|
+
frontend: ${{ steps.filter.outputs.frontend }}
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: dorny/paths-filter@v3
|
|
14
|
+
id: filter
|
|
15
|
+
with:
|
|
16
|
+
filters: |
|
|
17
|
+
backend:
|
|
18
|
+
- 'backend/**'
|
|
19
|
+
frontend:
|
|
20
|
+
- 'frontend/**'
|
|
21
|
+
backend:
|
|
22
|
+
needs: changes
|
|
23
|
+
if: needs.changes.outputs.backend == 'true'
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
services:
|
|
26
|
+
redis:
|
|
27
|
+
image: redis:7
|
|
28
|
+
ports:
|
|
29
|
+
- 6379:6379
|
|
30
|
+
steps:
|
|
31
|
+
- run: echo backend-ok
|
|
32
|
+
frontend:
|
|
33
|
+
needs: changes
|
|
34
|
+
if: needs.changes.outputs.frontend == 'true'
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
steps:
|
|
37
|
+
- run: echo frontend-ok
|
|
38
|
+
integration:
|
|
39
|
+
needs: [changes]
|
|
40
|
+
if: needs.changes.outputs.backend == 'true'
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
env:
|
|
43
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
44
|
+
steps:
|
|
45
|
+
- run: echo integration
|
|
46
|
+
deploy-staging:
|
|
47
|
+
needs: [backend]
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
environment: staging
|
|
50
|
+
steps:
|
|
51
|
+
- run: echo deploy
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
print("a")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# d
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log(1)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
jobs:
|
|
5
|
+
first:
|
|
6
|
+
runs-on: ubuntu-latest
|
|
7
|
+
steps:
|
|
8
|
+
- run: echo first >> "$RUN_LOG"
|
|
9
|
+
- name: reads GITHUB_ENV
|
|
10
|
+
run: |
|
|
11
|
+
echo "FROM_STEP=carried" >> "$GITHUB_ENV"
|
|
12
|
+
- run: test "$FROM_STEP" = carried
|
|
13
|
+
env:
|
|
14
|
+
RUN_LOG: run.log
|
|
15
|
+
second:
|
|
16
|
+
needs: first
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- run: echo second
|
|
20
|
+
failing:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- run: |
|
|
24
|
+
echo "about to fail"
|
|
25
|
+
exit 3
|
|
26
|
+
skippedbyneed:
|
|
27
|
+
needs: failing
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- run: echo never
|
|
31
|
+
slow:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
timeout-minutes: 1
|
|
34
|
+
steps:
|
|
35
|
+
- run: echo slow-ok
|
|
36
|
+
broken-install:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- run: npm ci --no-audit --registry=http://127.0.0.1:9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ok
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
stages: [build, test, deploy]
|
|
2
|
+
variables:
|
|
3
|
+
GREETING: hello
|
|
4
|
+
.template:
|
|
5
|
+
script:
|
|
6
|
+
- echo template
|
|
7
|
+
lint:
|
|
8
|
+
stage: test
|
|
9
|
+
image: node:20
|
|
10
|
+
rules:
|
|
11
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
12
|
+
changes:
|
|
13
|
+
- src/**/*
|
|
14
|
+
script:
|
|
15
|
+
- echo "$GREETING lint"
|
|
16
|
+
unit:
|
|
17
|
+
stage: test
|
|
18
|
+
extends: .template
|
|
19
|
+
only:
|
|
20
|
+
- merge_requests
|
|
21
|
+
withdb:
|
|
22
|
+
stage: test
|
|
23
|
+
services:
|
|
24
|
+
- postgres:16
|
|
25
|
+
script:
|
|
26
|
+
- echo db
|
|
27
|
+
manualjob:
|
|
28
|
+
stage: test
|
|
29
|
+
when: manual
|
|
30
|
+
script:
|
|
31
|
+
- echo manual
|
|
32
|
+
release:
|
|
33
|
+
stage: deploy
|
|
34
|
+
environment: production
|
|
35
|
+
script:
|
|
36
|
+
- echo release
|
|
37
|
+
docsonly:
|
|
38
|
+
stage: test
|
|
39
|
+
rules:
|
|
40
|
+
- changes:
|
|
41
|
+
- docs/**/*
|
|
42
|
+
script:
|
|
43
|
+
- echo docs
|