Actions: a job that runs when a save lands
An action is two files in a project, under .interlinked/actions/: a definition (<name>.yml: when it runs, where, what it may read and write, the steps) and the script its steps run. They travel with the project like any other file. When a save that matches arrives in the vault, the run happens on a machine, and whatever it writes inside its folders comes back as a new save, made by the action.
Two worked examples, each runnable from an empty project by following its page:
- A measured LOD chain for Roblox: meshes saved into
Characters/come back as a chain of levels, each rendered and scored against the original. Runs on the maker's machine. - Web avatars from any image: images dropped into
Avatars/come back inWeb/avatars/at three sizes, WebP. Runs on ours.
The definition, every key
name: Roblox-ready meshes # what lists show; the file's name is what commands take
on: # when it runs: one or more of the five below
push: # a save lands in the vault
branches: [main] # on these branches (left out: any)
paths: [Characters/**/*.obj] # touching these paths (globs; left out: any)
kinds: [added, changed] # in these ways: added, changed, deleted (left out: all three)
manual: # someone starts it: Run now, `ifile actions run`, the MCP
inputs: # named values given at the start, each IFILE_INPUT_<NAME>
quality: { description: "0 to 100", default: "82", required: false }
schedule: ["0 6 * * 1-5"] # cron lines, UTC, on the newest save of the default branch
api: true # an outside system may start it: POST /v1/projects/:id/actions/:name/runs
action: [meshes] # after these actions' saves (an action's save sets off nothing else)
runs-on: maker # maker | team | <a machine's name> | hosted (below)
image: ghcr.io/you/tools:1 # hosted only; not built yet: a hosted run uses our standard image
timeout: 60 # the most minutes a run may take, 1 to 360; past it it is stopped and fails
needs:
read: [Characters/**] # all a run can fetch; nothing else of the project reaches it
write: [Characters/**/*.lod*.obj] # all it may change; a change outside fails the run, nothing saved
tools: [node, npm] # checked on the machine before the run
secrets: [DEPLOY_TOKEN] # set with `ifile secrets set`; reach the steps as environment variables
steps: # run in order, in the project's folder; one failing ends the run
- name: Cut the chain
run: node .interlinked/actions/lod-chain.mjs
each: changed # once per changed file, its path in FILE
save:
message: "LOD chain for {count} meshes, worst score {score}" # {count}: the files that set it off; {key}: a key=value line a step wrote to IFILE_OUTPUT
to: same-branch # the branch it ran on (a branch of the action's own is not built yet)
Every step sees IFILE_ACTION, IFILE_RUN (the run's number), IFILE_BRANCH, IFILE_SAVE and IFILE_SAVE_NUMBER (the save it runs on), IFILE_TRIGGER (push, manual, api, schedule or action), IFILE_CHANGED (the changed paths, one a line), IFILE_OUTPUT (a file for key=value lines) and CI=true. A definition that does not check says what is wrong and on which line: ifile actions --in <project> lists every action with its problems.
Where it runs, and what it costs
runs-on | where | when to use it | cost |
|---|---|---|---|
maker (the default) | the machine that pushed the save | a solo developer or a small team: the files are already there | nothing for the run |
team | any machine the project's team switched on | work that needs a tool or a GPU only some machines have, or should not depend on who pushed | nothing for the run |
| a machine's name | that one registered machine | exactly one machine holds the licence or the hardware | nothing for the run |
hosted | a machine we start for the run: Linux, 2 CPUs, 8 GiB, Node, Python, vips, ImageMagick, ffmpeg, gltfpack and more; reads up to 12 GiB of the project | no machine of yours is on, or the run must not touch one | Cloudflare's container rates plus 25%, for the time the machine is up: about half a cent a minute, never rounded to minutes |
What a run reads and the save it makes are stored and moved like any other bytes of the project, on the organization's storage and transfer.
Nothing runs on a machine its owner did not switch on. A machine takes runs only after ifile runner start --in <project> (or --team <team>), or Run actions here on the app's Flow tab, and stops when told. Opening a link or cloning a project never runs anything.
Or your own pipe: a webhook and a key
Rather run the work on your own servers, in your own CI? Everything an action does can be done from outside with a webhook and a key:
- Hear about the save. Point a webhook at your service:
ifile auth webhooks add https://you.example/hooks --project <id> --events version.pushed. Each push sends a signedversion.pushed(Standard Webhooks: checkwebhook-signatureagainst the raw body with your secret, answer 2xx within five seconds, ignore awebhook-idyou have seen). - Read what you need. With an API key (
ifile auth keys new ci --project <id> --can project:read,status:write):GET /v1/projects/<id>/tree?at=<number>lists the files,GET /v1/projects/<id>/archive?at=<number>streams them as a tar. - Say how it went.
POST /v1/projects/<id>/commits/<number>/statusesandPUT .../reports/<name>put your result on the save, where the app and the site show it. - Save results back. On your server, with a sign-in key in
INTERLINKED_TOKEN(ifile auth login-key new ci --lasts 30don your own machine), the CLI works unattended:ifile clone <link>(a folder named for the project), change the files, thenifile save <folder> -m "..."andifile push <folder>.
The routes are on the API page.