Chapter 20Lesson 05~120 minutes

Capstone: Build a Production-Grade Release Orchestrator

The capstone combines the entire course into one coherent workflow: explicit inputs, safe temporary state, dependency checks, artifact verification, dry-run, observability, durable release metadata, and predictable failure behavior.

AdvancedProduction design & capstoneHands-on lab

Learning objectives

By the end of this lesson

  • Build a complete release lifecycle.
  • Combine validation, preflight, staging, and verification.
  • Separate stdout results from diagnostics.
  • Record current and previous releases.
  • Identify remaining hardening steps for production.

1. Capstone: a production-grade release orchestrator

The final project combines the course: argument validation, dependency preflight, private temporary state, artifact staging, checksums, dry-run, structured logs, explicit statuses, durable current/previous release state, and cleanup.

Release orchestrator lifecycle
flowchart TD
  A["parse and validate"] --> P["preflight"]
  P --> B["build artifact"]
  B --> V["verify"]
  V --> D{"dry run?"}
  D -->|"yes"| R["report plan"]
  D -->|"no"| U["publish release"]
  U --> H["verify health/state"]
  H --> C["commit release state"]

2. Define inputs and outputs first

Inputs are environment, version, source directory, and optional dry-run. Output is a clean release ID on stdout; logs and diagnostics go to stderr.

3. Use explicit strictness and error handling

#!/usr/bin/env bash
set -u
set -o pipefail

The capstone uses explicit error branches instead of treating set -e as a complete error model.

4. Validate tools and environment before mutation

require_command() {
  command -v "$1" >/dev/null 2>&1 || {
    printf 'required command missing: %s\n' "$1" >&2
    return 69
  }
}

require_command tar || exit $?

5. Build in a private temporary workspace

workdir=$(mktemp -d) || exit 70

cleanup() {
  local status=$?
  rm -rf -- "$workdir"
  exit "$status"
}

trap cleanup EXIT TERM INT

6. Record previous and current releases only after success

previous=""
[[ -f $STATE_DIR/current ]] &&
  previous=$(cat "$STATE_DIR/current")

printf '%s\n' "$previous" > "$STATE_DIR/previous"
printf '%s\n' "$VERSION" > "$STATE_DIR/current"

7. Hands-on lab: complete release orchestrator

mkdir -p "$HOME/devops-academy/bash/chapter20/lesson05/source"
cd "$HOME/devops-academy/bash/chapter20/lesson05"

printf 'hello release\n' > source/app.txt

cat > release.sh <<'EOF'
#!/usr/bin/env bash
set -u
set -o pipefail

ENVIRONMENT=${1:-}
VERSION=${2:-}
SOURCE_DIR=${3:-}
DRY_RUN=${DRY_RUN:-false}

[[ -n $ENVIRONMENT && -n $VERSION && -n $SOURCE_DIR ]] || {
  printf 'usage: %s ENV VERSION SOURCE_DIR\n' "$0" >&2
  exit 64
}

case $ENVIRONMENT in
  dev|staging|prod) ;;
  *) printf 'invalid environment=%q\n' "$ENVIRONMENT" >&2; exit 65 ;;
esac

RUN_ID="release-$(date +%s)-$$"
STATE_DIR=./state
mkdir -p -- "$STATE_DIR"

log() {
  local level=$1
  shift
  printf 'ts=%s run_id=%s level=%s msg=%q\n'                 "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"                 "$RUN_ID" "$level" "$*" >&2
}

command -v tar >/dev/null 2>&1 || {
  log ERROR "tar is required"
  exit 69
}

if command -v sha256sum >/dev/null 2>&1; then
  checksum_tool=sha256sum
elif command -v shasum >/dev/null 2>&1; then
  checksum_tool=shasum
else
  log ERROR "no SHA-256 tool available"
  exit 69
fi

workdir=$(mktemp -d) || exit 70

cleanup() {
  local status=$?
  rm -rf -- "$workdir"
  exit "$status"
}
trap cleanup EXIT TERM INT

artifact="$workdir/release-${VERSION}.tar.gz"

log INFO "build start env=$ENVIRONMENT version=$VERSION"

tar -czf "$artifact" -C "$SOURCE_DIR" . || {
  status=$?
  log ERROR "artifact build failed status=$status"
  exit "$status"
}

if [[ $checksum_tool == sha256sum ]]; then
  sha256sum "$artifact" > "$artifact.sha256"
else
  shasum -a 256 "$artifact" > "$artifact.sha256"
fi

if [[ $DRY_RUN == true ]]; then
  log INFO "dry_run=true artifact_verified=true"
  printf '%s\n' "$RUN_ID"
  exit 0
fi

release_dir="releases/$VERSION"
mkdir -p -- "$release_dir"
cp -- "$artifact" "$release_dir/"
cp -- "$artifact.sha256" "$release_dir/"

previous=""
[[ -f $STATE_DIR/current ]] &&
  previous=$(cat "$STATE_DIR/current")

printf '%s\n' "$previous" > "$STATE_DIR/previous"
printf '%s\n' "$VERSION" > "$STATE_DIR/current"

cat > "$release_dir/metadata.json" <<META
{
  "run_id": "$RUN_ID",
  "environment": "$ENVIRONMENT",
  "version": "$VERSION",
  "previous": "$previous"
}
META

log INFO "release complete version=$VERSION previous=$previous"
printf '%s\n' "$RUN_ID"
EOF

chmod u+x release.sh

DRY_RUN=true ./release.sh staging 1.0.0 ./source
./release.sh staging 1.0.0 ./source
./release.sh staging 1.1.0 ./source

printf '%s\n' '--- state ---'
cat state/current
cat state/previous

printf '%s\n' '--- release files ---'
find releases -maxdepth 2 -type f -print

Verification checklist

8. Production hardening exercises

Extend the capstone with Bats tests, ShellCheck/shfmt gates, locking, signature verification, real remote upload, health checks, retry classification, rollback commands, checkpoint recovery, and CI artifact publication.

9. Knowledge check

Question 1. What belongs on stdout in this capstone?

Question 2. When should current-version state be updated?

Question 3. What should dry-run avoid?

Question 4. Why store the previous version?

10. Course complete

You have now covered Bash from interactive foundations through production reliability, security, CI/CD, testing, remote automation, performance, architecture, and release orchestration.

Final practice

Take one real automation script and review it for inputs, quoting, idempotency, failures, logging, tests, security, portability, observability, rollback, and language fit.

11. Summary

Production-grade Bash is not about clever syntax. It is about explicit contracts, safe boundaries, repeatable tests, observable operations, recoverable failure, and knowing when another language is the better tool.

12. Further reading

  • GNU Bash Reference Manual.
  • ShellCheck and shfmt documentation.
  • Bats-core documentation.
  • Google Site Reliability Engineering and release-engineering guidance.
  • OWASP command-injection, logging, and secrets guidance.
Next

Bash for DevOps complete

All 20 chapters and 100 Bash lessons are now complete.

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.