agentsclimarketplace

Ci cd pipelines

Skill medy-gribkov/arcana/skills/ci-cd-pipelines

Universal AI development toolkit. 74 production-ready skills for every coding agent. Works with Claude Code, Cursor, Codex.

Install
npx -y skills add medy-gribkov/arcana --skill ci-cd-pipelines

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

GitHub Actions and GitLab CI/CD pipeline expertise. Workflow syntax, job matrix, dependency caching (npm, pip, go, docker layers), artifact management, reusable workflows, composite actions, environment secrets, deployment patterns (blue-green, canary, rolling), Docker builds in CI, version bumping, branch protection, status checks. Use when creating CI/CD pipelines, optimizing build times, setting up deployment automation, or configuring GitHub Actions workflows.

SKILL.md

13.0 KB, as published. Nobody here has run it

You are a senior DevOps engineer who builds fast, reliable CI/CD pipelines with zero tolerance for flaky builds.

Use this skill when

  • Creating or modifying GitHub Actions or GitLab CI workflows
  • Optimizing CI build times or caching strategies
  • Setting up deployment automation (staging, production)
  • Configuring Docker builds in CI
  • Designing branch protection and merge requirements
  • Automating version bumping or changelog generation

GitHub Actions: Workflow Fundamentals

Minimal Production Workflow

name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm test -- --coverage
      - run: pnpm build

Critical rules:

  • Always set timeout-minutes. Default is 360 (6 hours). A hung job burns your minutes.
  • Always use concurrency with cancel-in-progress on PRs. No reason to test stale commits.
  • Always pin action versions to major (@v4) or SHA for security-critical actions.
  • Always use --frozen-lockfile / --ci to prevent lockfile drift.

Job Matrix

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [18, 20, 22]
        exclude:
          - os: windows-latest
            node: 18
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}

Set fail-fast: false to see all failures, not just the first. Exclude combinations that don't matter.

Caching Strategies

Node.js (pnpm)

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: "pnpm"
# This caches the pnpm store. For node_modules caching:
- uses: actions/cache@v4
  with:
    path: node_modules
    key: modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}

Go

- uses: actions/setup-go@v5
  with:
    go-version: "1.26"
    cache: true  # Caches ~/go/pkg/mod and ~/.cache/go-build

Python (pip)

- uses: actions/setup-python@v5
  with:
    python-version: "3.14"
    cache: "pip"
    cache-dependency-path: requirements*.txt

Docker Layer Caching

- uses: docker/build-push-action@v5
  with:
    context: .
    push: ${{ github.ref == 'refs/heads/main' }}
    tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

type=gha uses GitHub Actions cache backend -- no external registry needed. mode=max caches all layers, not just final.

Reusable Workflows

Caller

# .github/workflows/ci.yml
jobs:
  test:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: 20
    secrets: inherit

Callee

# .github/workflows/reusable-test.yml
on:
  workflow_call:
    inputs:
      node-version:
        type: number
        default: 20
    secrets:
      NPM_TOKEN:
        required: false

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: pnpm install --frozen-lockfile
      - run: pnpm test

Use secrets: inherit to pass all secrets. Use explicit secrets: block for cross-repo calls.

Composite Actions

For reusable steps (not jobs). Lives in .github/actions/<name>/action.yml.

# .github/actions/setup-project/action.yml
name: Setup Project
description: Install deps and cache
inputs:
  node-version:
    default: "20"
runs:
  using: composite
  steps:
    - uses: pnpm/action-setup@v4
      with:
        version: 9
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
        cache: "pnpm"
    - run: pnpm install --frozen-lockfile
      shell: bash

Every run step in a composite action MUST specify shell: bash.

Secrets & Environments

jobs:
  deploy-staging:
    environment: staging  # Requires approval if configured
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
    steps:
      - run: echo "Deploying to staging"

  deploy-production:
    needs: deploy-staging
    environment: production  # Separate approvers, separate secrets
    steps:
      - run: echo "Deploying to production"

Never log secrets. Use ::add-mask:: if you must construct a secret dynamically:

- run: |
    TOKEN=$(generate-token)
    echo "::add-mask::$TOKEN"
    echo "TOKEN=$TOKEN" >> $GITHUB_ENV

Deployment Patterns

Blue-Green (zero-downtime swap)

deploy:
  steps:
    - run: |
        # Deploy to inactive slot
        az webapp deployment slot create --name myapp --slot staging
        az webapp deploy --slot staging --src-path dist.zip
        # Smoke test staging
        curl --fail https://myapp-staging.azurewebsites.net/health
        # Swap
        az webapp deployment slot swap --name myapp --slot staging --target-slot production

Canary (gradual rollout)

- run: |
    kubectl set image deployment/app app=myimage:${{ github.sha }}
    kubectl rollout status deployment/app --timeout=120s
    # Route 10% traffic to new version
    kubectl apply -f canary-10-percent.yaml
    sleep 300
    # Check error rate, proceed or rollback
    ERROR_RATE=$(curl -s prometheus/api/v1/query?query=error_rate)
    if [ $(echo "$ERROR_RATE > 0.01" | bc) -eq 1 ]; then
      kubectl rollout undo deployment/app
      exit 1
    fi
    kubectl apply -f canary-100-percent.yaml

Rolling (Kubernetes default)

Kubernetes RollingUpdate with maxSurge: 1 and maxUnavailable: 0 for zero-downtime.

Docker in CI

Multi-stage Dockerfile optimized for CI

FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile --prod

FROM node:24-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build

FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
CMD ["dist/index.js"]

Order layers by change frequency: OS < runtime < deps < source. Each line is a cache layer.

Version Bumping with Changesets

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

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"
      - run: pnpm install --frozen-lockfile
      - uses: changesets/action@v1
        with:
          publish: pnpm release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Branch Protection Checklist

Configure in repo Settings > Branches > Branch protection rules:

  • Require PR reviews (1 minimum, dismiss stale on new push)
  • Require status checks: test, lint, build must pass
  • Require branches up to date before merge
  • Require signed commits for high-security repos
  • Restrict force pushes (always, no exceptions on main)

GitLab CI Basics

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - .cache/pip
    - node_modules/

test:
  stage: test
  image: python:3.14
  script:
    - pip install -r requirements.txt
    - pytest --cov=src --junitxml=report.xml
  artifacts:
    reports:
      junit: report.xml

build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main

Key GitLab differences from GitHub Actions:

  • stages define order; jobs in same stage run in parallel.
  • cache is per-runner, not per-repo. Use key carefully.
  • artifacts pass files between stages (GitHub uses actions/upload-artifact).
  • services for Docker-in-Docker (GitHub uses docker/setup-buildx-action).

Artifact Caching Gotchas

GitHub Actions cache has a 10GB limit per repo. Older entries are evicted when full.

  • Cache thrashing: Don't use ${{ github.sha }} as key. Use ${{ hashFiles('**/lockfile') }}.
  • Never-hit cache: Key changes every run. Use restore-keys for fallback.
  • Stale deps: Include dependency file hash in key to force invalidation.

Game Engine CI (Unity)

Uses game-ci actions. Key concerns: LFS checkout, Library caching, platform matrix, license secret.

# .github/workflows/unity.yml
name: Unity Build
on:
  push:
    branches: [main, develop]
env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { lfs: true }
      - uses: actions/cache@v4
        with:
          path: Library
          key: Library-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
          restore-keys: Library-
      - uses: game-ci/unity-test-runner@v4
        with: { testMode: all, artifactsPath: test-results }
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: test-results, path: test-results }

  build:
    needs: test
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        targetPlatform: [StandaloneWindows64, StandaloneLinux64, WebGL, Android]
    steps:
      - uses: actions/checkout@v4
        with: { lfs: true }
      - uses: actions/cache@v4
        with:
          path: Library
          key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('Assets/**') }}
          restore-keys: Library-${{ matrix.targetPlatform }}-
      - uses: game-ci/unity-builder@v4
        with:
          targetPlatform: ${{ matrix.targetPlatform }}
          versioning: Semantic
          buildMethod: BuildScript.PerformBuild
      - uses: actions/upload-artifact@v4
        with:
          name: Build-${{ matrix.targetPlatform }}
          path: build/${{ matrix.targetPlatform }}
          retention-days: 14

BuildScript.cs

// Assets/Editor/BuildScript.cs
using UnityEditor;
using UnityEditor.Build.Reporting;

public class BuildScript
{
    public static void PerformBuild()
    {
        var report = BuildPipeline.BuildPlayer(new BuildPlayerOptions
        {
            scenes = new[] { "Assets/Scenes/Main.unity" },
            locationPathName = "build/Game.exe",
            target = BuildTarget.StandaloneWindows64,
            options = BuildOptions.None
        });
        if (report.summary.result != BuildResult.Succeeded)
            EditorApplication.Exit(1);
    }
}

PlayMode Test

using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using System.Collections;

public class PlayerMovementTests
{
    private GameObject _player;

    [UnitySetUp]
    public IEnumerator SetUp()
    {
        _player = Object.Instantiate(Resources.Load<GameObject>("Prefabs/Player"));
        yield return null;
    }

    [UnityTest]
    public IEnumerator Player_MovesForward_WhenInputApplied()
    {
        Vector3 startPos = _player.transform.position;
        _player.GetComponent<PlayerController>().SetInput(Vector2.up);
        yield return new WaitForSeconds(0.5f);
        Assert.Greater(_player.transform.position.z, startPos.z);
    }

    [UnityTearDown]
    public IEnumerator TearDown() { Object.Destroy(_player); yield return null; }
}

Unity CI rules:

  • Always lfs: true. Unity projects use Git LFS for assets.
  • Cache Library/ with per-platform keys. Regeneration takes 5-15 min.
  • testMode: all runs EditMode + PlayMode. Split if needed.
  • Build times: WebGL 10-20 min, Windows 5-10 min, Android 15-25 min.

CI Performance Rules

  1. Parallelize everything: lint, test, typecheck, build as separate jobs. They don't depend on each other.
  2. Cache aggressively: dependency installs are the #1 time sink. Cache both store and node_modules.
  3. Fail fast: put lint and typecheck before tests. They're 10x faster and catch 50% of issues.
  4. Skip unnecessary work: use path filters to skip CI for docs-only changes.
  5. Use ubuntu-latest: it's 2-3x faster than macos-latest and 5-10x faster than windows-latest.
on:
  push:
    paths-ignore:
      - "docs/**"
      - "*.md"
      - ".gitignore"

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.