Skip to main content
92 Nodes
All resources
Free tool

Free GitHub Actions Workflow Generator

Visually build a production-ready CI/CD workflow.

Answer a few questions or take full control — configure triggers, tests, coverage, SonarQube analysis, Docker builds, and deployments, then copy or download a secure, ready-to-use workflow YAML.

Loading GitHub Actions Generator…
Overview

What is GitHub Actions?

GitHub Actions is GitHub's built-in automation platform. It runs workflows — YAML files that describe what should happen in response to events in your repository, like a push, a pull request, a schedule, or a manual trigger.

A workflow YAML file describes one or more jobs, each running on a fresh virtual machine (a "runner"), made up of ordered steps — checking out code, installing dependencies, running tests, building an artifact, or deploying it.

Structure

Events, jobs, and steps explained

ConceptWhat it means
on:The event(s) that trigger the workflow — push, pull_request, schedule, workflow_dispatch, release.
jobs:One or more independent (or dependent, via `needs`) units of work, each on its own runner.
steps:Ordered actions within a job — either a reusable `uses:` action, or a `run:` shell command.

Workflow files always live in .github/workflows/ at the root of your repository — GitHub automatically discovers and runs every file it finds there.

CI vs CD

How CI differs from deployment

Continuous Integration (CI) verifies every change — linting, type-checking, running tests, and building — without touching anything outside the CI runner itself.

Continuous Deployment (CD) takes a verified change and ships it somewhere real: a server, a container registry, a hosting platform. This generator deliberately keeps deployment as its own gated job that only runs after every required check succeeds — never before.

Configuration

How to add GitHub secrets and variables

Open your repository's Settings → Secrets and variables → Actions. Secrets (tokens, passwords, private keys) are encrypted and masked in logs — use them for anything sensitive, referenced in YAML as ${{ secrets.NAME }}. Variables are plain text, visible in the UI — use them for non-sensitive configuration like a host URL, referenced as ${{ vars.NAME }}.

This generator never asks for a real secret value — only the name of the secret your workflow should reference.

Code quality

How SonarQube analysis works with GitHub Actions

SonarQube analyzes your source code for bugs, vulnerabilities, code smells, and duplication, then reports the result against a Quality Gate— a set of conditions (e.g. "no new critical issues", "coverage on new code above 80%") that a change must meet.

SonarQube Cloud is SonarSource's hosted service, configured with a project key, an organization key, and a token. SonarQube Server is self-hosted, and additionally needs a host URL — stored as a repository variable, not a secret, since a URL isn't sensitive on its own.

Coverage reaches SonarQube through a report file your test runner produces — LCOV for Jest/Vitest, Clover for PHPUnit/Pest, XML for pytest-cov, JaCoCo for Maven/Gradle, Cobertura for .NET, or Go's native coverage.out. The scan step must run after that report exists, which is exactly the order this generator enforces.

When "Wait for Quality Gate" and "Fail workflow if Quality Gate fails" are both enabled, a failing Quality Gate blocks the build and deploy jobs that depend on it — turning code quality from an FYI dashboard into an actual merge/deploy gate.

Deployment

Deploying Laravel, Next.js, and Docker projects safely

  • • Never let a deploy job run before tests and (if enabled) the Quality Gate — this generator wires the `needs:` graph so it can't.
  • • For Vercel, use the official Vercel CLI (`vercel pull` → `vercel build` → `vercel deploy --prebuilt`) rather than a third-party action.
  • • For GitHub Pages, use the official `actions/upload-pages-artifact` + `actions/deploy-pages` flow, which needs `pages: write` and `id-token: write` permissions.
  • • For Docker, scan the image with Trivy before pushing it — never publish an image you haven't scanned.
  • • For a VPS, authenticate with a dedicated SSH key (never your personal one), and keep the deploy script idempotent so re-running it is always safe.
  • • For AWS/Azure/GCP, prefer OpenID Connect (OIDC) over long-lived cloud credentials stored as secrets — this generator's cloud presets default to it.
Troubleshooting

Common GitHub Actions errors

  • Workflow never runs — no trigger enabled, or a branch/path filter that doesn't match your push.
  • Job depends on undefined job — a typo in a `needs:` reference, or a job ID that was renamed.
  • Secrets are empty in a step — usually a fork pull request, which never receives repository secrets.
  • Database connection refused — the app tried to connect before the service container's healthcheck passed.
  • Coverage report not found by SonarQube — the configured report path doesn't match what the test runner actually wrote.
  • Action not found / version error — a typo in `uses:`, or an action version that was deprecated or removed.
Security

GitHub Actions security best practices

  • • Set least-privilege `permissions:` — this generator defaults every workflow to `contents: read` and adds only what's actually needed.
  • • Never interpolate untrusted input (a PR title, an issue body) directly into a `run:` shell command — pass it through an environment variable instead.
  • • Treat `pull_request_target` as high-risk: it grants secret access even to forked pull requests, so never check out and execute untrusted PR code with it.
  • • Prefer pinning third-party actions to a maintained major version tag; pin to an exact commit SHA for maximum supply-chain safety on anything sensitive.
  • • Scan container images with Trivy, dependencies with Dependency Review or npm/Composer audit, and commits with Gitleaks before they reach production.
  • • Review generated output before production use — this generator, or any generator, is a strong starting point, not a substitute for your own review.
Performance

Reducing workflow execution time

Enable dependency caching (on by default here) so installs don't re-download the same packages on every run. Use `paths`/`paths-ignore` filters so documentation-only changes don't trigger a full test suite. Keep build matrices to the versions you actually support — each extra combination multiplies run time and billed minutes.

Caching speeds up installs, not correctness — a stale cache key can occasionally serve outdated dependencies. This generator keys caches off your lockfile automatically, so a changed lockfile always busts the cache rather than silently reusing an old one.

Examples

Complete, ready-to-use workflow examples

Real, parseable examples generated by the exact same code that powers the builder above.

Laravel + MySQL + Redis + Pest

A typical Laravel app that needs a real database and cache during CI, tested with Pest.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    services:
      mysql:
        image: mysql:8.4
        env:
          MYSQL_ROOT_PASSWORD: ci-test-password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
      redis:
        image: redis:7
        env: {}
        ports:
          - 6379:6379
        options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=5
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"
          extensions: mbstring, xml, ctype, curl, pdo, pdo_mysql, pdo_pgsql, bcmath, intl, redis
          coverage: pcov
          tools: composer:v2
      - name: Cache Composer dependencies
        uses: actions/cache@v4
        with:
          path: vendor
          key: composer-${{ hashFiles('composer.lock') }}
          restore-keys: composer-
      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --no-progress
      - name: Copy environment file
        run: cp .env.example .env
      - name: Generate application key
        run: php artisan key:generate
      - name: Run database migrations
        run: php artisan migrate --force
      - name: Run tests
        run: php artisan test --coverage --min=0 --coverage-clover=coverage.xml
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"
          extensions: mbstring, xml, ctype, curl, pdo, pdo_mysql, pdo_pgsql, bcmath, intl, redis
          coverage: pcov
          tools: composer:v2
      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --no-progress
      - name: Build
        run: npm ci && npm run build

Expected files

  • .github/workflows/ci.yml

Required secrets

None

Setup steps

  1. Ensure composer.json requires pestphp/pest
  2. Confirm .env.example has DB_CONNECTION=mysql and REDIS_HOST=127.0.0.1
  3. Commit the generated file to .github/workflows/ci.yml

Common failure points

  • Migrations run before MySQL's healthcheck passes if the healthcheck is removed
  • APP_KEY missing because `php artisan key:generate` step was deleted
  • Redis service port conflicts with a locally running Redis on a self-hosted runner

Laravel + PHPUnit + SonarQube + Quality Gate

A Laravel app where a failing SonarQube Quality Gate should block the merge, not just warn.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    services:
      mysql:
        image: mysql:8.4
        env:
          MYSQL_ROOT_PASSWORD: ci-test-password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"
          extensions: mbstring, xml, ctype, curl, pdo, pdo_mysql, pdo_pgsql, bcmath, intl, redis
          coverage: pcov
          tools: composer:v2
      - name: Cache Composer dependencies
        uses: actions/cache@v4
        with:
          path: vendor
          key: composer-${{ hashFiles('composer.lock') }}
          restore-keys: composer-
      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --no-progress
      - name: Copy environment file
        run: cp .env.example .env
      - name: Generate application key
        run: php artisan key:generate
      - name: Run database migrations
        run: php artisan migrate --force
      - name: Run tests
        run: vendor/bin/phpunit --coverage-clover=coverage.xml
      - name: SonarQube analysis
        uses: SonarSource/sonarqube-scan-action@v5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: Check SonarQube Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        timeout-minutes: 5
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"
          extensions: mbstring, xml, ctype, curl, pdo, pdo_mysql, pdo_pgsql, bcmath, intl, redis
          coverage: pcov
          tools: composer:v2
      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --no-progress
      - name: Build
        run: npm ci && npm run build

Expected files

  • .github/workflows/ci.yml
  • sonar-project.properties

Required secrets

  • SONAR_TOKEN

Setup steps

  1. Create the project in SonarQube Cloud/Server and note its project key
  2. Add SONAR_TOKEN as a repository secret
  3. Ensure PCOV or Xdebug is available so --coverage-clover produces real data

Common failure points

  • Quality Gate step times out because analysis hasn't finished processing yet
  • Coverage report path doesn't match what's configured in sonar-project.properties
  • Fork pull requests can't see SONAR_TOKEN, so analysis silently fails there

Next.js + ESLint + Jest + build

A standard Next.js app CI: lint, type-check, test, and confirm the production build succeeds.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Lint
        run: npm run lint
      - name: Type check
        run: tsc --noEmit
      - name: ESLint
        run: npx eslint .
      - name: Run tests
        run: npm run test
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build

Expected files

  • .github/workflows/ci.yml

Required secrets

None

Setup steps

  1. Confirm package.json has lint, test, and build scripts
  2. Add a .nvmrc or keep the Node version in sync with production
  3. Commit the workflow to .github/workflows/ci.yml

Common failure points

  • Build fails in CI only, due to environment variables only set locally
  • ESLint step fails on warnings if --max-warnings=0 wasn't intended
  • Jest hangs in CI because a test opens a resource it never closes

Node.js + PostgreSQL + SonarQube

A Node.js API tested against a real PostgreSQL service container, analyzed with SonarQube.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: ci-test-password
          POSTGRES_DB: testing
        ports:
          - 5432:5432
        options: --health-cmd=pg_isready --health-interval=10s --health-timeout=5s --health-retries=5
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Lint
        run: npm run lint
      - name: Type check
        run: tsc --noEmit
      - name: Run tests
        run: npm run test run --coverage
      - name: SonarQube analysis
        uses: SonarSource/sonarqube-scan-action@v5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: Check SonarQube Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        timeout-minutes: 5
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build

Expected files

  • .github/workflows/ci.yml
  • sonar-project.properties

Required secrets

  • SONAR_TOKEN

Setup steps

  1. Point your test database config at localhost:5432 with the generated CI credentials
  2. Add SONAR_TOKEN as a secret
  3. Confirm your test script writes coverage/lcov.info

Common failure points

  • App tries to connect before Postgres's healthcheck passes
  • LCOV path doesn't match the test runner's actual output directory
  • Connection string hardcodes a hostname instead of using the service name

Docker build + Trivy + GHCR

Build a container image, block the push if Trivy finds a critical/high vulnerability, then publish to GHCR.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
  packages: write
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  build:
    name: Build & Publish Image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Extract image metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}
          tags: |-
            type=ref,event=branch
            type=sha,format=short
            type=raw,value=latest,enable={{is_default_branch}}
      - name: Build image (local, for scanning)
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64
          load: true
          push: false
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@0.29.0
        with:
          scan-type: image
          image-ref: ${{ steps.meta.outputs.tags }}
          format: table
          exit-code: "1"
          severity: CRITICAL,HIGH
      - name: Push image
        if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Expected files

  • .github/workflows/ci.yml

Required secrets

None

Setup steps

  1. Add a Dockerfile at the repository root
  2. No extra secrets needed — GHCR authenticates with the built-in GITHUB_TOKEN
  3. Confirm the repository's package visibility settings after the first push

Common failure points

  • Trivy blocks the build on a base-image CVE with no available fix yet
  • packages: write permission missing, so the push step is denied
  • Multi-platform build times out without Buildx/QEMU set up correctly

WordPress plugin test workflow

A WordPress plugin tested against WordPress core's own PHPUnit scaffolding with a MySQL service.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    services:
      mysql:
        image: mysql:8.4
        env:
          MYSQL_ROOT_PASSWORD: ci-test-password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.2"
          extensions: mysqli
          coverage: none
          tools: composer:v2
      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --no-progress
      - name: Run tests
        run: vendor/bin/phpunit

Expected files

  • .github/workflows/ci.yml

Required secrets

None

Setup steps

  1. Add a bin/install-wp-tests.sh script (from the WordPress plugin boilerplate)
  2. Confirm phpunit.xml points at the installed WP test suite
  3. No secrets required for the test job itself

Common failure points

  • install-wp-tests.sh can't reach MySQL because the host/port doesn't match the service
  • WordPress version drifts from what's tested against production
  • Plugin activation fails silently, masking the real test failure

Python + pytest + SonarQube

A Python service tested with pytest and pytest-cov, analyzed with SonarQube.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: pip
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Lint
        run: ruff check .
      - name: Type check
        run: mypy .
      - name: Run tests
        run: pytest --cov=. --cov-report=xml
      - name: SonarQube analysis
        uses: SonarSource/sonarqube-scan-action@v5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: Check SonarQube Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        timeout-minutes: 5

Expected files

  • .github/workflows/ci.yml
  • sonar-project.properties

Required secrets

  • SONAR_TOKEN

Setup steps

  1. Add pytest-cov to requirements/dev dependencies
  2. Add SONAR_TOKEN as a secret
  3. Confirm sonar.python.coverage.reportPaths matches coverage.xml

Common failure points

  • ruff/mypy failures block CI on pre-existing issues after first enabling them
  • Coverage XML format mismatch between pytest-cov versions
  • Virtualenv/cache path mismatch causes a stale dependency to be used

Java + Maven + JaCoCo + SonarQube

A Java service built with Maven, coverage collected with JaCoCo, analyzed with SonarQube.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Java
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "21"
          cache: maven
      - name: Lint
        run: mvn -B checkstyle:check
      - name: Run tests
        run: mvn -B test jacoco:report
      - name: SonarQube analysis (Maven)
        run: mvn -B sonar:sonar -Dsonar.projectKey=your-org_java-service
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: Check SonarQube Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        timeout-minutes: 5
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Java
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "21"
          cache: maven
      - name: Build
        run: mvn -B package -DskipTests

Expected files

  • .github/workflows/ci.yml
  • sonar-project.properties

Required secrets

  • SONAR_TOKEN

Setup steps

  1. Bind the jacoco-maven-plugin to the test phase in pom.xml
  2. Add SONAR_TOKEN as a secret
  3. Confirm the JaCoCo report path matches target/site/jacoco/jacoco.xml

Common failure points

  • JaCoCo plugin not bound to the right lifecycle phase, producing no report
  • Maven dependency resolution timeouts without caching enabled
  • Sonar analysis runs before `mvn test` finishes writing the report

.NET + tests + SonarQube

A .NET solution tested with the built-in coverage collector, analyzed via the SonarScanner begin/end lifecycle.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 8.0.x
      - name: Set up Java (required by SonarScanner for .NET)
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "17"
      - name: Cache NuGet packages
        uses: actions/cache@v4
        with:
          path: ~/.nuget/packages
          key: nuget-${{ hashFiles('**/*.csproj') }}
          restore-keys: nuget-
      - name: Install dependencies
        run: dotnet restore
      - name: Run tests
        run: dotnet test --no-build --collect:"XPlat Code Coverage"
      - name: Install SonarScanner for .NET
        run: dotnet tool install --global dotnet-sonarscanner
      - name: SonarScanner begin
        run: dotnet sonarscanner begin /k:"your-org_dotnet-service" /d:sonar.token="$SONAR_TOKEN"
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: SonarScanner end
        run: dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: Check SonarQube Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        timeout-minutes: 5
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 8.0.x
      - name: Install dependencies
        run: dotnet restore
      - name: Build
        run: dotnet build --configuration Release

Expected files

  • .github/workflows/ci.yml
  • sonar-project.properties

Required secrets

  • SONAR_TOKEN

Setup steps

  1. Add the coverlet.collector NuGet package to test projects
  2. Add SONAR_TOKEN as a secret
  3. Confirm a JVM is available — SonarScanner for .NET requires one (added automatically here)

Common failure points

  • SonarScanner end step runs even though begin failed, producing a confusing error
  • Coverage file glob doesn't match multi-project solution output paths
  • Scanner begin/end wraps the wrong build step, so no coverage is attached

VPS deployment after successful tests and Quality Gate

Deploy to a VPS over SSH, but only once tests pass and the SonarQube Quality Gate succeeds.

# Generated with the 92 Nodes GitHub Actions Generator
# https://92nodes.com/resources/github-actions-generator
# Review before use: verify action versions, secrets, and deployment steps for your project.

name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
permissions:
  contents: read
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Lint
        run: npm run lint
      - name: Type check
        run: tsc --noEmit
      - name: Run tests
        run: npm run test run --coverage
      - name: SonarQube analysis
        uses: SonarSource/sonarqube-scan-action@v5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      - name: Check SonarQube Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        timeout-minutes: 5
  build:
    name: Build
    needs:
      - test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build
  deploy:
    name: Deploy
    needs:
      - build
    if: success()
    runs-on: ubuntu-latest
    environment: production
    timeout-minutes: 15
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Deploy over SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |-
            cd /var/www/your-app
            git pull origin main
            # Install dependencies and restart your app/service here
            # e.g. npm ci --omit=dev && pm2 restart your-app

Expected files

  • .github/workflows/ci.yml
  • sonar-project.properties

Required secrets

  • SONAR_TOKEN
  • SSH_HOST
  • SSH_USER
  • SSH_PRIVATE_KEY

Setup steps

  1. Add SSH_HOST, SSH_USER, and SSH_PRIVATE_KEY as secrets
  2. Add SONAR_TOKEN as a secret
  3. Customize the SSH deploy script's directory and restart command for your server

Common failure points

  • Deploy job runs even though an earlier job was skipped, not failed — check `if: success()` semantics
  • SSH private key format (needs the full PEM, including header/footer lines)
  • Server-side restart command exits non-zero for an unrelated reason, marking deployment failed
Getting started

How to use the GitHub Actions Generator

1. Pick a preset or start from your stack

Choose a Quick preset that matches your project (Laravel, Next.js, Docker, a deployment target…) or pick your stack manually in Beginner mode.

2. Answer a few simple questions (or go Advanced)

Beginner mode asks what your project is built with, when checks should run, and where it should deploy. Advanced mode exposes every trigger, job, permission, and SonarQube setting directly.

3. Turn on SonarQube, Docker, or additional checks if you need them

Each is its own section — SonarQube Cloud or Server, Docker build & publish, and a stack-aware list of additional quality/security checks like CodeQL, Gitleaks, or ESLint.

4. Review the health score and required secrets

The Validation tab scores syntax, security, reliability, performance, and maintainability, and the Secrets & Variables tab lists exactly what to add under Settings → Secrets and variables → Actions.

5. Copy or download

Grab the workflow YAML (and sonar-project.properties or dependabot.yml, if enabled) individually or as a single zip, and save it to .github/workflows/ in your repository.

Related tools

Pair it with the rest of your CI/CD setup

Building a local development stack too? Try the Docker Compose Generator. Need a hand with a cron schedule for the scheduled-trigger section above? Use the Cron Expression Builder. And once your CI is generating test cases, the Test Case Generator and .gitignore Generator round out a clean repository setup.

FAQ

Frequently asked questions

What is a GitHub Actions workflow?+

A YAML file that tells GitHub what to do automatically in response to events in your repository — run tests on every push, analyze code quality, build a Docker image, or deploy to production. Each workflow lives in its own file inside .github/workflows/.

Where should I save the generated YAML file?+

Inside .github/workflows/ at the root of your repository — for example .github/workflows/ci.yml. GitHub automatically discovers and runs any workflow file in that folder.

Is this GitHub Actions generator free?+

Yes. Every preset, stack, SonarQube configuration, and export is free with no signup, account, or usage limit.

Does 92 Nodes store my YAML or secrets?+

No. The generator runs entirely in your browser. Your configuration, generated YAML, and any secret or variable names you type are never sent to 92 Nodes. This tool never even asks for an actual secret value — only the name of the GitHub secret that holds it.

Can I generate a workflow for Laravel?+

Yes — presets cover Laravel with Pest or PHPUnit, optional MySQL, PostgreSQL, and Redis service containers, and SonarQube analysis with PHP coverage.

Can I use SonarQube with GitHub Actions?+

Yes. The Code Quality & SonarQube section supports both SonarQube Cloud and a self-hosted SonarQube Server, with stack-aware coverage setup (LCOV, Clover, JaCoCo, Cobertura, or Go's native format) and an optional Quality Gate check that can block deployment.

What is the difference between SonarQube Cloud and Server?+

SonarQube Cloud is SonarSource's hosted service — you only need a project key, an organization key, and a token. SonarQube Server is a self-hosted instance you run yourself, which additionally needs a host URL (stored as a repository variable, not a secret, since it's not sensitive).

Which SonarQube secrets are required?+

A SONAR_TOKEN secret is always required. Self-hosted SonarQube Server additionally needs a SONAR_HOST_URL repository variable pointing at your server. The generator's "Secrets & Variables" tab lists exactly what your current configuration needs.

Can the Quality Gate stop deployment?+

Yes, when "Fail workflow if Quality Gate fails" is enabled. The generator places the Quality Gate check after analysis and before the build/deploy jobs, and the deploy job's dependency chain means a failed gate blocks deployment.

How do I generate test coverage for SonarQube?+

Enable "Generate coverage" for your stack — the generator wires up the right tool automatically: Jest/Vitest LCOV for JavaScript and TypeScript, PCOV/Xdebug Clover for PHP, pytest-cov XML for Python, JaCoCo for Java, the built-in collector for .NET, and Go's native coverage profiling.

Can I use MySQL, PostgreSQL or Redis in GitHub Actions?+

Yes, as service containers that run alongside your job for its duration. The generator adds the correct image, environment variables, port mapping, and healthcheck for each database you enable, using fixed, disposable CI-only credentials.

How do I deploy to a VPS?+

Choose the "VPS through SSH" deployment target. You'll need SSH_HOST, SSH_USER, and SSH_PRIVATE_KEY as repository secrets — the generator's deploy step then runs your deploy script over SSH after every required check passes.

Can I import an existing workflow?+

Yes. Paste or upload a .yml/.yaml file and the generator parses it as plain data (never executing it), summarizes its triggers, jobs, and required secrets, flags anything risky, and offers a best-effort import of what it can confidently map into the visual builder.

Are GitHub Actions secrets safe?+

They're encrypted at rest and masked in logs, but they're only as safe as the workflow that uses them — for example, a workflow that echoes a secret into a log line, or that runs `pull_request_target` against untrusted code, can leak it. The generator's validator flags both patterns.

Why is my workflow not triggering?+

The most common causes: no `on:` trigger is actually enabled, the branch you pushed to doesn't match the configured `branches` filter, or the workflow file has a YAML syntax error and GitHub silently can't parse it. Check the Actions tab for a parsing error first.

Why are secrets unavailable in a pull request from a fork?+

GitHub deliberately withholds repository secrets from workflow runs triggered by pull requests from forks, to stop an external contributor from exfiltrating them. This means SonarQube analysis and deployment steps typically can't run on fork PRs unless you deliberately change the trigger strategy (and understand the security trade-offs of doing so).

How do I make GitHub Actions faster?+

Enable dependency caching (on by default here), avoid unnecessary matrix combinations, use `paths`/`paths-ignore` filters so unrelated changes don't trigger a full run, and only add a job timeout long enough for the job to realistically need — a stuck job otherwise wastes runner minutes for hours.

Does the generator support monorepos?+

Partially — set a working directory for build/test steps and a matching SonarQube project base directory. Full multi-project matrix pipelines with independent per-package workflows are beyond what the visual builder currently models; see the Advanced settings panel for what's available today.

92 Nodes

Have a project in mind? Let's build it.

Tell us about your goals and we'll get back to you within one business day with next steps.

Book a free call