Chapter 25Lesson 04~160 minutes

Git in CI/CD, Infrastructure as Code, Release Automation, and GitOps: Diagnostics, Failure Modes, Security, and Performance

Diagnose shallow-tag failures, detached-branch assumptions, mutable release tags, stale automation writes, credential exposure, broad trust exceptions, dirty runners, wrong remotes, and desired/applied GitOps state confusion.

DiagnosticsMutable tagsConcurrencySecurity

Learning objectives

  • Preserve exact runner/ref/deployment evidence before changing automation state.
  • Repair missing history/tag context without fabricating version provenance.
  • Replace branch-name assumptions with immutable commit identity in detached checkouts.
  • Treat non-fast-forward bot rejection as concurrency protection rather than a reason to force.
  • Separate desired-source commits from successful applied/observed deployment state.

1. Automation diagnostic sequence

  1. Preserve evidence: job checkout OID, requested ref/OID, shallow state, available refs/tags, status, remote/config origins, push output, deployment markers, and logs with secrets redacted.
  2. Identify the layer: fetch/graph, checkout/ref, version naming, remote concurrency, credential exposure, or reconciliation state.
  3. Correct the narrowest cause.
  4. Re-run the same verification.
  5. Do not destroy history or overwrite refs merely to make the job green.

2. Failure mode — pipeline cannot find a release tag in a shallow/no-tags clone

git rev-parse --is-shallow-repository
git tag --list
git describe --tags HEAD
echo "describe exit=$?"

Interpretation: a valid commit checkout can lack the graph/tag context required by describe. Inspect the job's actual versioning requirement. Repair by fetching the specific tag/history or unshallowing—rather than fabricating a version string.

git fetch --unshallow origin
git fetch --tags origin
git describe --tags --always HEAD

3. Intentionally broken example — script assumes a branch is checked out

Broken script:

BRANCH=$(git branch --show-current)
if test -z "$BRANCH"; then
  echo "ERROR: no branch; refusing to build"
  exit 1
fi

Line 1 asks for a current branch. In an exact CI checkout, detached HEAD intentionally has none. The error therefore diagnoses the script's assumption, not invalid Git state.

Repair by using immutable commit provenance:

BUILD_OID=$(git rev-parse --verify HEAD^{commit})
test -n "$BUILD_OID"
printf "Building commit %s\n" "$BUILD_OID"

If the scheduler's branch/event name matters for reporting, record it separately as untrusted context and never substitute it for the actual checkout OID.

4. Failure mode — the same published tag name identifies different source

Current Git's tag documentation explicitly treats published retagging as a trust hazard. Two consumers can retain different v1.0 objects because tag updates are not silently propagated like normal branch advancement.

Do not routinely move published release tags. Prefer a new corrected version/tag. If a tag has already been moved, communicate the incident and verify exact object IDs across consumers.
git rev-parse v1.0^{commit}
git ls-remote --tags origin refs/tags/v1.0 'refs/tags/v1.0^{}'

A release record should include the immutable commit OID so a human-readable tag mismatch is detectable.

5. Failure mode — automation force-pushes over human work

A normal non-fast-forward rejection is valuable evidence. Do not respond to it with blind force:

git fetch origin release-config
git log --graph --oneline --decorate --all --max-count=20
git merge-base --is-ancestor refs/remotes/origin/release-config HEAD
echo "ancestor check exit=$?"

If the remote tip is not an ancestor of the bot commit, recompute/integrate from the latest state and push a normal fast-forward. --force-with-lease exists for intentional history replacement, but the preferred release-state architecture avoids rewrite permission altogether.

6. Failure mode — credentials appear in a remote URL or job log

Never paste a token-bearing URL into examples or git remote -v output. If a credential is exposed, treat it as compromised: revoke/rotate it, remove it from logs/config, and correct secret-injection practice. Redaction after disclosure does not make the original credential safe.

git config --show-origin --get-all credential.helper
git remote get-url origin

Use a secure helper/platform credential mechanism and keep tracing/debug output from printing secrets.

7. Failure mode — broad safe.directory exception hides ownership problem

git config --show-origin --get-all safe.directory

If a runner image globally trusts *, Git's repository-ownership safety boundary has been broadly disabled. Repair filesystem ownership where feasible or replace the wildcard with the exact intended mounted workspace in protected configuration.

8. Failure mode — pipeline treats desired commit as successful deployment

A job logs “deployed $GIT_COMMIT” immediately after checkout, before validation/application. If application fails halfway, that record is false. Keep:

desired = fetched Git commit
candidate = validated artifact/config being applied
applied = last successful target state
observed = what the target currently reports

Only advance applied after the platform reports success.

9. Failure mode — GitOps write-back triggers itself indefinitely

If every reconciliation changes a file on the watched branch, the new commit can retrigger reconciliation. Diagnose ref/path ownership and event triggers. Prefer external status, dedicated generated refs, or a platform-specific trigger exclusion; do not hide the loop by dropping commits silently.

10. Failure mode — artifact built from a dirty workspace

git status --porcelain=v1
git diff --exit-code
git diff --cached --exit-code

A dirty runner can mix generated/leftover files with the intended commit. Ephemeral runners reduce this risk, but provenance should still assert cleanliness where the build contract requires tracked source to match HEAD.

11. Failure mode — bot pushes to the wrong remote/ref

git remote -v
git remote get-url --push origin
git rev-parse --symbolic-full-name HEAD
git push --dry-run --porcelain origin HEAD:refs/heads/release-config

Use an explicit destination ref in automation. A current branch name alone does not prove the intended remote target.

12. Security relevance — least privilege limits automation blast radius

A build-only job needs read access, not write access. A release-state bot should write only the dedicated ref it owns. Signing keys and deployment credentials should be separate from source-fetch credentials where practical. These are server/platform authorization decisions layered over Git ref mechanics.

13. Performance relevance — optimize transfer without removing evidence the job needs

Shallow and partial clones can reduce transfer but may shift cost/failure downstream. Measure end-to-end pipeline latency and define the provenance/version/analysis data required before selecting depth/filter policy.

14. Red-zone commands and responses

Do not use blind force push, hard reset, broad clean, tag retargeting, reflog expiry, or object pruning as generic CI repair. Those operations can overwrite work or remove evidence. Preserve the job checkout/remote state and repair fetch/ref/config/reconciliation logic first.

15. Symptom → likely layer → evidence

Symptom Layer Evidence
describe cannot name HEAD history/tag availability shallow flag, tags, fetch config
branch variable empty detached checkout HEAD OID + symbolic ref
release tag differs across clones mutable tag policy exact tag commit OIDs
bot push rejected remote concurrency fetch + graph + remote tip
GitOps says deployed but target failed state-model conflation desired/applied/observed records

16. Knowledge check

Question 1. An exact detached checkout has no branch name. Is that a build failure?

Question 2. A stale bot receives non-fast-forward. What is the first correction?

Question 3. Why is mutable release tagging dangerous?

Question 4. What should happen if a credential appears in logs?

Question 5. When may an applied-state marker advance?

17. Summary

Automation failures become manageable when you preserve exact checkout facts and classify the failing layer. Missing tags, detached HEAD, mutable releases, stale pushes, leaked credentials, and failed reconciliation have different causes and require different fixes.

Next

Run the integrated production-style checkpoint

Lesson 5 repeats the exact-commit runner flow, provenance reporting, least-privilege release-state update, stale-write protection, desired/applied reconciliation, failure behavior, and rollback runbook in one coherent lab.

Authoritative references

 git-fetch
 git-describe
 git-tag
 git-push
 gitcredentials

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.