Repository navigation
[Hackathon] fix(ci): generated workflows for different projects cancel each other #583
Description
Verified against (commit SHA or released version)
d61e6df — public main, package version 0.15.0. Built and reproduced locally on macOS, Node v25.7.0, October 20, 2026.
Discord Username / User ID
ololadestephen
What does this improvement do?
Prevent separate TestSprite CI workflows in the same repository from cancelling each other.
The workflow generator assigns every generated job the same concurrency group for a given pull request or deployment commit:
group: testsprite-${{ github.event.pull_request.number || github.event.deployment.sha }}
cancel-in-progress: true
Neither the workflow path nor the TestSprite project/environment identifies the group.
For example, generate testsprite-web.yml for a web project and testsprite-api.yml for an API project. On PR #42, both jobs use testsprite-42. Because GitHub concurrency groups are shared across workflows within a repository, one project's job can cancel the other project's validation.
The deployment-status configuration also collides when different project deployments share the same commit SHA.
This can prevent an independent validation from finishing. If tests were already dispatched, the remote TestSprite work may continue after the Actions job is cancelled. This is not a claim that cancelled checks count as passing.
Details / implementation notes
Affected source
testsprite-cli/src/commands/ci.ts
Lines 321 to 332 in d61e6df
| jobs: | |
| testsprite-tests: | |
| # B (pull_request): a new push to the pull request cancels the previous job. | |
| # A (deployment_status): one group per COMMIT — Vercel sets deployment.ref to | |
| # the commit sha, so a new push does not cancel the previous commit's job. | |
| # Job-level, so events the if: below drops (pending, inactive, another | |
| # project's deployment of the same commit) never cancel a running gate. | |
| # Cancelling only stops this job's CLI: the TestSprite run it started keeps | |
| # executing and reports in the Portal; the new push starts its own run. | |
| concurrency: | |
| group: testsprite-\${{ github.event.pull_request.number || github.event.deployment.sha }} | |
| cancel-in-progress: true |
Offline reproduction
From a built checkout:
node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { buildGithubWorkflow } from './dist/commands/ci.js';
const web = buildGithubWorkflow({
projectId: 'project_web',
environment: 'staging',
workflowPath: '.github/workflows/testsprite-web.yml',
});
const api = buildGithubWorkflow({
projectId: 'project_api',
environment: 'qa',
workflowPath: '.github/workflows/testsprite-api.yml',
});
const group = yaml =>
yaml.split('\n').find(line => line.trimStart().startsWith('group:')).trim();
assert.notEqual(web, api);
assert.equal(group(web), group(api));
assert.ok(web.includes('cancel-in-progress: true'));
assert.ok(api.includes('cancel-in-progress: true'));
console.log({ webGroup: group(web), apiGroup: group(api) });
JSBoth generated workflows have identical concurrency groups despite targeting different projects and using different workflow paths.
Local verification also parsed both generated workflows as YAML and confirmed the differing project commands and identical cancellation configuration. No hosted Actions runs, TestSprite API calls or testing credits were used.
The cancellation consequence follows from GitHub's documented cross-workflow concurrency behavior:
https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#example-only-cancel-in-progress-jobs-or-runs-for-the-current-workflow
Proposed approach
Include a stable workflow-path identifier, such as a short digest, in the generated concurrency group. Preserve the PR-number/deployment-SHA component so successive runs of the same workflow still cancel previous runs.
github.workflow alone would not distinguish the current generated files: they all have the display name TestSprite. Project ID alone would not distinguish separate environment gates for the same project.
Regression coverage should verify:
- Different workflow paths produce different groups.
- Separate workflows targeting the same project remain independent.
- Regenerating the same workflow produces the same group.
- PR and deployment-status configurations retain cancellation within the same workflow.
Confirmations
- I have searched existing issues and this is not a duplicate.
- I have provided my Discord identity above for reward coordination.
Activity
fyZhang66 commentedon Oct 22, 2026
Confirmed on v0.15.0, thanks @Ololadestephen. The generated job's concurrency group is
testsprite-<PR number or deployment sha>with no workflow in it (ci.ts:328-330), and concurrency groups are repository-wide. So a repo that gates two TestSprite projects (two sites, or a frontend and a backend project) with two workflows fromci init github --pathhas them cancel each other on every PR.Accepted, scope:
--path(e.g.testsprite-testsprite-api-${{ github.event.pull_request.number || github.event.deployment.sha }}). No digest needed: the generator already knows the path, and regenerating the same file must give the same group.--pathvalues give different groups; the same path regenerated gives the same group; both thepull_requestanddeployment_statusshapes still cancel within one workflow.No new dependency. Comment
/assignto claim it.Herbicide7665 commentedon Oct 22, 2026
/assign
testsprite-hob commentedon Oct 22, 2026
Thanks for the interest, @Herbicide7665! @Ololadestephen opened this issue, so it's reserved for them until 2026-10-24T01:25:53Z (48 hours after it was accepted). If they haven't claimed it by then, comment
/assignagain.Ololadestephen commentedon Oct 22, 2026
/assign
testsprite-hob commentedon Oct 22, 2026
Assigned to @Ololadestephen. Thanks for taking this on. (1 more slot available.)
Your open assigned issues (2/3):
testsprite-hob commentedon Oct 23, 2026
@Ololadestephen, your claim on this issue expires in about 24 hours unless you open a PR that links it (for example,
Closes #583orPart of #583). You can re-claim later with/assignif the issue is still free.