Skip to content

Update CI to label-driven testing#450

Open
Micky774 wants to merge 12 commits intodevfrom
zain/ci
Open

Update CI to label-driven testing#450
Micky774 wants to merge 12 commits intodevfrom
zain/ci

Conversation

@Micky774
Copy link
Contributor

@Micky774 Micky774 commented Feb 17, 2026

Description

With this PR, testing is mediated by labels such as: ci-level {1, 2, 3}. No tests will be run without at least one label present. At any given time, the highest-level label will take precedence, and only its corresponding level of tests will be run. The workflow triggers on labeling, unlabeling, and pushing commits. When removing a label, the workflow will check to see if there are any labels remaining and if there are, it will dispatch to the highest of them.

Fixes # (issue)

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

Please list the changes introduced in this PR:

  • Change A
  • Change B

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Collaborator

@ipanfilo ipanfilo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make approach more generic - ci_level% label specifies CI level to use.

@Micky774 Micky774 changed the title Add label-specific workflow for level 3 testing Update CI to label-driven testing Feb 18, 2026
let test_level = requires_dispatch ? level : '';
core.setOutput('test_level', test_level);
core.setOutput('current_level', String(currentLevel));
const shouldCancelOthers = (test_level !== '') || (action === 'unlabeled' && removedWasHighest);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing label should not automatically cancel running CI

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now only removing the last remaining ci-level label will cancel past CI. I want this to serve as an explicit request to save resources on unnecessary tests.

// Determine if a CI level label was added, and what level it was.
const addedLevel = action === 'labeled' ? parseLevelLabel(context.payload.label?.name) : 0;

core.info(`Dispatch debug: action=${action}, addedLabel=${context.payload.label?.name || ''}, addedLevel=${addedLevel}, currentLevel=${currentLevel}, labels=[${labels.join(',')}]`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Info here makes sense only for labeled action

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

const sameSha = run.head_sha === headSha;
const sameHeadRef = run.head_branch === context.payload.pull_request.head.ref;
const activeOrDone =
run.status === 'queued' ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like all possible status values

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misunderstood the API a bit -- I've updated to what I think is a more accurate reflection of what I want (basically it can be any of the active statuses, OR it can be completed AND successful).


const candidateRuns = runs.filter(run => {
const prMatch = (run.pull_requests || []).some(pr => pr.number === prNumber);
const sameSha = run.head_sha === headSha;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

head_sha is possible query parameter for listWorkflowRuns

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

core.info(`Dispatch debug: total workflow runs fetched=${runs.length}`);

const candidateRuns = runs.filter(run => {
const prMatch = (run.pull_requests || []).some(pr => pr.number === prNumber);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to documentation this list contains all PR's with matching head_sha or head_branch so it does not help to associate the PR. IMHO the most precise matching is branch query parameter to match the PR target branch and head_sha and head_branch for the run both match the PR too.
Even this one is not ideal because PR run does merging of source and target so strictly speaking it may run different code after each target update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you help me understand what scenario might this fail in specifically? I've updated it to only rely on the PR match + head sha. Is there a situation where that wouldn't be precise enough?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One scenario is base branch change for PR. And the other is target branch update - they do not directly affect PR code, so head_sha remains the same but test results may differ.
It is OK not to automatically rerun CI in those cases though because they are rather corner cases and mainstream usage will not include label playing w/o code touching.
By the same reason it is not to worry mach about multiple PRs from the same commit to different targets.

if (m) {
detectedLevel = Math.max(detectedLevel, Number(m[1]));
}
core.info(`Dispatch debug: run_id=${run.id}, job_id=${job.id}, job_name=${job.name}, detectedLevel=${detectedLevel}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially excessive logging with this and the next message in the loop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

core.setOutput('test_level', requiresDispatch ? String(currentLevel) : '');

maybe_cancel_all:
if: ${{ github.event.action == 'unlabeled' }}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still opposed of using labels as a tool to stop workflowd. User may cancel workflow in actions menu, if they want

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the cancellation now.

core.info(`Dispatch debug: total workflow runs fetched=${runs.length}`);

const candidateRuns = runs.filter(run => {
const prMatch = (run.pull_requests || []).some(pr => pr.number === prNumber);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One scenario is base branch change for PR. And the other is target branch update - they do not directly affect PR code, so head_sha remains the same but test results may differ.
It is OK not to automatically rerun CI in those cases though because they are rather corner cases and mainstream usage will not include label playing w/o code touching.
By the same reason it is not to worry mach about multiple PRs from the same commit to different targets.

core.info(`Dispatch debug: candidate runs matching PR+sha=${candidateRuns.length}`);

let satisfiesAddedLevel = false;
for (const run of candidateRuns) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't run-name (CI Level) appear in github.rest.actions.listWorkflowRuns?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants