Skip to content

About

Common github actions

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

121 Commits

Folders and files

Repository files navigation

common-actions

Reusable GitHub Actions and workflows for the dallay organization.

Overview

This repository provides composite actions and reusable workflows that standardize CI/CD patterns across all repositories in the organization. Instead of duplicating workflow logic, every repo calls these shared building blocks.

Repository Structure

common-actions/
├── actions/                              # Composite Actions
│   ├── setup-node/action.yml             # Node.js + pnpm setup with caching
│   ├── setup-java/action.yml             # Java + Gradle setup with caching
│   ├── setup-rust/action.yml             # Rust toolchain + caching
│   ├── setup-python/action.yml           # Python + pip/uv caching
│   ├── setup-buildx/action.yml           # QEMU + Docker Buildx for multi-arch
│   ├── docker-build-push/action.yml      # Multi-registry Docker build & push
│   ├── docker-security-scan/action.yml   # Trivy vulnerability scanning + SARIF
│   ├── codecov/action.yml                # Upload coverage to Codecov
│   └── playwright-test/action.yml        # Playwright E2E tests with caching
│
└── .github/workflows/                    # Workflows (reusable + local)
    ├── semantic-pr.yml                   # Lint PR titles (Conventional Commits)
    ├── gitguardian-scan.yml              # Scan repository history for exposed secrets
    ├── pr-labeler.yml                    # Path-based PR labeling
    ├── issue-labeler.yml                 # Keyword-based issue labeling
    ├── labels-sync.yml                   # Sync labels from YAML config
    ├── semantic-release.yml              # Semantic Release with GitHub App
    ├── stale.yml                         # Close stale issues/PRs
    ├── greetings.yml                     # Welcome first-time contributors
    ├── cleanup-cache.yml                 # Cleanup caches for closed branches
    ├── cache-maintenance.yml             # Cleanup caches by age/type (workflow_call)
    ├── dependabot-auto-merge.yml         # Auto-merge Dependabot PRs (workflow_call)
    ├── release.yml                       # Release this repository
    ├── pr-size-labeler.yml               # Label PRs by size (XS, S, M, L, XL)
    └── test-actions.yml                  # Smoke-test all composite actions

Composite Actions vs Reusable Workflows

Feature Composite Action Reusable Workflow
Scope Steps within a job Entire jobs
Secrets Inherits caller context Must be passed explicitly
Logging Shows as single step Each job/step logged individually
Use case Setup tools, build steps Standalone processes

Composite Actions

actions/setup-node

Sets up Node.js and pnpm with caching and dependency installation.

steps:
  - uses: actions/checkout@v6
  - uses: dallay/common-actions/actions/setup-node@v1
    with:
      node-version: "24.12.0"    # optional, this is the default
      # node-version-file: ".nvmrc"  # alternative: read from file
      # cache-dependency-path: "pnpm-lock.yaml"  # optional: override lockfile path
      # cwd: "./frontend"           # optional: working directory for install
      # install-deps: "true"        # optional: run pnpm install (default: true)
      # ignore-scripts: "true"      # optional: add --ignore-scripts to pnpm install

actions/setup-buildx

Sets up QEMU and Docker Buildx for multi-platform container builds.

steps:
  - uses: dallay/common-actions/actions/setup-buildx@v1
    # with:
    #   buildx-version: "v0.12.0"                  # optional
    #   buildkit-image: "moby/buildkit:v0.12.5"     # optional

actions/docker-build-push

Builds and pushes Docker images to both GHCR and Docker Hub with multi-arch support and registry-based caching.

steps:
  - uses: dallay/common-actions/actions/setup-buildx@v1

  - uses: dallay/common-actions/actions/docker-build-push@v1
    with:
      image-name: my-app
      image-title: My App
      image-description: "My application"
      dockerfile: ./Dockerfile
      github-owner: ${{ github.repository_owner }}
      dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
      github-token: ${{ secrets.GITHUB_TOKEN }}
      dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
      version: "1.2.3"
      major-version: "1"
      # platforms: "linux/amd64,linux/arm64"  # optional, this is the default
      # build-args: |                          # optional
      #   VERSION=1.2.3

Outputs: tags, digest, image-id

actions/setup-java

Sets up Java JDK and Gradle with dependency caching.

steps:
  - uses: actions/checkout@v6
  - uses: dallay/common-actions/actions/setup-java@v1
    with:
      java-version: "24"       # optional, this is the default
      distribution: "temurin"  # optional (temurin, corretto, zulu, etc.)
      # cache: "gradle"        # optional: gradle (default), maven, sbt, or "" to disable

actions/setup-rust

Sets up Rust toolchain with optional components, cross-compile targets, and Swatinem/rust-cache. Supports optional sccache for distributed compilation.

steps:
  - uses: actions/checkout@v6
  - uses: dallay/common-actions/actions/setup-rust@v1
    with:
      rust-version: "stable"                # optional (stable, nightly, beta, or semver)
      # components: "clippy,rustfmt"        # optional, this is the default
      # targets: "wasm32-unknown-unknown"   # optional: cross-compile targets
      # cache: "true"                       # optional: Swatinem/rust-cache
      # cache-workspaces: ""                # optional: extra paths for cache key
      # sccache: "false"                    # optional: distributed compilation

actions/setup-python

Sets up Python with pip or uv caching and optional dependency installation.

steps:
  - uses: actions/checkout@v6
  - uses: dallay/common-actions/actions/setup-python@v1
    with:
      python-version: "3.12"      # optional, this is the default
      cache: ""                    # optional: "pip", "uv", or empty (default; disables caching)
      # cache-dependency-path: "requirements.txt" # required for pip caching; uv paths are relative to working-directory
      # install-deps: "true"      # optional: install requirements.txt / pyproject.toml
      # working-directory: "."    # optional

actions/codecov

Uploads code coverage reports to Codecov with graceful failure handling. Skips upload if no token is provided.

steps:
  - uses: dallay/common-actions/actions/codecov@v1
    with:
      codecov-token: ${{ secrets.CODECOV_TOKEN }}
      files: "**/build/reports/jacoco/test/jacocoTestReport.xml"
      flags: backend
      name: backend-coverage
      slug: owner/repo
      # fail-ci-if-error: "false"  # optional
      # fail-on-upload-error: "false"  # optional
      # verbose: "false"           # optional

Outputs: upload-failed (boolean string)

actions/playwright-test

Runs Playwright E2E tests with intelligent browser caching, artifact uploads, and step summary.

Prerequisite: Node.js and pnpm must be set up before using this action. Use actions/setup-node first.

steps:
  - uses: actions/checkout@v6
  - uses: dallay/common-actions/actions/setup-node@v1
  - uses: dallay/common-actions/actions/playwright-test@v1
    with:
      browser: chromium              # optional: chromium (default), firefox, webkit, or all
      # working-directory: "."       # optional: directory with playwright config
      # test-command: "test:e2e"     # optional: pnpm script to run
      # headed: "false"              # optional
      # upload-results: "true"       # optional: upload report + traces
      # retention-days: "30"         # optional

Outputs: tests-status (success or failed), report-path

actions/docker-security-scan

Scans Docker images for vulnerabilities using Trivy. Uploads SARIF reports to the GitHub Security tab and saves results as artifacts.

Runner note: If you upgrade github/codeql-action/upload-sarif or actions/upload-artifact to versions that use upload-artifact v6, ensure self-hosted runners are >= 2.327.1 and Node.js 24 is available.

steps:
  - uses: dallay/common-actions/actions/docker-security-scan@v1
    with:
      image-ref: ghcr.io/owner/repo/my-app:latest
      report-name: my-app-security
      category: my-app-trivy
      # severity: "HIGH,CRITICAL"  # optional
      # fail-on-error: "true"      # optional: fail pipeline on vulnerabilities (default: true)

Outputs: scan-result (success, failed, or error), sarif-file


Reusable Workflows

Tip: Most reusable workflows accept an optional github-token secret to override the default GITHUB_TOKEN (e.g., use a GitHub App token). The semantic-release workflow uses its own GitHub App credentials.

gitguardian-scan.yml — Scan for Exposed Secrets

Runs GitGuardian's ggshield secret scan. The calling repository chooses the trigger events and must define a GITGUARDIAN_API_KEY Actions secret.

# .github/workflows/gitguardian-scan.yml in the consuming repository
name: GitGuardian scan
on: [ push, pull_request ]

permissions:
  contents: read

jobs:
  scan:
    uses: dallay/common-actions/.github/workflows/gitguardian-scan.yml@v1
    secrets:
      gitguardian-api-key: ${{ secrets.GITGUARDIAN_API_KEY }}

The reusable workflow checks out the full history and pins GitGuardian/ggshield to a commit SHA. GitHub does not provide repository secrets to workflows triggered by pull requests from forks, so this scan cannot authenticate for those events with the repository secret. Do not switch to pull_request_target and check out untrusted PR code to work around that restriction; use GitGuardian's GitHub integration if fork coverage is required.

semantic-pr.yml — Lint PR Titles

Validates PR titles follow Conventional Commits. Posts a helpful sticky comment on failure.

# .github/workflows/semantic-pr.yml
name: Lint PR title
on:
  pull_request_target:
    types: [ opened, edited, synchronize ]

jobs:
  lint:
    uses: dallay/common-actions/.github/workflows/semantic-pr.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override
    # with:
    #   skip-bot: "dependabot[bot]"  # optional, this is the default

pr-labeler.yml — Label PRs by Path

Auto-labels PRs based on changed file paths. Requires a .github/labeler.yml config in your repo.

# .github/workflows/pr-labeler.yml
name: PR Labeler
on:
  pull_request_target:
    types: [ opened, synchronize, reopened ]

jobs:
  label:
    uses: dallay/common-actions/.github/workflows/pr-labeler.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override
    # with:
    #   sync-labels: true                          # optional
    #   configuration-path: ".github/labeler.yml"  # optional

issue-labeler.yml — Label Issues by Keywords

Auto-labels issues based on title/body keywords. Requires a .github/issue-labeler-config.yml in your repo.

# .github/workflows/issue-labeler.yml
name: Issue Labeler
on:
  issues:
    types: [ opened, edited ]

jobs:
  label:
    uses: dallay/common-actions/.github/workflows/issue-labeler.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override

labels-sync.yml — Sync Repository Labels

Synchronizes GitHub labels from a YAML config file. Requires a .github/labels.yml in your repo.

# .github/workflows/labels-sync.yml
name: Sync Labels
on:
  push:
    branches: [ main ]
    paths: [ ".github/labels.yml" ]
  workflow_dispatch:

jobs:
  sync:
    uses: dallay/common-actions/.github/workflows/labels-sync.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override

semantic-release.yml — Automated Releases

Runs semantic-release with a GitHub App token for authentication. Outputs version info for downstream jobs.

# .github/workflows/release.yml
name: Release
on:
  push:
    branches: [ main ]

jobs:
  release:
    uses: dallay/common-actions/.github/workflows/semantic-release.yml@v1
    secrets:
      app-id: ${{ secrets.APP_ID }}
      app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
    # with:
    #   node-version: "24.12.0"
    #   dry-run: false
    #   install-deps: true
    #   semantic-release-command: "pnpm exec semantic-release"

  # Use release outputs in downstream jobs
  deploy:
    needs: release
    if: needs.release.outputs.new-release-published == 'true'
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying version ${{ needs.release.outputs.new-release-version }}"

Outputs: new-release-published, new-release-version, new-release-major-version, new-release-minor-version, new-release-patch-version, new-release-git-tag

This repo ships a local release workflow at .github/workflows/release.yml that calls the reusable semantic-release workflow using npx semantic-release and the .releaserc.json config.

stale.yml — Close Stale Issues/PRs

Marks inactive issues and PRs as stale, then closes them after a grace period.

# .github/workflows/stale.yml
name: Stale
on:
  schedule:
    - cron: "0 2 * * *"

jobs:
  stale:
    uses: dallay/common-actions/.github/workflows/stale.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override
    # with:
    #   days-before-stale: 120
    #   days-before-close: 60
    #   exempt-issue-labels: "priority:high,security"

greetings.yml — Welcome First-Time Contributors

Posts a welcome message for first-time issue creators and PR authors.

# .github/workflows/greetings.yml
name: Greetings
on: [ pull_request, issues ]

jobs:
  greet:
    uses: dallay/common-actions/.github/workflows/greetings.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override
    # with:
    #   issue-message: "Thanks for opening this issue!"
    #   pr-message: "Thanks for your contribution!"

contributor-report.yml — Contributor Quality Report

Generates a quality report for PR contributors based on merge rate, account age, and reactions.

# .github/workflows/contributor-report.yml
name: Contributor Report
on:
  pull_request:
    types: [ opened, reopened, synchronize, edited ]

jobs:
  report:
    uses: dallay/common-actions/.github/workflows/contributor-report.yml@v1
    secrets: inherit
    # with:
    #   threshold-pr-merge-rate: '0.3'
    #   threshold-account-age: '30'
    #   on-fail: 'comment'

cleanup-cache.yml — Cleanup Branch Caches

Removes GitHub Actions caches associated with a closed PR's branch.

# .github/workflows/cleanup-cache.yml
name: Cleanup caches
on:
  pull_request:
    types: [ closed ]

jobs:
  cleanup:
    uses: dallay/common-actions/.github/workflows/cleanup-cache.yml@v1
    secrets: inherit
    # secrets:
    #   github-token: ${{ secrets.GITHUB_TOKEN }}  # optional override
    # with:
    #   pr-number: ${{ github.event.pull_request.number }}  # optional override

cache-maintenance.yml — Cleanup Caches by Age and Type

Cleans up GitHub Actions caches older than a configurable retention period. Supports filtering by cache type (pnpm, playwright, build, or all). Outputs a summary table with deletion counts.

Unlike cleanup-cache.yml (which targets a specific PR branch), this workflow cleans all caches across the repository based on age and optional type prefix.

# .github/workflows/cache-maintenance.yml
name: Cache Maintenance
on:
  workflow_dispatch:
    inputs:
      retention-days:
        description: "Delete caches older than this many days"
        type: number
        required: false
        default: 7
      cache-types:
        description: "Which cache types to clean: pnpm, playwright, build, or all"
        type: string
        required: false
        default: "all"
  schedule:
    - cron: "0 3 * * *"  # Run daily at 3 AM

jobs:
  cleanup:
    uses: dallay/common-actions/.github/workflows/cache-maintenance.yml@v1
    secrets: inherit
    # with:
    #   retention-days: 7
    #   cache-types: "all"

pr-size-labeler.yml — Label PRs by Size

Automatically labels PRs based on their size (number of lines changed). Uses codelytv/pr-size-labeler to categorize PRs into XS, S, M, L, or XL.

# .github/workflows/pr-size-labeler.yml
name: PR Size Labeler
on:
  pull_request_target:
    types: [ opened, synchronize, reopened ]
  workflow_call:

jobs:
  label:
    uses: dallay/common-actions/.github/workflows/pr-size-labeler.yml@v1
    secrets: inherit

Size thresholds (default):

Label Max changes
size/xs 10
size/s 100
size/m 500
size/l 1000
size/xl > 1000

You can customize labels and thresholds by calling the action directly:

jobs:
  labeler:
    runs-on: ubuntu-latest
    steps:
      - uses: CodelyTV/pr-size-labeler@095a41fca88b8764fd9e008ad269bcdb82bb38b9 # v1.10.4
        with:
          xs_label: 'size/xs'
          xs_max_size: '10'
          l_label: 'size/l'
          l_max_size: '1000'
          fail_if_xl: 'true'
          files_to_ignore: 'package-lock.json *.lock'

dependabot-auto-merge.yml — Auto-Merge Dependabot PRs

Automatically approves and merges Dependabot (and Renovate) PRs once all checks pass. Supports GitHub App authentication for merged commits and posts sticky status comments on the PR.

# .github/workflows/dependabot-auto-merge.yml
name: Dependabot Auto-Merge
on:
  workflow_call:
    inputs:
      target:
        description: 'Merge strategy: squash, merge, or rebase'
        required: false
        type: string
        default: 'squash'
      approve:
        description: 'Auto-approve the PR before merging'
        required: false
        type: boolean
        default: true
    secrets:
      APP_ID:
        description: 'GitHub App ID (optional)'
        required: false
      APP_PRIVATE_KEY:
        description: 'GitHub App Private Key (optional)'
        required: false
      token:
        description: 'Fallback GitHub token'
        required: false

jobs:
  auto-merge:
    uses: dallay/common-actions/.github/workflows/dependabot-auto-merge.yml@v1
    secrets: inherit
    # with:
    #   target: squash
    #   approve: true

private-deploy.yml — Private deployment request

Sends a single JSON POST to a full URL through a temporary Tailscale connection using OAuth or OIDC. OIDC remains the default for compatibility. The caller controls when to deploy, job dependencies, and the JSON body. An HTTP 2xx confirms only that the request was accepted: the workflow does not wait for or verify deployment completion.

Contract:

  • deployment-url input (optional): non-secret full HTTP/HTTPS URL, including a path and query if needed; no credentials, fragment, or spaces.
  • deployment-url secret (optional): full HTTP/HTTPS URL for credential-bearing webhook URLs. When set, it takes precedence over the input. Do not put secret URL values in with inputs.
  • deployment-payload input (optional): non-secret valid JSON, sent without modification.
  • deployment-payload-secret secret (optional): valid JSON body for sensitive payloads. When set, it takes precedence over the input.
  • api-key-header (optional): HTTP header name; defaults to x-api-key.
  • extra-header-name and extra-header-value (optional, non-secret): one additional static request header sent with the POST. Provide both or neither. Use it for endpoints that require a provider event header, not for credentials.
  • tailscale-auth-mode (optional): oidc (default) or oauth.
  • tailscale-client-id and tailscale-tags inputs (required in oidc mode): the OAuth client ID and authorized Tailscale tags.
  • tailscale-client-id-secret and tailscale-tags-secret secrets (required in oauth mode): the OAuth client ID and authorized tags. When provided, these secrets take precedence over the matching inputs.
  • tailscale-audience (required in oidc mode): the audience configured for the Tailscale federated identity provider. It is not used in oauth mode.
  • tailscale-oauth-secret secret (required in oauth mode): the OAuth client secret. It is not used in oidc mode.
  • api-key secret (optional): full header value, without line breaks. For an API that uses Authorization, include the required prefix in the secret, for example Bearer …. When omitted, the request does not include the API-key header.

OIDC inputs are visible in workflow metadata and must not contain secrets. OAuth credentials can be passed as GitHub secrets; the secret forms take precedence over matching inputs when both are provided. Secret values use GitHub masking and are not printed by the workflow scripts.

Consumer example (replace <IMMUTABLE_SHA> with a reviewed, real 40-character commit SHA; the placeholder is not an executable reference):

name: Request private deployment
on:
  workflow_dispatch:

permissions: { }

jobs:
  deploy:
    permissions:
      id-token: write
    uses: dallay/common-actions/.github/workflows/private-deploy.yml@<IMMUTABLE_SHA>
    with:
      tailscale-auth-mode: oidc
      deployment-url: http://internal.service:8080/api/deploy
      deployment-payload: '{"version":"1.2.3"}'
      api-key-header: x-api-key
      tailscale-client-id: ${{ vars.TAILSCALE_CLIENT_ID }}
      tailscale-audience: ${{ vars.TAILSCALE_AUDIENCE }}
      tailscale-tags: tag:ci-deploy
    secrets:
      # Optional when the endpoint does not require an API-key header
      api-key: ${{ secrets.DEPLOYMENT_API_KEY }}
      # Use this for webhook URLs that contain a credential in the path
      deployment-url: ${{ secrets.DEPLOYMENT_WEBHOOK_URL }}

For OIDC, configure a Tailscale federated identity for the GitHub Actions OIDC issuer and audience. Restrict trust to the caller's authorized repositories and branches with token conditions. For OAuth, pass the client secret explicitly as tailscale-oauth-secret; grant the OAuth client only the required tags and network access. Do not authorize every repository or branch. Limit ACLs/grants to the service destination and port. The service and its private DNS must be reachable from the Tailscale network. Do not inherit all secrets.

OAuth caller example (use the same immutable SHA and endpoint inputs as above):

jobs:
  deploy:
    uses: dallay/common-actions/.github/workflows/private-deploy.yml@<IMMUTABLE_SHA>
    with:
      tailscale-auth-mode: oauth
      deployment-url: http://internal.service:8080/api/deploy
    secrets:
      deployment-payload-secret: ${{ secrets.DEPLOYMENT_PAYLOAD }}
      tailscale-client-id-secret: ${{ secrets.TAILSCALE_CLIENT_ID }}
      tailscale-tags-secret: ${{ secrets.TAILSCALE_TAGS }}
      tailscale-oauth-secret: ${{ secrets.TAILSCALE_OAUTH_SECRET }}
      deployment-url: ${{ secrets.DEPLOYMENT_WEBHOOK_URL }}

The OAuth job inside the reusable workflow does not receive id-token: write. GitHub validates permissions across the complete reusable-workflow file, so the calling job must allow id-token: write even when tailscale-auth-mode is oauth; the OAuth job itself keeps permissions: {}.

The reusable workflow grants id-token: write only to its OIDC job. The calling workflow must also grant that permission to the reusable-workflow job in OIDC mode. OAuth mode does not use id-token: write at runtime. Both jobs use ubuntu-latest; the deployment jobs have a five-minute timeout. It serializes requests by repository, caller workflow, ref, and target URL without canceling active requests. GitHub may replace pending runs; this is not a FIFO queue or a global lock across workflows or branches.

The request has a 10-second connection timeout and a 60-second total timeout. It does not follow redirects or retry the POST. The workflow discards the response and curl diagnostics and does not print the URL, body, or secrets in script output. HTTPS verifies TLS; HTTP is allowed inside the tunnel. A transport error can occur after the service accepts the POST. Check the status before retrying manually.

The Tailscale action is pinned by SHA and registers a post-job logout for normal cleanup, including when the POST fails. The node is ephemeral. Cleanup failure does not fail the job. Abrupt runner termination can prevent the post-job step; node removal then depends on Tailscale ephemeral-node cleanup, not on a guarantee that this workflow disconnects immediately.

Local verification, without requests to the private network:

node --test test/*.test.mjs
actionlint .github/workflows/private-deploy.yml

Security Practices

  • SHA Pinning: All third-party actions are pinned to full commit SHAs (not mutable tags)
  • GitHub App Tokens: Used instead of PATs for release operations
  • Minimal Permissions: Each workflow declares only the permissions it needs
  • No Secrets in Logs: Secrets are never echoed or exposed in step summaries
  • Fork Safety: Label sync workflows skip forks that lack permissions

Versioning

This repository uses Semantic Versioning. Pin your usage to a major version tag:

uses: dallay/common-actions/actions/setup-node@v1      # Recommended: major version
uses: dallay/common-actions/actions/setup-node@v1.2.3  # Exact version
uses: dallay/common-actions/actions/setup-node@main     # Latest (not recommended)

Contributing

  1. Create a feature branch
  2. Make your changes
  3. Test locally where possible
  4. Open a PR with a Conventional Commits title
  5. Request review

License

This project is open source under the MIT License.

About

Common github actions

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages