# 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 - [x] 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`. - [x] Publishing cannot dry-run before final approval. - [x] Dry run fails if generic Markdown/MDX content-shape validation fails. - [x] Publish commit cannot run until dry run passes. - [x] Content bundle uses site-configured path templates and frontmatter mapping. - [x] Active YAML/script config version is included in publish logs/manifest. - [x] Publish writes commit to configured production branch in a test repository. - [x] Non-fast-forward push or conflict fails without automatic rebase. - [x] Publish commit record stores repository URL, branch, commit SHA, bundle manifest, and status. - [x] 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 ^{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)