@link-assistant/hive-mind 1.31.1 → 1.31.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/CHANGELOG.md +31 -0
- package/package.json +1 -1
- package/src/github-merge-ci.lib.mjs +62 -19
- package/src/telegram-merge-queue.lib.mjs +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.31.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b77704d: fix: set Docker image version labels to actual release version (Issue #1419)
|
|
8
|
+
|
|
9
|
+
The `docker/metadata-action@v5` defaulted the `org.opencontainers.image.version`
|
|
10
|
+
OCI label to the Git ref name `"main"` instead of the actual release version.
|
|
11
|
+
Added explicit `labels` override to all four Docker metadata steps in both regular
|
|
12
|
+
and instant release pipelines.
|
|
13
|
+
|
|
14
|
+
Also added `.config` directory ownership and write-access verification to the Docker
|
|
15
|
+
image verification script to prevent the permission regression from recurring.
|
|
16
|
+
|
|
17
|
+
## 1.31.2
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- efe3506: fix: /merge command no longer falsely fails when latest CI is in progress (Issue #1425)
|
|
22
|
+
|
|
23
|
+
The `checkBranchCIHealth` function previously queried only `status=completed` runs
|
|
24
|
+
to determine if the default branch CI was healthy. When a new commit had an in-progress
|
|
25
|
+
CI run, the function returned the previous (now superseded) commit's failure as the
|
|
26
|
+
"latest" CI status, causing the merge queue to be blocked with a false positive error.
|
|
27
|
+
|
|
28
|
+
The fix resolves the actual HEAD SHA of the branch first, then queries CI runs
|
|
29
|
+
specifically for that SHA (without a status filter). If the latest commit's runs are
|
|
30
|
+
in progress, the function returns `pending: true` (healthy) instead of reporting a
|
|
31
|
+
failure from an older commit. The merge queue then proceeds to the existing
|
|
32
|
+
`waitForTargetBranchCI` step which correctly waits for those runs to complete.
|
|
33
|
+
|
|
3
34
|
## 1.31.1
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -141,62 +141,105 @@ export async function waitForCommitCI(owner, repo, sha, options = {}, verbose =
|
|
|
141
141
|
/**
|
|
142
142
|
* Check if the default branch has any recent failed CI runs
|
|
143
143
|
* Issue #1341: Used to detect pre-existing failures before starting the merge queue
|
|
144
|
+
* Issue #1425: Fixed to resolve the actual HEAD SHA first, then check CI for that SHA,
|
|
145
|
+
* so that in-progress runs on the latest commit are not mistaken for failures.
|
|
144
146
|
*
|
|
145
147
|
* @param {string} owner - Repository owner
|
|
146
148
|
* @param {string} repo - Repository name
|
|
147
149
|
* @param {string} branch - Branch name (usually 'main' or 'master')
|
|
148
|
-
* @param {Object} options - Check options
|
|
149
|
-
* @param {number} options.lookbackCount - Number of recent runs to check (default: 5)
|
|
150
|
+
* @param {Object} options - Check options (currently unused, kept for API compatibility)
|
|
150
151
|
* @param {boolean} verbose - Whether to log verbose output
|
|
151
|
-
* @returns {Promise<{healthy: boolean, failedRuns: Array, error: string|null}>}
|
|
152
|
+
* @returns {Promise<{healthy: boolean, pending: boolean, failedRuns: Array, pendingRuns: Array, error: string|null}>}
|
|
152
153
|
*/
|
|
153
|
-
export async function checkBranchCIHealth(owner, repo, branch = 'main', options
|
|
154
|
-
const { lookbackCount = 5 } = options;
|
|
155
|
-
|
|
154
|
+
export async function checkBranchCIHealth(owner, repo, branch = 'main', options, verbose = false) {
|
|
156
155
|
try {
|
|
157
|
-
//
|
|
158
|
-
|
|
156
|
+
// Issue #1425: First, resolve the actual HEAD SHA of the branch.
|
|
157
|
+
// This avoids the bug where only completed runs are queried: if the latest commit has
|
|
158
|
+
// an in-progress CI run, querying ?status=completed would return the previous commit's
|
|
159
|
+
// runs and could incorrectly report a failure from an older (now superseded) commit.
|
|
160
|
+
let headSha;
|
|
161
|
+
try {
|
|
162
|
+
const { stdout: refOut } = await exec(`gh api "repos/${owner}/${repo}/git/ref/heads/${branch}" --jq '.object.sha'`);
|
|
163
|
+
headSha = refOut.trim();
|
|
164
|
+
} catch (refError) {
|
|
165
|
+
if (verbose) {
|
|
166
|
+
console.log(`[VERBOSE] /merge: Error resolving HEAD SHA for ${branch}: ${refError.message}`);
|
|
167
|
+
}
|
|
168
|
+
// On error, assume healthy to avoid blocking merges due to API issues
|
|
169
|
+
return { healthy: true, pending: false, failedRuns: [], pendingRuns: [], error: null };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!headSha) {
|
|
173
|
+
if (verbose) {
|
|
174
|
+
console.log(`[VERBOSE] /merge: Could not resolve HEAD SHA for ${branch}, assuming healthy`);
|
|
175
|
+
}
|
|
176
|
+
return { healthy: true, pending: false, failedRuns: [], pendingRuns: [], error: null };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (verbose) {
|
|
180
|
+
console.log(`[VERBOSE] /merge: Checking CI for latest ${branch} commit ${headSha.substring(0, 7)}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Issue #1425: Query CI runs specifically for the HEAD SHA (no status filter).
|
|
184
|
+
// This ensures we see in-progress runs for the latest commit, not just completed ones.
|
|
185
|
+
const { stdout } = await exec(`gh api "repos/${owner}/${repo}/actions/runs?head_sha=${headSha}&per_page=20" --jq '[.workflow_runs[] | {id: .id, name: .name, status: .status, conclusion: .conclusion, html_url: .html_url, head_sha: .head_sha, created_at: .created_at}]'`);
|
|
159
186
|
const runs = JSON.parse(stdout.trim() || '[]');
|
|
160
187
|
|
|
161
188
|
if (verbose) {
|
|
162
|
-
console.log(`[VERBOSE] /merge:
|
|
189
|
+
console.log(`[VERBOSE] /merge: Found ${runs.length} CI run(s) for HEAD commit ${headSha.substring(0, 7)} on ${owner}/${repo} branch ${branch}`);
|
|
163
190
|
}
|
|
164
191
|
|
|
165
192
|
if (runs.length === 0) {
|
|
166
|
-
// No
|
|
167
|
-
|
|
193
|
+
// No runs for the latest commit - CI may not have started yet or is not configured.
|
|
194
|
+
// Assume healthy to avoid blocking merges.
|
|
195
|
+
return { healthy: true, pending: false, failedRuns: [], pendingRuns: [], error: null };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Issue #1425: Check for in-progress runs on the latest commit.
|
|
199
|
+
// If the latest commit's CI is still running, we should NOT report failure —
|
|
200
|
+
// the previous commit's failure (which may appear in completed runs) is no longer relevant.
|
|
201
|
+
const pendingRuns = runs.filter(r => r.status === 'in_progress' || r.status === 'queued' || r.status === 'waiting' || r.status === 'requested' || r.status === 'pending');
|
|
202
|
+
if (pendingRuns.length > 0) {
|
|
203
|
+
if (verbose) {
|
|
204
|
+
console.log(`[VERBOSE] /merge: ${pendingRuns.length} CI run(s) still in progress on ${branch} (latest commit ${headSha.substring(0, 7)})`);
|
|
205
|
+
for (const run of pendingRuns) {
|
|
206
|
+
console.log(`[VERBOSE] /merge: - ${run.name}: ${run.status} (${run.html_url})`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Healthy but pending: caller should wait for CI rather than block the queue
|
|
210
|
+
return { healthy: true, pending: true, failedRuns: [], pendingRuns, error: null };
|
|
168
211
|
}
|
|
169
212
|
|
|
170
|
-
//
|
|
171
|
-
const
|
|
172
|
-
const latestRuns = runs.filter(r => r.head_sha === latestSha);
|
|
173
|
-
const failedRuns = latestRuns.filter(r => r.conclusion === 'failure' || r.conclusion === 'timed_out');
|
|
213
|
+
// All runs for the latest commit are completed — check for failures
|
|
214
|
+
const failedRuns = runs.filter(r => r.conclusion === 'failure' || r.conclusion === 'timed_out');
|
|
174
215
|
|
|
175
216
|
if (failedRuns.length > 0) {
|
|
176
217
|
if (verbose) {
|
|
177
|
-
console.log(`[VERBOSE] /merge: Found ${failedRuns.length} failed CI run(s) on ${branch}:`);
|
|
218
|
+
console.log(`[VERBOSE] /merge: Found ${failedRuns.length} failed CI run(s) on ${branch} (latest commit ${headSha.substring(0, 7)}):`);
|
|
178
219
|
for (const run of failedRuns) {
|
|
179
220
|
console.log(`[VERBOSE] /merge: - ${run.name}: ${run.conclusion} (${run.html_url})`);
|
|
180
221
|
}
|
|
181
222
|
}
|
|
182
223
|
return {
|
|
183
224
|
healthy: false,
|
|
225
|
+
pending: false,
|
|
184
226
|
failedRuns,
|
|
227
|
+
pendingRuns: [],
|
|
185
228
|
error: `${failedRuns.length} CI run(s) failed on ${branch}: ${failedRuns.map(r => r.name).join(', ')}`,
|
|
186
229
|
};
|
|
187
230
|
}
|
|
188
231
|
|
|
189
232
|
if (verbose) {
|
|
190
|
-
console.log(`[VERBOSE] /merge: Branch ${branch} CI is healthy (${
|
|
233
|
+
console.log(`[VERBOSE] /merge: Branch ${branch} CI is healthy (${runs.length} run(s) passed for commit ${headSha.substring(0, 7)})`);
|
|
191
234
|
}
|
|
192
235
|
|
|
193
|
-
return { healthy: true, failedRuns: [], error: null };
|
|
236
|
+
return { healthy: true, pending: false, failedRuns: [], pendingRuns: [], error: null };
|
|
194
237
|
} catch (error) {
|
|
195
238
|
if (verbose) {
|
|
196
239
|
console.log(`[VERBOSE] /merge: Error checking branch CI health: ${error.message}`);
|
|
197
240
|
}
|
|
198
241
|
// On error, assume healthy to avoid blocking merges due to API issues
|
|
199
|
-
return { healthy: true, failedRuns: [], error: null };
|
|
242
|
+
return { healthy: true, pending: false, failedRuns: [], pendingRuns: [], error: null };
|
|
200
243
|
}
|
|
201
244
|
}
|
|
202
245
|
|
|
@@ -552,6 +552,19 @@ export class MergeQueueProcessor {
|
|
|
552
552
|
};
|
|
553
553
|
}
|
|
554
554
|
|
|
555
|
+
// Issue #1425: If the latest commit's CI is still in progress, wait for it to complete
|
|
556
|
+
// rather than proceeding immediately. The WAIT_FOR_TARGET_BRANCH_CI step (below) will
|
|
557
|
+
// also wait, but checking here ensures we don't skip the health check entirely.
|
|
558
|
+
if (healthResult.pending) {
|
|
559
|
+
this.log(`Branch ${targetBranch} has ${healthResult.pendingRuns.length} CI run(s) in progress on the latest commit. Will wait for them to complete.`);
|
|
560
|
+
// Return healthy so the queue proceeds to the waitForTargetBranchCI step which handles waiting
|
|
561
|
+
return {
|
|
562
|
+
healthy: true,
|
|
563
|
+
failedRuns: [],
|
|
564
|
+
error: null,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
555
568
|
this.log(`Branch ${targetBranch} CI is healthy. Ready to proceed.`);
|
|
556
569
|
return {
|
|
557
570
|
healthy: true,
|