Chapter 16Lesson 03~100 minutes

Artifacts, Workspaces, and Cross-Step State

CI state disappears at boundaries unless you persist it deliberately. Reliable pipelines distinguish short-lived workspace files, cross-step metadata, build artifacts, caches, and durable operational state.

IntermediateCI/CD automationHands-on lab

Learning objectives

By the end of this lesson

  • Distinguish step, job, and workflow state lifetimes.
  • Publish artifacts instead of relying on local runner files.
  • Treat caches as disposable optimization.
  • Attach checksums and provenance to build outputs.
  • Keep durable deployment truth outside ephemeral workspaces.

1. Steps, jobs, and workflows have different state lifetimes

BoundaryTypical behaviorState mechanism
Within one shell stepVariables and shell state may persist until process exitShortest lifetime
Across steps in one jobFilesystem may persist; shell variables usually do notNeeds explicit export/output mechanisms
Across jobsOften isolated runners/filesystemsUse artifacts/cache/platform outputs
Across workflow runsNo implicit continuityUse durable external state

Always consult your CI platform for exact semantics, but design as if process state disappears at every step boundary.

2. Workspace files are ordinary files, not guaranteed durable artifacts

build_dir=${CI_WORKSPACE:-"$PWD"}/build
mkdir -p -- "$build_dir"

printf 'binary\n' > "$build_dir/app.bin"

The workspace is convenient within a job, but later jobs may run on another machine.

3. Persist small cross-step values explicitly

artifact_id='artifact-123'

printf 'artifact_id=%s\n' "$artifact_id" \
  >> "$CI_OUTPUT_FILE"

Each CI system has its own supported output mechanism. Use it for small metadata rather than relying on environment mutation in one shell process to affect another.

4. Environment files need escaping and schema rules

cat > build.env <<EOF
ARTIFACT_ID=$artifact_id
VERSION=$version
EOF
Not a generic serializer

Simple KEY=value files become fragile when values contain newlines, shell syntax, or untrusted text. Use a structured format when values are not constrained.

5. Build artifacts should be immutable outputs of a job

tar -czf release.tar.gz -C build .

sha256sum release.tar.gz > release.tar.gz.sha256

Publish the artifact and checksum through the CI platform's artifact mechanism so downstream jobs consume the exact build result.

6. Cache is an optimization, not authoritative state

Dependency caches can speed builds, but a cache miss should not make a correct pipeline fail. A cache hit should not silently change what source revision or declared dependency versions mean.

Artifacts and caches are different

Artifacts are workflow outputs; caches are disposable performance accelerators.

7. Give artifacts stable identity

version=${VERSION:?}
commit=${COMMIT_SHA:?}

name="myapp-${version}-${commit}.tar.gz"
printf 'artifact_name=%s\n' "$name"

A name that includes version or commit identity helps trace an artifact back to source.

8. Store machine-readable metadata beside the artifact

jq -n \
  --arg version "$version" \
  --arg commit "$commit" \
  --arg created "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
  '{
    version: $version,
    commit: $commit,
    created: $created
  }' > release-metadata.json

This avoids scraping filenames later to recover important provenance.

9. Downstream jobs should validate downloaded artifacts

sha256sum -c release.tar.gz.sha256

tar -tzf release.tar.gz >/dev/null

Do not assume a transferred artifact is valid merely because the CI platform delivered a file.

10. Durable deployment state belongs outside ephemeral workspaces

If later workflow runs must know which version is deployed, store that truth in the deployment system, release registry, artifact repository, Git tag, database, or another durable source rather than a leftover runner file.

11. Cleanup temporary workspace state without deleting required artifacts

tmpdir=$(mktemp -d) || exit 1
trap 'rm -rf -- "$tmpdir"' EXIT

# Final artifact is written outside tmpdir:
cp -- "$tmpdir/output.tar.gz" ./release.tar.gz

Separate scratch paths from outputs that the CI platform still needs to upload after your script finishes.

12. Hands-on lab: build an artifact with provenance

mkdir -p "$HOME/devops-academy/bash/chapter16/lesson03"
cd "$HOME/devops-academy/bash/chapter16/lesson03"

mkdir -p build
printf 'hello release\n' > build/app.txt

version=1.2.0
commit=demo1234
artifact="myapp-${version}-${commit}.tar.gz"

tar -czf "$artifact" -C build .
sha256sum "$artifact" > "$artifact.sha256"

cat > metadata.json <<EOF
{
  "version": "$version",
  "commit": "$commit",
  "artifact": "$artifact"
}
EOF

sha256sum -c "$artifact.sha256"
tar -tzf "$artifact"
cat metadata.json

Verification checklist

13. Knowledge check

Question 1. Why don't ordinary shell variables normally survive across CI steps?

Question 2. What is the difference between an artifact and a cache?

Question 3. Where should durable deployment truth live?

Question 4. Why verify artifacts after transfer?

14. Summary

CI state has explicit lifetimes. Keep small cross-step values in supported output channels, publish build results as artifacts, treat caches as disposable, attach provenance and checksums, and store durable operational truth outside ephemeral runner filesystems.

15. Further reading

  • GNU Coreutils checksum utilities.
  • tar documentation.
  • jq manual for artifact metadata.
  • Your CI platform's documentation for workspaces, artifacts, caches, and step/job outputs.
Next lesson

Fail-Fast versus Best-Effort Pipeline Steps

Continue Chapter 16 by making Bash behavior inside CI/CD pipelines more explicit and portable.

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.