Skip to content

gitlab schedules sync

homelabctl2 gitlab schedules sync reconciles GitLab pipeline schedules against an in-source declarative list. The reconciler identifies its own schedules by the [homelab-bot] description prefix and leaves everything else alone, so schedules created by hand in the GitLab UI are never touched.

Terminal window
homelabctl2 gitlab schedules sync # dry-run (default)
homelabctl2 gitlab schedules sync --apply # actually create/update
FlagDefaultDescription
--applyfalse (dry-run)Apply changes; without it, prints diff only.
--projectjoelmuehlena/homelabGitLab project path or numeric id.
--refmainRef the schedules target.
--gitlab-hostgitlab.comGitLab host.

The command picks a token in this order:

  1. $GITLAB_TOKEN env var
  2. op item get --vault HomeLab GitLab --fields api_token --reveal (reuses your signed-in 1Password desktop session)

The PAT needs the api scope.

The source of truth is var schedules in code/homelabctl2/cmd/homelabctl2/gitlab/schedules/schedules.go:

var schedules = []ScheduleSpec{
{"refresh pdns RPZs", "0 */6 * * *", "RUN_REFRESH_RPZS", "true"},
{"rebuild pdns images", "0 4 * * 0", "RUN_REBUILD_PDNS", "true"},
{"rebuild netbox image", "30 4 * * 0", "RUN_REBUILD_NETBOX", "true"},
{"rebuild vault-autoinit image", "0 5 * * 0", "RUN_REBUILD_VAULT_AUTOINIT", "true"},
{"rebuild paperless-backup image", "30 5 * * 0", "RUN_REBUILD_PAPERLESS_BACKUP","true"},
{"rebuild homelabctl2 image", "0 6 * * 0", "RUN_REBUILD_HOMELABCTL2", "true"},
}

Each row produces one GitLab pipeline schedule with description [homelab-bot] <Name>, the given cron (UTC), and a single CI variable gating the relevant pipeline file. Edit cadence/values here; commit; run --apply.

For each managed row:

State in GitLabAction
MissingCreated (cron + ref + active=true) and variable set
Exists, variable matchesNo-op
Exists, variable missingVariable added
Exists, variable has different valueVariable updated
Exists, cron/ref differsLeft untouched — edit in the UI if you want a new cadence
Has [homelab-bot] prefix but not in sourceLogged as unmanaged (no delete)
No [homelab-bot] prefixLogged as unmanaged (no touch)

The reconciler intentionally never deletes — a row dropped from the source list stays in GitLab and is reported as unmanaged. Disable it via the UI when you want it actually gone.

Structured slog output (text by default, JSON with --log-format json). Example dry-run on a fresh project:

INFO syncing pipeline schedules mode=dry-run project=joelmuehlena/homelab ref=main
INFO would create schedule (dry-run) description="[homelab-bot] refresh pdns RPZs" cron="0 */6 * * *" ref=main variable=RUN_REFRESH_RPZS value=true
INFO would create schedule (dry-run) description="[homelab-bot] rebuild pdns images" cron="0 4 * * 0" ref=main variable=RUN_REBUILD_PDNS value=true
...
INFO dry-run only — re-run with --apply to commit changes

Run with --apply to perform the changes.