Skip to content

How CI works here

neosource runs CI from workflow files stored in your repository. The shape will be familiar if you have used GitHub Actions, but two things differ enough to cover before you write anything: where the files live, and which triggers actually fire.

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

Three rules, and each of them has bitten someone:

  • The directory is .neosource/workflows/, not .github/workflows/.
  • Files must end in .yml or .yaml.
  • Only the top level of that directory is read. Files in subdirectories are not picked up.

If your repository has a .github/workflows/ directory, neosource ignores it completely. It is not read, not parsed, and no diagnostic is produced — a repository imported from GitHub with a full set of Actions workflows will simply have no CI, silently, until you move the files.

This is the single most likely reason for “why is nothing running”. See Coming from GitHub Actions for what to do with the files once you move them.

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.

Two consequences follow from “replaces”:

  • 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. Three events start a run today:

Trigger Fires when
push You push to the repository.
pull_request A PR is opened, or updated with new commits (synchronize).
schedule A cron entry comes due.

workflow_dispatch, 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 run by hand, call POST /api/workflow-runs with a workflow_id and a cause. It needs Write permission on the repository.

A concurrency: block is ignored. Pushing twice in quick succession starts two runs and neither cancels the other. If you rely on GitHub’s cancel-in-progress behaviour to keep your queue clean, that behaviour is not here yet; cancel the superseded run yourself via POST /api/workflow-runs/{run_id}/cancel.

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.

Step-level if: conditions are evaluated. Job-level if: is not — a condition written on a job does not gate that job. Put the condition on the steps instead.

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.

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

The web UI does not currently render these diagnostics. The Actions page lists workflows by path and name only. So a workflow that parsed with warnings — an ignored container:, a dropped trigger, a misspelled key — looks completely normal in the UI. Until that gap closes, the API is the only place to see them, and it is worth checking after any migration.

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.