正在学习

Example usage

Map source paths to tests. In real projects you might parse import graphs.

mapping = { "app/app.py": ["tests/test_app.py"] }

changed_json = sys.stdin.read() changed = json.loads(changed_json)["changed"] selected = set()

for path in changed: if path in mapping: for t in mapping[path]: if Path(t).exists(): selected.add(t)

Fallback to full suite if we didn't match anything

if not selected: selected = {"tests"}

print(" ".join(sorted(selected)))

scripts/build_image.sh
```bash
#!/usr/bin/env bash
set -euo pipefail

APP_NAME="opt-pipeline-demo"
IMAGE="{IMAGE:-ghcr.io/{GITHUB_REPOSITORY}/${APP_NAME}}"
TAG="{TAG:-{GITHUB_SHA::7}}"

echo "[build] Building TAG with cache"
docker build \
    --file - \
    --tag "TAG" \
    --tag "$IMAGE:latest" \
    --cache-from "$IMAGE:latest" \
    . <<'DOCKER'
FROM python:3.11-slim
WORKDIR /app
COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
DOCKER

.github/workflows/ci.yml

name: Optimized CI
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  workflow_dispatch: {}
concurrency:
  group: opt-ci-${{ github.ref }}
  cancel-in-progress: true
env:
  PYTHON_VERSION: "3.11"
  CLAUDE_MODEL: claude-3.5-sonnet
  CLAUDE_BUDGET_USD: "0.05"
  EXPECTED_OUTPUT_TOKENS: "1800"
jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      changed: ${{ steps.diff.outputs.changed }}
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - id: diff
        run: |
          python3 scripts/changed_paths.py > changed.json
          echo "changed=GITHUB_OUTPUT
  test:
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-python@v5
        with: { python-version: ${{ env.PYTHON_VERSION }} }
      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: pip-{{ env.PYTHON_VERSION }}-${{ hashFiles('app/requirements.txt') }}
          restore-keys: pip-{{ env.PYTHON_VERSION }}-
      - name: Install deps + pytest
        run: |
          python -m pip install --upgrade pip
          pip install -r app/requirements.txt pytest
      - name: Selective test list
        id: select
        run: |
          echo '{{ needs.prepare.outputs.changed }}' '{changed:$changed}' \
          | python scripts/selective_tests.py > tests_to_run.txt
          echo "tests=GITHUB_OUTPUT
      - name: Run tests
        run: |
          echo "Running: ${{ steps.select.outputs.tests }}"
          pytest -q ${{ steps.select.outputs.tests }}
  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
  • uses: actions/checkout@v4

  • name: Login GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}

  • name: Pull latest for cache run: | docker pull ghcr.io/${{ github.repository }}/opt-pipeline-demo:latest || true

  • name: Build with cache + push env: GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_SHA: ${{ github.sha }} run: | bash scripts/build_image.sh docker push ghcr.io/${{ github.repository }}/opt-pipeline-demo:latest docker push ghcr.io/{GITHUB_SHA::7}

docs_with_claude: needs: [test] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4

- name: Prepare prompt
  run: |
    cat > .prompt.txt <<'PROMPT'
    Summarize the FastAPI service and generate a concise README section with:

    - Purpose and endpoints
    - Local run instructions
    - Health check example
    PROMPT

- name: Estimate Claude cost and gate
  id: estimate
  env:
    CLAUDE_MODEL: ${{ env.CLAUDE_MODEL }}
    CLAUDE_BUDGET_USD: ${{ env.CLAUDE_BUDGET_USD }}
    EXPECTED_OUTPUT_TOKENS: ${{ env.EXPECTED_OUTPUT_TOKENS }}
  run: |
    python3 scripts/estimate_tokens.py .prompt.txt > estimate.json || exit_code=$?
    cat estimate.json
    echo "decision=GITHUB_OUTPUT
    echo "model=GITHUB_OUTPUT
    echo "est_cost=GITHUB_OUTPUT
    exit ${exit_code:-0}

- name: Generate docs with Claude (mock)
  if: steps.estimate.outputs.decision == 'allow'
  run: |
    echo "## README (Auto) " > README.md
    echo "" >> README.md
    echo "- Model: ${{ steps.estimate.outputs.model }}" >> README.md
    echo "- Est. Cost: $${{ steps.estimate.outputs.est_cost }}" >> README.md
    echo "" >> README.md
    echo "### Service" >> README.md
    echo "FastAPI app exposes /health and runs with Uvicorn." >> README.md

- name: Fallback to cheaper model result (mock)
  if: steps.estimate.outputs.decision == 'switch'
  run: |
    echo "## README (Auto, Budget Fallback to Haiku)" > README.md
    echo "FastAPI app exposes /health and runs with Uvicorn." >> README.md

- uses: actions/upload-artifact@v4
  with:
    name: generated-readme
    path: README.md

This workflow demonstrates the combined optimizations:

  • Deterministic dependency cache keyed by requirements.txt.
  • Docker layer reuse by pulling :latest before build and tagging deterministically.
  • Change-based test selection that falls back to the full suite when necessary.
  • Token-cost estimation that gates or downgrades Claude usage automatically.
  • Concurrency cancellation so only the latest commit’s pipeline runs to completion.

Clarification Table: Optimization, Mechanism, and Effect

Optimization Mechanism Where Implemented Expected Impact
Deterministic dependency caching Cache by hash of requirements actions/cache in test job 2–10× faster installs
Docker layer caching Pull latest image and reuse layers build job pull + cache-from 30–80% less build time
Change-based execution Map changed files to tests selective_tests.py and changed_paths.py Skips unrelated tests, faster feedback
Concurrency cancel Cancel previous runs on same ref concurrency block Saves runner minutes and cost
AI spend gating Estimate tokens and compare to budget estimate_tokens.py in docs_with_claude Predictable Claude costs
Fallback strategy Switch to cheaper model or stub docs_with_claude decision branch Keeps pipeline green under budget
Artifact reuse Upload generated docs upload-artifact Clear outputs without reruns

By layering cache reuse, path-aware execution, Docker layer caching, parallelization, and Claude token gating, you turned a costly, slow pipeline into a predictable, fast, and budget-respecting system. The approach scales: add more mappings to refine selective testing, promote image caching to a remote registry cache, or expand the cost gate to choose among multiple Claude models and maximum output sizes. With these patterns, your CI remains both developer-friendly and finance-friendly while preserving the same quality bars for code and documentation.

Chapter 13 – Troubleshooting and Fine-Tuning Claude Code

练习题

In the CI workflow, what is the primary purpose of the scripts/estimate_tokens.py script when integrated with the docs_with_claude job?

A. To calculate the number of changed files in the repository
B. To estimate the cost of calling Claude API based on token counts and model pricing
C. To build and push Docker images to the registry
D. To run selective tests based on changed files

Which of the following are best practices for AI observability as mentioned in the prior knowledge points and are applicable to the CI workflow described?

A. Centralize Logs: Store all usage logs in a single repository or database for analysis and auditability.
B. Automate Alerts: Configure alerts for cost thresholds, high-latency responses, or unusual activity spikes.
C. Use FastAPI for all microservices to ensure consistency.
D. Integrate Dashboards: Use tools like Prometheus + Grafana or Elastic Stack for real-time visual insights.
E. Always use the most expensive Claude model to ensure high-quality outputs.

The build job in the CI workflow depends on the successful completion of the test job to ensure that only tested and validated code is built and pushed to the registry.

In the CI workflow, the ___ job is responsible for preparing a list of changed files to determine which tests need to be run selectively.

登录后解锁笔记、知识点解析、AI 问答

立即登录