Automation
What happens by itself after a merge to main, and how to trigger each step by hand when you need to.
For writing content, see Editorial.
The chain
Weekdays 06:00 (Europe/Berlin)
└─ sync:changelog reads the CHANGELOG.md of six repositories
├─ nothing new -> stop, no commit
└─ something new -> commit to main
│
Push to main ─────────────────┘
├─ deploy:production deploy the docs to Vercel
└─ notify:slack post to #guppy if there is anything to announceThe deploy runs before the announcement. GitLab skips a stage when an earlier one failed, so a broken deploy suppresses the message instead of posting one full of dead links.
No loop is possible: sync:changelog runs only on a schedule, and notify:slack never pushes.
What triggers a message
| Change | Announced |
|---|---|
New file in docs/news/ | yes |
| Release opening a new minor or major line | yes |
Release with Breaking changes | yes, whatever the version segment |
| Withdrawn release | yes |
Release candidate such as 2.8.0-rc1 | yes, tagged #ReleaseCandidate |
| Plain patch release | no, counted in a trailing line |
| Edit to an existing post | no, only added files count |
Everything from one push becomes a single message, never one per release.
The point that matters most for you as a developer: a genuine breaking change belongs under a Breaking changes heading, even in a patch. That heading is what decides between announcing and silently counting.
The three jobs
All of it lives in .gitlab-ci.yml.
| Job | Runs when | Does what |
|---|---|---|
sync:changelog | schedule only | Read changelogs, commit and push if anything changed |
deploy:production | push to main | vercel deploy --prod |
notify:slack | push to main | Digest into #guppy |
Why not Vercel's git integration
Vercel attributes a push to the Vercel account behind the commit author. That account is a viewer on the team, so those deployments were refused. Deploying from the pipeline sidesteps the attribution and uses a token from an account that may deploy. The git integration has since been disconnected, so GitLab is the single source.
Doing it by hand
Collect the changelogs
npm run changelog:syncWrites docs/.vitepress/data/changelog.json. Needs GUPPY_GITLAB_TOKEN in .env.local.
When nothing changed the file stays byte identical, including the generatedAt field. An empty git diff therefore reliably means "nothing new", and that is exactly what the scheduled job branches on.
Preview a message
node scripts/notify-slack.mjs --dry-run
node scripts/notify-slack.mjs --dry-run --since <commit>Prints the finished Slack payload as JSON and sends nothing. Without a token configured the script never sends anything anyway, so a local run cannot post by accident.
The JSON pastes straight into Slack's Block Kit Builder if you want to see it rendered.
Deploy
npx vercel deploy --yes # preview
npx vercel deploy --prod --yes # productionYou normally do not need this, the pipeline handles it. Useful for showing something before merging.
Run the schedule now
Build, Pipeline schedules, Play on Nightly changelog sync. Runs immediately and is otherwise identical to the nightly run.
Test the pipeline on a branch
Start a pipeline with the variable SLACK_NOTIFY_TEST set to 1. Then notify:slack also runs outside main, as a manual job.
Careful: the Slack variables are protected, so they only reach protected branches. On an ordinary feature branch the job runs, finds no token, prints the message instead of sending it, and goes green. That looks like success but sent nothing.
Variables
All under Settings, CI/CD, Variables of the guppy-docs project, each masked and protected.
| Variable | Purpose |
|---|---|
GUPPY_GITLAB_TOKEN | Read the changelogs of the other projects |
GUPPY_PUSH_TOKEN | Push the commit to main, needs the Maintainer role |
VERCEL_TOKEN | Deployment, a user token scoped to the team |
SLACK_BOT_TOKEN | Send the message, scope chat:write |
SLACK_CHANNEL | Target channel, an id is stabler than a name |
Values belong nowhere in the repository. .env.local is for local runs and is listed in .gitignore.
Troubleshooting
| Symptom | Cause |
|---|---|
| Job green but nothing in Slack | No token reached the job, usually protected on an unprotected branch. It says so in the log |
invalid_auth | Slack token wrong or revoked |
not_in_channel | App not in the channel. /invite @Guppy Docs, or add scope chat:write.public |
Could not retrieve Project Settings | Vercel token has the wrong scope. It must be a user token scoped to the team |
| Schedule does not push | GUPPY_PUSH_TOKEN lacks the Maintainer role. main is protected |
| Nothing announced although a release landed | Patch without Breaking changes. Working as intended |
The script reports every Slack error in plain text along with what to do about it, so the job log is the first place to look rather than guesswork.