Skip to content

Workflows

The lexicon provides 2 resource types and several property types:

TypeDescription
WorkflowTop-level workflow configuration — name, triggers, permissions, concurrency
JobA job within a workflow — runs-on, steps, strategy, needs, outputs
TypeUsed inDescription
StepJobA single step — run command or action usage
StrategyJobMatrix strategy for parallel job execution
PermissionsWorkflow, JobGITHUB_TOKEN permission scopes
ConcurrencyWorkflow, JobConcurrency group and cancel-in-progress settings
PushTriggerWorkflow (on)Push event trigger with branch/tag/path filters
PullRequestTriggerWorkflow (on)Pull request event trigger with filters
ScheduleTriggerWorkflow (on)Cron-based schedule trigger
WorkflowDispatchTriggerWorkflow (on)Manual dispatch with typed inputs
EnvironmentJobDeployment environment with protection rules
OutputJobJob output values for downstream jobs

Triggers define when a workflow runs. Pass them in the on: field:

import { Workflow } from "@intentius/chant-lexicon-github";
export const workflow = new Workflow({
name: "CI",
on: {
push: { branches: ["main", "release/*"] },
pull_request: {
branches: ["main"],
types: ["opened", "synchronize"],
},
schedule: [{ cron: "0 0 * * 1" }], // Weekly on Monday
workflow_dispatch: { // Manual trigger
inputs: {
environment: {
description: "Deploy target",
required: true,
type: "choice",
options: ["staging", "production"],
},
},
},
},
});
TriggerDescriptionCommon options
pushRuns on push eventsbranches, tags, paths, paths-ignore
pull_requestRuns on PR eventsbranches, types, paths
scheduleRuns on a cron schedulecron
workflow_dispatchManual trigger from GitHub UIinputs
workflow_callCalled by another workflowinputs, outputs, secrets
releaseRuns on release eventstypes
repository_dispatchRuns on repository dispatchtypes

Each exported Job becomes a job entry under jobs:. Steps run sequentially within a job:

import { Job, Step, Checkout, SetupNode } from "@intentius/chant-lexicon-github";
export const test = new Job({
"runs-on": "ubuntu-latest",
timeoutMinutes: 10,
steps: [
Checkout({}).step,
SetupNode({ nodeVersion: "22", cache: "npm" }).step,
new Step({ name: "Install", run: "npm ci" }),
new Step({ name: "Test", run: "npm test" }),
],
});

Key job properties:

  • runs-onrequired. Runner label (ubuntu-latest, macos-latest, windows-latest).
  • stepsrequired. Array of Step objects.
  • timeoutMinutes — maximum job duration (recommended; flagged by GHA014 if missing).
  • needs — job dependencies for execution ordering.
  • if — conditional execution using Expressions.
  • strategy — matrix builds for parallel execution.

Control GITHUB_TOKEN permissions at the workflow or job level:

import { Workflow, Job, Step } from "@intentius/chant-lexicon-github";
// Workflow-level (applies to all jobs)
export const workflow = new Workflow({
name: "Release",
on: { push: { tags: ["v*"] } },
permissions: {
contents: "write",
packages: "write",
"id-token": "write",
},
});
// Job-level (overrides workflow permissions for this job)
export const publish = new Job({
"runs-on": "ubuntu-latest",
permissions: { contents: "read", packages: "write" },
steps: [
new Step({ name: "Publish", run: "npm publish" }),
],
});

Available permission scopes: actions, checks, contents, deployments, id-token, issues, packages, pages, pull-requests, repository-projects, security-events, statuses. Values: "read", "write", "none".

Prevent concurrent runs of the same workflow or job:

import { Workflow } from "@intentius/chant-lexicon-github";
export const workflow = new Workflow({
name: "Deploy",
on: { push: { branches: ["main"] } },
concurrency: {
group: "deploy-${{ github.ref }}",
"cancel-in-progress": true,
},
});

Use needs to order jobs and pass outputs between them:

import { Job, Step } from "@intentius/chant-lexicon-github";
export const build = new Job({
"runs-on": "ubuntu-latest",
outputs: { version: "${{ steps.version.outputs.value }}" },
steps: [
new Step({
id: "version",
name: "Get version",
run: 'echo "value=$(node -p \\"require(\'./package.json\').version\\")" >> $GITHUB_OUTPUT',
}),
],
});
export const deploy = new Job({
"runs-on": "ubuntu-latest",
needs: ["build"],
steps: [
new Step({
name: "Deploy",
run: "echo Deploying version ${{ needs.build.outputs.version }}",
}),
],
});

The lexicon serializes resources into .github/workflows/*.yml YAML files. Keys use kebab-case for job properties and snake_case for trigger event names.

Chant (TypeScript)YAML outputRule
export const buildApp = new Job({...})jobs: build-app:Export name to kebab-case job key
"runs-on": "ubuntu-latest"runs-on: ubuntu-latestProperty names match GitHub spec
timeoutMinutes: 15timeout-minutes: 15camelCase to kebab-case for job properties
new Step({ uses: "actions/checkout@v4" })- uses: actions/checkout@v4Steps serialize as sequence entries

Build with:

Terminal window
chant build src/ --output .github/workflows/ci.yml

Instead of exporting standalone Job entities, you can scope jobs directly on the Workflow using the jobs prop. This is required when a source directory exports multiple workflows:

export const ci = new Workflow({
name: "CI",
on: { push: { branches: ["main"] } },
jobs: {
build: new Job({ "runs-on": "ubuntu-latest", ... }),
test: new Job({ "runs-on": "ubuntu-latest", ... }),
},
});

See Multiple Workflows for the full pattern.