Skip to content

Publishing & conformance

Publishing has four stages:

validate → package → upload to Studio → review

From the template project created by @port60/template-kit, run:

Terminal window
npm run validate
npm run validate:json

The validator uses the same contract and dialect enforcement as the upload service. It checks:

  1. manifest.json matches the manifest schema.
  2. Every declared section exists in the section catalogue and has a renderer.
  3. Every renderer parses under the restricted Liquid dialect.
  4. Every renderer survives both minimal and sample fixtures.
  5. Placed islands are declared and available in the island registry.
  6. assets/theme.css exists and is not empty.
  7. A declared layout contains exactly one {% content %} slot and renders the layout fixture.
  8. Declared worship and hero imagery support is proven by the rendered output.
  9. Declared page templates exist and render their documented data fixtures.
  10. Every requiresCapabilities entry is backed by a section, page template, island or context surface the artifact actually uses.

Errors block packaging and upload. Warnings do not block them, but should be understood before a version is submitted.

Terminal window
npm run package

Packaging validates again, then creates dist/<name>-<version>.zip (dist/ is recreated on every run, so it only ever holds the current build). Only contract shaped files are included: the manifest, layout, section and page renderers, and CSS assets. Project notes, dependencies and source control files are excluded.

Exactly the contract-shaped set: manifest.json, layout.liquid, sections/*.liquid, pages/*.liquid and assets/*.css, where assets/theme.css is the one stylesheet the platform loads. Everything else beside your template stays on your machine: notes, dependencies, preview content, design references, and any image, font or script file. From kit 0.14.3, package and publish list what they left out, so a photograph dropped into assets/ is a line in the output rather than a broken URL on a live site. The upload refuses anything outside the set.

Photographs belong in the charity’s media library, where the charity chooses them and holds them (a starter media pack a template can offer is on the roadmap). Decorative textures and patterns fit inside theme.css as data URIs within the stylesheet size limit.

  1. Sign in to your organisation’s tenant admin.
  2. Open Studio and select your template.
  3. Open Versions and upload the zip.
  4. Review the validation result and preview.
  5. Submit the version for review.

The platform validates the uploaded bytes again. A successful local run is therefore useful before upload, but the Studio result is the final gate for that exact zip.

The kit can also sign you in and publish without leaving your terminal. The Studio upload above still works and is unchanged; this is the same result in one command, for when you would rather stay on the command line or automate the loop.

Sign in once. This opens your browser to approve the device, then stores an offline token in ~/.port60. It finds your studio workspace automatically:

Terminal window
p60-template-kit login

Then, from your template project:

Terminal window
p60-template-kit publish

publish runs the same four stages as above in order: validate, package, upload to your studio and submit the version for review. Add --no-submit to upload the version without submitting it, and submit later from Studio. The manifest name must match a template you own (an unclaimed name creates the template on first publish; a name owned by another studio is refused).

Two more sign-in commands:

  • p60-template-kit whoami: who is signed in, and which studio workspace.
  • p60-template-kit logout: sign this machine out and revoke its token.

By default the kit talks to Port60’s hosted stack (auth.port60.com / api.port60.com). To point it at a local or self-hosted environment, set P60_AUTH_BASE, P60_API_BASE, P60_REALM and P60_CLIENT_ID before running login.

Your repository can publish on its own, the way a release pipeline would: push a version tag, the workflow validates and uploads, and the template or its new version arrives in the review queue exactly as an upload from Studio does. Review still decides; nothing is approved automatically.

There is no token to create, store or rotate. GitHub signs every workflow run with a short-lived OIDC token, and the platform verifies that signature and checks it against a repository you trusted in Studio. Remove the trust and publishing stops from the very next run.

In Studio choose Automate (next to the upload button), paste the repository as owner/name or its GitHub URL, and add it. That is the whole registration, done once per repository for the whole studio, the way a trust policy names a repository. From then on the workflow can do what you can from a terminal: create a template from its manifest or add a version to one you own.

Behind the one field, the platform checks GitHub’s permanent numeric id for the repository on every run, never the name, so a renamed or re-created repository never inherits the trust. For a public repository the id is looked up when you add it; for a private one it is learned from the first workflow run and shown in Studio as verified from then on. Any branch or tag may publish; choose what triggers the workflow in the workflow file itself.

Save this as .github/workflows/publish.yml. The id-token permission is what lets GitHub sign the run for you; no secret is needed.

name: Publish template
on:
push:
tags: ['v*']
permissions:
id-token: write
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx @port60/template-kit publish --ci

Bump version in manifest.json, describe the change in changelog, then tag and push:

Terminal window
git tag v1.2.0
git push --tags

The run validates the template locally, packages it, and posts it with the run’s own token. The job log shows the validation report and whether the version was submitted for review. Add --no-submit to upload without submitting.

What CI can and cannot do: it can create a template from the manifest name (the first publish claims it, label and description included) and add versions to any template the studio owns. It cannot publish from an untrusted repository, take a name another studio owns, or run more than a modest number of times a day per repository. Every refusal is explained in the job log and shown under Automate in Studio.

A reviewed version cannot be overwritten. For every change:

  1. Increase version in manifest.json using semantic versioning.
  2. Describe the release in changelog.
  3. Validate and package again.
  4. Upload the new zip as a new version.

Sites pinned to an older version continue to render that version until an update is deliberately applied. This protects live sites from unexpected design changes.

Use a development version with real content

Section titled “Use a development version with real content”

The local and Studio previews use safe contract fixtures and non-interactive island skeletons. To check your own site with real navigation, content lengths, imagery, worship data and enabled features, upload a version such as 1.2.0-dev.1. Once it validates, choose Use on this site in Studio.

A development version is deliberately restricted:

  • only the template owner’s own tenant can activate it;
  • its artifact is read from quarantine through an internal tenant-bound route;
  • it cannot be submitted for review, promoted or listed in the catalogue;
  • the site pin is explicit and responses are served with no-store caching.

Increase the final number for each development build. When the design is ready, upload a stable version such as 1.2.0 and submit that version for review.