Skip to main content
GitHub Actions is the primary automation plane for Tuturuuu.

Workflow Gatekeeping

Every major workflow can be disabled centrally through tuturuuu.ts. The reusable workflow ci-check.yml reads that config and emits should_run, which downstream jobs use before doing real work. For Vercel deployments, ci-check.yml also performs affected-app gating before any install, build, or deploy step starts. It checks the current GitHub event’s changed files against Vercel app metadata and workspace dependency closures from tuturuuu.ts. The changed-file resolver must evaluate the full effective change range:
  • Manual workflow_dispatch runs bypass affected-app gating when the workflow is enabled.
  • Vercel production push workflows first look for the latest successful GitHub Deployment marker for the same workflow and branch, then diff that marker SHA to the current GITHUB_SHA.
  • If no marker is available, push events use the GitHub event payload’s complete commit file list instead of falling back to only the latest commit.
  • Pull request events diff the PR base SHA to the PR head SHA.
  • If the resolver cannot prove the full range, it leaves changed-file state unavailable so Vercel gating defaults open.
Affected Vercel rules:
  • apps/<app>/** runs that app’s preview or production Vercel workflow.
  • packages/<package>/** runs every Vercel app whose transitive workspace:* dependency closure includes that package.
  • apps/*/package.json and packages/*/package.json are dependency changes for their owning workspaces.
  • bun.lock, root package.json, turbo.json, tuturuuu.ts, and .github/workflows/ci-check.yml run every Vercel app workflow.
  • A Vercel workflow file change, such as .github/workflows/vercel-preview-calendar.yaml, runs that specific workflow.
  • If changed files cannot be computed, the Vercel gate defaults open.
bun.lock-only changes intentionally run all Vercel app deploys because ownership is ambiguous without a manifest or source path.

Main Workflow Groups

Hosted web deployments

  • vercel-preview-platform.yaml
  • vercel-preview-apps.yaml
  • vercel-preview-calendar.yaml
  • vercel-preview-cms.yaml
  • vercel-preview-finance.yaml
  • vercel-preview-inventory.yaml
  • vercel-preview-mail.yaml
  • vercel-preview-meet.yaml
  • vercel-preview-nova.yaml
  • vercel-preview-qr.yaml
  • vercel-preview-rewise.yaml
  • vercel-preview-shortener.yaml
  • vercel-preview-tasks.yaml
  • vercel-preview-track.yaml
  • vercel-preview-learn.yaml
  • vercel-preview-teach.yaml
  • Matching vercel-production-*.yaml workflows for production
CMS uses the same Vercel pattern as the other satellite apps:
  • preview workflow: vercel-preview-cms.yaml
  • production workflow: vercel-production-cms.yaml
  • project secret: VERCEL_CMS_PROJECT_ID
  • environments: vercel-preview-cms and vercel-production-cms
The Apps gateway uses the same pattern:
  • preview workflow: vercel-preview-apps.yaml
  • production workflow: vercel-production-apps.yaml
  • project secret: VERCEL_APPS_PROJECT_ID
  • environments: vercel-preview-apps and vercel-production-apps
QR uses the same pattern:
  • preview workflow: vercel-preview-qr.yaml
  • production workflow: vercel-production-qr.yaml
  • project secret: VERCEL_QR_PROJECT_ID
  • environments: vercel-preview-qr and vercel-production-qr
Mail uses the same pattern:
  • preview workflow: vercel-preview-mail.yaml
  • production workflow: vercel-production-mail.yaml
  • project secret: VERCEL_MAIL_PROJECT_ID
  • environments: vercel-preview-mail and vercel-production-mail
These workflows:
  • run with default contents: read permissions
  • bind deploy jobs to vercel-preview-<app> or vercel-production-<app> GitHub Environments
  • run preview deployments only through manual dispatch from main, with a required preview_ref input and an actor present in the TRUSTED_PREVIEW_DEPLOY_ACTORS repository variable
  • reject production manual dispatches from non-production branches before install/build/deploy work starts
  • install Bun and Node 24
  • install dependencies
  • build selected shared workspace dependencies before Vercel resolves package exports: @tuturuuu/types, @tuturuuu/supabase, and @tuturuuu/internal-api
  • run vercel pull
  • run vercel build
  • deploy prebuilt artifacts
  • skip production push work when a newer commit already exists on the target branch
  • record a non-blocking GitHub Deployment marker after a successful Vercel build/deploy so the next production push can evaluate every change since the last successful run

Database automation

  • supabase-staging.yaml
  • supabase-production.yaml
  • supabase-types.yaml
These cover staging schema promotion, production schema promotion, and generated type verification.

Docker automation

  • docker-setup-check.yaml
  • go-backend.yml
docker-setup-check.yaml is the workflow to watch whenever Docker files, compose files, or Docker helper scripts change. go-backend.yml owns the apps/backend Go service checks: formatting, module download, go vet, tests, binary build, and Docker image build.

Quality and security

  • type-check.yaml
  • turbo-unit-tests.yaml
  • biome-check.yaml
  • codeql.yml
  • codecov.yaml
  • i18n-check.yaml
  • check-migrations.yml
  • check-migration-timestamps.yml
  • branch-name-check.yaml
The test workflows (turbo-unit-tests.yaml and codecov.yaml) run bun setup before executing tests so dependency installation and workspace package builds match the local setup path. Vercel workflows should invoke Turborepo through bun turbo:local ... after bun install so CI uses the pinned repo dependency instead of resolving a global or downloaded Turbo binary. Vercel deploy credentials must stay environment-scoped. Do not put ${{ secrets.* }} values in a Vercel workflow-level env: block, and do not export production Supabase, encryption, provider, or Turborepo remote-cache secrets from GitHub Actions. Store app runtime and build-time configuration in the Vercel project environment. The regression test bun test scripts/ci/release-workflows.test.js enforces this for every vercel-preview-*.yaml and vercel-production-*.yaml workflow. Preview deploy credentials must not be exposed to arbitrary branch push code. Preview Vercel workflows are manual-only: run the workflow from main, provide the branch, tag, or SHA in preview_ref, and keep TRUSTED_PREVIEW_DEPLOY_ACTORS limited to maintainers who can approve secret-backed preview builds. The workflow still checks out and builds the requested preview_ref, so reviewers should treat that ref as code that can execute during install/build.

Other delivery surfaces

  • discord-modal-deploy.yml
  • mobile-build-android.yaml
  • mobile-build-ios.yaml
  • mobile-build-macos.yaml
  • mobile-build-windows.yaml
  • release-*.yaml package publishing workflows

Operational Rules

Manual dispatch

Use workflow_dispatch when:
  • you need to rerun a deployment intentionally
  • you need to promote a migration outside the normal trigger timing
  • you need to recover from a failed but otherwise understood automation path
Manual dispatch bypasses affected-app gating when the workflow is enabled in tuturuuu.ts. Disabled ci entries still skip, even for manual dispatch. For preview Vercel deployments, dispatch the workflow from main, set preview_ref to the branch, tag, or SHA to deploy, and confirm the actor is in TRUSTED_PREVIEW_DEPLOY_ACTORS. Do not reintroduce preview push triggers for secret-backed Vercel workflows. Package release workflows that expose production secrets or trusted publishing authority must add their own ref guard before dependency installation or publish jobs. Release Please is the only workflow that generates monorepo version and changelog PRs. It runs from production, uses secrets.RELEASE_PLEASE_TOKEN so generated PRs and releases can trigger downstream workflows, and falls back to github.token only to keep the job from failing when the bot token is not provisioned. The github.token fallback does not trigger downstream workflow runs from generated release PRs. Release Please rejects non-production manual dispatches before the write-capable release job. Package publish workflows consume release-please version bumps on production; they must not recreate checksum or PR-title version bump automation. Package release workflows use npm trusted publishing: build and artifact-pack work runs before any OIDC permission is granted, then the publish-npm job downloads one tarball, verifies its package name and version, and runs npm publish without NPM_TOKEN. Each publish job is bound to its package release environment, and the matching npm trusted publisher must use this repository, the workflow filename, and that environment. Manual dispatch is only valid with the branch selector set to production; non-production refs are rejected before the package or publish jobs can start. Every workflow-published package manifest must also declare repository.type: "git", repository.url: "https://github.com/tutur3u/platform", and repository.directory matching the package path. npm validates those fields against GitHub Actions provenance and rejects publishes with E422 when the packed manifest has an empty or mismatched repository URL. Keep internal Tuturuuu dependencies as workspace:* in source package manifests so local builds always use the checked-out workspace. Package release workflows must first run node scripts/ci/package-release-readiness.js wait-workspace-dependencies packages/<name> so publishable Tuturuuu dependencies are visible on npm before the dependent tarball is packed. That wait also inspects the dependency package workflow run for the same production SHA and fails immediately when that related workflow has already failed instead of burning the full npm polling timeout. It must then rewrite the checked-out manifest immediately before npm pack with node scripts/ci/prepare-npm-package-manifest.js packages/<name>. That temporary rewrite replaces workspace: protocol ranges with the current workspace package versions so npm consumers can install the tarball. After npm publish, the publish job polls npm view for the exact published version before it reports success. If npm returns first-publish or permission errors, fix the npm package access and trusted publisher setup instead of skipping the package. If a published package depends on another release-please-managed Tuturuuu package, that dependency also needs its own release-*-package.yaml workflow and matching tuturuuu.ts entry. The platform production Vercel workflow also runs node scripts/ci/package-release-readiness.js wait-changed-package-versions before installing dependencies. Release-please package bumps therefore publish and become visible on npm before the normal production platform deployment flow continues. When the push payload omits changed package files, the helper fetches the missing base SHA before falling back to git diff, so shallow checkout depth does not break the release gate.

Newer-commit guards

Production Vercel push workflows intentionally skip if the branch already has a newer commit than the current run. A skipped deployment may be correct rather than broken. The deployment marker closes the gap created by these stale-run skips. When a later push reaches the latest commit, affected-app gating compares against the last successful marker rather than only the newest push payload, so app changes from skipped intermediate runs still queue the correct Vercel workflow.

Prerequisite guards

  • supabase-staging.yaml requires a successful main preview trigger unless manually dispatched.
  • Its deploy step links the staging project and runs supabase db push --include-all.
  • supabase-production.yaml runs after production deployment and also re-evaluates after a matching main staging migration succeeds. It still checks both the latest production Vercel result and a completed successful staging migration for the same commit before running supabase db push.

Mobile iOS native assets

mobile-build-ios.yaml caches CocoaPods and the CocoaPods trunk cache only. Do not cache apps/mobile/.dart_tool in this workflow: Flutter native asset hook outputs can become stale across simulator/device builds and SDK updates. A stale .dart_tool cache can leave NativeAssetsManifest.json referencing objective_c while build/native_assets/ios/ is missing the generated framework, which fails the simulator build during Xcode’s embed phase. The workflow explicitly runs flutter config --no-enable-swift-package-manager after Flutter setup. Keep the iOS CI path on CocoaPods while image_cropper and dkimagepickercontroller resolve incompatible TOCropViewController Swift package ranges; otherwise Flutter 3.44+ can fail before the simulator build with an Xcode package dependency resolution error.

Fast Triage Checklist

  1. Check whether ci-check.yml disabled the workflow through tuturuuu.ts.
  2. For Vercel workflows, inspect the ci-check.yml decision reason and matched paths.
  3. Inspect the changed-file source, base SHA, head SHA, and path count emitted by resolve-changed-files.ts.
  4. Check whether the workflow was skipped because a newer commit exists.
  5. Check whether the prior successful GitHub Deployment marker exists for that Vercel workflow and branch.
  6. Check path filters to confirm GitHub should have started the lightweight workflow.
  7. For database workflows, inspect the prerequisite evaluation job before the deploy job.
  8. For Docker changes, verify docker-setup-check.yaml specifically.

When You Add New Automation

  1. Add the workflow file under .github/workflows/.
  2. Add its key to tuturuuu.ts unless you intentionally want it always on by default.
  3. Document the workflow in apps/docs.
  4. If it changes a docs-visible page, add that page to apps/docs/docs.json.