How CI works here
neosource runs CI from YAML workflows in your repository.
Where workflows live
Section titled “Where workflows live”.neosource/workflows/├── ci.yml└── nightly.yamlWorkflow files must meet these requirements:
- The directory is
.neosource/workflows/— plus.github/workflows/in a repository that enables it (below). - Files must end in
.ymlor.yaml. - Only the top level of that directory is read. Files in subdirectories are not picked up.
.github/workflows/ runs only when you enable it
Section titled “.github/workflows/ runs only when you enable it”By default neosource checks files in .github/workflows/ for compatibility but
does not run them. While the repository has no runs, its Actions tab lists
these files with any warnings or errors. The workflows API returns the same
report in discovered[].
To run them in place, with no file move, a repository admin enables the directory:
neo repo set --ci-workflow-source neosource-and-githubThe default branch’s current files register at once, but nothing runs
immediately: a scheduled workflow fires at its next tick, the others on the next
push. --ci-workflow-source neosource stops new runs, re-runs and approvals of
them; runs already queued or in progress are not cancelled. A file with the
same name in .neosource/workflows/ wins: its .github/workflows/ twin stays
reported, with a warning, and does not run. Moving the files into
.neosource/workflows/ works too; either way, check
Coming from GitHub Actions.
How workflows get registered
Section titled “How workflows get registered”Workflows are indexed from the default branch only. When you push to it,
neosource reads .neosource/workflows/ out of the tree that push just created
and replaces the repository’s registered workflow set with what it finds.
On each push to the default branch:
- A workflow file added in a push is registered before that same push’s triggers are evaluated — so a workflow you add can fire on the very push that adds it.
- A workflow file deleted from the tree is deregistered. There is no stale leftover definition.
Pushes to other branches do not change the registered set. A workflow that only exists on a feature branch is not registered, though it can still run against that branch once it is on the default branch — the definition comes from the default branch, the code under test does not.
What actually triggers a run
Section titled “What actually triggers a run”The parser accepts more trigger events than the server dispatches. Five events start a run today:
| Trigger | Fires when |
|---|---|
push |
You push to the repository. |
pull_request |
A PR is opened or updated (synchronize), plus listed types:. |
merge_group |
A behind PR enters the merge queue of a branch that requires up-to-date branches (checks_requested). |
schedule |
A cron entry comes due. |
workflow_dispatch |
You start it by hand. |
repository_dispatch and change parse but do not dispatch. The full status
table and the filter rules are in
Writing a workflow — including the glob
semantics, which are stricter than most people expect (release/* does not
match release/v1/rc1).
To start a workflow_dispatch workflow by hand, use Run workflow on the
Actions tab or neo actions run; both need Write permission on the
repository. See manual runs.
concurrency: and merge-queue cancellation
Section titled “concurrency: and merge-queue cancellation”A concurrency: block works, at both workflow and job level, following
GitHub’s rules:
- At most one run per group is active.
- At most one run per group is pending. A newer arrival cancels the older pending run and takes its place.
cancel-in-progress: trueadditionally cancels the active run. The default isfalse.- Group names are compared case-insensitively, scoped per repository.
A run held by the gate carries the status concurrency_blocked, so the run
list says why nothing is happening. A held job simply stays ready, which
already reads as queued.
The gate runs inside the enqueue transaction, so it holds at every entry point
— push, pull request, merge group, schedule, manual POST /api/workflow-runs,
and rerun alike. In addition, when a behind pull request enters the merge queue
of a branch whose protection rule requires branches to be up to date, and
merge_group runs start on its merge candidate, the queue cancels any
still-running pull_request runs of those same workflows on that head (marking
their checks superseded rather than failed) so runners never test both the
stale head and the merge candidate at once. Without that requirement a behind
pull request is gated on its own head’s checks, no candidate is built, and
nothing is cancelled.
Job ordering
Section titled “Job ordering”Within a run, needs: is enforced server-side. A job waits until everything it
depends on has finished, and if a dependency fails, the jobs downstream of it are
skipped transitively rather than run against a broken state.
if: conditions are evaluated at both step and job level. A job-level if:
follows GitHub’s rules, including the two that surprise people: a skipped
dependency does not satisfy success(), so it skips its downstream jobs too;
and a condition naming no status function is implicitly &&-ed with
success(). Name always(), failure() or !cancelled() to opt out of that.
A skipped job shows a green check, not a red one.
Two divergences: cancelling a run cancels always() jobs too, and cancelled()
never evaluates true. See
Job-level if:.
Seeing diagnostics
Section titled “Seeing diagnostics”When a workflow file is indexed, its parse diagnostics are stored alongside it:
unknown keys and unsupported triggers as warning, a whole-file parse failure as
error. A file that fails to parse is still indexed — as an empty definition
that can never run — so it shows up as broken rather than vanishing from the
list.
On the Actions tab, click a workflow’s warning or error badge to read its diagnostics. You can also retrieve them from the workflows API:
GET /api/repos/{owner}/{repo}/workflowsEach workflow in the response carries a warnings array with a severity and a
message.
Watching a run
Section titled “Watching a run”| Purpose | Endpoint |
|---|---|
| List a repo’s runs | GET /api/repos/{owner}/{repo}/workflow-runs |
| Runs for one commit | GET /api/repos/{owner}/{repo}/commits/{sha}/workflow-runs |
| One run | GET /api/workflow-runs/{run_id} |
| Jobs in a run | GET /api/workflow-runs/{run_id}/jobs |
| Job logs | GET /api/jobs/{job_id}/logs |
| Follow logs live | GET /api/jobs/{job_id}/logs/stream |
| Test/lint annotations | GET /api/runs/{run_id}/annotations |
| Cancel a run | POST /api/workflow-runs/{run_id}/cancel |
| Re-run | POST /api/workflow-runs/{run_id}/rerun |
| Cancel one job | POST /api/jobs/{job_id}/cancel |
Full schemas are in the API reference. There is also an Actions view in the web UI covering the run list, the job graph, steps and logs.
- Writing a workflow — the schema in detail.
- The job environment — choosing between the base
environment,
packages:andflake:. - Setup time, caching and billing — what setup costs, what is cached, and what is billed.
- Coming from GitHub Actions — compatibility and migration.