Skip to content

Publish from CI

CI is the canonical machine publisher. Give the pipeline a scoped API key and POST your build to the ingest API. Nothing here touches server or proxy credentials.

Create a key limited to the group you deploy into. For PR previews that get cleaned up, include unpublish:

Terminal window
gotifacts keys create --name ci --grant "previews:publish,unpublish"

Store the printed token as a CI secret, e.g. GOTIFACTS_API_KEY. See create & scope API keys.

For a multi-file site, upload a .tar.gz with a top-level index.html as the bundle part:

Terminal window
tar -czf site.tgz -C ./dist .
printf '{"group":"previews","slug":"pr-%s","title":"PR #%s"}' "$PR" "$PR" > meta.json
curl -fsS \
-H "Authorization: Bearer ${GOTIFACTS_API_KEY}" \
-F 'meta=<meta.json;type=application/json' \
-F 'bundle=@site.tgz;type=application/gzip' \
"${GOTIFACTS_URL}/ingest/sites"

For a single self-contained page, use the index part instead of bundle (see publishing your first site).

Publishing is idempotent — re-running replaces the site at the same group/slug.

A group and a flat site may share a name: publishing previews under group: "decks" (reachable at pr-6.decks.<base>) coexists with a flat decks site at decks.<base>. Each site’s content is isolated, so deploying the flat site never disturbs the previews beneath it. See the URL ⇄ path convention.

When a PR closes, delete its preview (requires the unpublish capability):

Terminal window
curl -fsS -X DELETE \
-H "Authorization: Bearer ${GOTIFACTS_API_KEY}" \
"${GOTIFACTS_URL}/ingest/sites/previews/pr-${PR}"
.github/workflows/preview.yml
- name: Publish preview
env:
GOTIFACTS_URL: https://example.com
GOTIFACTS_API_KEY: ${{ secrets.GOTIFACTS_API_KEY }}
PR: ${{ github.event.number }}
run: |
tar -czf site.tgz -C ./dist .
printf '{"group":"previews","slug":"pr-%s","title":"PR #%s"}' "$PR" "$PR" > meta.json
curl -fsS \
-H "Authorization: Bearer ${GOTIFACTS_API_KEY}" \
-F 'meta=<meta.json;type=application/json' \
-F 'bundle=@site.tgz;type=application/gzip' \
"${GOTIFACTS_URL}/ingest/sites"
StatusCause
401Missing or invalid API key.
403The key lacks the capability for that group/site.
400Invalid path (bad label or too deep), or missing index.html.

See the full HTTP API reference.