Skip to content

labrats-work/actions.common

Repository files navigation

actions.common

Shared GitHub Actions composite actions and reusable workflows for the labrats-work organization.

Purpose

Provide standardized, reusable CI/CD building blocks so every repository in the organization can build container images, create semantic releases, and perform housekeeping tasks without duplicating workflow logic.

Quick Start

Composite actions are referenced by directory name:

steps:
  - uses: labrats-work/actions.common/buildx-build@main
    with:
      context: .
      destination: ghcr.io/labrats-work/my-app:latest
      registry-token: ${{ secrets.GITHUB_TOKEN }}

Reusable workflows are referenced by file path:

jobs:
  build:
    uses: labrats-work/actions.common/.github/workflows/build-image-buildx.yml@main
    with:
      context: .
      image-name: ghcr.io/labrats-work/my-app
      tag: "1.0.0"
    secrets:
      registry-token: ${{ secrets.GITHUB_TOKEN }}

Composite Actions

Composite actions live in top-level directories and are called as labrats-work/actions.common/<name>@main.

Action Description ARC-native Outputs
buildx-build Build and push container images using Docker buildx. Polls for Docker daemon sidecar, creates docker-container driver builder. Yes --
kaniko-build Build and push using docker/build-push-action and marketplace Buildx setup. Deprecated -- use buildx-build instead. No --
publish-image High-level version-tagged publish. Extracts version from tag (supports prefixes like sidecar-), builds, pushes <image>:<version> and <image>:latest. Uses marketplace Docker actions. No version
publish-image-buildx Same as publish-image but delegates the build to buildx-build. Yes version
semver-release Automatic semantic versioning from conventional commits. Reads git log since last tag, determines bump (feat! = major, feat: = minor, else patch), creates a GitHub release. Supports tag prefixes and path scoping. -- version, tag, created
submit-ai-job Submit a job to the ai-agents API (POST /api/jobs/submit) and optionally poll until it reaches a terminal status. Yes job-id, status, result, failed-reason

Inputs reference (composite actions)

buildx-build
Input Required Default Description
context Yes -- Build context path
dockerfile No Dockerfile Dockerfile path relative to context
destination No "" Image destination (ghcr.io/org/repo:tag)
extra-destinations No "" Additional destinations (comma-separated)
push No true Push to registry
registry-token No "" Registry auth token
registry-user No "" Registry username (defaults to github.actor)
cache-repo No "" Registry path for buildx cache
build-args No "" Build arguments (KEY=VALUE, one per line)
publish-image / publish-image-buildx
Input Required Default Description
tag Yes -- Release tag or manual version (e.g. sidecar-0.2.0)
tag-prefix No "" Prefix to strip from tag to get version
context Yes -- Docker build context path
dockerfile No Dockerfile Dockerfile path relative to context
image-name Yes -- Full image name (e.g. ghcr.io/labrats-work/apps.my-diet)
build-args No "" Docker build args (multiline KEY=VALUE)
cache-repo No "" Registry path for layer cache
registry-token Yes -- GHCR push token

Output: version -- the extracted version without prefix or v.

submit-ai-job
Input Required Default Description
api-url No https://ai-agents.hcl.labrats.work Base URL of the ai-agents API
api-key Yes -- API key (Bearer token). Pass as a secret.
source No github-actions Origin identifier stored on the job
type Yes -- Job type / agent trigger name (e.g. implement-issue)
prompt No "" Prompt for the agent (required unless type=error-reporting)
agent-role No "" Agent role trigger name
priority No "" Queue priority (lower = higher); empty = server default
git-repo No "" Git clone URL for the workspace
git-ref No "" Branch/tag to checkout
git-depth No "" Shallow clone depth
model No "" Model override (sonnet, opus, ...)
effort No "" Effort override (low, medium, high)
provider No "" Provider override (claude, codex)
timeout-ms No "" Per-job execution timeout in ms
callback-url No "" Webhook URL for lifecycle callbacks
metadata No "" JSON object string of arbitrary metadata
workspace-files No "" JSON array of {name, path} or {name, content} entries; the action base64-encodes them into the API's workspaceFiles[]
extra-payload No "" JSON object string deep-merged into the request body (advanced fields like pipeline, errors)
wait No true Poll until the job reaches a terminal status
wait-timeout-seconds No 1800 Max wait time when wait=true
poll-interval-seconds No 10 Poll interval when wait=true
fail-on-job-failure No true Fail the step if the job ends in failed/cancelled

Outputs: job-id, status (when waiting), result (JSON returnvalue), failed-reason.

Example 1 — manual dispatch, implement an issue

name: ai-implement-issue
on:
  workflow_dispatch:
    inputs:
      issue:
        description: Issue number
        required: true

jobs:
  run:
    runs-on: k8s-hetzner-arc
    steps:
      - uses: labrats-work/actions.common/submit-ai-job@main
        id: ai
        with:
          api-key: ${{ secrets.AI_AGENTS_API_KEY }}
          type: implement-issue
          prompt: "Implement the feature described in issue #${{ inputs.issue }}"
          git-repo: https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
          git-ref: ${{ github.ref_name }}
          metadata: '{"issueNumber": ${{ inputs.issue }}}'
      - run: echo "job=${{ steps.ai.outputs.job-id }} status=${{ steps.ai.outputs.status }}"

Example 2 — nightly scheduled audit

Cron-driven jobs are a natural fit: pin a model, give the agent a long timeout, and don't block the runner waiting on it (use wait: false plus callback-url, or accept the long wait if your runner pool is sized for it).

name: nightly-dependency-audit
on:
  schedule:
    - cron: "0 3 * * *"   # 03:00 UTC daily

jobs:
  audit:
    runs-on: k8s-hetzner-arc
    steps:
      - uses: labrats-work/actions.common/submit-ai-job@main
        with:
          api-key: ${{ secrets.AI_AGENTS_API_KEY }}
          type: dependency-audit
          model: opus
          effort: high
          timeout-ms: "1800000"             # 30 min
          wait-timeout-seconds: "2100"      # poll a bit longer than the job timeout
          git-repo: https://github.com/${{ github.repository }}.git
          git-ref: main
          prompt: |
            Audit package.json + lockfiles. Flag CVEs, abandoned packages, and
            stale majors. Open one issue per finding using `gh issue create`.
          metadata: '{"trigger": "nightly", "repo": "${{ github.repository }}"}'

Example 3 — fire-and-forget on PR opened

Don't block the PR check on the agent. Submit with wait: false and let the agent post results back via a callback.

name: ai-pr-review
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: k8s-hetzner-arc
    steps:
      - uses: labrats-work/actions.common/submit-ai-job@main
        with:
          api-key: ${{ secrets.AI_AGENTS_API_KEY }}
          type: code-review
          wait: "false"
          git-repo: https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
          git-ref: ${{ github.event.pull_request.head.sha }}
          prompt: "Review PR #${{ github.event.pull_request.number }} and comment with findings."
          callback-url: ${{ secrets.PR_REVIEW_CALLBACK_URL }}
          metadata: |
            {
              "prNumber": ${{ github.event.pull_request.number }},
              "headSha": "${{ github.event.pull_request.head.sha }}"
            }

Example 4 — weekly cross-repo sweep (matrix)

Run the same agent against several repos on a schedule. Each repo becomes one job; submit them in parallel with a matrix.

name: weekly-readme-sweep
on:
  schedule:
    - cron: "0 9 * * 1"   # Mondays 09:00 UTC

jobs:
  sweep:
    runs-on: k8s-hetzner-arc
    strategy:
      fail-fast: false
      matrix:
        repo:
          - labrats-work/apps.my-budget
          - labrats-work/apps.my-diet
          - labrats-work/apps.my-cams
    steps:
      - uses: labrats-work/actions.common/submit-ai-job@main
        with:
          api-key: ${{ secrets.AI_AGENTS_API_KEY }}
          type: docs-refresh
          git-repo: https://github.com/${{ matrix.repo }}.git
          git-ref: main
          prompt: "Refresh README.md so it matches the current code. Open a PR if anything changes."
          metadata: '{"sweep": "weekly-readme", "repo": "${{ matrix.repo }}"}'

Example 5 — injecting workspace files

Use workspace-files to inject files into the agent's workspace. Each entry takes either a path (read from the runner's filesystem) or inline content — the action base64-encodes them for the API.

- uses: actions/checkout@v4

- uses: labrats-work/actions.common/submit-ai-job@main
  with:
    api-key: ${{ secrets.AI_AGENTS_API_KEY }}
    type: code-review
    prompt: "Review the repo using the constraints in context.md."
    git-repo: https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
    git-ref: ${{ github.ref_name }}
    workspace-files: |
      [
        { "name": "context.md", "path": "./prompts/context.md" },
        { "name": "BRIEF.md",   "content": "Refactor the auth module. Keep the public API stable." }
      ]

For pipelines or other advanced fields, combine with extra-payload:

- uses: labrats-work/actions.common/submit-ai-job@main
  with:
    api-key: ${{ secrets.AI_AGENTS_API_KEY }}
    type: pdf-sections
    prompt: "Extract chapter outlines from input/notes.pdf."
    workspace-files: |
      [{ "name": "input/notes.pdf", "path": "./uploads/notes.pdf" }]
    extra-payload: '{"pipeline": {"type": "pdf-sections"}}'
semver-release
Input Required Default Description
tag-prefix No "" Tag prefix (e.g. sidecar-). Empty = plain semver.
paths No "" Space-separated paths for git log scoping
release-name-prefix No Release Prefix for GitHub release name
token Yes -- GitHub token with contents:write
release-files No "" Newline-separated files to attach to the release

Outputs: version, tag, created.

Reusable Workflows

Reusable workflows live in .github/workflows/ and are called as labrats-work/actions.common/.github/workflows/<file>@main.

Workflow Description ARC-native
build-image.yml Build and push a container image using marketplace Docker actions. Inputs: context, dockerfile, image-name, tag, extra-tags, build-args, cache-repo. Secret: registry-token. Yes
build-image-buildx.yml Same as build-image but delegates to the buildx-build composite action. Yes
repo-info.yml Retrieve and dump repository metadata (context, size). No inputs. Yes
create-repo-issue.yml Create an issue in the calling repository. Inputs: title, body. Secret: token. Yes
create-repo-issue-inherit.yml Same as create-repo-issue but uses inherited GITHUB_TOKEN (no explicit secret). Yes
create-repo-issue-output.yml Same as create-repo-issue-inherit with an issue-num output. Yes
verify-contrib-file.yml Verify that CONTRIBUTING.md exists in the repository. No inputs. Yes
pr-version-preview.yml PR-time bot comment showing the semver bump that release will produce on merge. Uses semver-release dry-run. Inputs: tag-prefix (optional), paths (optional). Yes
dev-smoke-test.yml After a release publishes, waits for dev to roll, runs Playwright smoke spec, uploads evidence to the GitHub Release. Trigger via workflow_run (NOT release: publishedGITHUB_TOKEN doesn't fire downstream release events). Inputs: base-url (required), version (optional), app-dir, playwright-project, max-wait-attempts. Yes
pr-image-cleanup.yml Drops pr-<N>-* GHCR tags when a PR closes; backfill mode (workflow_dispatch) prunes tags for any closed PR. Inputs: packages (newline-separated), org (optional). Yes
release.yml Internal workflow: creates a semver release on push to main using the semver-release action. Yes

All workflows run on k8s-hetzner-arc self-hosted runners.

Structure

actions.common/
├── buildx-build/              # Composite: Docker buildx build (ARC-native)
│   └── action.yml
├── kaniko-build/              # Composite: Docker build (deprecated)
│   └── action.yml
├── publish-image/             # Composite: version-tagged publish (marketplace)
│   └── action.yml
├── publish-image-buildx/      # Composite: version-tagged publish (buildx)
│   └── action.yml
├── semver-release/            # Composite: conventional-commit releases
│   └── action.yml
├── .github/
│   └── workflows/
│       ├── build-image.yml
│       ├── build-image-buildx.yml
│       ├── repo-info.yml
│       ├── create-repo-issue.yml
│       ├── create-repo-issue-inherit.yml
│       ├── create-repo-issue-output.yml
│       ├── verify-contrib-file.yml
│       ├── release.yml
│       └── *-test.yml         # Test workflows
├── CLAUDE.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
└── .gitignore

Limitations

  • Maximum 4 levels of workflow nesting.
  • Maximum 20 reusable workflows per caller (nested workflows count toward this limit).
  • Environment variables do not propagate to called workflows -- pass them as inputs or secrets.
  • Private repositories can only reference workflows within the same repository.

See the GitHub documentation on reusable workflows for full details.

Contributing

See CONTRIBUTING.md for guidelines on adding workflows and actions.

License

MIT License -- see LICENSE for details.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors