You are reading the docs for v0.0.1-beta-2. View the latest docs.
Skip to content

Marketing video in CI (opt-in) ​

The video suite (Add videos to your project) can build a launch video, social cuts and a preview of video changes in GitHub Actions, from committed sources, with no secrets. It is off by default: a repository that does not opt in gets no jobs, no cost and no new permissions.

← Home · Add videos to your project · The plan behind it

Switch it on ​

Three switches, all off until you flip them:

  1. Repository variable. Settings → Secrets and variables → Actions → Variables → VIDEO_CI = true. Without it every job in the Video workflow is skipped. This is the kill switch: set it back to false to stop everything.
  2. video/ci.json. Set "enabled": true, then turn on the triggers you want (triggers.release, triggers.pullRequest, triggers.schedule). A manual run does not need enabled.
  3. The marketing environment (Settings → Environments). Add required reviewers. Publishing (attaching to a release, opening a site pull request) waits for approval there, and only runs when publish.release or publish.site is true.
json
{
  "enabled": true,
  "triggers": { "release": true, "pullRequest": false, "schedule": false },
  "projects": [
    { "project": "launch", "looks": [{}, { "theme": "midnight", "format": "9:16" }, { "format": "4:5" }] }
  ],
  "publish": { "release": true, "site": false },
  "budgets": { "mp4MB": 10, "seconds": 90, "buildMinutes": 20 }
}

What runs, and when ​

TriggerWhenBuildsPublishes
Manual (Actions → Video → Run workflow)You pick project, theme, format, adapterExactly that, one lookNothing: download the artifact
Release (triggers.release)After the Release workflow succeeds for a tagEvery look in projects (the launch video, a vertical cut, a 4:5 cut)The release (publish.release) and a site pull request (publish.site), each behind the marketing environment
Pull request (triggers.pullRequest)A same-repository PR with the video-preview label, or one that changes a previewPaths fileThe preview set, fast (a quicker encode)Nothing: artifact and a job summary
Schedule (triggers.schedule)The first of each monthEvery look in projectsNothing

Pull requests from forks never build. Nothing here needs a secret: the build uses the workflow's read-only token, and only the two publish jobs get contents: write (and pull-requests: write for the site pull request).

The jobs ​

plan ──► claims ──► build (one job per look) ──► publish-release / publish-site (reviewed)
  • plan decides whether to build and what, in seconds.
  • claims is the honesty gate: it fails when a video says something the product does not support (for kinhin, any of the planned platforms such as Buildkite or Windows without saying they are planned, and tone words like "fastest"). The rules live in the adapter (video/adapters/<name>/claims.json) and the project file.
  • build runs video/scripts/build.sh --container --runtime docker on a hosted Linux runner (the same command you run locally), then verifies the result and writes a summary table. A release build pulls the image published for that release (ghcr.io/rabinapps/kinhin-video:<version>, made by video-image.yml) instead of building it once per look; a pull request, a manual run, or a release whose image is not published yet builds it from the checked-out sources.
  • verify is the same checker the end-to-end tests use (video/scripts/verify.mjs): the frame size, the length against the timeline, an audio track near -16 LUFS, no black frames, one caption per cue, a poster and a hero GIF that are not blank, and the size and length budgets. A video that fails is not published.

A failed video job never blocks a release, a docs deploy or a pull request: nothing depends on it.

Store screenshots in the mobile cut ​

examples/mobile-30s is a launch cut that shows store screenshots. Its look sets "screens": true in video/ci.json, so the plan adds one job before the build:

  • mobile-capture runs on the macOS pool (kinhin-xcode), because the iOS simulator needs macOS. It generates the iOS example, runs bundle exec fastlane screenshots for en-US and fr-FR, and uploads the PNGs as the mobile-screens artifact. Only looks that set screens run it; other builds are unchanged.
  • build (Linux, in the container) downloads mobile-screens into video/screens/mobile/ for that look only, then renders. The cut's device scenes point at those files with props.screens (see making a video).

There is one capture job per platform a look needs (ios and android), and each look picks its platform in video/ci.json. The Android capture boots the pool's emulator (KINHIN_AVD, default Pixel_9) and needs its system image on the runner; the iOS capture is verified locally, the Android one is not yet verified on the pool.

Your own product's videos on your fleet ​

kinhin video build <name> --on-fleet runs a video of your repository on a runner kinhin serves, with no video/ folder: kinhin video init --workflow writes .github/workflows/kinhin-video.yml (pinned actions, read-only token, no secrets), and --on-fleet dispatches it with gh, follows the run and downloads the result. See Building on your fleet. The opt-in video.yml described on this page is a different thing: it builds kinhin's own videos from this repository.

Try it without GitHub ​

The decisions are a script you can run here:

bash
cd video
node scripts/video-ci.mjs validate                        # is ci.json valid?
node scripts/video-claims-lint.mjs --all                  # does every video say only true things?
VIDEO_CI=true GITHUB_EVENT_NAME=workflow_dispatch INPUT_PROJECT=launch \
  node scripts/video-ci.mjs plan                          # what would a manual run build?
scripts/build.sh --container --project examples/hello     # the build itself
node scripts/video-ci.mjs verify out                      # the checks, on a finished build

scripts/video-check.sh runs the first two plus the unit tests, and scripts/check.sh and the CI docs job run it on every change. See the tests (kinhin's own guide).

Use it for another product ​

Do not copy video.yml: it builds kinhin's own videos from this repository. For your product, keep your videos in .kinhin/videos/ and build them with kinhin video build on your Mac, or on your fleet with kinhin video init --workflow and --on-fleet (see Add videos to your project). That generated workflow is manual (workflow_dispatch) and pulls the published video image, so you need no video/ folder; to build on every release, add a release: trigger to it yourself.

When something fails ​

SymptomLikely cause
The workflow shows every job skippedVIDEO_CI is not true, or ci.json has "enabled": false, or the trigger's switch is off. The plan job's log says which.
claims failsA scene mentions something the adapter's claims.json lists as planned, or a tone word. The message names the scene and the text.
build fails in verifyThe log lists each failed check (size, length, loudness, black frames, captions). Budgets are in ci.json.
A pull request got no previewIt is from a fork, or has neither the video-preview label nor a changed previewPaths file.
publish-* waitsIt is waiting for approval in the marketing environment.