Skip to content

Pipeline templates

A cratly website is built by a pipeline, and that pipeline is the same everywhere: install, build, check what the build reported, publish the result. Only the last step differs — and it differs by hosting, not by website.

These templates hold that shared part once, so a site repository declares which hosting it publishes to instead of carrying a copy of how.

Including them

The templates are served as plain static files from cratly.io. Any GitLab instance can read them — a self-hosted one, gitlab.com, one behind a company firewall — without an account anywhere and without access to a container registry:

yaml
include:
	- remote: 'https://cratly.io/ci/v1/build.yml'
	- remote: 'https://cratly.io/ci/v1/deploy-s3.yml'
	  inputs:
		bucket: example.com
		prefix: www

That is deliberately not a CI/CD component. Components can only be included from the catalogue of the same GitLab instance, which would restrict these templates to projects on gitlab.com. A static URL has no such border.

/ci/v1/build.ymlInstall, build, check the build report, store the site as an artifact.
/ci/v1/deploy-s3.ymlPublish to S3-compatible object storage.
/ci/v1/deploy-rsync-ssh.ymlPublish over SSH with rsync.
/ci/v1/deploy-sftp.ymlPublish over SFTP, for hosts that offer nothing else.

Every file documents its own inputs in its spec: header — the copy served at the URL above is the authoritative list.

Versions

The version is the path, not a git tag: /ci/v1/… keeps working as it is, and a change that would break an existing pipeline appears at /ci/v2/… instead. Pinning is therefore the default rather than something to remember.

A pipeline including v1 gets fixes and new optional inputs as they are published. A site that wants no movement at all can vendor the file into its own repository — it is MIT, and that is an intended use.

Stages

The jobs land in GitLab's predefined build and deploy stages, so a site that declares no stages: of its own works unchanged. One that does declare them must list both names, or override the stage input.

Secrets are variables, never inputs

Inputs are readable by anyone who can read the pipeline configuration. Everything that grants access therefore comes from masked CI/CD variables instead, and the templates fail with an explanatory message when one is missing rather than proceeding half-configured:

TemplateVariables
build.ymlCRATLY_NPM_TOKEN — only when npm_scope names a private registry.
deploy-s3.ymlS3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY
deploy-rsync-ssh.ymlSSH_PRIVATE_KEY, SSH_KNOWN_HOSTS
deploy-sftp.ymlSSH_KNOWN_HOSTS, plus SSH_PRIVATE_KEY or SFTP_PASSWORD

A masked variable must be a single line, which a PEM key is not. The SSH templates accept either form and base64-decode the key when it does not look like PEM.

Host keys

The SSH and SFTP templates refuse to run without SSH_KNOWN_HOSTS, and they do not offer a way to turn that off. StrictHostKeyChecking=no would make every publication trust whatever answers at that address; on a shared runner, that is a network the pipeline knows nothing about. Produce the value once with

sh
ssh-keyscan -p 22 www.example.com

and compare the fingerprint against what the host's operator publishes before storing it.

What a deploy can promise

Replacing a website is never one operation on the wire, so the question is only how long it can be seen half-replaced, and what the far end has to support to shorten that. The answer differs per protocol, and no strategy is available everywhere:

MethodVisitors during a deployThe target must provide
deploy-s3Object by object; uploads run before deletions, so assets are in place before the pages referencing them change.An S3 API. No directories, no rename, nothing to stage with.
deploy-rsync-ssh, strategy: in-place (default)Per file, but each file is renamed in from a staging copy (rsync --delay-updates), so a slow or failed upload never lands partially. Deletions run afterwards.A shell, rsync, and a filesystem that can rename.
deploy-rsync-ssh, strategy: release-symlinkThe whole site swaps at once: the new copy is uploaded beside the live one, then a symlink is moved over the old one in a single operation. Past releases stay for rollback.Symlinks, GNU coreutils (mv -T), and a document root you may repoint.
deploy-sftpPer file, no staging. The weakest of the four.The SFTP subsystem, nothing more.

release-symlink is the only one that swaps a whole site atomically, and it is not the default on purpose: shared hosting frequently fails at least one of its three requirements — a document root the provider maps for you cannot be repointed at a release directory, and some jailed SFTP servers reject SYMLINK outright. The default asks for rename and nothing else, which is the widest ground available here.

For a host where even that is too much, deploy-sftp is the floor. A visitor who reloads during those seconds may get a page from one version and an image from the other; nothing in the protocol can prevent it. Deploy when the site is quiet, and keep the upload small by letting delete prune rather than re-uploading everything.

Build reports

build.yml reads .cratly/build-report.json and fails merge-request pipelines when a link or image points at a file the build does not contain. On the default branch the same finding only warns: the deploy has to keep running, or a stale site would be the punishment for a broken link. The report is kept as an artifact even when the job fails — it is what the editor reads to show the reason to whoever wrote the page.

Set fail_on_content_problems: "false" to make it advisory everywhere.

Private package registries

Nothing in a cratly site needs a licensed package, and the templates assume the public registry. Where a site does use one — a commercial component library, say — set npm_scope and npm_registry, and provide the token as CRATLY_NPM_TOKEN.

The credentials are configured in the job rather than baked into a build image on purpose: an image carrying licensed packages could not be distributed, and GitLab replaces a job image's ENTRYPOINT, so an image cannot configure itself from the environment either.