Skip to content

How CI works here

neosource runs CI from YAML workflows in your repository.

.neosource/workflows/
├── ci.yml
└── nightly.yaml

Workflow files must meet these requirements:

  • The directory is .neosource/workflows/ — plus .github/workflows/ in a repository that enables it (below).
  • Files must end in .yml or .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:

Terminal window
neo repo set --ci-workflow-source neosource-and-github

The 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.

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.

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.

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: true additionally cancels the active run. The default is false.
  • 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.

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:.

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}/workflows

Each workflow in the response carries a warnings array with a severity and a message.

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.