GitHub Actions
Generate a CI workflow that tests against multiple Node.js versions using lifting over a version list.
Source
examples/github-actions/main.rank
/// Generates a GitHub Actions CI workflow.
/// Shows string template composition, lifting map over a job matrix,
/// and conditional step inclusion via ternary expressions.
use std::collections::{ map }
use std::Emit
use std::Path
Step = Object {
name: string,
uses?: string,
run?: string,
with?: Object { ...: string },
env?: Object { ...: string },
}
Job = Object {
name: string,
`runs-on`: string,
steps: [Step],
}
Workflow = Object {
name: string,
on: Object {
push: Object { branches: [string] },
pull_request: Object { branches: [string] },
},
jobs: Object { ...: Job },
}
node_versions: [string] = [`18`, `20`, `22`]
checkout_step: Step = {
name: `Checkout`,
uses: `actions/checkout@v4`,
}
setup_node = |version: string| -> Step {
return {
name: `Setup Node ${version}`,
uses: `actions/setup-node@v4`,
with: { `node-version`: version },
}
}
install_step: Step = {
name: `Install dependencies`,
run: `npm ci`,
}
test_step: Step = {
name: `Run tests`,
run: `npm test`,
}
build_job = |version: string| -> Job {
return {
name: `build-node-${version}`,
`runs-on`: `ubuntu-latest`,
steps: [
checkout_step,
setup_node(version),
install_step,
test_step,
],
}
}
jobs: [Job] = node_versions |> map(|v: string| build_job(v))
workflow: Workflow = {
name: `CI`,
on: {
push: { branches: [`main`] },
pull_request: { branches: [`main`] },
},
jobs: {
`build-18`: jobs[0],
`build-20`: jobs[1],
`build-22`: jobs[2],
},
}
pub main = || Emit::manifest({
entries: [
{
path: Path::join([`.github`, `workflows`, `ci.yml`]),
format: `yaml`,
value: workflow,
},
]
})
Output
# .github/workflows/ci.yml
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-18:
name: build-node-18
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node 18
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
build-20:
# ... same pattern for 20 and 22
Key concepts
mapover a list —node_versions |> map(|v| build_job(v))produces oneJobper version with no boilerplate.- String interpolation in keys — backtick strings like
`build-node-${version}`work in field names and values. - Quoted field names —
`runs-on`uses a backtick string as a field name to handle the hyphen. Path::join— constructs the nested output path.github/workflows/ci.ymlsafely.
Run it
rank examples/github-actions