Skip to content

Writing .gl scripts

.gl files turn a chain of gapline commands into a single, committable artifact. They are the right tool when a cleanup is worth re-running weekly, shipping in a repo, or handing to a colleague as a reproducible pipeline.

This guide builds a weekly-fix script from scratch: validate, patch a few known issues, re-validate, save.

A .gl file is plain text:

example.gl
# Comments start with '#'. They can be on their own line or at the end of one.
feed ./data/gtfs.zip # First directive — load the feed into memory.
validate --min-severity error # Any gapline command, without the -f flag.
save ./data/gtfs-patched.zip # Atomic write. Without a path, rewrites the source.

Three things to know up front:

  1. feed always comes first. It loads the archive once; every subsequent command works against that in-memory copy.
  2. save is explicit. Nothing reaches disk until save runs — if the script crashes in the middle, the source feed is untouched.
  3. Stop-on-error. The first failing command halts the script. Combined with the point above, this makes .gl files safe to re-run.

The full grammar is documented on .gl syntax; the runtime semantics are in concepts / .gl session model.

The goal: every Monday morning, validate the production feed, apply the known cleanups, and hand the team a fresh JSON report.

weekly-fix.gl
# Load the latest feed dropped by the nightly sync.
feed ./data/gtfs.zip
# Prune the retired line and everything that references it.
delete trips --where "route_id=OLD_LINE" --confirm
delete routes --where "route_id=OLD_LINE" --confirm
# Normalize the mis-coded bus routes.
update routes --where "route_type=700" --set route_type=3 --confirm
# Snapshot the post-cleanup report for the team.
validate --format json -o ./reports/weekly.json
# Atomic write. Only reached if every step above succeeded.
save ./data/gtfs-patched.zip

Run it:

Terminal window
gapline run weekly-fix.gl

If any command fails, the run halts, save is skipped, and ./data/gtfs.zip on disk is unchanged. Fix the reported issue and re-run.

Every flag the CLI accepts works inside a .gl file, with one exception: -f / --feed is supplied by the feed directive, so you omit it on every subsequent command. --output still works if you want to dump an intermediate report to a different path than the one save will use.

feed ./data/gtfs.zip
# The feed flag is implicit — validate operates on the in-memory copy.
validate --min-severity warning --format json -o pre-cleanup.json
update stops --where "stop_id=S01" --set stop_name="Gare Centrale" --confirm
validate --min-severity warning --format json -o post-cleanup.json
save

Calling save without a path rewrites the source file — in this case ./data/gtfs.zip. Pass a path to preserve the original.

  • No variables, no substitution. Paths and values are literal.
  • No conditionals, no loops, no error-handling blocks. Execution is strictly linear.
  • No nested run. You cannot call a .gl file from another .gl file.

If your workflow needs branching or templating, drive gapline from a shell script and keep the .gl file small. .gl is a declarative format on purpose — readable at a glance, safe to version-control, no hidden control flow.

- run: gapline run weekly-fix.gl
- uses: actions/upload-artifact@v4
with: { name: weekly-reports, path: reports/ }
- run: |
case $? in
0) ;; # Script completed, save succeeded.
1) echo "A step failed — source feed unchanged." ; exit 1 ;;
*) exit 1 ;;
esac

Exit codes mirror the first failing sub-command’s code. See concepts / exit codes.