SpolettaDocs

Building flows

Schedules

A schedule runs a flow on its own: every morning, every ten minutes, every Monday. Schedules are kept on their own page, reached from the Schedules link on the flow list.

Adding a schedule

  1. Pick the flow

    Under Flow, choose any flow or procedure (procedures are marked “(procedure)”).

  2. Pick when

    When offers presets: Every minute, Every 5 minutes, Every 15 minutes, Every hour, Every day at 9:00, Weekdays at 9:00, Mondays at 9:00, or Custom. Cron holds the expression: five fields, or a descriptor such as @hourly.

  3. Pick the time zone

    Time zone defaults to your browser's. A preview below shows Next: …, or what is wrong with the expression. Press New schedule.

CronRuns
0 7 * * 1-5Weekdays at 7:00
*/10 * * * *Every ten minutes
0 9 * * 1Mondays at 9:00
@hourlyAt the start of every hour

Managing schedules

Each schedule shows its next time (or Disabled) and how its last run ended. Toggle it off and on, edit it with Save and Cancel, or delete it; its runs stay. Scheduled runs show a clock in the flow's runs list.

When times are skipped

  • A time is skipped while the flow's last run is still going, for example waiting on a person. The schedule then reads Skipping its times until this run ends.
  • Times missed while the server was down are skipped too; they are not caught up later.

Designing for no one at the keyboard

A scheduled run has no one to fill anything in, so start it with a step that fetches its work: a Sentry or script process, or an agent that finds the work itself. Reviews later in the flow are fine: the run waits for them, but later times are skipped while it does.

Fetching only what changed

A script in a run that a schedule started gets $SINCE (or $LAST_RUN, the same value): when the schedule's last run that ended done started, as a Unix timestamp. Use it to fetch only what changed since, such as the pull requests updated after it.

  • It is empty on the schedule's first run, and in runs started by hand, so have the script fall back.
  • A run that fails, is stopped or is terminated doesn't move it on, so the next run fetches that stretch again.
  • Make what the script lists stored with a unique attribute, so what was already handled in the overlap is skipped. An item whose own run fails comes round again only if it changes again.
Fetch changes: the pull requests updated since the last run that got throughbash
# Empty on the schedule's first run: take the last day then.
since=${SINCE:-$(( $(date +%s) - 86400 ))}
curl -fsS "https://api.github.com/repos/acme/shop/pulls?state=all&sort=updated&direction=desc" \
  -H "Authorization: Bearer $GITHUB_TOKEN" |
  jq --argjson since "$since" \
    '[.[] | select((.updated_at | fromdateiso8601) >= $since) | {number, title, url: .html_url}]'

A cursor of your own

When a timestamp isn't enough, such as for the newest updated_at seen or an offset, keep a cursor in an object instead. Make a Cursor object, tick Store what runs generate, and have the fetching process both take and yield it. Each run then starts with the Cursor the last run that ended done left; see Stored objects.

Fetch changes: takes Cursor, yields Cursor and Changesbash
# No Cursor file on the first run.
after=$(jq -r '.updated_at // empty' "$INPUTS/Cursor" 2>/dev/null)
changes=$(curl -fsS "https://tracker.example.com/api/changes?after=$after" \
  -H "Authorization: Bearer $TRACKER_TOKEN")
jq -n --argjson c "$changes" --arg after "$after" \
  '{Changes: $c, Cursor: {updated_at: ([$c[].updated_at, $after] | max)}}'

Leave the Cursor's attributes not unique: a run that leaves it as it was would otherwise be a duplicate. Only a whole run of the flow carries it over, not a flow process running the flow.

Spoletta · spoletta.comBusiness processes, drawn and run.