Skip to content
Coritan Docs

Deploy on every push

Add an app's push webhook to GitLab, Gitea, Forgejo, Gogs or GitHub so that each push to the app's branch builds and deploys that commit.

View as Markdown

Every app that builds from git has a push webhook. Add its address and secret to the repository at your git host, and each push to the app's branch starts a release of the pushed commit. It works with GitHub, GitLab, Gitea, Forgejo and Gogs.

For a repository on GitHub, connecting the GitHub App on the deployment's Git tab does the same without a webhook, and adds previews and commit statuses (Connect GitHub). Use the push webhook for a repository on another host. Its address and secret are read and made through the Apps API; the dashboard does not show them.

  • The app must build from git. An app that runs an image has no push webhook.
  • Send your access token as Authorization: Bearer $CORITAN_TOKEN (Authentication). The requests take the app's uuid, which GET /api/v1/client/apps lists.
  • You need the webhook's secret. It appears once, in the answer to the request that creates the app. If you did not save it, make a new secret.
  • You need permission to add webhooks to the repository at your git host.
  • For an organization's app, you need the owner or admin role in the organization to make a new secret.

Add the webhook at your git host

Section titled Add the webhook at your git host
  1. Find the webhook's address: the app's webhook.url in GET /api/v1/client/apps/{app_uuid}, such as https://api.coritan.com/api/v1/apps/hooks/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21.
  2. At your git host, open the repository's webhook settings and add a webhook with that address.
  3. Where the host asks for a content type, choose application/json. GitHub's form encoded deliveries work too.
  4. Paste the app's webhook secret into the webhook's secret field. GitLab calls it the secret token.
  5. Choose push events. The webhook ignores every other event, so choosing more only adds deliveries that do nothing.
  6. Save the webhook. GitHub then sends a test delivery called a ping, which the webhook answers with 200, so GitHub shows the webhook as working.

The address takes no access token. The secret proves that a delivery comes from your git host. The app's webhook.secret_set says whether it has a secret, and webhook.secret_hint shows its last four characters. While no secret is set, pushes are refused.

A delivery starts a release when all of these hold:

  • It is a push to a branch, and that branch is the app's branch. A push to a tag deploys nothing, and neither does a push to another branch unless the app makes previews.
  • It comes from the app's own repository. When the delivery names its repository, we compare the host and path with the app's repository address.
  • It names the commit at the head of the push.
  • The app is active.
  • When the app has watch paths, the push changed at least one file they name (Choose which pushes build).

The release builds the pushed commit and records its message and author. Its trigger is push and its actor_type is webhook, so you can tell it apart from one you started. The Releases tab reads it as Push to the branch's name. When several pushes arrive close together, each starts a release, and the newest to go live cancels any older one still on its way (Releases).

When the app makes previews, a push to another branch builds a preview of that branch instead, and deleting the branch closes its previews (Turn on previews).

A delivery deploys at most once. We remember each delivery's ID for a day, so a redelivery from your git host is ignored. A push of the commit that the app's latest push release already has is ignored too. To run the same commit again, rebuild the app.

An app can deploy 30 pushes in 5 minutes. Past that, the webhook answers 429 until the 5 minutes have passed.

We check the delivery against the app's secret before we read it. The header depends on the git host:

Git host Header What it holds
GitHub X-Hub-Signature-256 sha256= and the HMAC SHA-256 of the body, keyed with the secret
Gitea, Forgejo, Gogs X-Gitea-Signature, X-Forgejo-Signature or X-Gogs-Signature The HMAC SHA-256 of the body, keyed with the secret
GitLab X-Gitlab-Token The secret itself

We read the delivery's ID from the host's delivery header, such as X-GitHub-Delivery or X-Gitlab-Event-UUID. A delivery can be up to 10 MB.

Make a new secret when you have lost the old one, or when someone who should not have it may have seen it. The new secret appears once, and the old one stops working at once. Paste the new secret into your git host's webhook straight away, or pushes answer 401 and deploy nothing.

POST /api/v1/client/apps/{app_uuid}/webhook/rotate makes a new secret, or the first one for an app that has none. For an organization's app, send it to POST /api/v1/orgs/{org_slug}/apps/{app_uuid}/webhook/rotate.

Shell
curl -X POST https://api.coritan.com/api/v1/client/apps/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21/webhook/rotate \
  -H "Authorization: Bearer $CORITAN_TOKEN"
JSON
{
  "webhook": {
    "url": "https://api.coritan.com/api/v1/apps/hooks/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21",
    "secret": "4f1d2c3b5a69788796a5b4c3d2e1f0a94f1d2c3b5a69788796a5b4c3d2e1f0a9"
  }
}

secret appears in this answer only. Afterwards the app's webhook.secret_hint shows its last four characters, so you can check which secret your git host holds. An app that runs an image answers 409 with {"error": "not_git", "message": "Only an app that builds from git has a push webhook"}.

Each push to the app's branch appears as a new release in the app's history, with the commit's ID, message and author, and on the app's Releases tab. Through the API its trigger is push. Your git host's delivery log shows each answer: 201 for a push that started a release, 202 for a delivery the webhook ignored and why, and 200 for a ping or a deleted branch whose previews it closed.

Your git host's delivery log shows the webhook's answer to each delivery. A 202 answer names what the webhook ignored in ignored:

push to feature; the app deploys main
The push was to another branch, and the app makes no previews. Push to the app's branch, or change the branch the app deploys (Change the settings). When the answer also has "previews": "the plan does not include previews", previews are on for the app but the plan of the account it bills to does not include them (Turn on previews).
no changed file matches the watch paths
The push changed none of the files the app's watch paths name, so it builds nothing.
tag push
Tags do not deploy on their own. GitLab's tag pushes show as not a push: tag_push. Deploy the tag yourself (Choose what to deploy).
push from another repository
The delivery came from a repository other than the app's, such as a fork, or the repository moved. Correct the app's repository address.
the app is suspended
We suspended the app. Contact support to find out why.
this delivery was already received or the latest push release already has this commit
The push was deployed already. Rebuild the app to run it again.
not a push: issues
The webhook sends other events. Choose push events only.

The webhook refuses a delivery with one of these, in detail:

Sign the delivery with the app's webhook secret.
The delivery has no signature. Paste the secret into the webhook at your git host.
The signature does not match the app's webhook secret.
Your git host holds another secret, often one from before you made a new one. Paste the current secret, or make a new one.
This app has no webhook secret. Create one in its settings.
The app has no secret yet. Make a new secret, then paste it into your git host.
No git app with this id.
The address does not belong to an app that builds from git. The app may have been deleted. Read webhook.url from the app again.
The delivery is not JSON.
Set the webhook's content type to application/json.
Too many pushes to deploy. Wait a few minutes.
The app deployed 30 pushes in the last 5 minutes. Later pushes deploy again once the 5 minutes have passed.

Your git host sends deliveries to POST /api/v1/apps/hooks/{app_uuid}, the address in webhook.url. It is not in the API reference, because only a git host calls it. A success answers with ok set to true and the header Cache-Control: no-store:

JSON
{"ok": true, "deployment": "0b6f3d2e-7a41-4c59-8e1d-2f9a6b3c4d5e", "status": "queued"}
Status Body Meaning
200 {"ok": true, "event": "ping", "app": "<app uuid>", "branch": "main"} A ping. Nothing deploys.
200 {"ok": true, "closed": ["<deployment uuid>"], "branch": "feature"} The branch was deleted, and its previews closed.
201 {"ok": true, "deployment": "<deployment uuid>", "status": "queued"} The push started this release, which the answer calls deployment.
202 {"ok": true, "ignored": "tag push"} The delivery deploys nothing, for the reason given. A repeated delivery also has "duplicate": true.

A refusal has the error in detail, as in {"detail": {"ok": false, "error": "signature_mismatch", "message": "The signature does not match the app's webhook secret."}}:

Status error Cause
400 invalid_payload The body is not JSON.
401 signature_missing The delivery has no signature header.
401 signature_mismatch The signature does not match the app's secret.
403 webhook_not_configured The app has no secret.
404 not_found No app that builds from git has this ID, or Apps is turned off (Not found.).
409 refused The release was refused, and message says why.
413 too_large The body is larger than 10 MB.
422 repository_invalid The app's repository address cannot be built from, and message says why.
429 rate_limited The app deployed 30 pushes in 5 minutes. Retry-After gives the wait in seconds.

To deploy from a CI job instead, send a deploy request with an access token (Deploy through the API), or use a deploy hook (Deploy with a hook).

API operations on this page

MethodPathWhat it does
POST/api/v1/client/apps/{app_uuid}/webhook/rotateA new secret for the push webhook, shown once; the old one stops verifying at once
POST/api/v1/orgs/{org_slug}/apps/{app_uuid}/webhook/rotateA new secret for the push webhook, shown once; the old one stops verifying at once