CI/CD & DevOps65 min total · 17 parts
CI/CD Pipelines Explained: From Push to Production
Part 7 of 17 · ~1 min
Jobs, Steps, and Runners
Every job above spins up on a completely fresh machine — its own disk, its own clean checkout, nothing left over from whatever ran an hour ago or from a different job in the same workflow. That's deliberate, and it's the thing that catches Marcus off guard the first week: he adds a step to unit-tests assuming a file lint wrote earlier in the same run would still be sitting there, and it isn't, because lint and unit-tests aren't just running at different times — they're running on two entirely separate computers that were never going to share a disk.
Two mechanisms exist specifically to bridge that gap on purpose. The first is artifacts — one job deliberately publishes something, and a later job deliberately fetches it:
jobs:
build-app:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: web-dist
path: dist/
build-image:
needs: [lint, unit-tests, build-app]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: web-dist
path: dist/
- run: docker build -t ghcr.io/snapledger/web:${{ github.sha }} .
- run: docker push ghcr.io/snapledger/web:${{ github.sha }}
build-app compiles once; build-image downloads the exact output instead of recompiling from scratch inside the Docker build, which is the same "compile once, ship the result" idea as the multi-stage Dockerfile from this team's earlier build, just moved up a level from Docker stages to pipeline jobs.
The second mechanism, caching, looks similar but means something different: it's a purely optional speedup, never a required handoff, and a cache miss should degrade to "a bit slower," never to "broken." That distinction earns its own chapter next, because it's where the team's build times actually start to matter.