Book a call
All talks and takeaways

Template

A starter GitHub Actions workflow for a vibe-coded product

A copy-ready CI workflow: lint, types, tests, build, dependency review, CodeQL, and an OIDC deploy from main with no stored keys.

This is the workflow behind the safety net. It runs the checks on every pull request, reviews new dependencies, and scans the code. It deploys from main with short-lived AWS credentials and no stored keys. It assumes a Node.js project, but the shape is the same for any language.

The two files go into the repository as they are. Four repository settings sit outside the files, and the workflow can't switch them on by itself. The list below covers all four.

From the talk: Ship fast, break nothing: CI/CD for vibe-coded startups

The workflow

.github/workflows/ci.yml
# .github/workflows/ci.yml
# Starter pipeline from the talk "Ship fast, break nothing" (netninjas.eu/events).
# Assumes a Node.js project whose package.json has lint, typecheck, test, and build scripts.
name: ci

on:
  pull_request:
  push:
    branches: [main]

# Least privilege by default; jobs that need more ask for it.
permissions:
  contents: read

# A new push to a pull request cancels its older run. Runs on main always finish.
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  checks:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test
      - run: npm run build

  dependency-review:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/dependency-review-action@v5
        with:
          fail-on-severity: high

  codeql:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v7
      - uses: github/codeql-action/init@v4
        with:
          languages: javascript-typescript
      - uses: github/codeql-action/analyze@v4

  deploy:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    needs: [checks, codeql]
    runs-on: ubuntu-latest
    environment: production
    permissions:
      contents: read
      id-token: write # lets the job ask AWS for short-lived credentials (OIDC)
    steps:
      - uses: actions/checkout@v7
      - uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }}
          aws-region: eu-central-1
      - run: ./scripts/deploy.sh

Automated dependency updates

Weekly update pull requests for packages and for the actions themselves. Minor and patch updates arrive together in one pull request.

.github/dependabot.yml
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    groups:
      minor-and-patch:
        update-types: [minor, patch]
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

Four settings the file can't set

  1. We protect main. A branch ruleset requires a pull request, plus passing checks and codeql jobs, before any merge.
  2. We turn on secret scanning with push protection. It sits in the repository's security settings, next to Dependabot alerts.
  3. We create the deploy role in AWS. GitHub's OIDC identity provider (token.actions.githubusercontent.com, audience sts.amazonaws.com), plus a role whose trust policy only accepts the subject repo:<owner>/<repo>:environment:production. Its ARN goes into the repository variable AWS_DEPLOY_ROLE_ARN.
  4. We add the production environment. An environment named production, limited to the main branch. Required reviewers add a human approval to each deploy.

Before relying on it

Two lines need local values: ./scripts/deploy.sh becomes the real deploy step, and eu-central-1 becomes the real region. On a private repository, CodeQL and dependency review need a GitHub Code Security licence, or Semgrep instead. On a public repository, both are free. Action versions move, and Dependabot keeps them current once its file is in place. Pinning each action to a full commit SHA is the stricter option.

What I leave out on purpose

I leave out matrix builds, self-hosted runners, and a separate staging pipeline. Each one solves a problem a small team rarely has on day one. I add them when the team needs them, not when a template suggests them.

Next step

Stuck on the OIDC role or the deploy step? I highly recommend a thirty-minute call with the repository open. We'll get the first green run together. What does your deploy step look like today?

Book a call

These takeaways are an engineer's reading of the rules and the tools. I wrote them to help teams decide what to build. They aren't legal advice. I recommend checking the current text with your own counsel before planning around a date or a threshold.

Let's talk.

A 30-minute call, no pitch. We look at your current setup, your stage, and the thing that's bothering you about it. I give a straight opinion on what to change, what to leave alone, and whether the problem is the one it looks like. What's the one thing in your stack you'd fix tomorrow?