Skip to main content
CodeOath
← All posts

CI/CD & DevOps65 min total · 17 parts

CI/CD Pipelines Explained: From Push to Production

Part 12 of 17 · ~2 min

Secrets Management

This is where Marcus's near-miss from the Docker chapters actually gets resolved. Back then, trying to let worker call a payments API during a local integration test, he reached for docker build --build-arg STRIPE_SECRET_KEY=sk_test_... and Priya caught it in review before it shipped — a build argument isn't private, and docker history would have printed it straight back out to anyone who ever pulled that image.

Now that deploys run through the pipeline instead of a human's terminal, the same key has to reach the running container some other way, and GitHub's encrypted secrets store is that way:

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: { name: production }
    steps:
      - run: ./deploy.sh production ghcr.io/snapledger/web:${{ github.sha }}
        env:
          STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}

The key itself lives in the repository's own settings, not the file — the YAML only ever names it, and reading the workflow file top to bottom, for anyone who has access to do so, reveals nothing about what that name actually resolves to. GitHub also actively scans build logs for known secret values and masks them automatically, which is a second layer of protection against exactly the kind of accidental echo $STRIPE_SECRET_KEY that a tired engineer types while debugging at 11pm.

There's a sharper edge worth knowing before it surprises anyone: an outside contribution — a PR opened from someone's own fork rather than a branch inside the repo — runs the pipeline with a deliberately narrowed token, and repository secrets simply aren't handed to it. It's a real near-miss snapledger avoids purely by luck of timing — an early open-source-curious contributor's pull request includes a throwaway debug step that dumps every environment variable the job can see, out of curiosity about how the deploy is wired together. The step runs, and STRIPE_SECRET_KEY isn't among what prints, because nothing about a fork's checkout ever had it to begin with. That's the platform quietly doing its job; a maintainer who instead wired that same trigger through a more privileged variant built for testing fork PRs against real infrastructure would have handed a stranger's pull request a live payments key without meaning to.