Semi-opinionated GitHub Actions workflows for my personal sites and consultancy shop RavenFlight Industries, LLC
Point uses in your configuration to a workflow, e.g. in your .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main, next, live]
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:
jobs:
validate:
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@mainPass runner (defaults to GitHub runner ubuntu-latest)
jobs:
validate:
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@main
with:
runner: my-hosted-runnerRuns lint, typecheck, check for GPL licenses, e2e with Playwright
site-ci.yml runs bun run test:e2e:lighthouse on PRs into live. Sites
implement that script with the shared runner here, which serves the built
static directory on a free port and runs
Unlighthouse against it, reusing Playwright's
Chromium. It exits non-zero if a category budget in the site's config fails.
In the site's package.json (pin the tag):
"test:e2e:lighthouse": "bun run build && bunx --package @taraxvoid/voidflow@0.6.0 unlighthouse-runner --dir dist"Options: --dir (default dist, use dist/client for Cloudflare adapter
builds), --config (default unlighthouse.config.ts), --version (pinned
Unlighthouse version). Start from
scripts/unlighthouse/unlighthouse.config.example.ts
and add .unlighthouse/ to the site's .gitignore.
Shared playwright.config for Astro sites that run e2e against
astro preview. It picks a free port outside CI, sets
ASTRO_PREVIEW_BACKGROUND=false (Astro backgrounds preview in agent
environments, which makes Playwright think the server exited), and drops the
webServer when PLAYWRIGHT_BASE_URL is set.
bun add -d @taraxvoid/voidflow # or: pnpm add -D @taraxvoid/voidflow// playwright.config.js
import { definePreviewConfig } from '@taraxvoid/voidflow/playwright'
export default definePreviewConfig({ ciPort: 4141 })The same release is also published unscoped as voidflow (identical
contents and version), so bun add -d voidflow and
import ... from 'voidflow/playwright' work too. @taraxvoid/voidflow is the
canonical name.
Options: ciPort (required in CI), runner (default bun, e.g. pnpm),
timeout (default 30000), projects (default Pixel 7 and Desktop Chrome),
testDir (default ./test/e2e). Requires @playwright/test in the site.
Example deployment job added to your workflow above. resolve-env maps the
branch to dev/staging/prod once, up front, so its output can be used both to
drive the deploy and to label the job (Deploy [dev], Deploy [staging],
Deploy [prod]) — a job's name: can reference needs.<job>.outputs.* but
not a step output from within itself, hence the split:
jobs:
resolve-env:
needs: validate
if: github.event_name == 'push'
uses: taraxvoid/voidflow/.github/workflows/resolve-env.yml@main
deploy:
name: Deploy [${{ needs.resolve-env.outputs.env }}]
needs: resolve-env
if: github.event_name == 'push'
environment: ${{ needs.resolve-env.outputs.env }}
steps:
- uses: actions/checkout@v7.0.1
- uses: taraxvoid/voidflow/actions/cloudflare-deploy@main
with:
env: ${{ needs.resolve-env.outputs.env }}
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}This project uses semver, released with release-please
(release-please.yml). It keeps a standing
chore(release): X.Y.Z PR open against main, updated as commits land. Merging it bumps
package.json and CHANGELOG.md, tags vX.Y.Z, creates the GitHub release,
and publishes to npm. The PR is opened with the org's release-bot GitHub App token
(RELEASE_BOT_CLIENT_ID / RELEASE_BOT_APP_PRIVATE_KEY) so CI runs on it.
Commit messages should follow Conventional Commits —
enforced loosely, as an advisory commit-msg hint (see
scripts/check-commit-msg.sh), not a blocking check. They
drive the version bump and generated changelog.
Treat @main as unstable. Consumers should pin to an exact release tag, e.g. @v0.3.0 —
there is no floating major-version tag (@v1) to track yet.
This repo validates its own workflows and composite actions (.github/workflows/ci.yml):
- actionlint — workflow schema/logic; also shellchecks
run:steps in.github/workflows/*.ymlautomatically (shellcheckships onubuntu-latest) - check-jsonschema — schema-checks
actions/**/action.ymland the workflow files against SchemaStore'sgithub-action.json/github-workflow.json - shellcheck (
scripts/shellcheck-actions.sh) — checksrun:steps insideactions/**/action.yml, which actionlint doesn't reach - zizmor — security audit (unpinned refs, script injection via
${{ }}inrun:, excess permissions, etc.)
brew install just lefthook actionlint shellcheck yq act uv
lefthook installjust validate— run everything CI runs, locallyjust lint-workflows/lint-actions/lint-shell/security— run one check at a timejust dry-run— full local run ofci.ymlviaact(needs Docker running); pass extra args, e.g.just dry-run -j validation- lefthook runs the fast checks (
lint-workflows,lint-actions,lint-shell) onpre-commit, andzizmoronpre-push
Colima users: .actrc already disables the docker-socket mount (--container-daemon-socket -) — act binds /var/run/docker.sock by default, which doesn't exist under Colima and fails with operation not supported. None of these workflow steps need docker-in-docker, so this is safe.
Run just dry-run from a normal checkout, not a linked git worktree. act copies the working tree via docker cp rather than a real clone, so a worktree's .git pointer back to the parent repo doesn't resolve inside the container — git ls-files-based steps (schema validation, scripts/shellcheck-actions.sh) silently see zero files instead of erroring. Verified clean end-to-end (🏁 Job succeeded) from a plain clone.
YMMV, caveat emptor, your satisfaction not guaranteed