pf-ci — lower the CI DAG to workflows
pf-ci is the second lowering of the projectfile toolchain: it turns the
org.projectfile.ci extension — a dependency-ordered DAG of CI goals — into the
workflow files a forge actually runs.
| Step | Tool | What it does |
|---|---|---|
| 1. Read / write | pf-cli | get, set, add, del, validate, convert |
| 2. Sync files | pf-bridge | package.json, CITATION.cff, README.md, … |
| 3. Lower to CI | pf-ci (this page) | org.projectfile.ci → workflow YAML |
Installation
Section titled “Installation”Release binary
Latest release 3.4.0. Published for 5 platforms: macOS, Linux.
base=https://kiota.ch/projectfile/ci/releases/download/v3.4.0curl --fail --location --output ~/.local/bin/pf-ci \ "$base/pf-ci-$(uname -s | tr A-Z a-z)-$(uname -m)"chmod +x ~/.local/bin/pf-ci| Platform | Asset |
|---|---|
| macOS / ARM64 | pf-ci-darwin-arm64 |
| macOS / x86-64 | pf-ci-darwin-x86_64 |
base=https://kiota.ch/projectfile/ci/releases/download/v3.4.0curl --fail --location --output ~/.local/bin/pf-ci \ "$base/pf-ci-$(uname -s | tr A-Z a-z)-$(uname -m)"chmod +x ~/.local/bin/pf-ci| Platform | Asset |
|---|---|
| Linux / ARM64 | pf-ci-linux-aarch64 |
| Linux / RISC-V | pf-ci-linux-riscv64 |
| Linux / x86-64 | pf-ci-linux-x86_64 |
Verify what you downloaded
Every binary ships a detached signature beside it. Confirm it with the maintainer's release key, which the keyserver serves:
gpg --keyserver hkps://keys.openpgp.org --recv-keys 8F89CBF7C8D51F8BEC7533C00925F8F79EBD3257curl --fail --location --output pf-ci.asc \ "https://kiota.ch/projectfile/ci/releases/download/v3.4.0/pf-ci-$(uname -s | tr A-Z a-z)-$(uname -m).asc"gpg --verify pf-ci.asc ~/.local/bin/pf-ciContainer image
Run the tool without installing it. The alias makes every command on this site work unchanged against your working directory:
ghcr.io/projectfile-org/ci — linux/amd64, linux/arm64, linux/riscv64
docker pull ghcr.io/projectfile-org/ci:3.4.0alias pf-ci='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \ --volume "$PWD:/app/ws" --workdir /app/ws ghcr.io/projectfile-org/ci:3.4.0 pf-ci'damianbuho/projectfile-ci — linux/amd64
docker pull damianbuho/projectfile-ci:3.4.0alias pf-ci='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \ --volume "$PWD:/app/ws" --workdir /app/ws damianbuho/projectfile-ci:3.4.0 pf-ci'kiota.ch/projectfile/ci — linux/amd64
docker pull kiota.ch/projectfile/ci:3.4.0alias pf-ci='docker run --rm --user "$(id -u):$(id -g)" --group-add 0 \ --volume "$PWD:/app/ws" --workdir /app/ws kiota.ch/projectfile/ci:3.4.0 pf-ci'Stable releases also publish X.Y.Z, X.Y and X tags — pull the precision you want to pin.
Uninstall
rm -f ~/.local/bin/pf-ci # release binarydocker image rm ghcr.io/projectfile-org/ci # container imagepf-ci resolve [-pf PATH] [-verbose|-q]pf-ci generate -target gha|forgejo|lefthook [-pf PATH] [-o PATH] [-check|-n] [-force=false] [-verbose|-q]pf-ci help [COMMAND]pf-ci version| Command | What it does |
|---|---|
resolve | Emit the vendor-neutral job model as JSON (the template input). |
generate | Render the job model to a target’s workflow YAML, or (-check) verify the committed workflow still matches the source DAG. |
help | Print this help, or one command’s flags (also: pf-ci COMMAND -h). |
version | Print the resolver version (the codegen provenance stamp). |
4 commands — the listing pf-ci v3.4.0 reported when the page was built.
resolve
Section titled “resolve”pf-ci resolve # the whole job model as JSONpf-ci resolve -pf projectfile.yaml # read a specific documentpf-ci resolve -q # mute progress; the verdict still printsgenerate
Section titled “generate”pf-ci generate -target gha # render .github/workflows/*.yamlpf-ci generate -target forgejo # render .forgejo/workflows/*.yamlpf-ci generate -target lefthook # render lefthook.yamlpf-ci generate -check # freshness gate: fail if the committed workflow driftedpf-ci generate -target gha -n # print the write plan, change nothing| Flag | Description |
|---|---|
-target |
render target: forgejo, gha, lefthook |
-pf |
projectfile path, or - for a YAML/JSON document on stdin (default: discover) |
-o |
output directory for the per-goal workflow files (default: the target’s dir) |
-check |
exit non-zero if the committed workflow drifted from the source DAG |
-dry-run |
print the write/remove plan on stdout and change nothing |
-force |
remove orphaned generated workflows; -force=false reports and fails instead |
-quiet |
mute progress lines; warnings, errors and the verdict still print |
-verbose |
show operational log lines (e.g. interpolation lookups) |
Environment
Section titled “Environment”| Variable | Effect |
|---|---|
PF_CI_WORKFLOW_EXT |
committed-workflow file extension, .yaml or .yml. m6e’s make act-* derives its default from the same variable |
PF_CLI_VERBOSE |
same effect as -verbose, for a run that cannot pass flags |
Reading the DAG
Section titled “Reading the DAG”A projectfile declares its pipeline as a set of named nodes with their
dependencies; pf-ci walks that graph and emits one job per node:
org: projectfile: ci: nodes: site-is-built: needs: container-is-ok: true dist-extract: true inputs-are-ok: needs: specification-generated: trueNothing in the emitted workflow is hardcoded: the lifecycle runs the nodes the
document declares, and an empty org.projectfile.ci produces an empty
pipeline.
The freshness gate
Section titled “The freshness gate”pf-ci generate -check is the command that keeps a repository honest — it
re-renders from the document and fails when the committed workflow files no
longer match, so a hand-edited workflow or a changed DAG is caught before it
ships. Run it as a CI step of its own:
- name: Verify CI workflows match org.projectfile.ci run: pf-ci generate -target gha -check