@jonit-dev/night-watch-cli 1.8.12-beta.0 → 1.8.12-beta.3
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.d.ts +3 -0
- package/dist/cli.js +1812 -631
- package/dist/cli.js.map +1 -0
- package/dist/commands/agent.d.ts +12 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +307 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/analytics.d.ts +14 -0
- package/dist/commands/analytics.js +69 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/commands/audit.d.ts +19 -0
- package/dist/commands/audit.js +144 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/board.d.ts +9 -0
- package/dist/commands/board.js +702 -0
- package/dist/commands/board.js.map +1 -0
- package/dist/commands/cancel.d.ts +46 -0
- package/dist/commands/cancel.js +239 -0
- package/dist/commands/cancel.js.map +1 -0
- package/dist/commands/cron.d.ts +8 -0
- package/dist/commands/cron.js +134 -0
- package/dist/commands/cron.js.map +1 -0
- package/dist/commands/dashboard/tab-actions.d.ts +10 -0
- package/dist/commands/dashboard/tab-actions.js +247 -0
- package/dist/commands/dashboard/tab-actions.js.map +1 -0
- package/dist/commands/dashboard/tab-config.d.ts +21 -0
- package/dist/commands/dashboard/tab-config.js +874 -0
- package/dist/commands/dashboard/tab-config.js.map +1 -0
- package/dist/commands/dashboard/tab-logs.d.ts +10 -0
- package/dist/commands/dashboard/tab-logs.js +202 -0
- package/dist/commands/dashboard/tab-logs.js.map +1 -0
- package/dist/commands/dashboard/tab-schedules.d.ts +21 -0
- package/dist/commands/dashboard/tab-schedules.js +320 -0
- package/dist/commands/dashboard/tab-schedules.js.map +1 -0
- package/dist/commands/dashboard/tab-status.d.ts +32 -0
- package/dist/commands/dashboard/tab-status.js +424 -0
- package/dist/commands/dashboard/tab-status.js.map +1 -0
- package/dist/commands/dashboard/types.d.ts +42 -0
- package/dist/commands/dashboard/types.js +5 -0
- package/dist/commands/dashboard/types.js.map +1 -0
- package/dist/commands/dashboard.d.ts +11 -0
- package/dist/commands/dashboard.js +242 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/doctor.d.ts +16 -0
- package/dist/commands/doctor.js +195 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/history.d.ts +7 -0
- package/dist/commands/history.js +49 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/init.d.ts +45 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +65 -0
- package/dist/commands/install.js +405 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/logs.d.ts +15 -0
- package/dist/commands/logs.js +155 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/merge.d.ts +26 -0
- package/dist/commands/merge.js +159 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/notify.d.ts +7 -0
- package/dist/commands/notify.js +43 -0
- package/dist/commands/notify.js.map +1 -0
- package/dist/commands/plan.d.ts +19 -0
- package/dist/commands/plan.js +88 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/prd-state.d.ts +12 -0
- package/dist/commands/prd-state.js +47 -0
- package/dist/commands/prd-state.js.map +1 -0
- package/dist/commands/prd.d.ts +18 -0
- package/dist/commands/prd.js +363 -0
- package/dist/commands/prd.js.map +1 -0
- package/dist/commands/prds.d.ts +13 -0
- package/dist/commands/prds.js +194 -0
- package/dist/commands/prds.js.map +1 -0
- package/dist/commands/prs.d.ts +14 -0
- package/dist/commands/prs.js +104 -0
- package/dist/commands/prs.js.map +1 -0
- package/dist/commands/qa.d.ts +34 -0
- package/dist/commands/qa.js +214 -0
- package/dist/commands/qa.js.map +1 -0
- package/dist/commands/queue.d.ts +8 -0
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +398 -0
- package/dist/commands/queue.js.map +1 -0
- package/dist/commands/resolve.d.ts +26 -0
- package/dist/commands/resolve.js +186 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/retry.d.ts +9 -0
- package/dist/commands/retry.js +71 -0
- package/dist/commands/retry.js.map +1 -0
- package/dist/commands/review.d.ts +77 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +1 -74
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +73 -0
- package/dist/commands/serve.d.ts +19 -0
- package/dist/commands/serve.js +142 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/shared/env-builder.d.ts +49 -0
- package/dist/commands/shared/env-builder.d.ts.map +1 -1
- package/dist/commands/shared/env-builder.js +152 -0
- package/dist/commands/shared/env-builder.js.map +1 -0
- package/dist/commands/slice.d.ts +35 -0
- package/dist/commands/slice.js +316 -0
- package/dist/commands/slice.js.map +1 -0
- package/dist/commands/state.d.ts +8 -0
- package/dist/commands/state.js +54 -0
- package/dist/commands/state.js.map +1 -0
- package/dist/commands/status.d.ts +14 -0
- package/dist/commands/status.js +297 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/summary.d.ts +14 -0
- package/dist/commands/summary.js +193 -0
- package/dist/commands/summary.js.map +1 -0
- package/dist/commands/uninstall.d.ts +25 -0
- package/dist/commands/uninstall.js +134 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +22 -0
- package/dist/commands/update.js +90 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/scripts/night-watch-audit-cron.sh +1 -0
- package/dist/scripts/night-watch-cron.sh +1 -0
- package/dist/scripts/night-watch-helpers.sh +22 -1
- package/dist/scripts/night-watch-merger-cron.sh +32 -3
- package/dist/scripts/night-watch-plan-cron.sh +2 -0
- package/dist/scripts/night-watch-pr-resolver-cron.sh +6 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +18 -5
- package/dist/scripts/night-watch-qa-cron.sh +8 -1
- package/dist/scripts/night-watch-slicer-cron.sh +3 -0
- package/dist/templates/night-watch.config.json +21 -20
- package/dist/web/assets/index-C-xpWpS8.css +1 -0
- package/dist/web/assets/index-CEYe-290.js +412 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uninstall command for Night Watch CLI
|
|
3
|
+
* Removes crontab entries for the current project
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import { dim, generateMarker, getEntries, getProjectEntries, getProjectName, removeEntriesForProject, success, error as uiError, unregisterProject, warn, } from '@night-watch/core';
|
|
8
|
+
/**
|
|
9
|
+
* Core uninstall logic, reusable from dashboard.
|
|
10
|
+
* Returns result without printing to console.
|
|
11
|
+
*/
|
|
12
|
+
export function performUninstall(projectDir, options) {
|
|
13
|
+
try {
|
|
14
|
+
const projectName = getProjectName(projectDir);
|
|
15
|
+
const marker = generateMarker(projectName);
|
|
16
|
+
const existingEntries = Array.from(new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
|
|
17
|
+
if (existingEntries.length === 0) {
|
|
18
|
+
unregisterProject(projectDir);
|
|
19
|
+
return { success: true, removedCount: 0 };
|
|
20
|
+
}
|
|
21
|
+
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
22
|
+
unregisterProject(projectDir);
|
|
23
|
+
if (!options?.keepLogs) {
|
|
24
|
+
const logDir = path.join(projectDir, 'logs');
|
|
25
|
+
if (fs.existsSync(logDir)) {
|
|
26
|
+
const logFiles = [
|
|
27
|
+
'executor.log',
|
|
28
|
+
'reviewer.log',
|
|
29
|
+
'slicer.log',
|
|
30
|
+
'audit.log',
|
|
31
|
+
'pr-resolver.log',
|
|
32
|
+
];
|
|
33
|
+
logFiles.forEach((logFile) => {
|
|
34
|
+
const logPath = path.join(logDir, logFile);
|
|
35
|
+
if (fs.existsSync(logPath)) {
|
|
36
|
+
fs.unlinkSync(logPath);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
const remainingFiles = fs.readdirSync(logDir);
|
|
41
|
+
if (remainingFiles.length === 0) {
|
|
42
|
+
fs.rmdirSync(logDir);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Ignore errors removing directory
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { success: true, removedCount };
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
removedCount: 0,
|
|
56
|
+
error: err instanceof Error ? err.message : String(err),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Uninstall crontab entries for night-watch
|
|
62
|
+
*/
|
|
63
|
+
export function uninstallCommand(program) {
|
|
64
|
+
program
|
|
65
|
+
.command('uninstall')
|
|
66
|
+
.description('Remove crontab entries')
|
|
67
|
+
.option('--keep-logs', 'Preserve log files')
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
try {
|
|
70
|
+
// Get project directory
|
|
71
|
+
const projectDir = process.cwd();
|
|
72
|
+
// Get project name
|
|
73
|
+
const projectName = getProjectName(projectDir);
|
|
74
|
+
const marker = generateMarker(projectName);
|
|
75
|
+
// Check if there are entries to remove
|
|
76
|
+
const existingEntries = Array.from(new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
|
|
77
|
+
if (existingEntries.length === 0) {
|
|
78
|
+
warn(`No Night Watch crontab entries found for ${projectName}.`);
|
|
79
|
+
dim('Nothing to uninstall.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Show entries that will be removed
|
|
83
|
+
dim(`Removing Night Watch crontab entries for ${projectName}:`);
|
|
84
|
+
existingEntries.forEach((entry) => dim(` ${entry}`));
|
|
85
|
+
// Remove entries
|
|
86
|
+
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
87
|
+
// Handle log files
|
|
88
|
+
if (!options.keepLogs) {
|
|
89
|
+
const logDir = path.join(projectDir, 'logs');
|
|
90
|
+
if (fs.existsSync(logDir)) {
|
|
91
|
+
const logFiles = [
|
|
92
|
+
'executor.log',
|
|
93
|
+
'reviewer.log',
|
|
94
|
+
'slicer.log',
|
|
95
|
+
'audit.log',
|
|
96
|
+
'pr-resolver.log',
|
|
97
|
+
];
|
|
98
|
+
let logsRemoved = 0;
|
|
99
|
+
logFiles.forEach((logFile) => {
|
|
100
|
+
const logPath = path.join(logDir, logFile);
|
|
101
|
+
if (fs.existsSync(logPath)) {
|
|
102
|
+
fs.unlinkSync(logPath);
|
|
103
|
+
logsRemoved++;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// Try to remove log directory if empty
|
|
107
|
+
try {
|
|
108
|
+
const remainingFiles = fs.readdirSync(logDir);
|
|
109
|
+
if (remainingFiles.length === 0) {
|
|
110
|
+
fs.rmdirSync(logDir);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Ignore errors removing directory
|
|
115
|
+
}
|
|
116
|
+
if (logsRemoved > 0) {
|
|
117
|
+
console.log();
|
|
118
|
+
dim(`Removed ${logsRemoved} log file(s).`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log();
|
|
124
|
+
dim('Log files preserved.');
|
|
125
|
+
}
|
|
126
|
+
success(`Successfully removed ${removedCount} crontab entry/entries.`);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
uiError(`Error uninstalling Night Watch: ${err instanceof Error ? err.message : String(err)}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command for Night Watch CLI
|
|
3
|
+
* Reinstalls global CLI and refreshes cron entries for one or more projects.
|
|
4
|
+
*/
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
export declare const DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
|
|
7
|
+
export interface IUpdateOptions {
|
|
8
|
+
projects?: string;
|
|
9
|
+
globalSpec: string;
|
|
10
|
+
global?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse project directories from a comma-separated CLI option.
|
|
14
|
+
* Defaults to current working directory when option is omitted.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseProjectDirs(projects: string | undefined, cwd: string): string[];
|
|
17
|
+
export declare function shouldInstallGlobal(options: Pick<IUpdateOptions, 'global'>): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Register update command.
|
|
20
|
+
*/
|
|
21
|
+
export declare function updateCommand(program: Command): void;
|
|
22
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command for Night Watch CLI
|
|
3
|
+
* Reinstalls global CLI and refreshes cron entries for one or more projects.
|
|
4
|
+
*/
|
|
5
|
+
import { spawnSync } from 'child_process';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { dim, success, error as uiError, warn } from '@night-watch/core';
|
|
9
|
+
export const DEFAULT_GLOBAL_SPEC = '@jonit-dev/night-watch-cli@latest';
|
|
10
|
+
/**
|
|
11
|
+
* Parse project directories from a comma-separated CLI option.
|
|
12
|
+
* Defaults to current working directory when option is omitted.
|
|
13
|
+
*/
|
|
14
|
+
export function parseProjectDirs(projects, cwd) {
|
|
15
|
+
if (!projects || projects.trim().length === 0) {
|
|
16
|
+
return [cwd];
|
|
17
|
+
}
|
|
18
|
+
const dirs = projects
|
|
19
|
+
.split(',')
|
|
20
|
+
.map((entry) => entry.trim())
|
|
21
|
+
.filter((entry) => entry.length > 0)
|
|
22
|
+
.map((entry) => path.resolve(cwd, entry));
|
|
23
|
+
return Array.from(new Set(dirs));
|
|
24
|
+
}
|
|
25
|
+
export function shouldInstallGlobal(options) {
|
|
26
|
+
return options.global !== false;
|
|
27
|
+
}
|
|
28
|
+
function runCommand(command, args, cwd) {
|
|
29
|
+
const result = spawnSync(command, args, {
|
|
30
|
+
cwd,
|
|
31
|
+
env: process.env,
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
});
|
|
34
|
+
if (result.error) {
|
|
35
|
+
throw result.error;
|
|
36
|
+
}
|
|
37
|
+
if ((result.status ?? 1) !== 0) {
|
|
38
|
+
const location = cwd ? ` in ${cwd}` : '';
|
|
39
|
+
throw new Error(`Command failed${location}: ${command} ${args.join(' ')}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function resolveNightWatchBin() {
|
|
43
|
+
const result = spawnSync('which', ['night-watch'], {
|
|
44
|
+
encoding: 'utf-8',
|
|
45
|
+
env: process.env,
|
|
46
|
+
});
|
|
47
|
+
if (result.status === 0 && typeof result.stdout === 'string' && result.stdout.trim().length > 0) {
|
|
48
|
+
return result.stdout.trim();
|
|
49
|
+
}
|
|
50
|
+
return 'night-watch';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Register update command.
|
|
54
|
+
*/
|
|
55
|
+
export function updateCommand(program) {
|
|
56
|
+
program
|
|
57
|
+
.command('update')
|
|
58
|
+
.description('Update global CLI and refresh cron for project(s)')
|
|
59
|
+
.option('--projects <dirs>', 'Comma-separated project directories (default: current directory)')
|
|
60
|
+
.option('--global-spec <spec>', 'npm package spec used for global install', DEFAULT_GLOBAL_SPEC)
|
|
61
|
+
.option('--no-global', 'Skip global npm install and only refresh project cron')
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
try {
|
|
64
|
+
const cwd = process.cwd();
|
|
65
|
+
const projectDirs = parseProjectDirs(options.projects, cwd);
|
|
66
|
+
if (shouldInstallGlobal(options)) {
|
|
67
|
+
dim(`Updating global install: npm install -g ${options.globalSpec}`);
|
|
68
|
+
runCommand('npm', ['install', '-g', options.globalSpec]);
|
|
69
|
+
success('Global CLI update completed.');
|
|
70
|
+
}
|
|
71
|
+
const nightWatchBin = resolveNightWatchBin();
|
|
72
|
+
for (const projectDir of projectDirs) {
|
|
73
|
+
if (!fs.existsSync(projectDir) || !fs.statSync(projectDir).isDirectory()) {
|
|
74
|
+
warn(`Skipping invalid project directory: ${projectDir}`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
dim(`Refreshing cron in ${projectDir}`);
|
|
78
|
+
runCommand(nightWatchBin, ['uninstall'], projectDir);
|
|
79
|
+
runCommand(nightWatchBin, ['install'], projectDir);
|
|
80
|
+
success(`Refreshed project: ${projectDir}`);
|
|
81
|
+
}
|
|
82
|
+
success('Update completed.');
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
uiError(`Update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,7 @@ mkdir -p "${LOG_DIR}"
|
|
|
28
28
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
29
29
|
# shellcheck source=night-watch-helpers.sh
|
|
30
30
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
31
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
31
32
|
|
|
32
33
|
# emit_result helper - must be defined before use
|
|
33
34
|
emit_result() {
|
|
@@ -50,6 +50,7 @@ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
|
50
50
|
# NOTE: Lock file path must match executorLockPath() in src/utils/status-data.ts
|
|
51
51
|
LOCK_FILE="/tmp/night-watch-${PROJECT_RUNTIME_KEY}.lock"
|
|
52
52
|
SCRIPT_TYPE="executor"
|
|
53
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
53
54
|
|
|
54
55
|
emit_result() {
|
|
55
56
|
local status="${1:?status required}"
|
|
@@ -126,6 +126,7 @@ project_git_push_command() {
|
|
|
126
126
|
NW_EXECUTOR_PARTIAL_LABEL="${NW_EXECUTOR_PARTIAL_LABEL:-nw:partial}"
|
|
127
127
|
NW_EXECUTOR_RESUMABLE_LABEL="${NW_EXECUTOR_RESUMABLE_LABEL:-nw:resumable}"
|
|
128
128
|
NW_EXECUTOR_READY_REVIEW_LABEL="${NW_EXECUTOR_READY_REVIEW_LABEL:-nw:ready-review}"
|
|
129
|
+
NW_PR_RESOLVER_READY_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
|
|
129
130
|
|
|
130
131
|
csv_has_label() {
|
|
131
132
|
local csv="${1:-}"
|
|
@@ -188,7 +189,8 @@ find_executor_resume_pr() {
|
|
|
188
189
|
printf '%s' "${pr_list}" \
|
|
189
190
|
| jq -c \
|
|
190
191
|
--arg primary_prefix "${branch_prefix}/" \
|
|
191
|
-
--arg resumable_label "${NW_EXECUTOR_RESUMABLE_LABEL}"
|
|
192
|
+
--arg resumable_label "${NW_EXECUTOR_RESUMABLE_LABEL}" \
|
|
193
|
+
--arg ready_label "${NW_PR_RESOLVER_READY_LABEL}" '
|
|
192
194
|
[
|
|
193
195
|
.[]
|
|
194
196
|
| select(
|
|
@@ -198,6 +200,7 @@ find_executor_resume_pr() {
|
|
|
198
200
|
)
|
|
199
201
|
| .labelNames = ((.labels // []) | map(.name))
|
|
200
202
|
| select((.labelNames | index($resumable_label)) != null)
|
|
203
|
+
| select((.labelNames | index($ready_label)) == null)
|
|
201
204
|
]
|
|
202
205
|
| sort_by(.createdAt // "")
|
|
203
206
|
| .[0] // empty
|
|
@@ -1335,6 +1338,22 @@ arm_global_queue_cleanup() {
|
|
|
1335
1338
|
append_exit_trap "__night_watch_queue_cleanup \$?"
|
|
1336
1339
|
}
|
|
1337
1340
|
|
|
1341
|
+
skip_if_job_paused() {
|
|
1342
|
+
local script_type="${1:?script_type required}"
|
|
1343
|
+
local project_dir="${2:?project_dir required}"
|
|
1344
|
+
|
|
1345
|
+
local cli_bin
|
|
1346
|
+
cli_bin=$(resolve_night_watch_cli) || return 0
|
|
1347
|
+
|
|
1348
|
+
if (cd "${project_dir}" && "${cli_bin}" job is-paused "${script_type}" >/dev/null 2>&1); then
|
|
1349
|
+
log "SKIP: ${script_type} is paused in night-watch.config.json"
|
|
1350
|
+
if command -v emit_result >/dev/null 2>&1; then
|
|
1351
|
+
emit_result "skip_paused"
|
|
1352
|
+
fi
|
|
1353
|
+
exit 0
|
|
1354
|
+
fi
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1338
1357
|
# Atomically claim a queue slot or enqueue for later dispatch.
|
|
1339
1358
|
# Uses DB transaction (via `queue claim` CLI) for atomicity — no flock needed.
|
|
1340
1359
|
# Sets NW_QUEUE_ENTRY_ID on success and arms the cleanup trap.
|
|
@@ -1342,6 +1361,8 @@ arm_global_queue_cleanup() {
|
|
|
1342
1361
|
claim_or_enqueue() {
|
|
1343
1362
|
local script_type="${1:?script_type required}"
|
|
1344
1363
|
local project_dir="${2:?project_dir required}"
|
|
1364
|
+
skip_if_job_paused "${script_type}" "${project_dir}"
|
|
1365
|
+
|
|
1345
1366
|
local provider_key
|
|
1346
1367
|
provider_key=$(resolve_provider_key "${project_dir}" "${script_type}")
|
|
1347
1368
|
|
|
@@ -27,6 +27,7 @@ MIN_REVIEW_SCORE="${NW_MERGER_MIN_REVIEW_SCORE:-80}"
|
|
|
27
27
|
REBASE_BEFORE_MERGE="${NW_MERGER_REBASE_BEFORE_MERGE:-1}"
|
|
28
28
|
MAX_PRS_PER_RUN="${NW_MERGER_MAX_PRS_PER_RUN:-0}"
|
|
29
29
|
BRANCH_PATTERNS_RAW="${NW_MERGER_BRANCH_PATTERNS:-}"
|
|
30
|
+
READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
|
|
30
31
|
SCRIPT_START_TIME=$(date +%s)
|
|
31
32
|
DRY_RUN="${NW_DRY_RUN:-0}"
|
|
32
33
|
PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
|
|
@@ -55,6 +56,7 @@ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
|
55
56
|
# NOTE: Lock file path must match mergerLockPath() in src/utils/status-data.ts
|
|
56
57
|
LOCK_FILE="/tmp/night-watch-merger-${PROJECT_RUNTIME_KEY}.lock"
|
|
57
58
|
SCRIPT_TYPE="merger"
|
|
59
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
58
60
|
|
|
59
61
|
MERGED_PRS=0
|
|
60
62
|
FAILED_PRS=0
|
|
@@ -140,6 +142,23 @@ rebase_pr() {
|
|
|
140
142
|
return $?
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
cleanup_watchdog() {
|
|
146
|
+
local pid="${1:-}"
|
|
147
|
+
local child_pids=""
|
|
148
|
+
|
|
149
|
+
if [ -z "${pid}" ]; then
|
|
150
|
+
return 0
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
child_pids=$(pgrep -P "${pid}" 2>/dev/null || true)
|
|
154
|
+
if [ -n "${child_pids}" ]; then
|
|
155
|
+
kill ${child_pids} 2>/dev/null || true
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
kill "${pid}" 2>/dev/null || true
|
|
159
|
+
wait "${pid}" 2>/dev/null || true
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
log() {
|
|
144
163
|
echo "[$(date '+%Y-%m-%dT%H:%M:%S')] $*" | tee -a "${LOG_FILE}"
|
|
145
164
|
}
|
|
@@ -158,7 +177,7 @@ cd "${PROJECT_DIR}"
|
|
|
158
177
|
|
|
159
178
|
log "========================================"
|
|
160
179
|
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>}"
|
|
180
|
+
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>}"
|
|
162
181
|
log "========================================"
|
|
163
182
|
|
|
164
183
|
if ! acquire_lock "${LOCK_FILE}"; then
|
|
@@ -187,7 +206,7 @@ fi
|
|
|
187
206
|
kill -TERM $$ 2>/dev/null || true
|
|
188
207
|
) &
|
|
189
208
|
WATCHDOG_PID=$!
|
|
190
|
-
append_exit_trap "
|
|
209
|
+
append_exit_trap "cleanup_watchdog ${WATCHDOG_PID}"
|
|
191
210
|
|
|
192
211
|
# Discover open PRs sorted by creation date (oldest first = FIFO)
|
|
193
212
|
log "INFO: Scanning open PRs..."
|
|
@@ -224,6 +243,11 @@ while IFS= read -r pr_json; do
|
|
|
224
243
|
continue
|
|
225
244
|
fi
|
|
226
245
|
|
|
246
|
+
if csv_has_label "${pr_labels:-}" "${READY_TO_MERGE_LABEL}"; then
|
|
247
|
+
log "INFO: PR #${pr_number} (${pr_branch}): Skipping PR labeled ${READY_TO_MERGE_LABEL}"
|
|
248
|
+
continue
|
|
249
|
+
fi
|
|
250
|
+
|
|
227
251
|
# Check branch pattern filter
|
|
228
252
|
if ! matches_branch_patterns "${pr_branch}"; then
|
|
229
253
|
log "DEBUG: PR #${pr_number} (${pr_branch}): Branch pattern mismatch, skipping"
|
|
@@ -284,12 +308,17 @@ while IFS= read -r pr_json; do
|
|
|
284
308
|
# Rebase remaining PRs after each successful merge
|
|
285
309
|
log "INFO: Rebasing remaining open PRs after merging #${pr_number}..."
|
|
286
310
|
REMAINING_JSON=$(gh pr list --state open \
|
|
287
|
-
--json number,headRefName \
|
|
311
|
+
--json number,headRefName,labels \
|
|
288
312
|
2>/dev/null || echo "[]")
|
|
289
313
|
while IFS= read -r remaining_pr; do
|
|
290
314
|
remaining_number=$(echo "${remaining_pr}" | jq -r '.number')
|
|
291
315
|
remaining_branch=$(echo "${remaining_pr}" | jq -r '.headRefName')
|
|
316
|
+
remaining_labels=$(echo "${remaining_pr}" | jq -r '[.labels[]?.name] | join(",")')
|
|
292
317
|
if [ "${remaining_number}" != "${pr_number}" ]; then
|
|
318
|
+
if csv_has_label "${remaining_labels:-}" "${READY_TO_MERGE_LABEL}"; then
|
|
319
|
+
log "INFO: Skipping post-merge rebase for PR #${remaining_number} (${remaining_branch}) because it is labeled ${READY_TO_MERGE_LABEL}"
|
|
320
|
+
continue
|
|
321
|
+
fi
|
|
293
322
|
log "INFO: Rebasing remaining PR #${remaining_number} (${remaining_branch})"
|
|
294
323
|
gh pr update-branch --rebase "${remaining_number}" 2>/dev/null || \
|
|
295
324
|
log "WARN: PR #${remaining_number}: Rebase failed (continuing)"
|
|
@@ -61,6 +61,8 @@ PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PRO
|
|
|
61
61
|
PRD_DIR="${NW_PRD_DIR:-docs/PRDs}"
|
|
62
62
|
PLAN_TASK="${NW_PLAN_TASK:-}"
|
|
63
63
|
|
|
64
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
65
|
+
|
|
64
66
|
rotate_log
|
|
65
67
|
log_separator
|
|
66
68
|
log "RUN-START: planner invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
|
|
@@ -63,6 +63,7 @@ PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PRO
|
|
|
63
63
|
# NOTE: Lock file path must match resolverLockPath() in src/utils/status-data.ts
|
|
64
64
|
LOCK_FILE="/tmp/night-watch-pr-resolver-${PROJECT_RUNTIME_KEY}.lock"
|
|
65
65
|
SCRIPT_TYPE="pr-resolver"
|
|
66
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
66
67
|
|
|
67
68
|
emit_result() {
|
|
68
69
|
local status="${1:?status required}"
|
|
@@ -359,6 +360,11 @@ while IFS= read -r pr_line; do
|
|
|
359
360
|
continue
|
|
360
361
|
fi
|
|
361
362
|
|
|
363
|
+
if csv_has_label "${labels:-}" "${READY_LABEL}"; then
|
|
364
|
+
log "INFO: Skipping PR #${pr_number} (${pr_branch}) because it is labeled ${READY_LABEL}"
|
|
365
|
+
continue
|
|
366
|
+
fi
|
|
367
|
+
|
|
362
368
|
# Apply branch pattern filter
|
|
363
369
|
if ! matches_branch_patterns "${pr_branch}"; then
|
|
364
370
|
log "DEBUG: Skipping PR #${pr_number} — branch '${pr_branch}' does not match patterns" "patterns=${BRANCH_PATTERNS_RAW}"
|
|
@@ -63,11 +63,6 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
63
63
|
# shellcheck source=night-watch-helpers.sh
|
|
64
64
|
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
65
65
|
|
|
66
|
-
# Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
|
|
67
|
-
if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
68
|
-
echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
|
|
69
|
-
exit 127
|
|
70
|
-
fi
|
|
71
66
|
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
72
67
|
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|
|
73
68
|
GLOBAL_LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
|
|
@@ -79,8 +74,10 @@ else
|
|
|
79
74
|
fi
|
|
80
75
|
|
|
81
76
|
SCRIPT_TYPE="reviewer"
|
|
77
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
82
78
|
READY_FOR_REVIEW_LABEL="${NW_READY_FOR_REVIEW_LABEL:-ready-for-review}"
|
|
83
79
|
READY_FOR_REVIEW_MARKER_NAME="night-watch-ready-for-review"
|
|
80
|
+
READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
|
|
84
81
|
|
|
85
82
|
emit_result() {
|
|
86
83
|
local status="${1:?status required}"
|
|
@@ -92,6 +89,13 @@ emit_result() {
|
|
|
92
89
|
fi
|
|
93
90
|
}
|
|
94
91
|
|
|
92
|
+
require_provider_on_path() {
|
|
93
|
+
if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
94
|
+
echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
|
|
95
|
+
exit 127
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
95
99
|
extract_review_score_from_text() {
|
|
96
100
|
local review_text="${1:-}"
|
|
97
101
|
printf '%s' "${review_text}" \
|
|
@@ -701,6 +705,11 @@ while IFS=$'\t' read -r pr_number pr_branch pr_labels; do
|
|
|
701
705
|
continue
|
|
702
706
|
fi
|
|
703
707
|
|
|
708
|
+
if csv_has_label "${pr_labels:-}" "${READY_TO_MERGE_LABEL}"; then
|
|
709
|
+
log "INFO: PR #${pr_number} (${pr_branch}) is labeled ${READY_TO_MERGE_LABEL}; skipping automated review"
|
|
710
|
+
continue
|
|
711
|
+
fi
|
|
712
|
+
|
|
704
713
|
if printf '%s\n' "${pr_labels:-}" | tr ',' '\n' | grep -Fxq 'needs-human-review'; then
|
|
705
714
|
log "INFO: PR #${pr_number} (${pr_branch}) is labeled needs-human-review; skipping automated review"
|
|
706
715
|
continue
|
|
@@ -847,6 +856,8 @@ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED
|
|
|
847
856
|
exit 0
|
|
848
857
|
fi
|
|
849
858
|
|
|
859
|
+
require_provider_on_path
|
|
860
|
+
|
|
850
861
|
log "PARALLEL: Launching ${#PR_NUMBER_ARRAY[@]} reviewer worker(s)"
|
|
851
862
|
|
|
852
863
|
declare -a WORKER_PIDS=()
|
|
@@ -1016,6 +1027,8 @@ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
|
|
|
1016
1027
|
exit 0
|
|
1017
1028
|
fi
|
|
1018
1029
|
|
|
1030
|
+
require_provider_on_path
|
|
1031
|
+
|
|
1019
1032
|
if ! prepare_detached_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "${DEFAULT_BRANCH}" "${LOG_FILE}"; then
|
|
1020
1033
|
log "FAIL: Unable to create isolated reviewer worktree ${REVIEW_WORKTREE_DIR}"
|
|
1021
1034
|
exit 1
|
|
@@ -26,6 +26,7 @@ PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
|
|
|
26
26
|
BRANCH_PATTERNS_RAW="${NW_BRANCH_PATTERNS:-feat/,night-watch/}"
|
|
27
27
|
SKIP_LABEL="${NW_QA_SKIP_LABEL:-skip-qa}"
|
|
28
28
|
VALIDATED_LABEL="${NW_QA_VALIDATED_LABEL:-e2e-validated}"
|
|
29
|
+
READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
|
|
29
30
|
QA_ARTIFACTS="${NW_QA_ARTIFACTS:-both}"
|
|
30
31
|
QA_AUTO_INSTALL_PLAYWRIGHT="${NW_QA_AUTO_INSTALL_PLAYWRIGHT:-1}"
|
|
31
32
|
SCRIPT_START_TIME=$(date +%s)
|
|
@@ -45,6 +46,7 @@ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
|
45
46
|
# NOTE: Lock file path must match qaLockPath() in src/utils/status-data.ts
|
|
46
47
|
LOCK_FILE="/tmp/night-watch-qa-${PROJECT_RUNTIME_KEY}.lock"
|
|
47
48
|
SCRIPT_TYPE="qa"
|
|
49
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
48
50
|
|
|
49
51
|
emit_result() {
|
|
50
52
|
local status="${1:?status required}"
|
|
@@ -366,7 +368,7 @@ fi
|
|
|
366
368
|
rotate_log
|
|
367
369
|
log_separator
|
|
368
370
|
log "RUN-START: qa invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
|
|
369
|
-
log "CONFIG: max_runtime=${MAX_RUNTIME}s artifacts=${QA_ARTIFACTS} skip_label=${SKIP_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW}"
|
|
371
|
+
log "CONFIG: max_runtime=${MAX_RUNTIME}s artifacts=${QA_ARTIFACTS} skip_label=${SKIP_LABEL} ready_label=${READY_TO_MERGE_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW}"
|
|
370
372
|
|
|
371
373
|
if ! acquire_lock "${LOCK_FILE}"; then
|
|
372
374
|
emit_result "skip_locked"
|
|
@@ -431,6 +433,11 @@ while IFS=$'\t' read -r pr_number pr_branch pr_title pr_labels; do
|
|
|
431
433
|
continue
|
|
432
434
|
fi
|
|
433
435
|
|
|
436
|
+
if csv_has_label "${pr_labels:-}" "${READY_TO_MERGE_LABEL}"; then
|
|
437
|
+
log "SKIP-QA: PR #${pr_number} (${pr_branch}) is labeled ${READY_TO_MERGE_LABEL}"
|
|
438
|
+
continue
|
|
439
|
+
fi
|
|
440
|
+
|
|
434
441
|
# Skip PRs with the skip label
|
|
435
442
|
if echo "${pr_labels}" | grep -q "${SKIP_LABEL}"; then
|
|
436
443
|
log "SKIP-QA: PR #${pr_number} (${pr_branch}) has '${SKIP_LABEL}' label"
|
|
@@ -35,6 +35,7 @@ source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
|
35
35
|
ensure_provider_on_path "${PROVIDER_CMD}" || true
|
|
36
36
|
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
37
37
|
LOCK_FILE="/tmp/night-watch-slicer-${PROJECT_RUNTIME_KEY}.lock"
|
|
38
|
+
SCRIPT_TYPE="slicer"
|
|
38
39
|
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|
|
39
40
|
|
|
40
41
|
emit_result() {
|
|
@@ -53,6 +54,8 @@ if ! validate_provider "${PROVIDER_CMD}"; then
|
|
|
53
54
|
exit 1
|
|
54
55
|
fi
|
|
55
56
|
|
|
57
|
+
skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
58
|
+
|
|
56
59
|
rotate_log
|
|
57
60
|
log_separator
|
|
58
61
|
log "RUN-START: slicer invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
|
|
@@ -55,9 +55,28 @@
|
|
|
55
55
|
"audit": 10
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
|
-
"
|
|
59
|
-
"
|
|
58
|
+
"webhookTriggers": {
|
|
59
|
+
"enabled": false,
|
|
60
|
+
"secretEnv": "NIGHT_WATCH_WEBHOOK_SECRET",
|
|
61
|
+
"allowedJobIds": [
|
|
62
|
+
"executor",
|
|
63
|
+
"reviewer",
|
|
64
|
+
"pr-resolver",
|
|
65
|
+
"slicer",
|
|
66
|
+
"qa",
|
|
67
|
+
"audit",
|
|
68
|
+
"analytics",
|
|
69
|
+
"merger"
|
|
70
|
+
],
|
|
71
|
+
"requireTimestamp": false,
|
|
72
|
+
"maxSkewSeconds": 300,
|
|
73
|
+
"github": {
|
|
74
|
+
"enabled": false,
|
|
75
|
+
"events": [],
|
|
76
|
+
"rules": []
|
|
77
|
+
}
|
|
60
78
|
},
|
|
79
|
+
"jobProviders": {},
|
|
61
80
|
"autoMerge": false,
|
|
62
81
|
"autoMergeMethod": "squash",
|
|
63
82
|
"fallbackOnRateLimit": true,
|
|
@@ -75,23 +94,5 @@
|
|
|
75
94
|
"enabled": true,
|
|
76
95
|
"schedule": "50 3 * * 1",
|
|
77
96
|
"maxRuntime": 1800
|
|
78
|
-
},
|
|
79
|
-
"providerPresets": {
|
|
80
|
-
"g51claude": {
|
|
81
|
-
"name": "GLM-5.1 Claude",
|
|
82
|
-
"command": "claude",
|
|
83
|
-
"promptFlag": "-p",
|
|
84
|
-
"autoApproveFlag": "--dangerously-skip-permissions",
|
|
85
|
-
"modelFlag": "--model",
|
|
86
|
-
"model": "glm-5.1",
|
|
87
|
-
"envVars": {
|
|
88
|
-
"ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
|
|
89
|
-
"API_TIMEOUT_MS": "3000000",
|
|
90
|
-
"ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-5.1",
|
|
91
|
-
"ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-5.1",
|
|
92
|
-
"ANTHROPIC_AUTH_TOKEN": "efaeca456dab4ae69cb9ce1fa8d99a1a.KSTifNTWPS6NgC6L",
|
|
93
|
-
"ANTHROPIC_API_KEY": "efaeca456dab4ae69cb9ce1fa8d99a1a.KSTifNTWPS6NgC6L"
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
97
|
}
|
|
97
98
|
}
|