Files
content-factory/tasks/017-git-publishing-dry-run-and-commit.md
T

6.1 KiB

Task 017: Git Publishing Dry Run And Commit

Development description: Implement the Git-backed publishing path that builds a content bundle, runs best-effort generic Markdown/MDX dry-run validation, executes Admin-defined transforms on the runner host, and commits directly to the configured production branch.

Implementation Details

  • Backend endpoints:
    • POST /api/articles/{article_id}/publishing/dry-run
    • POST /api/articles/{article_id}/publishing/create-commit
    • GET /api/articles/{article_id}/publishing/status
    • GET /api/articles/{article_id}/publishing/commits
  • Content bundle includes:
    • Markdown/MDX article file.
    • Frontmatter JSON/YAML according to site mapping.
    • Referenced approved asset files.
    • Bundle manifest.
  • Publishing behavior:
    • Materialize active versioned YAML/script config into checked-out site repo workspace.
    • Run transform script directly on runner host inside site repo workspace.
    • Run generic Markdown/MDX dry-run preview validation, not target-site build.
    • Commit directly to configured production branch.
    • Rely on target repo CI/CD after push.
    • Primary done state is PUBLISH_COMMIT_CREATED.
  • Failure behavior:
    • Non-fast-forward push or conflict fails publish step.
    • No automatic rebase.
    • If delayed deployment verification fails, alert Admin/Editor and leave production commit in place.

Public Interface

  • Editor runs publishing dry run after final approval.
  • Editor creates publish commit after dry run passes.
  • UI shows commit SHA, branch, repository, and opportunistic deployment status.

Acceptance Criteria

  • TDD pre-requirement: before implementation, write one failing integration test against a temporary local Git repository proving a final-approved article can create a commit; add dry-run and failure tests one behavior at a time and record evidence in Result.
  • Publishing cannot dry-run before final approval.
  • Dry run fails if generic Markdown/MDX content-shape validation fails.
  • Publish commit cannot run until dry run passes.
  • Content bundle uses site-configured path templates and frontmatter mapping.
  • Active YAML/script config version is included in publish logs/manifest.
  • Publish writes commit to configured production branch in a test repository.
  • Non-fast-forward push or conflict fails without automatic rebase.
  • Publish commit record stores repository URL, branch, commit SHA, bundle manifest, and status.
  • UI clearly labels validation as best-effort content-shape validation only.

Verification

  • Run publishing integration tests using a local bare Git repository.
  • Run generic preview validation tests.
  • Run Docker Compose smoke flow from final approval to publish commit against a demo site repo.

Result

  • Status: Done (TDD RED -> GREEN completed).

  • TDD progression:

    1. Restored and expanded apps/backend/tests/integration/test_publishing_git_flow_public_api.py from RED baseline.
    2. Added incremental behavior coverage:
      • dry-run blocked before final approval,
      • dry-run content-shape validation failure path,
      • create-commit blocked without successful dry-run,
      • successful publish commit into local bare repo main,
      • non-fast-forward conflict fail without auto rebase,
      • publishing status/commits metadata assertions.
    3. Implemented publishing backend flow:
      • endpoints:
        • POST /api/articles/{article_id}/publishing/dry-run
        • POST /api/articles/{article_id}/publishing/create-commit
        • GET /api/articles/{article_id}/publishing/status
        • GET /api/articles/{article_id}/publishing/commits
      • gates:
        • dry-run only after final approval,
        • create-commit only after successful dry-run.
      • content-shape checks:
        • non-empty markdown,
        • at least one markdown heading,
        • balanced fenced code blocks.
      • bundle/manifest:
        • site-configured content_path_template and asset_path_template,
        • frontmatter mapping usage,
        • active script config version id/hash metadata,
        • repository/branch/base head/commit sha metadata.
      • git publish:
        • direct commit+push to configured production branch,
        • non-fast-forward detection and fail path without rebase.
    4. Added frontend detail-model mapping for validation label and surfaced label in article detail UI.
    5. Regenerated shared OpenAPI contracts.
  • Green evidence:

    • Integration suite passes with all required publishing behaviors in one flow-oriented file: apps/backend/tests/integration/test_publishing_git_flow_public_api.py (6 tests, all green).
    • Commit object is present in bare repo (git cat-file -e <sha>^{commit} in test).
    • Manifest metadata includes config_version and git blocks and best-effort validation label.
  • Refactor notes:

    • Added dedicated PublishCommitsRepository with list/latest helpers and status filtering.
    • Added articles.update_publishing_status(...) for explicit workflow/publishing-state sync.
    • Kept implementation scoped to task 017 API/domain/repository/frontend model changes; no unrelated workflow rewrites.
  • Verification output:

    • PYTHONPATH=/private/tmp/pupline-backend-deps python3 -m unittest apps/backend/tests/integration/test_publishing_git_flow_public_api.py -> Ran 6 tests ... OK
    • PYTHONPATH=/private/tmp/pupline-backend-deps python3 -m unittest apps/backend/tests/integration/test_final_approval_gate_public_api.py -> Ran 7 tests ... OK
    • PYTHONPATH=/private/tmp/pupline-backend-deps python3 -m unittest apps/backend/tests/integration/test_draft_assembly_public_api.py -> Ran 5 tests ... OK
    • PYTHONPATH=/private/tmp/pupline-backend-deps python3 -m unittest apps/backend/tests/integration/test_assets_media_library_public_api.py -> Ran 7 tests ... OK
    • PYTHONPATH=/private/tmp/pupline-backend-deps python3 scripts/generate_openapi_contracts.py -> wrote:
      • packages/shared/openapi.json
      • packages/shared/src/api-types.ts
    • node apps/frontend/tests/article_detail.model.test.mjs -> OK (exit 0)
    • pnpm --dir apps/frontend typecheck -> tsc --noEmit completed successfully (exit 0)