@jonit-dev/night-watch-cli 1.8.13 → 1.8.14-beta.1
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 +52 -87
- package/dist/commands/dashboard/tab-config.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-config.js +1 -11
- package/dist/commands/dashboard/tab-config.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -6
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -59
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/merge.d.ts.map +1 -1
- package/dist/commands/merge.js +4 -0
- package/dist/commands/merge.js.map +1 -1
- package/dist/commands/notify.d.ts.map +1 -1
- package/dist/commands/notify.js +3 -13
- package/dist/commands/notify.js.map +1 -1
- package/dist/scripts/night-watch-merger-cron.sh +118 -9
- package/dist/web/assets/index-6Yf-Q6Di.js +442 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/commands/notify.js
CHANGED
|
@@ -3,17 +3,7 @@
|
|
|
3
3
|
* Designed for bash script integration.
|
|
4
4
|
*/
|
|
5
5
|
import { basename } from 'path';
|
|
6
|
-
import { loadConfig, sendNotifications } from '@night-watch/core';
|
|
7
|
-
const VALID_EVENTS = [
|
|
8
|
-
'run_started',
|
|
9
|
-
'run_succeeded',
|
|
10
|
-
'run_failed',
|
|
11
|
-
'run_timeout',
|
|
12
|
-
'review_completed',
|
|
13
|
-
'rate_limit_fallback',
|
|
14
|
-
'pr_auto_merged',
|
|
15
|
-
'qa_completed',
|
|
16
|
-
];
|
|
6
|
+
import { NOTIFICATION_EVENTS, loadConfig, sendNotifications } from '@night-watch/core';
|
|
17
7
|
export function notifyCommand(program) {
|
|
18
8
|
program
|
|
19
9
|
.command('notify <event> <projectDir>')
|
|
@@ -24,8 +14,8 @@ export function notifyCommand(program) {
|
|
|
24
14
|
.option('--exit-code <n>', 'Exit code', '0')
|
|
25
15
|
.option('--pr-number <n>', 'PR number')
|
|
26
16
|
.action(async (event, projectDir, options) => {
|
|
27
|
-
if (!
|
|
28
|
-
process.stderr.write(`Invalid event: ${event}. Must be one of: ${
|
|
17
|
+
if (!NOTIFICATION_EVENTS.includes(event)) {
|
|
18
|
+
process.stderr.write(`Invalid event: ${event}. Must be one of: ${NOTIFICATION_EVENTS.join(', ')}\n`);
|
|
29
19
|
process.exit(2);
|
|
30
20
|
}
|
|
31
21
|
const config = loadConfig(projectDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGvF,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO;SACJ,OAAO,CAAC,6BAA6B,CAAC;SACtC,WAAW,CAAC,mDAAmD,CAAC;SAChE,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC;SAClC,MAAM,CAAC,iBAAiB,EAAE,aAAa,CAAC;SACxC,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC;SAC5C,MAAM,CAAC,iBAAiB,EAAE,WAAW,EAAE,GAAG,CAAC;SAC3C,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC;SACtC,MAAM,CACL,KAAK,EACH,KAAa,EACb,UAAkB,EAClB,OAMC,EACD,EAAE;QACF,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,KAA0B,CAAC,EAAE,CAAC;YAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kBAAkB,KAAK,qBAAqB,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAC/E,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;QAEtC,MAAM,iBAAiB,CAAC,MAAM,EAAE;YAC9B,KAAK,EAAE,KAA0B;YACjC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC;YACjC,OAAO,EAAE,OAAO,CAAC,GAAG;YACpB,UAAU,EAAE,OAAO,CAAC,MAAM;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;YAC7C,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;SACxE,CAAC,CAAC;IACL,CAAC,CACF,CAAC;AACN,CAAC"}
|
|
@@ -14,6 +14,8 @@ set -euo pipefail
|
|
|
14
14
|
# NW_MERGER_BRANCH_PATTERNS= - Comma-separated branch prefixes (empty = all)
|
|
15
15
|
# NW_MERGER_REBASE_BEFORE_MERGE=1 - Set to 1 to rebase before merging
|
|
16
16
|
# NW_MERGER_MAX_PRS_PER_RUN=0 - Max PRs to merge per run (0 = unlimited)
|
|
17
|
+
# NW_MERGER_CI_POLICY=fallback-local - CI gate: ci-only|fallback-local|ignore
|
|
18
|
+
# NW_MERGER_LOCAL_CHECK_COMMAND=... - Command run in temp PR worktree for local fallback
|
|
17
19
|
# NW_MERGER_CI_MAX_WAIT=300 - Max seconds to wait for checks after rebase
|
|
18
20
|
# NW_MERGER_CI_POLL_INTERVAL=15 - Seconds between check polls after rebase
|
|
19
21
|
# NW_DRY_RUN=0 - Set to 1 for dry-run mode
|
|
@@ -30,6 +32,8 @@ REBASE_BEFORE_MERGE="${NW_MERGER_REBASE_BEFORE_MERGE:-1}"
|
|
|
30
32
|
MAX_PRS_PER_RUN="${NW_MERGER_MAX_PRS_PER_RUN:-0}"
|
|
31
33
|
CI_MAX_WAIT="${NW_MERGER_CI_MAX_WAIT:-300}"
|
|
32
34
|
CI_POLL_INTERVAL="${NW_MERGER_CI_POLL_INTERVAL:-15}"
|
|
35
|
+
CI_POLICY="${NW_MERGER_CI_POLICY:-fallback-local}"
|
|
36
|
+
LOCAL_CHECK_COMMAND="${NW_MERGER_LOCAL_CHECK_COMMAND:-yarn install --frozen-lockfile && yarn verify && yarn test}"
|
|
33
37
|
BRANCH_PATTERNS_RAW="${NW_MERGER_BRANCH_PATTERNS:-}"
|
|
34
38
|
READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
|
|
35
39
|
SCRIPT_START_TIME=$(date +%s)
|
|
@@ -55,6 +59,10 @@ case "${MERGE_METHOD}" in
|
|
|
55
59
|
squash|merge|rebase) ;;
|
|
56
60
|
*) MERGE_METHOD="squash" ;;
|
|
57
61
|
esac
|
|
62
|
+
case "${CI_POLICY}" in
|
|
63
|
+
ci-only|fallback-local|ignore) ;;
|
|
64
|
+
*) CI_POLICY="fallback-local" ;;
|
|
65
|
+
esac
|
|
58
66
|
|
|
59
67
|
mkdir -p "${LOG_DIR}"
|
|
60
68
|
|
|
@@ -261,6 +269,97 @@ wait_for_ci_passing_on_head() {
|
|
|
261
269
|
[ "${LAST_CI_STATUS}" = "passing" ]
|
|
262
270
|
}
|
|
263
271
|
|
|
272
|
+
run_local_checks_for_head() {
|
|
273
|
+
local pr_number="${1}"
|
|
274
|
+
local expected_head="${2}"
|
|
275
|
+
local temp_parent=""
|
|
276
|
+
local worktree_dir=""
|
|
277
|
+
local temp_ref=""
|
|
278
|
+
local check_exit=1
|
|
279
|
+
|
|
280
|
+
if [ -z "${LOCAL_CHECK_COMMAND}" ]; then
|
|
281
|
+
log "INFO: PR #${pr_number}: Local check command is empty, treating fallback as failed"
|
|
282
|
+
return 1
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
temp_parent=$(mktemp -d "${TMPDIR:-/tmp}/night-watch-merger-${pr_number}.XXXXXX")
|
|
286
|
+
worktree_dir="${temp_parent}/worktree"
|
|
287
|
+
temp_ref="refs/night-watch/merger/${pr_number}-${expected_head}"
|
|
288
|
+
|
|
289
|
+
cleanup_local_check_worktree() {
|
|
290
|
+
git worktree remove --force "${worktree_dir}" >/dev/null 2>&1 || true
|
|
291
|
+
git update-ref -d "${temp_ref}" >/dev/null 2>&1 || true
|
|
292
|
+
rm -rf "${temp_parent}" >/dev/null 2>&1 || true
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
log "INFO: PR #${pr_number}: Running local checks for head ${expected_head}: ${LOCAL_CHECK_COMMAND}"
|
|
296
|
+
|
|
297
|
+
if ! git cat-file -e "${expected_head}^{commit}" >/dev/null 2>&1; then
|
|
298
|
+
log "INFO: PR #${pr_number}: Fetching PR head ${expected_head} for local checks"
|
|
299
|
+
git fetch --quiet --force origin "pull/${pr_number}/head:${temp_ref}" >/dev/null 2>&1 || {
|
|
300
|
+
log "INFO: PR #${pr_number}: Unable to fetch PR head for local checks"
|
|
301
|
+
cleanup_local_check_worktree
|
|
302
|
+
return 1
|
|
303
|
+
}
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
if ! git worktree add --detach --quiet "${worktree_dir}" "${expected_head}" >/dev/null 2>&1; then
|
|
307
|
+
if ! git worktree add --detach --quiet "${worktree_dir}" "${temp_ref}" >/dev/null 2>&1; then
|
|
308
|
+
log "INFO: PR #${pr_number}: Unable to create local check worktree"
|
|
309
|
+
cleanup_local_check_worktree
|
|
310
|
+
return 1
|
|
311
|
+
fi
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
set +e
|
|
315
|
+
(
|
|
316
|
+
cd "${worktree_dir}"
|
|
317
|
+
bash -lc "${LOCAL_CHECK_COMMAND}"
|
|
318
|
+
) 2>&1 | tee -a "${LOG_FILE}"
|
|
319
|
+
check_exit=${PIPESTATUS[0]}
|
|
320
|
+
set -e
|
|
321
|
+
|
|
322
|
+
cleanup_local_check_worktree
|
|
323
|
+
|
|
324
|
+
if [ "${check_exit}" -eq 0 ]; then
|
|
325
|
+
log "INFO: PR #${pr_number}: Local checks passed for head ${expected_head}"
|
|
326
|
+
return 0
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
log "INFO: PR #${pr_number}: Local checks failed for head ${expected_head} (exit ${check_exit})"
|
|
330
|
+
return 1
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
ci_gate_allows_head() {
|
|
334
|
+
local pr_number="${1}"
|
|
335
|
+
local pr_branch="${2}"
|
|
336
|
+
local expected_head="${3}"
|
|
337
|
+
local context="${4:-CI}"
|
|
338
|
+
local ci_status
|
|
339
|
+
|
|
340
|
+
if [ "${CI_POLICY}" = "ignore" ]; then
|
|
341
|
+
log "INFO: PR #${pr_number} (${pr_branch}): CI policy is ignore; skipping ${context} gate for head ${expected_head}"
|
|
342
|
+
return 0
|
|
343
|
+
fi
|
|
344
|
+
|
|
345
|
+
ci_status=$(ci_status_for_head "${pr_number}" "${expected_head}")
|
|
346
|
+
if [ "${ci_status}" = "passing" ]; then
|
|
347
|
+
return 0
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
if [ "${CI_POLICY}" = "fallback-local" ]; then
|
|
351
|
+
log "INFO: PR #${pr_number} (${pr_branch}): ${context} not passing on head ${expected_head} (${ci_status}); trying local checks"
|
|
352
|
+
if run_local_checks_for_head "${pr_number}" "${expected_head}"; then
|
|
353
|
+
return 0
|
|
354
|
+
fi
|
|
355
|
+
log "INFO: PR #${pr_number} (${pr_branch}): Local check fallback failed, skipping"
|
|
356
|
+
return 1
|
|
357
|
+
fi
|
|
358
|
+
|
|
359
|
+
log "INFO: PR #${pr_number} (${pr_branch}): ${context} not passing on head ${expected_head} (${ci_status}), skipping"
|
|
360
|
+
return 1
|
|
361
|
+
}
|
|
362
|
+
|
|
264
363
|
# Rebase a PR against its base branch
|
|
265
364
|
rebase_pr() {
|
|
266
365
|
local pr_number="${1}"
|
|
@@ -308,7 +407,7 @@ cd "${PROJECT_DIR}"
|
|
|
308
407
|
|
|
309
408
|
log "========================================"
|
|
310
409
|
log "RUN-START: merger invoked project=${PROJECT_DIR} dry_run=${DRY_RUN}"
|
|
311
|
-
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 ready_label=${READY_TO_MERGE_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW:-<all>}"
|
|
410
|
+
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 ci_policy=${CI_POLICY} local_check_command=${LOCAL_CHECK_COMMAND} ready_label=${READY_TO_MERGE_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW:-<all>}"
|
|
312
411
|
log "========================================"
|
|
313
412
|
|
|
314
413
|
if ! acquire_lock "${LOCK_FILE}"; then
|
|
@@ -391,10 +490,8 @@ while IFS= read -r pr_json; do
|
|
|
391
490
|
continue
|
|
392
491
|
fi
|
|
393
492
|
|
|
394
|
-
# Check CI status
|
|
395
|
-
|
|
396
|
-
if [ "${ci_status}" != "passing" ]; then
|
|
397
|
-
log "INFO: PR #${pr_number} (${pr_branch}): CI not passing on head ${pr_head_oid} (${ci_status}), skipping"
|
|
493
|
+
# Check CI status, optionally falling back to local checks for provider/billing failures.
|
|
494
|
+
if ! ci_gate_allows_head "${pr_number}" "${pr_branch}" "${pr_head_oid}" "CI"; then
|
|
398
495
|
continue
|
|
399
496
|
fi
|
|
400
497
|
|
|
@@ -436,10 +533,22 @@ while IFS= read -r pr_json; do
|
|
|
436
533
|
log "INFO: PR #${pr_number}: Head unchanged after rebase (${pr_head_after_rebase}); confirming CI"
|
|
437
534
|
fi
|
|
438
535
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
536
|
+
if [ "${CI_POLICY}" = "ignore" ]; then
|
|
537
|
+
log "INFO: PR #${pr_number}: CI policy is ignore; skipping fresh CI gate after rebase"
|
|
538
|
+
else
|
|
539
|
+
# Poll CI until all checks attached to the post-rebase head are complete and passing.
|
|
540
|
+
if ! wait_for_ci_passing_on_head "${pr_number}" "${pr_head_after_rebase}"; then
|
|
541
|
+
log "INFO: PR #${pr_number}: Fresh CI not passing on head ${pr_head_after_rebase} after rebase (${LAST_CI_STATUS}, waited ${CI_MAX_WAIT}s)"
|
|
542
|
+
if [ "${CI_POLICY}" = "fallback-local" ]; then
|
|
543
|
+
if ! run_local_checks_for_head "${pr_number}" "${pr_head_after_rebase}"; then
|
|
544
|
+
log "INFO: PR #${pr_number}: Fresh local check fallback failed after rebase, skipping"
|
|
545
|
+
continue
|
|
546
|
+
fi
|
|
547
|
+
else
|
|
548
|
+
log "INFO: PR #${pr_number}: Skipping because CI policy requires GitHub CI"
|
|
549
|
+
continue
|
|
550
|
+
fi
|
|
551
|
+
fi
|
|
443
552
|
fi
|
|
444
553
|
fi
|
|
445
554
|
|