@jonit-dev/night-watch-cli 1.8.8-beta.1 → 1.8.8-beta.11
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/dist/cli.js +981 -78
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +39 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +8 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +50 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/merge.d.ts +26 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +159 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/qa.d.ts.map +1 -1
- package/dist/commands/qa.js +2 -0
- package/dist/commands/qa.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +27 -4
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/resolve.d.ts +26 -0
- package/dist/commands/resolve.d.ts.map +1 -0
- package/dist/commands/resolve.js +186 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/review.d.ts +5 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +18 -18
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/slice.d.ts +1 -0
- package/dist/commands/slice.d.ts.map +1 -1
- package/dist/commands/slice.js +19 -19
- package/dist/commands/slice.js.map +1 -1
- package/dist/commands/summary.d.ts +14 -0
- package/dist/commands/summary.d.ts.map +1 -0
- package/dist/commands/summary.js +193 -0
- package/dist/commands/summary.js.map +1 -0
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +14 -2
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/scripts/night-watch-helpers.sh +10 -1
- package/dist/scripts/night-watch-merger-cron.sh +321 -0
- package/dist/scripts/night-watch-pr-resolver-cron.sh +402 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +23 -142
- package/dist/scripts/night-watch-qa-cron.sh +30 -4
- package/dist/scripts/test-helpers.bats +45 -0
- package/dist/templates/night-watch-pr-reviewer.md +2 -1
- package/dist/templates/pr-reviewer.md +2 -1
- package/dist/templates/slicer.md +54 -64
- package/dist/web/assets/index-CPQbZ1BL.css +1 -0
- package/dist/web/assets/index-CiRJZI4z.js +386 -0
- package/dist/web/assets/index-ZE5lOeJp.js +386 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Night Watch Merge Orchestrator Cron Runner
|
|
5
|
+
# Usage: night-watch-merger-cron.sh /path/to/project
|
|
6
|
+
#
|
|
7
|
+
# Scans all open PRs, filters eligible ones, and merges them in FIFO order
|
|
8
|
+
# (oldest PR first by creation date). Rebases remaining PRs after each merge.
|
|
9
|
+
#
|
|
10
|
+
# Required env vars (with defaults shown):
|
|
11
|
+
# NW_MERGER_MAX_RUNTIME=1800 - Maximum runtime in seconds (30 min)
|
|
12
|
+
# NW_MERGER_MERGE_METHOD=squash - Merge method: squash|merge|rebase
|
|
13
|
+
# NW_MERGER_MIN_REVIEW_SCORE=80 - Minimum review score threshold
|
|
14
|
+
# NW_MERGER_BRANCH_PATTERNS= - Comma-separated branch prefixes (empty = all)
|
|
15
|
+
# NW_MERGER_REBASE_BEFORE_MERGE=1 - Set to 1 to rebase before merging
|
|
16
|
+
# NW_MERGER_MAX_PRS_PER_RUN=0 - Max PRs to merge per run (0 = unlimited)
|
|
17
|
+
# NW_DRY_RUN=0 - Set to 1 for dry-run mode
|
|
18
|
+
|
|
19
|
+
PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
|
|
20
|
+
PROJECT_NAME=$(basename "${PROJECT_DIR}")
|
|
21
|
+
LOG_DIR="${PROJECT_DIR}/logs"
|
|
22
|
+
LOG_FILE="${LOG_DIR}/merger.log"
|
|
23
|
+
MAX_RUNTIME="${NW_MERGER_MAX_RUNTIME:-1800}"
|
|
24
|
+
MAX_LOG_SIZE="524288" # 512 KB
|
|
25
|
+
MERGE_METHOD="${NW_MERGER_MERGE_METHOD:-squash}"
|
|
26
|
+
MIN_REVIEW_SCORE="${NW_MERGER_MIN_REVIEW_SCORE:-80}"
|
|
27
|
+
REBASE_BEFORE_MERGE="${NW_MERGER_REBASE_BEFORE_MERGE:-1}"
|
|
28
|
+
MAX_PRS_PER_RUN="${NW_MERGER_MAX_PRS_PER_RUN:-0}"
|
|
29
|
+
BRANCH_PATTERNS_RAW="${NW_MERGER_BRANCH_PATTERNS:-}"
|
|
30
|
+
SCRIPT_START_TIME=$(date +%s)
|
|
31
|
+
DRY_RUN="${NW_DRY_RUN:-0}"
|
|
32
|
+
PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
|
|
33
|
+
PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
|
|
34
|
+
|
|
35
|
+
# Normalize numeric settings
|
|
36
|
+
if ! [[ "${MAX_PRS_PER_RUN}" =~ ^[0-9]+$ ]]; then
|
|
37
|
+
MAX_PRS_PER_RUN="0"
|
|
38
|
+
fi
|
|
39
|
+
if ! [[ "${MIN_REVIEW_SCORE}" =~ ^[0-9]+$ ]]; then
|
|
40
|
+
MIN_REVIEW_SCORE="80"
|
|
41
|
+
fi
|
|
42
|
+
# Clamp merge method to valid values
|
|
43
|
+
case "${MERGE_METHOD}" in
|
|
44
|
+
squash|merge|rebase) ;;
|
|
45
|
+
*) MERGE_METHOD="squash" ;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
mkdir -p "${LOG_DIR}"
|
|
49
|
+
|
|
50
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
51
|
+
# shellcheck source=night-watch-helpers.sh
|
|
52
|
+
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
53
|
+
|
|
54
|
+
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
55
|
+
# NOTE: Lock file path must match mergerLockPath() in src/utils/status-data.ts
|
|
56
|
+
LOCK_FILE="/tmp/night-watch-merger-${PROJECT_RUNTIME_KEY}.lock"
|
|
57
|
+
SCRIPT_TYPE="merger"
|
|
58
|
+
|
|
59
|
+
MERGED_PRS=0
|
|
60
|
+
FAILED_PRS=0
|
|
61
|
+
MERGED_PR_LIST=""
|
|
62
|
+
|
|
63
|
+
emit_result() {
|
|
64
|
+
local status="${1:?status required}"
|
|
65
|
+
local details="${2:-}"
|
|
66
|
+
if [ -n "${details}" ]; then
|
|
67
|
+
echo "NIGHT_WATCH_RESULT:${status}|${details}"
|
|
68
|
+
else
|
|
69
|
+
echo "NIGHT_WATCH_RESULT:${status}"
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
74
|
+
# Atomically claim a DB slot or enqueue for later dispatch — no flock needed.
|
|
75
|
+
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
76
|
+
if [ "${NW_QUEUE_DISPATCHED:-0}" = "1" ]; then
|
|
77
|
+
arm_global_queue_cleanup
|
|
78
|
+
else
|
|
79
|
+
claim_or_enqueue "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
# Check if branch matches configured patterns
|
|
85
|
+
matches_branch_patterns() {
|
|
86
|
+
local branch="${1}"
|
|
87
|
+
if [ -z "${BRANCH_PATTERNS_RAW}" ]; then
|
|
88
|
+
return 0 # No filter = match all
|
|
89
|
+
fi
|
|
90
|
+
IFS=',' read -ra patterns <<< "${BRANCH_PATTERNS_RAW}"
|
|
91
|
+
for pattern in "${patterns[@]}"; do
|
|
92
|
+
pattern="${pattern# }" # trim leading space
|
|
93
|
+
if [ -n "${pattern}" ] && [[ "${branch}" == "${pattern}"* ]]; then
|
|
94
|
+
return 0
|
|
95
|
+
fi
|
|
96
|
+
done
|
|
97
|
+
return 1
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Get review score from PR labels/comments
|
|
101
|
+
get_review_score() {
|
|
102
|
+
local pr_number="${1}"
|
|
103
|
+
# Look for review score comment from night-watch
|
|
104
|
+
local score
|
|
105
|
+
score=$(gh pr view "${pr_number}" --json comments \
|
|
106
|
+
--jq '[.comments[].body | select(test("review score|score:? [0-9]+/100"; "i")) | capture("(?i)score:? *(?<s>[0-9]+)/100") | .s] | last | tonumber // -1' \
|
|
107
|
+
2>/dev/null || echo "-1")
|
|
108
|
+
echo "${score}"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Check if CI is passing for a PR (all checks must be complete and none failing)
|
|
112
|
+
ci_passing() {
|
|
113
|
+
local pr_number="${1}"
|
|
114
|
+
local checks_json
|
|
115
|
+
checks_json=$(gh pr checks "${pr_number}" --json name,state,conclusion 2>/dev/null || echo "[]")
|
|
116
|
+
# Fail if any checks have explicit failures
|
|
117
|
+
local fail_count
|
|
118
|
+
fail_count=$(echo "${checks_json}" | jq '[.[] | select(.conclusion == "FAILURE" or .conclusion == "TIMED_OUT" or .conclusion == "CANCELLED" or .state == "FAILURE")] | length' 2>/dev/null || echo "999")
|
|
119
|
+
if [ "${fail_count}" != "0" ]; then
|
|
120
|
+
return 1
|
|
121
|
+
fi
|
|
122
|
+
# Fail if any checks are still pending/in-progress (not yet concluded)
|
|
123
|
+
local pending_count
|
|
124
|
+
pending_count=$(echo "${checks_json}" | jq '[.[] | select(.state == "PENDING" or .state == "IN_PROGRESS" or (.conclusion == null and .state != "SUCCESS"))] | length' 2>/dev/null || echo "999")
|
|
125
|
+
if [ "${pending_count}" != "0" ]; then
|
|
126
|
+
return 1
|
|
127
|
+
fi
|
|
128
|
+
return 0
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Rebase a PR against its base branch
|
|
132
|
+
rebase_pr() {
|
|
133
|
+
local pr_number="${1}"
|
|
134
|
+
log "INFO: Rebasing PR #${pr_number} against base branch"
|
|
135
|
+
if [ "${DRY_RUN}" = "1" ]; then
|
|
136
|
+
log "INFO: [DRY RUN] Would rebase PR #${pr_number}"
|
|
137
|
+
return 0
|
|
138
|
+
fi
|
|
139
|
+
gh pr update-branch --rebase "${pr_number}" 2>/dev/null
|
|
140
|
+
return $?
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
log() {
|
|
144
|
+
echo "[$(date '+%Y-%m-%dT%H:%M:%S')] $*" | tee -a "${LOG_FILE}"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
rotate_log() {
|
|
148
|
+
if [ -f "${LOG_FILE}" ] && [ "$(stat -c%s "${LOG_FILE}" 2>/dev/null || echo 0)" -ge "${MAX_LOG_SIZE}" ]; then
|
|
149
|
+
mv "${LOG_FILE}" "${LOG_FILE}.bak" 2>/dev/null || true
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# ── Log rotation ──────────────────────────────────────────────────────────────
|
|
154
|
+
rotate_log
|
|
155
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
cd "${PROJECT_DIR}"
|
|
158
|
+
|
|
159
|
+
log "========================================"
|
|
160
|
+
log "RUN-START: merger invoked project=${PROJECT_DIR} dry_run=${DRY_RUN}"
|
|
161
|
+
log "CONFIG: merge_method=${MERGE_METHOD} min_review_score=${MIN_REVIEW_SCORE} rebase_before_merge=${REBASE_BEFORE_MERGE} max_prs=${MAX_PRS_PER_RUN} max_runtime=${MAX_RUNTIME}s branch_patterns=${BRANCH_PATTERNS_RAW:-<all>}"
|
|
162
|
+
log "========================================"
|
|
163
|
+
|
|
164
|
+
if ! acquire_lock "${LOCK_FILE}"; then
|
|
165
|
+
emit_result "skip_locked"
|
|
166
|
+
exit 0
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# ── Dry-run mode ────────────────────────────────────────────────────────────
|
|
170
|
+
if [ "${DRY_RUN}" = "1" ]; then
|
|
171
|
+
echo "=== Dry Run: Merge Orchestrator ==="
|
|
172
|
+
echo "Merge Method: ${MERGE_METHOD}"
|
|
173
|
+
echo "Min Review Score: ${MIN_REVIEW_SCORE}/100"
|
|
174
|
+
echo "Rebase Before Merge: ${REBASE_BEFORE_MERGE}"
|
|
175
|
+
echo "Max PRs Per Run: ${MAX_PRS_PER_RUN} (0=unlimited)"
|
|
176
|
+
echo "Max Runtime: ${MAX_RUNTIME}s"
|
|
177
|
+
echo "Branch Patterns: ${BRANCH_PATTERNS_RAW:-<all>}"
|
|
178
|
+
log "INFO: Dry run mode — exiting without processing"
|
|
179
|
+
emit_result "skip_dry_run"
|
|
180
|
+
exit 0
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# Timeout watchdog
|
|
184
|
+
(
|
|
185
|
+
sleep "${MAX_RUNTIME}"
|
|
186
|
+
log "TIMEOUT: Merger exceeded ${MAX_RUNTIME}s, terminating"
|
|
187
|
+
kill -TERM $$ 2>/dev/null || true
|
|
188
|
+
) &
|
|
189
|
+
WATCHDOG_PID=$!
|
|
190
|
+
trap 'kill ${WATCHDOG_PID} 2>/dev/null || true; rm -f "${LOCK_FILE}"' EXIT
|
|
191
|
+
|
|
192
|
+
# Discover open PRs sorted by creation date (oldest first = FIFO)
|
|
193
|
+
log "INFO: Scanning open PRs..."
|
|
194
|
+
PR_LIST_JSON=$(gh pr list --state open \
|
|
195
|
+
--json number,headRefName,createdAt,isDraft \
|
|
196
|
+
--jq 'sort_by(.createdAt)' \
|
|
197
|
+
2>/dev/null || echo "[]")
|
|
198
|
+
|
|
199
|
+
PR_COUNT=$(echo "${PR_LIST_JSON}" | jq 'length')
|
|
200
|
+
log "INFO: Found ${PR_COUNT} open PRs"
|
|
201
|
+
|
|
202
|
+
if [ "${PR_COUNT}" = "0" ]; then
|
|
203
|
+
log "SKIP: No open PRs found. Exiting."
|
|
204
|
+
emit_result "skip_no_prs"
|
|
205
|
+
exit 0
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# Process each PR in FIFO order
|
|
209
|
+
PROCESSED=0
|
|
210
|
+
while IFS= read -r pr_json; do
|
|
211
|
+
pr_number=$(echo "${pr_json}" | jq -r '.number')
|
|
212
|
+
pr_branch=$(echo "${pr_json}" | jq -r '.headRefName')
|
|
213
|
+
is_draft=$(echo "${pr_json}" | jq -r '.isDraft')
|
|
214
|
+
|
|
215
|
+
# Skip drafts
|
|
216
|
+
if [ "${is_draft}" = "true" ]; then
|
|
217
|
+
log "INFO: PR #${pr_number} (${pr_branch}): Skipping draft"
|
|
218
|
+
continue
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Check branch pattern filter
|
|
222
|
+
if ! matches_branch_patterns "${pr_branch}"; then
|
|
223
|
+
log "DEBUG: PR #${pr_number} (${pr_branch}): Branch pattern mismatch, skipping"
|
|
224
|
+
continue
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# Check CI status
|
|
228
|
+
if ! ci_passing "${pr_number}"; then
|
|
229
|
+
log "INFO: PR #${pr_number} (${pr_branch}): CI not passing, skipping"
|
|
230
|
+
continue
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Check review score
|
|
234
|
+
if [ "${MIN_REVIEW_SCORE}" -gt "0" ]; then
|
|
235
|
+
score=$(get_review_score "${pr_number}")
|
|
236
|
+
if [ "${score}" -lt "0" ] || [ "${score}" -lt "${MIN_REVIEW_SCORE}" ]; then
|
|
237
|
+
log "INFO: PR #${pr_number} (${pr_branch}): Review score ${score} < ${MIN_REVIEW_SCORE} (or no score found), skipping"
|
|
238
|
+
continue
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
log "INFO: PR #${pr_number} (${pr_branch}): Eligible for merge"
|
|
243
|
+
|
|
244
|
+
# Rebase before merge if configured
|
|
245
|
+
if [ "${REBASE_BEFORE_MERGE}" = "1" ]; then
|
|
246
|
+
if ! rebase_pr "${pr_number}"; then
|
|
247
|
+
log "WARN: PR #${pr_number}: Rebase failed, skipping"
|
|
248
|
+
FAILED_PRS=$((FAILED_PRS + 1))
|
|
249
|
+
continue
|
|
250
|
+
fi
|
|
251
|
+
log "INFO: PR #${pr_number}: Rebase successful"
|
|
252
|
+
|
|
253
|
+
# Poll CI until all checks complete after rebase (up to 5 minutes)
|
|
254
|
+
local ci_max_wait=300
|
|
255
|
+
local ci_waited=0
|
|
256
|
+
local ci_poll=15
|
|
257
|
+
while [ "${ci_waited}" -lt "${ci_max_wait}" ]; do
|
|
258
|
+
sleep "${ci_poll}"
|
|
259
|
+
ci_waited=$((ci_waited + ci_poll))
|
|
260
|
+
if ci_passing "${pr_number}"; then
|
|
261
|
+
break
|
|
262
|
+
fi
|
|
263
|
+
log "INFO: PR #${pr_number}: Waiting for CI after rebase (${ci_waited}s/${ci_max_wait}s)..."
|
|
264
|
+
done
|
|
265
|
+
if ! ci_passing "${pr_number}"; then
|
|
266
|
+
log "INFO: PR #${pr_number}: CI not passing after rebase (waited ${ci_waited}s), skipping"
|
|
267
|
+
continue
|
|
268
|
+
fi
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# Merge the PR
|
|
272
|
+
log "INFO: Merging PR #${pr_number} with method: ${MERGE_METHOD}..."
|
|
273
|
+
if gh pr merge "${pr_number}" "--${MERGE_METHOD}" --delete-branch 2>&1 | tee -a "${LOG_FILE}"; then
|
|
274
|
+
log "INFO: PR #${pr_number}: Merged successfully"
|
|
275
|
+
MERGED_PRS=$((MERGED_PRS + 1))
|
|
276
|
+
MERGED_PR_LIST="${MERGED_PR_LIST}${pr_number},"
|
|
277
|
+
|
|
278
|
+
# Rebase remaining PRs after each successful merge
|
|
279
|
+
log "INFO: Rebasing remaining open PRs after merging #${pr_number}..."
|
|
280
|
+
REMAINING_JSON=$(gh pr list --state open \
|
|
281
|
+
--json number,headRefName \
|
|
282
|
+
2>/dev/null || echo "[]")
|
|
283
|
+
while IFS= read -r remaining_pr; do
|
|
284
|
+
remaining_number=$(echo "${remaining_pr}" | jq -r '.number')
|
|
285
|
+
remaining_branch=$(echo "${remaining_pr}" | jq -r '.headRefName')
|
|
286
|
+
if [ "${remaining_number}" != "${pr_number}" ]; then
|
|
287
|
+
log "INFO: Rebasing remaining PR #${remaining_number} (${remaining_branch})"
|
|
288
|
+
gh pr update-branch --rebase "${remaining_number}" 2>/dev/null || \
|
|
289
|
+
log "WARN: PR #${remaining_number}: Rebase failed (continuing)"
|
|
290
|
+
fi
|
|
291
|
+
done < <(echo "${REMAINING_JSON}" | jq -c '.[]')
|
|
292
|
+
else
|
|
293
|
+
log "WARN: PR #${pr_number}: Merge failed"
|
|
294
|
+
FAILED_PRS=$((FAILED_PRS + 1))
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
PROCESSED=$((PROCESSED + 1))
|
|
298
|
+
|
|
299
|
+
# Check max PRs per run limit
|
|
300
|
+
if [ "${MAX_PRS_PER_RUN}" -gt "0" ] && [ "${PROCESSED}" -ge "${MAX_PRS_PER_RUN}" ]; then
|
|
301
|
+
log "INFO: Reached max PRs per run limit (${MAX_PRS_PER_RUN}). Stopping."
|
|
302
|
+
break
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
# Enforce global timeout
|
|
306
|
+
elapsed=$(( $(date +%s) - SCRIPT_START_TIME ))
|
|
307
|
+
if [ "${elapsed}" -ge "${MAX_RUNTIME}" ]; then
|
|
308
|
+
log "WARN: Global timeout reached (${MAX_RUNTIME}s), stopping early"
|
|
309
|
+
break
|
|
310
|
+
fi
|
|
311
|
+
done < <(echo "${PR_LIST_JSON}" | jq -c '.[]')
|
|
312
|
+
|
|
313
|
+
# Trim trailing comma from PR list
|
|
314
|
+
MERGED_PR_LIST="${MERGED_PR_LIST%,}"
|
|
315
|
+
|
|
316
|
+
log "========================================"
|
|
317
|
+
log "RUN-END: merger complete merged=${MERGED_PRS} failed=${FAILED_PRS}"
|
|
318
|
+
log "========================================"
|
|
319
|
+
|
|
320
|
+
emit_result "success" "merged=${MERGED_PRS}|failed=${FAILED_PRS}|prs=${MERGED_PR_LIST}"
|
|
321
|
+
exit 0
|