name: Deploy to Production
on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' - run: npm ci - run: npm test - uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - run: npm run deploy
name: Deploy to Production
on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' - run: npm ci - run: npm test - uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - run: npm run deploy
name: Deploy to Production
on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' - run: npm ci - run: npm test - uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - run: npm run deploy
jobs: lint: uses: org/shared-workflows/.github/workflows/lint.yml@main with: node-version: '22' secrets: inherit
jobs: lint: uses: org/shared-workflows/.github/workflows/lint.yml@main with: node-version: '22' secrets: inherit
jobs: lint: uses: org/shared-workflows/.github/workflows/lint.yml@main with: node-version: '22' secrets: inherit
# .gitlab-ci.yml
stages: - build - test - deploy variables: DOCKER_DRIVER: overlay2 build: stage: build image: node:22-alpine script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 hour cache: key: $CI_COMMIT_REF_SLUG paths: - node_modules/ test: stage: test image: node:22-alpine script: - npm ci - npm run test:coverage coverage: '/Lines\s*:\s*(\d+\.\d+)%/' deploy: stage: deploy environment: name: production url: https://app.example.com only: - main script: - ./scripts/deploy.sh
# .gitlab-ci.yml
stages: - build - test - deploy variables: DOCKER_DRIVER: overlay2 build: stage: build image: node:22-alpine script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 hour cache: key: $CI_COMMIT_REF_SLUG paths: - node_modules/ test: stage: test image: node:22-alpine script: - npm ci - npm run test:coverage coverage: '/Lines\s*:\s*(\d+\.\d+)%/' deploy: stage: deploy environment: name: production url: https://app.example.com only: - main script: - ./scripts/deploy.sh
# .gitlab-ci.yml
stages: - build - test - deploy variables: DOCKER_DRIVER: overlay2 build: stage: build image: node:22-alpine script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 hour cache: key: $CI_COMMIT_REF_SLUG paths: - node_modules/ test: stage: test image: node:22-alpine script: - npm ci - npm run test:coverage coverage: '/Lines\s*:\s*(\d+\.\d+)%/' deploy: stage: deploy environment: name: production url: https://app.example.com only: - main script: - ./scripts/deploy.sh
test:unit: needs: [build] test:integration: needs: [build] deploy: needs: [test:unit, test:integration]
test:unit: needs: [build] test:integration: needs: [build] deploy: needs: [test:unit, test:integration]
test:unit: needs: [build] test:integration: needs: [build] deploy: needs: [test:unit, test:integration]
version: 2.1 orbs: node: circleci/[email protected] docker: circleci/[email protected] aws-cli: circleci/[email protected] jobs: build-and-test: docker: - image: cimg/node:22.0 resource_class: large # 4 vCPUs, 8GB RAM steps: - checkout - node/install-packages: cache-path: ~/project/node_modules override-ci-command: npm ci - run: name: Run Tests command: npm test -- --ci --coverage - docker/build: image: myapp tag: $CIRCLE_SHA1 workflows: build-test-deploy: jobs: - build-and-test - deploy: requires: - build-and-test filters: branches: only: main
version: 2.1 orbs: node: circleci/[email protected] docker: circleci/[email protected] aws-cli: circleci/[email protected] jobs: build-and-test: docker: - image: cimg/node:22.0 resource_class: large # 4 vCPUs, 8GB RAM steps: - checkout - node/install-packages: cache-path: ~/project/node_modules override-ci-command: npm ci - run: name: Run Tests command: npm test -- --ci --coverage - docker/build: image: myapp tag: $CIRCLE_SHA1 workflows: build-test-deploy: jobs: - build-and-test - deploy: requires: - build-and-test filters: branches: only: main
version: 2.1 orbs: node: circleci/[email protected] docker: circleci/[email protected] aws-cli: circleci/[email protected] jobs: build-and-test: docker: - image: cimg/node:22.0 resource_class: large # 4 vCPUs, 8GB RAM steps: - checkout - node/install-packages: cache-path: ~/project/node_modules override-ci-command: npm ci - run: name: Run Tests command: npm test -- --ci --coverage - docker/build: image: myapp tag: $CIRCLE_SHA1 workflows: build-test-deploy: jobs: - build-and-test - deploy: requires: - build-and-test filters: branches: only: main
parallelism: 4
steps: - run: command: | TESTFILES=$(circleci tests glob "src/**/*.test.js" | circleci tests split --split-by=timings) npm test -- $TESTFILES
parallelism: 4
steps: - run: command: | TESTFILES=$(circleci tests glob "src/**/*.test.js" | circleci tests split --split-by=timings) npm test -- $TESTFILES
parallelism: 4
steps: - run: command: | TESTFILES=$(circleci tests glob "src/**/*.test.js" | circleci tests split --split-by=timings) npm test -- $TESTFILES - Runner performance: Hosted runners are slower than CircleCI's resource classes
- Self-hosting complexity: GitHub Actions on self-hosted runners requires maintenance overhead
- Caching: Cache invalidation and dependency management can be tricky - Free: 2,000 minutes/month (Linux), 500 minutes (macOS)
- Pro: $4/month, 3,000 minutes
- Enterprise: Custom pricing with self-hosted runners - UI complexity: GitLab's interface has a steep learning curve
- Resource requirements: Self-hosted GitLab requires significant infrastructure (4GB RAM minimum)
- Slower free tier: 400 min/month is significantly less than competitors - Free: 400 CI/CD minutes/month on GitLab.com
- Premium: $29/user/month, 10,000 minutes
- Self-hosted CE: Free forever - Vendor lock-in: CircleCI syntax doesn't translate to other platforms
- Pricing complexity: Resource class pricing can surprise teams
- No self-hosted free tier: Self-hosted (CircleCI Server) requires an enterprise contract - Free: 6,000 minutes/month, 1 concurrent job
- Performance: $15/month base + compute credits
- Scale: Custom pricing - GitLab: Built-in SAST, DAST, dependency scanning, secret detection
- GitHub Actions: GHAS (GitHub Advanced Security) — paid add-on
- CircleCI: Context permissions and audit logs, but no built-in SAST - Your code is already on GitHub
- You rely heavily on community integrations
- You want zero setup overhead
- Open-source project (unlimited minutes on public repos) - You need self-hosted everything
- Compliance requires full infrastructure control
- You want a single platform for issue tracking, CI/CD, and deployment
- Enterprise security scanning is a requirement - You have complex Docker-heavy pipelines
- Build speed is your primary bottleneck
- You need fine-grained compute resource control
- You run large test suites that benefit from parallelism - Comparing Docker and Podman — container runtime for your CI jobs
- Terraform vs Pulumi — infrastructure for your CD targets
- GitHub Actions cost calculator — estimate your monthly bill