From 3e621e46383da95736c44080d5b14992efcd0f40 Mon Sep 17 00:00:00 2001 From: JK Date: Tue, 28 Jul 2026 15:57:15 +0900 Subject: [PATCH 1/4] =?UTF-8?q?confluence-mdx:=20=EB=B9=88=20QCP=20catalog?= =?UTF-8?q?=EB=A5=BC=20=EB=AA=85=EC=8B=9C=ED=95=A9=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary QM 변환의 cross-profile preflight가 빈 QCP catalog를 YAML null로 해석하지 않도록 유효한 빈 목록으로 명시합니다. - `pages.qcp.yaml`을 빈 YAML list로 초기화합니다. - QM Confluence 동기화 workflow가 sibling catalog를 안전하게 검사하도록 합니다. ## Test plan - [x] `confluence-mdx/venv/bin/python -m pytest tests/test_convert_all_folders.py -q` - [x] `git diff --check` 🤖 Generated with Codex Co-Authored-By: Atlas --- confluence-mdx/var/pages.qcp.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/confluence-mdx/var/pages.qcp.yaml b/confluence-mdx/var/pages.qcp.yaml index e69de29bb..fe51488c7 100644 --- a/confluence-mdx/var/pages.qcp.yaml +++ b/confluence-mdx/var/pages.qcp.yaml @@ -0,0 +1 @@ +[] From 215ec9088c77a6130b5d802bda2c39a864ae5c56 Mon Sep 17 00:00:00 2001 From: JK Date: Tue, 28 Jul 2026 16:12:53 +0900 Subject: [PATCH 2/4] =?UTF-8?q?confluence-mdx:=20manifest=20=EC=9D=BD?= =?UTF-8?q?=EA=B8=B0=20=EA=B6=8C=ED=95=9C=EC=9D=84=20=EB=B3=B4=EC=9E=A5?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Container가 atomic replace로 생성한 conversion manifest를 후속 Git 단계가 읽을 수 있도록 파일 권한을 명시합니다. - 임시 manifest를 교체하기 전에 권한을 `0644`로 설정합니다. - 생성된 manifest 권한을 회귀 테스트로 검증합니다. ## Test plan - [x] `confluence-mdx/venv/bin/python -m pytest tests/test_convert_all_folders.py -q` - [x] `git diff --check` 🤖 Generated with Codex Co-Authored-By: Atlas --- confluence-mdx/bin/convert_all.py | 1 + confluence-mdx/tests/test_convert_all_folders.py | 1 + 2 files changed, 2 insertions(+) diff --git a/confluence-mdx/bin/convert_all.py b/confluence-mdx/bin/convert_all.py index e536e3cd0..4168dda80 100755 --- a/confluence-mdx/bin/convert_all.py +++ b/confluence-mdx/bin/convert_all.py @@ -615,6 +615,7 @@ def finalize_manifest( sort_keys=False, ) temp_path = Path(temp_file.name) + temp_path.chmod(0o644) os.replace(temp_path, manifest_path) diff --git a/confluence-mdx/tests/test_convert_all_folders.py b/confluence-mdx/tests/test_convert_all_folders.py index 2bdebab30..41d28a5e5 100644 --- a/confluence-mdx/tests/test_convert_all_folders.py +++ b/confluence-mdx/tests/test_convert_all_folders.py @@ -244,6 +244,7 @@ def test_manifest_removes_only_previous_owned_outputs(tmp_path): assert manual.read_text() == "manual" assert current.read_text() == "new" assert yaml.safe_load(manifest_path.read_text())["outputs"] == current_outputs + assert manifest_path.stat().st_mode & 0o777 == 0o644 def test_manifest_preserves_stale_output_owned_by_another_profile(tmp_path): From 5105c1ed015de7abc8342faf8f6585270343c37f Mon Sep 17 00:00:00 2001 From: JK Date: Tue, 28 Jul 2026 16:16:17 +0900 Subject: [PATCH 3/4] =?UTF-8?q?ci(confluence-mdx):=20catalog=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=8B=9C=20=EC=A0=84=EC=B2=B4=20fetch=EB=A5=BC=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=ED=95=A9=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary main의 Space catalog가 변경되면 Docker image hierarchy cache도 전체 갱신하도록 fetch mode를 선택합니다. - main push에서 `pages.*.yaml` 변경을 감지하면 `--remote --attachments`를 사용합니다. - 일반 PR build와 catalog 변경이 없는 push는 `--recent --attachments`를 유지합니다. - workflow_dispatch에서는 사용자가 선택한 fetch arguments를 그대로 사용합니다. ## Test plan - [x] catalog 변경 commit이 remote mode로 선택되는지 확인합니다. - [x] converter-only commit이 recent mode로 선택되는지 확인합니다. - [x] `git diff --check` 🤖 Generated with Codex Co-Authored-By: Atlas --- .../workflows/build-and-push-docker-image.yml | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-push-docker-image.yml b/.github/workflows/build-and-push-docker-image.yml index cbd536f16..5c5008baa 100644 --- a/.github/workflows/build-and-push-docker-image.yml +++ b/.github/workflows/build-and-push-docker-image.yml @@ -33,6 +33,32 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine fetch arguments + id: fetch-args + env: + EVENT_NAME: ${{ github.event_name }} + BEFORE_SHA: ${{ github.event.before }} + CURRENT_SHA: ${{ github.sha }} + INPUT_FETCH_ARGUMENTS: ${{ inputs.fetch_arguments }} + run: | + set -o errexit -o nounset -o pipefail + + fetch_arguments='--recent --attachments' + if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then + fetch_arguments="${INPUT_FETCH_ARGUMENTS:---recent --attachments}" + elif [[ "$EVENT_NAME" == "push" ]]; then + if ! git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null || + ! git diff --quiet "$BEFORE_SHA" "$CURRENT_SHA" -- \ + ':(glob)confluence-mdx/var/pages.*.yaml'; then + fetch_arguments='--remote --attachments' + fi + fi + + echo "arguments=$fetch_arguments" >> "$GITHUB_OUTPUT" + echo "Fetch arguments: $fetch_arguments" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -55,7 +81,7 @@ jobs: run: | set -o errexit -o nounset -o pipefail -o xtrace docker compose --progress=plain build \ - --build-arg "FETCH_ARGS=${{ inputs.fetch_arguments || '--recent --attachments' }}" + --build-arg "FETCH_ARGS=${{ steps.fetch-args.outputs.arguments }}" - name: Push Docker image if: github.event_name != 'pull_request' @@ -73,4 +99,3 @@ jobs: DOCKERHUB_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_CREDENTIAL }} run: python3 bin/dockerhub-cleanup.py - From 2d433ff87744c1ac677d66e03440a2bd168c9c61 Mon Sep 17 00:00:00 2001 From: JK Date: Tue, 28 Jul 2026 16:29:45 +0900 Subject: [PATCH 4/4] =?UTF-8?q?ci(confluence-mdx):=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=90=9C=20Space=EB=A7=8C=20=EC=A0=84=EC=B2=B4=20fetch?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary catalog 변경이 있는 main push에서 해당 Space의 hierarchy를 정확히 갱신합니다. - 변경된 catalog 파일명에서 sync code를 도출하고 복수 Space를 순차 fetch합니다. - fetch하지 않는 Space의 tracked catalog를 Docker builder에 보존합니다. - 이전 commit을 조회할 수 없으면 모든 catalog를 full fetch 대상으로 사용합니다. ## Test plan - [x] QCP-only, catalog 미변경, missing-before, 복수 catalog fetch plan을 검증합니다. - [x] `docker buildx build --check confluence-mdx`를 통과합니다. - [x] `confluence-mdx/venv/bin/python -m pytest confluence-mdx/tests/test_convert_all_folders.py -q`를 통과합니다. 🤖 Generated with Codex --- .../workflows/build-and-push-docker-image.yml | 32 +++++++++++++++---- confluence-mdx/Dockerfile | 15 ++++++--- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-and-push-docker-image.yml b/.github/workflows/build-and-push-docker-image.yml index 5c5008baa..57fa3cc58 100644 --- a/.github/workflows/build-and-push-docker-image.yml +++ b/.github/workflows/build-and-push-docker-image.yml @@ -36,8 +36,8 @@ jobs: with: fetch-depth: 0 - - name: Determine fetch arguments - id: fetch-args + - name: Determine fetch plan + id: fetch-plan env: EVENT_NAME: ${{ github.event_name }} BEFORE_SHA: ${{ github.event.before }} @@ -47,18 +47,37 @@ jobs: set -o errexit -o nounset -o pipefail fetch_arguments='--recent --attachments' + fetch_sync_codes='qm' if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then fetch_arguments="${INPUT_FETCH_ARGUMENTS:---recent --attachments}" elif [[ "$EVENT_NAME" == "push" ]]; then - if ! git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null || - ! git diff --quiet "$BEFORE_SHA" "$CURRENT_SHA" -- \ - ':(glob)confluence-mdx/var/pages.*.yaml'; then + if git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null; then + changed_catalogs="$( + git diff --name-only "$BEFORE_SHA" "$CURRENT_SHA" -- \ + ':(glob)confluence-mdx/var/pages.*.yaml' + )" + else + changed_catalogs="$( + find confluence-mdx/var -maxdepth 1 -type f \ + -name 'pages.*.yaml' -print + )" + fi + + if [[ -n "$changed_catalogs" ]]; then fetch_arguments='--remote --attachments' + fetch_sync_codes="$( + printf '%s\n' "$changed_catalogs" | + sed -E 's#^confluence-mdx/var/pages\.([a-z0-9_-]+)\.yaml$#\1#' | + sort -u | + paste -sd ' ' - + )" fi fi echo "arguments=$fetch_arguments" >> "$GITHUB_OUTPUT" + echo "sync-codes=$fetch_sync_codes" >> "$GITHUB_OUTPUT" echo "Fetch arguments: $fetch_arguments" + echo "Fetch sync codes: $fetch_sync_codes" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -81,7 +100,8 @@ jobs: run: | set -o errexit -o nounset -o pipefail -o xtrace docker compose --progress=plain build \ - --build-arg "FETCH_ARGS=${{ steps.fetch-args.outputs.arguments }}" + --build-arg "FETCH_ARGS=${{ steps.fetch-plan.outputs.arguments }}" \ + --build-arg "FETCH_SYNC_CODES=${{ steps.fetch-plan.outputs.sync-codes }}" - name: Push Docker image if: github.event_name != 'pull_request' diff --git a/confluence-mdx/Dockerfile b/confluence-mdx/Dockerfile index 58e2da5ce..78e35d4ea 100644 --- a/confluence-mdx/Dockerfile +++ b/confluence-mdx/Dockerfile @@ -19,16 +19,23 @@ RUN pip install --no-cache-dir -r requirements.txt COPY bin/ ./bin/ RUN chmod +x bin/*.py bin/*.sh COPY etc/ ./etc/ +COPY var/pages.*.yaml ./var/ # Restore cache at its expected path (for Stage 3 attachment fallback) COPY cache/ ./cache/ # Populate var/ baseline from cache and fetch updates in one step. -# cp -a creates var/ with all cached YAML/content/attachment files, -# then fetch_cli.py downloads pages according to FETCH_ARGS. -# Default: --recent --attachments (incremental). Use --remote --attachments for full fetch. +# The tracked catalogs preserve Spaces that are not part of this fetch plan. +# cp -a adds all cached YAML/content/attachment files, +# then fetch_cli.py downloads each Space according to FETCH_ARGS. +# Defaults: qm with --recent --attachments (incremental). +# Use FETCH_SYNC_CODES="qm qcp" and --remote --attachments for multiple full fetches. ARG FETCH_ARGS="--recent --attachments" -RUN cp -a cache/. var/ && python3 bin/fetch_cli.py ${FETCH_ARGS} +ARG FETCH_SYNC_CODES="qm" +RUN cp -a cache/. var/ && \ + for sync_code in ${FETCH_SYNC_CODES}; do \ + python3 bin/fetch_cli.py --sync-code "${sync_code}" ${FETCH_ARGS}; \ + done # ── Stage 2: Final image ──────────────────────────── FROM python:3.12-slim