From f89b28124de4cf770e5f74ca8555b25032850305 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 08:49:50 -0700 Subject: [PATCH 01/25] add test for xcel files --- tests/cda/blobs/blob_CDA_test.py | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/tests/cda/blobs/blob_CDA_test.py b/tests/cda/blobs/blob_CDA_test.py index 7535921c..54b3f3bf 100644 --- a/tests/cda/blobs/blob_CDA_test.py +++ b/tests/cda/blobs/blob_CDA_test.py @@ -1,13 +1,16 @@ # tests/test_blob.py from __future__ import annotations +import base64 +import mimetypes from datetime import datetime, timezone +from pathlib import Path from typing import Optional import pandas as pd import pytest -import cwms +import cwms.catalog.blobs as blobs TEST_OFFICE = "MVP" TEST_BLOB_ID = "PYTEST_BLOB_ALPHA" @@ -25,12 +28,12 @@ def ensure_clean_slate(): """Delete the test blob (if it exists) before/after running this module.""" try: - cwms.delete_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) + blobs.delete_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) except Exception: pass yield try: - cwms.delete_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) + blobs.delete_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) except Exception: pass @@ -44,7 +47,7 @@ def _find_blob_row(office: str, blob_id: str) -> Optional[pd.Series]: """ Helper: return the row for blob_id from cwms.get_blobs(...).df if present. """ - res = cwms.get_blobs(office_id=office, blob_id_like=blob_id) + res = blobs.get_blobs(office_id=office, blob_id_like=blob_id) df = res if isinstance(res, pd.DataFrame) else getattr(res, "df", None) if df is None or df.empty: return None @@ -55,6 +58,28 @@ def _find_blob_row(office: str, blob_id: str) -> Optional[pd.Series]: return match.iloc[0] if not match.empty else None +def test_store_blob_xcel(): + excel_file_path = Path(__file__).parent.parent / "resources" / "blob_test.xlsx" + with open(excel_file_path, "rb") as f: + file_data = f.read() + mime_type, _ = mimetypes.guess_type(excel_file_path) + xcel_blob_id = "TEST_BLOB_XCEL" + payload = { + "office-id": TEST_OFFICE, + "id": xcel_blob_id, + "description": "testing excel file", + "media-type-id": mime_type, + "value": base64.b64encode(file_data).decode("utf-8"), + } + blobs.store_blobs(data=payload) + try: + row = _find_blob_row(TEST_OFFICE, xcel_blob_id) + assert row is not None, "Stored blob not found in listing" + finally: + # Cleanup second specified level + blobs.delete_blob(blob_id=xcel_blob_id, office_id=TEST_OFFICE) + + def test_store_blob(): # Build request JSON for store_blobs payload = { @@ -64,7 +89,7 @@ def test_store_blob(): "media-type-id": TEST_MEDIA_TYPE, "value": TEST_TEXT, } - cwms.store_blobs(payload, fail_if_exists=True) + blobs.store_blobs(payload, fail_if_exists=True) # Verify via listing metadata row = _find_blob_row(TEST_OFFICE, TEST_BLOB_ID) @@ -76,14 +101,14 @@ def test_store_blob(): assert TEST_DESC in str(row["description"]) # Verify content by downloading - content = cwms.get_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) + content = blobs.get_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) assert isinstance(content, str) and content, "Empty blob content" assert TEST_TEXT in content def test_get_blob(): # Do a simple read of the blob created in test_store_blob - content = cwms.get_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) + content = blobs.get_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_ID) assert TEST_TEXT in content assert len(content) >= len(TEST_TEXT) @@ -97,7 +122,7 @@ def test_update_blob(): "media-type-id": TEST_MEDIA_TYPE, "value": TEST_TEXT_UPDATED, } - cwms.update_blob(update, fail_if_not_exists=True) + blobs.update_blob(update, fail_if_not_exists=True) # Confirm updated metadata row = _find_blob_row(TEST_OFFICE, TEST_BLOB_UPDATED_ID) @@ -106,5 +131,5 @@ def test_update_blob(): assert TEST_DESC_UPDATED in str(row["description"]) # Verify new content - content = cwms.get_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_UPDATED_ID) + content = blobs.get_blob(office_id=TEST_OFFICE, blob_id=TEST_BLOB_UPDATED_ID) assert TEST_TEXT_UPDATED in content From 1b2844d822c41bea54e05f6051925917f895a6cc Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 08:51:56 -0700 Subject: [PATCH 02/25] add excel file --- tests/cda/resources/blob_test.xlsx | Bin 0 -> 8869 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/cda/resources/blob_test.xlsx diff --git a/tests/cda/resources/blob_test.xlsx b/tests/cda/resources/blob_test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..53c979653e37a3114190346c237b3d5bb7cfcb8f GIT binary patch literal 8869 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 Nd=N^GOa1fL{{ztMWzGNq literal 0 HcmV?d00001 From 56ed86be206c651745634439f0d41798e67f0b75 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 08:55:30 -0700 Subject: [PATCH 03/25] update --- tests/cda/blobs/blob_CDA_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cda/blobs/blob_CDA_test.py b/tests/cda/blobs/blob_CDA_test.py index 54b3f3bf..62f8fa6f 100644 --- a/tests/cda/blobs/blob_CDA_test.py +++ b/tests/cda/blobs/blob_CDA_test.py @@ -58,26 +58,26 @@ def _find_blob_row(office: str, blob_id: str) -> Optional[pd.Series]: return match.iloc[0] if not match.empty else None -def test_store_blob_xcel(): +def test_store_blob_excel(): excel_file_path = Path(__file__).parent.parent / "resources" / "blob_test.xlsx" with open(excel_file_path, "rb") as f: file_data = f.read() mime_type, _ = mimetypes.guess_type(excel_file_path) - xcel_blob_id = "TEST_BLOB_XCEL" + excel_blob_id = "TEST_BLOB_EXCEL" payload = { "office-id": TEST_OFFICE, - "id": xcel_blob_id, + "id": excel_blob_id, "description": "testing excel file", "media-type-id": mime_type, "value": base64.b64encode(file_data).decode("utf-8"), } blobs.store_blobs(data=payload) try: - row = _find_blob_row(TEST_OFFICE, xcel_blob_id) + row = _find_blob_row(TEST_OFFICE, excel_blob_id) assert row is not None, "Stored blob not found in listing" finally: - # Cleanup second specified level - blobs.delete_blob(blob_id=xcel_blob_id, office_id=TEST_OFFICE) + # Cleanup excel + blobs.delete_blob(blob_id=excel_blob_id, office_id=TEST_OFFICE) def test_store_blob(): From 8248242bf6c249f82cf2d78e98167f056a2e80e1 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 12:02:45 -0500 Subject: [PATCH 04/25] Update Keycloak image version to 25.0.1 --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 47e1ec10..1ff26b95 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -89,7 +89,7 @@ services: - "traefik.http.routers.data-api.entryPoints=web" auth: - image: quay.io/keycloak/keycloak:19.0.1 + image: quay.io/keycloak/keycloak:25.0.1 command: ["start-dev", "--features-disabled=admin2","--import-realm"] healthcheck: test: "/usr/bin/curl -If localhost:${APP_PORT:-8082}/auth/health/ready || exit 1" @@ -142,4 +142,4 @@ services: labels: - "traefik.enable=true" - "traefik.http.routers.traefik.rule=PathPrefix(`/traefik`)" - - "traefik.http.routers.traefik.service=api@internal" \ No newline at end of file + - "traefik.http.routers.traefik.service=api@internal" From e09969421e954fe7f1eafd3b60cef4681696092f Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 13:11:40 -0500 Subject: [PATCH 05/25] Capture auth service logs in CI workflow Added a step to capture logs from the auth service. --- .github/workflows/CDA-testing.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index eb17ad52..35c1914a 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -16,7 +16,10 @@ jobs: - name: set up backend run: docker compose up --build -d - + + - name: Capture auth servivce logs + run: docker-compose logs auth || true + - name: Set Up Python uses: actions/setup-python@v6 with: From a3a4b036946fe7dd2b3e250fd946a761d1fd72aa Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 13:27:45 -0500 Subject: [PATCH 06/25] Change Keycloak image version and log level Downgrade Keycloak image version from 25.0.1 to 19.0.1 and set log level to DEBUG. --- docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1ff26b95..2202d98b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -89,7 +89,7 @@ services: - "traefik.http.routers.data-api.entryPoints=web" auth: - image: quay.io/keycloak/keycloak:25.0.1 + image: quay.io/keycloak/keycloak:19.0.1 command: ["start-dev", "--features-disabled=admin2","--import-realm"] healthcheck: test: "/usr/bin/curl -If localhost:${APP_PORT:-8082}/auth/health/ready || exit 1" @@ -108,6 +108,7 @@ services: - KC_PROXY=none - KC_HTTP_ENABLED=true - KC_HTTP_RELATIVE_PATH=/auth + - KC_LOG_LEVEL=DEBUG volumes: - ./compose_files/keycloak/realm.json:/opt/keycloak/data/import/realm.json:ro labels: From 0ed953f7c5c66a5fe4e8fe490f50d94cda348fc2 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 13:40:58 -0500 Subject: [PATCH 07/25] Capture auth service logs only on failure --- .github/workflows/CDA-testing.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 35c1914a..a61513a6 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -17,8 +17,9 @@ jobs: - name: set up backend run: docker compose up --build -d - - name: Capture auth servivce logs - run: docker-compose logs auth || true + - name: Capture auth servivce logs on failure + if: failure() + run: docker-compose logs auth - name: Set Up Python uses: actions/setup-python@v6 From 62381332705871f1a0a1e6366412ffff1a3efda0 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 13:43:25 -0500 Subject: [PATCH 08/25] Update CDA-testing.yml --- .github/workflows/CDA-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index a61513a6..e5c89b48 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -19,7 +19,7 @@ jobs: - name: Capture auth servivce logs on failure if: failure() - run: docker-compose logs auth + run: docker compose logs auth - name: Set Up Python uses: actions/setup-python@v6 From 399b65bafb8e9eb48e2fc5f03bf241b428cc3ed5 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 13:59:24 -0500 Subject: [PATCH 09/25] Add KC_HOSTNAME environment variable to Keycloak --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 2202d98b..b994b449 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -108,6 +108,7 @@ services: - KC_PROXY=none - KC_HTTP_ENABLED=true - KC_HTTP_RELATIVE_PATH=/auth + - KC_HOSTNAME=localhost - KC_LOG_LEVEL=DEBUG volumes: - ./compose_files/keycloak/realm.json:/opt/keycloak/data/import/realm.json:ro From 263486706beeb5a6b5fe640c7fb901f82f7dd9fd Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 14:09:40 -0500 Subject: [PATCH 10/25] Refactor Keycloak command and healthcheck configuration --- docker-compose.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b994b449..7c7d0bb8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -90,9 +90,12 @@ services: auth: image: quay.io/keycloak/keycloak:19.0.1 - command: ["start-dev", "--features-disabled=admin2","--import-realm"] + command: + - "/opt/keycloak/bin/kc.sh" + - "start-dev" + - "--import-realm" healthcheck: - test: "/usr/bin/curl -If localhost:${APP_PORT:-8082}/auth/health/ready || exit 1" + test: ["CMD-SHELL", "/usr/bin/curl -If localhost:${APP_PORT:-8081}/auth/health/ready || exit 1"] interval: 5s timeout: 1s retries: 100 @@ -110,6 +113,7 @@ services: - KC_HTTP_RELATIVE_PATH=/auth - KC_HOSTNAME=localhost - KC_LOG_LEVEL=DEBUG + - KC_DB=dev-file volumes: - ./compose_files/keycloak/realm.json:/opt/keycloak/data/import/realm.json:ro labels: From 755b2dd92a92008321eb147666265ff7dceb790f Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 14:22:53 -0500 Subject: [PATCH 11/25] Change Keycloak healthcheck port to 8082 Updated healthcheck port for Keycloak service. --- docker-compose.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7c7d0bb8..8e2775c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -91,11 +91,10 @@ services: auth: image: quay.io/keycloak/keycloak:19.0.1 command: - - "/opt/keycloak/bin/kc.sh" - "start-dev" - "--import-realm" healthcheck: - test: ["CMD-SHELL", "/usr/bin/curl -If localhost:${APP_PORT:-8081}/auth/health/ready || exit 1"] + test: ["CMD-SHELL", "/usr/bin/curl -If localhost:${APP_PORT:-8082}/auth/health/ready || exit 1"] interval: 5s timeout: 1s retries: 100 From a3728d289d5d6c14a990d33a255cdb2ae19e20b9 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 15:28:35 -0500 Subject: [PATCH 12/25] Refactor CI workflow for Docker caching and tests Updated CI workflow to improve Docker image caching and testing steps. --- .github/workflows/CDA-testing.yml | 62 +++++++++++++++++++------------ 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index e5c89b48..2b4fff92 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [main, githubAction-testing] + branches: [main] pull_request: branches: [main] workflow_dispatch: @@ -13,45 +13,59 @@ jobs: steps: - uses: actions/checkout@v5 + + # Docker image caching steps + - name: Cache Docker Images + id: cache-docker-images + uses: actions/cache@v4 + with: + path: /tmp/.docker-images + key: ${{ runner.os }}-docker-${{ hashFiles('docker-compose.yml') }} + + - name: Load images from cache + if: steps.cache-docker-images.outputs.cache-hit == 'true' + run: docker load --input /tmp/.docker-images/images.tar - name: set up backend - run: docker compose up --build -d - + run: | + docker compose pull + docker compose up --build -d + - name: Capture auth servivce logs on failure if: failure() run: docker compose logs auth + - name: Archive and save images to cache + if: steps.cache-docker-images.outputs.cache-hit != 'true' + run: | + mkdir -p /tmp/.docker-images + docker image save $(docker-compose config --services | xargs docker-compose images -q) --output /tmp/.docker-images/images.tar + + + - name: Set Up Python uses: actions/setup-python@v6 with: python-version: '3.9.X' - # Unlike the code-check workflow, this job requires the dev dependencies to be - # installed to make sure we have the necessary, tools, stub files, etc. - - name: Install Poetry + # Use actions-poetry to handle installation + - name: Install Poetry and Dependencies uses: abatilo/actions-poetry@v4 - - - name: Add Poetry to PATH (for act) - if: env.ACT - run: echo "/root/.local/bin" >> $GITHUB_PATH - - - name: Cache Virtual Environment + with: + python-version: '3.9.X' + # Poetry will handle installation and caching + + - name: Cache Python dependencies uses: actions/cache@v4 with: - path: ./.venv - key: venv-${{ hashFiles('poetry.lock') }} - - - name: Install Dependencies - run: poetry install + path: ~/.cache/pypoetry/artifacts + key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} # Run pytest and generate coverage report data. - - name: Run Tests - run: poetry run pytest tests/cda/ --doctest-modules --cov --cov-report=xml:out/coverage.xml - - # Run mypy with strict mode enabled. Only the main source code is type checked (test - # and example code is excluded). - - name: Check Types - run: poetry run mypy --strict cwms/ + - name: Run Tests and Check Types + run: | + poetry run pytest tests/cda/ --doctest-modules --cov --cov-report=xml:out/coverage.xml + poetry run mypy --strict cwms/ - name: Generate Coverage Report uses: irongut/CodeCoverageSummary@v1.3.0 From e55f329c49b1f941bbbf5e13829d2a254e26776d Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Fri, 17 Oct 2025 15:35:31 -0500 Subject: [PATCH 13/25] Update log capture command for auth service Modify log capture command to include timestamps and limit output. --- .github/workflows/CDA-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 2b4fff92..84c8f17b 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -33,7 +33,7 @@ jobs: - name: Capture auth servivce logs on failure if: failure() - run: docker compose logs auth + run: docker compose logs -t --tail 100 auth - name: Archive and save images to cache if: steps.cache-docker-images.outputs.cache-hit != 'true' From 3120f9a2a029c436f477ee86fe5a923a626271b1 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Mon, 20 Oct 2025 14:41:25 -0500 Subject: [PATCH 14/25] Refactor CDA testing workflow by removing log capture Removed log capture step for auth service on failure. --- .github/workflows/CDA-testing.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 84c8f17b..67895a9b 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -30,19 +30,13 @@ jobs: run: | docker compose pull docker compose up --build -d - - - name: Capture auth servivce logs on failure - if: failure() - run: docker compose logs -t --tail 100 auth - name: Archive and save images to cache if: steps.cache-docker-images.outputs.cache-hit != 'true' run: | mkdir -p /tmp/.docker-images - docker image save $(docker-compose config --services | xargs docker-compose images -q) --output /tmp/.docker-images/images.tar - - - + docker image save $(docker compose config --services | xargs docker compose images -q) --output /tmp/.docker-images/images.tar + - name: Set Up Python uses: actions/setup-python@v6 with: From ac191f8c23b5f58945e1be158fcbf2581462c18a Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Mon, 20 Oct 2025 15:33:21 -0500 Subject: [PATCH 15/25] update caching --- .github/workflows/CDA-testing.yml | 33 ++++++++++++--------------- docker-compose.yml | 38 ++++++++++++------------------- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 67895a9b..2a841da6 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -13,29 +13,27 @@ jobs: steps: - uses: actions/checkout@v5 - - # Docker image caching steps - - name: Cache Docker Images - id: cache-docker-images - uses: actions/cache@v4 - with: - path: /tmp/.docker-images - key: ${{ runner.os }}-docker-${{ hashFiles('docker-compose.yml') }} - - name: Load images from cache - if: steps.cache-docker-images.outputs.cache-hit == 'true' - run: docker load --input /tmp/.docker-images/images.tar + # Docker Buildx caching steps + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and cache Docker images with Buildx + uses: docker/build-push-action@v4 + with: + context: . + push: false + load: true + cache-from: type=gha + cache-to: type=gha,mode=max - name: set up backend run: | docker compose pull docker compose up --build -d - - - name: Archive and save images to cache - if: steps.cache-docker-images.outputs.cache-hit != 'true' - run: | - mkdir -p /tmp/.docker-images - docker image save $(docker compose config --services | xargs docker compose images -q) --output /tmp/.docker-images/images.tar - name: Set Up Python uses: actions/setup-python@v6 @@ -48,7 +46,6 @@ jobs: with: python-version: '3.9.X' # Poetry will handle installation and caching - - name: Cache Python dependencies uses: actions/cache@v4 with: diff --git a/docker-compose.yml b/docker-compose.yml index 8e2775c1..acba714a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,10 +10,9 @@ services: - CWMS_PASSWORD=simplecwmspasswD1 - OFFICE_ID=HQ - OFFICE_EROC=s0 - ports: - - "1526:1521" + ports: ["1526:1521"] healthcheck: - test: ["CMD","tnsping", "FREEPDB1"] + test: ["CMD", "tnsping", "FREEPDB1"] interval: 30s timeout: 50s retries: 50 @@ -32,9 +31,9 @@ services: - INSTALLONCE=1 - QUIET=1 command: > - sh -xc "sqlplus CWMS_20/$$CWMS_PASSWORD@$$DB_HOST_PORT$$DB_NAME @/setup_sql/users $$OFFICE_EROC" - volumes: - - ./compose_files/sql:/setup_sql:ro + sh -xc "sqlplus CWMS_20/$$CWMS_PASSWORD@$$DB_HOST_PORT$$DB_NAME @/setup_sql/users + $$OFFICE_EROC" + volumes: [./compose_files/sql:/setup_sql:ro] depends_on: db: condition: service_healthy @@ -43,7 +42,6 @@ services: traefik: condition: service_healthy - data-api: depends_on: auth: @@ -75,10 +73,9 @@ services: - cwms.dataapi.access.openid.altAuthUrl=http://localhost:${APP_PORT:-8082} - cwms.dataapi.access.openid.useAltWellKnown=true - cwms.dataapi.access.openid.issuer=http://localhost:${APP_PORT:-8082}/auth/realms/cwms - expose: - - 7000 + expose: [7000] healthcheck: - test: ["CMD","/usr/bin/curl", "-I","localhost:7000/cwms-data/offices/HEC"] + test: ["CMD", "/usr/bin/curl", "-I", "localhost:7000/cwms-data/offices/HEC"] interval: 5s timeout: 1s retries: 100 @@ -90,11 +87,12 @@ services: auth: image: quay.io/keycloak/keycloak:19.0.1 - command: - - "start-dev" - - "--import-realm" + command: ["start-dev", "--import-realm"] healthcheck: - test: ["CMD-SHELL", "/usr/bin/curl -If localhost:${APP_PORT:-8082}/auth/health/ready || exit 1"] + test: + - "CMD-SHELL" + - "/usr/bin/curl -If localhost:${APP_PORT:-8082}/auth/health/ready || exit\ + \ 1" interval: 5s timeout: 1s retries: 100 @@ -111,7 +109,6 @@ services: - KC_HTTP_ENABLED=true - KC_HTTP_RELATIVE_PATH=/auth - KC_HOSTNAME=localhost - - KC_LOG_LEVEL=DEBUG - KC_DB=dev-file volumes: - ./compose_files/keycloak/realm.json:/opt/keycloak/data/import/realm.json:ro @@ -124,17 +121,12 @@ services: traefik: condition: service_healthy - - # Proxy for HTTPS for OpenID traefik: image: traefik:v3.3.3 - ports: - - "${APP_PORT:-8082}:80" - expose: - - "8080" - volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" + ports: ["${APP_PORT:-8082}:80"] + expose: ["8080"] + volumes: ["/var/run/docker.sock:/var/run/docker.sock:ro"] healthcheck: test: traefik healthcheck --ping command: From f0a8514e63cf6d6e3912fe6b36d9a84537b34e09 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 07:23:01 -0500 Subject: [PATCH 16/25] add caching --- .github/workflow_utils/buildx_services.py | 85 +++++++++++++++++++++++ .github/workflows/CDA-testing.yml | 36 ++++++---- 2 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 .github/workflow_utils/buildx_services.py diff --git a/.github/workflow_utils/buildx_services.py b/.github/workflow_utils/buildx_services.py new file mode 100644 index 00000000..fc144d0a --- /dev/null +++ b/.github/workflow_utils/buildx_services.py @@ -0,0 +1,85 @@ +import subprocess +import sys + +import yaml + + +def get_services_to_build(compose_file="docker-compose.yml"): + """ + Parses a docker-compose file and returns a list of services to build. + """ + try: + with open(compose_file, "r") as f: + doc = yaml.safe_load(f) + except FileNotFoundError: + print(f"Error: {compose_file} not found.", file=sys.stderr) + return [] + + services_to_build = [] + services = doc.get("services", {}) + for svc, details in services.items(): + if "build" in details: + if isinstance(details["build"], str): + context = details["build"] + dockerfile = "Dockerfile" + elif isinstance(details["build"], dict): + context = details["build"].get("context", ".") + dockerfile = details["build"].get("dockerfile", "Dockerfile") + else: + context = None + dockerfile = None + + if context: + services_to_build.append( + { + "name": svc, + "context": context, + "dockerfile": dockerfile, + } + ) + return services_to_build + + +def main(): + """ + Main function to orchestrate the build process with buildx caching. + """ + services = get_services_to_build() + if not services: + print("No services with build context found. Exiting.") + return + + print("Detected services and building per-service with buildx cache.") + for svc_info in services: + svc = svc_info["name"] + context = svc_info["context"] + dockerfile = svc_info["dockerfile"] + + print(f"Processing service: {svc}") + print(f"Building {svc} from context={context} dockerfile={dockerfile}") + + try: + subprocess.run( + [ + "docker", + "buildx", + "build", + "--file", + f"{context}/{dockerfile}", + "--tag", + f"local-build/{svc}:latest", + "--load", + "--cache-from=type=gha", + "--cache-to=type=gha,mode=max", + context, + ], + check=True, + ) + print(f"Successfully built {svc}\n") + except subprocess.CalledProcessError as e: + print(f"Error building service {svc}: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 2a841da6..bb6c1a9b 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -21,20 +21,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - name: Build and cache Docker images with Buildx - uses: docker/build-push-action@v4 - with: - context: . - push: false - load: true - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: set up backend - run: | - docker compose pull - docker compose up --build -d - - name: Set Up Python uses: actions/setup-python@v6 with: @@ -52,6 +38,28 @@ jobs: path: ~/.cache/pypoetry/artifacts key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} + - name: Build services (docker compose) + env: + DOCKER_BUILDKIT: 1 + run: | + # Build service images using docker compose which uses each service's build.context/Dockerfile + docker compose build --progress=plain --pull + + - name: Per-service Buildx caching (execute Python script) + env: + DOCKER_BUILDKIT: 1 + working-directory: .github/workflow_utils + run: | + # Install PyYAML to parse the compose file + python -m pip install pyyaml + # Execute the script + python buildx_services.py + + - name: set up backend + run: | + docker compose pull + docker compose up --build -d + # Run pytest and generate coverage report data. - name: Run Tests and Check Types run: | From 84df43fd7cba19284af8e41fbbc563689101f67d Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 08:50:18 -0500 Subject: [PATCH 17/25] fix pytest error --- .github/workflows/CDA-testing.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index bb6c1a9b..7d142fa5 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -34,10 +34,16 @@ jobs: # Poetry will handle installation and caching - name: Cache Python dependencies uses: actions/cache@v4 + id: cache-poetry-venv with: - path: ~/.cache/pypoetry/artifacts + path: .venv key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} + # Install dependencies only if cache is missed + - name: Install dependencies + if: steps.cache-poetry-venv.outputs.cache-hit != 'true' + run: poetry install --no-root + - name: Build services (docker compose) env: DOCKER_BUILDKIT: 1 @@ -48,12 +54,11 @@ jobs: - name: Per-service Buildx caching (execute Python script) env: DOCKER_BUILDKIT: 1 - working-directory: .github/workflow_utils run: | # Install PyYAML to parse the compose file python -m pip install pyyaml # Execute the script - python buildx_services.py + python .github/workflow_utils/buildx_services.py - name: set up backend run: | From 19230f0e5b0abc7868c48559f7af4a6c7d7e9da0 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 10:03:13 -0500 Subject: [PATCH 18/25] update caching --- .github/workflow_utils/buildx_services.py | 85 ----------------------- .github/workflows/CDA-testing.yml | 42 ++++------- 2 files changed, 14 insertions(+), 113 deletions(-) delete mode 100644 .github/workflow_utils/buildx_services.py diff --git a/.github/workflow_utils/buildx_services.py b/.github/workflow_utils/buildx_services.py deleted file mode 100644 index fc144d0a..00000000 --- a/.github/workflow_utils/buildx_services.py +++ /dev/null @@ -1,85 +0,0 @@ -import subprocess -import sys - -import yaml - - -def get_services_to_build(compose_file="docker-compose.yml"): - """ - Parses a docker-compose file and returns a list of services to build. - """ - try: - with open(compose_file, "r") as f: - doc = yaml.safe_load(f) - except FileNotFoundError: - print(f"Error: {compose_file} not found.", file=sys.stderr) - return [] - - services_to_build = [] - services = doc.get("services", {}) - for svc, details in services.items(): - if "build" in details: - if isinstance(details["build"], str): - context = details["build"] - dockerfile = "Dockerfile" - elif isinstance(details["build"], dict): - context = details["build"].get("context", ".") - dockerfile = details["build"].get("dockerfile", "Dockerfile") - else: - context = None - dockerfile = None - - if context: - services_to_build.append( - { - "name": svc, - "context": context, - "dockerfile": dockerfile, - } - ) - return services_to_build - - -def main(): - """ - Main function to orchestrate the build process with buildx caching. - """ - services = get_services_to_build() - if not services: - print("No services with build context found. Exiting.") - return - - print("Detected services and building per-service with buildx cache.") - for svc_info in services: - svc = svc_info["name"] - context = svc_info["context"] - dockerfile = svc_info["dockerfile"] - - print(f"Processing service: {svc}") - print(f"Building {svc} from context={context} dockerfile={dockerfile}") - - try: - subprocess.run( - [ - "docker", - "buildx", - "build", - "--file", - f"{context}/{dockerfile}", - "--tag", - f"local-build/{svc}:latest", - "--load", - "--cache-from=type=gha", - "--cache-to=type=gha,mode=max", - context, - ], - check=True, - ) - print(f"Successfully built {svc}\n") - except subprocess.CalledProcessError as e: - print(f"Error building service {svc}: {e}", file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 7d142fa5..4734f59e 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -14,12 +14,16 @@ jobs: steps: - uses: actions/checkout@v5 - # Docker Buildx caching steps - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + - name: Cache Docker images + uses: ScribeMD/docker-cache@0.5.0 + with: + key: docker-images-${{ runner.os }}-${{ hashFiles('docker-compose.yml') + }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: set up backend + run: | + docker compose pull + docker compose up --build -d - name: Set Up Python uses: actions/setup-python@v6 @@ -29,8 +33,11 @@ jobs: # Use actions-poetry to handle installation - name: Install Poetry and Dependencies uses: abatilo/actions-poetry@v4 - with: - python-version: '3.9.X' + + # Set Poetry to use an in-project virtual environment + - name: Configure Poetry for in-project venv + run: poetry config virtualenvs.in-project true + # Poetry will handle installation and caching - name: Cache Python dependencies uses: actions/cache@v4 @@ -44,27 +51,6 @@ jobs: if: steps.cache-poetry-venv.outputs.cache-hit != 'true' run: poetry install --no-root - - name: Build services (docker compose) - env: - DOCKER_BUILDKIT: 1 - run: | - # Build service images using docker compose which uses each service's build.context/Dockerfile - docker compose build --progress=plain --pull - - - name: Per-service Buildx caching (execute Python script) - env: - DOCKER_BUILDKIT: 1 - run: | - # Install PyYAML to parse the compose file - python -m pip install pyyaml - # Execute the script - python .github/workflow_utils/buildx_services.py - - - name: set up backend - run: | - docker compose pull - docker compose up --build -d - # Run pytest and generate coverage report data. - name: Run Tests and Check Types run: | From 686a13c75d27d5635cc8672216dba1f701277551 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 10:16:37 -0500 Subject: [PATCH 19/25] store cache as zip --- .github/workflows/CDA-testing.yml | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 4734f59e..195892bf 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -14,16 +14,35 @@ jobs: steps: - uses: actions/checkout@v5 - - name: Cache Docker images - uses: ScribeMD/docker-cache@0.5.0 + # --- Docker Image Caching with Compression --- + - name: Pull and Cache Docker images (on cache miss) + id: cache-docker-images + uses: actions/cache@v4 with: - key: docker-images-${{ runner.os }}-${{ hashFiles('docker-compose.yml') + path: docker-image-cache.tar.gz + key: ${{ runner.os }}-docker-images-${{ hashFiles('docker-compose.yml') }} - - name: set up backend + # On cache hit, load the images from the compressed cache + - name: Load compressed images from cache (on cache hit) + if: steps.cache-docker-images.outputs.cache-hit == 'true' + run: | + gzip -dc docker-image-cache.tar.gz | docker load + + - name: Pull and save compressed images (on cache miss) + if: steps.cache-docker-images.outputs.cache-hit != 'true' run: | + # Use docker compose pull to get images docker compose pull - docker compose up --build -d + # Get the image names and save them as a single compressed tarball + images=$(docker compose config --images) + echo "Saving images: $images" + docker save $images | gzip -9 > docker-image-cache.tar.gz + + - name: Set up backend + run: | + # The images are already pulled or loaded from cache + docker compose up -d - name: Set Up Python uses: actions/setup-python@v6 From a7a06ad0ef675fb125bc8f126740e382b50c0244 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 10:37:36 -0500 Subject: [PATCH 20/25] caching fix --- .github/workflows/CDA-testing.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 195892bf..e7877812 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -15,28 +15,30 @@ jobs: - uses: actions/checkout@v5 # --- Docker Image Caching with Compression --- - - name: Pull and Cache Docker images (on cache miss) + - name: Pull and tag images + id: pull-images + run: | + # Pull images and generate cache key from their digests + docker compose pull + images_digests=$(docker compose images --quiet | sort | xargs docker inspect --format='{{.RepoDigests}}' | sort | xargs) + echo "cache_key=${{ runner.os }}-docker-images-$(echo "$images_digests" | md5sum | awk '{print $1}')" >> $GITHUB_OUTPUT + + - name: Restore Docker images from cache id: cache-docker-images uses: actions/cache@v4 with: path: docker-image-cache.tar.gz - key: ${{ runner.os }}-docker-images-${{ hashFiles('docker-compose.yml') - }} + key: ${{ steps.pull-images.outputs.cache_key }} - # On cache hit, load the images from the compressed cache - - name: Load compressed images from cache (on cache hit) + - name: Load compressed images (on cache hit) if: steps.cache-docker-images.outputs.cache-hit == 'true' run: | gzip -dc docker-image-cache.tar.gz | docker load - - name: Pull and save compressed images (on cache miss) + - name: Save compressed images to cache (on cache miss) if: steps.cache-docker-images.outputs.cache-hit != 'true' run: | - # Use docker compose pull to get images - docker compose pull - # Get the image names and save them as a single compressed tarball images=$(docker compose config --images) - echo "Saving images: $images" docker save $images | gzip -9 > docker-image-cache.tar.gz - name: Set up backend From 6d842bb74c8fac8ff37eade02fdc8ca749bdae96 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 10:50:29 -0500 Subject: [PATCH 21/25] fix storage issue --- .github/workflows/CDA-testing.yml | 46 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index e7877812..12902c47 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -14,32 +14,38 @@ jobs: steps: - uses: actions/checkout@v5 - # --- Docker Image Caching with Compression --- - - name: Pull and tag images - id: pull-images - run: | - # Pull images and generate cache key from their digests - docker compose pull - images_digests=$(docker compose images --quiet | sort | xargs docker inspect --format='{{.RepoDigests}}' | sort | xargs) - echo "cache_key=${{ runner.os }}-docker-images-$(echo "$images_digests" | md5sum | awk '{print $1}')" >> $GITHUB_OUTPUT + # --- Disk Space Management --- + # Check disk space before any operations + - name: Pre-cleanup disk space check + run: df -h + + # Free up disk space to prevent "no space left" errors + - name: Free up disk space on runner + run: docker system prune -af + + # Check disk space after cleanup + - name: Post-cleanup disk space check + run: df -h - - name: Restore Docker images from cache + - name: Pull and Cache Docker images id: cache-docker-images uses: actions/cache@v4 with: - path: docker-image-cache.tar.gz - key: ${{ steps.pull-images.outputs.cache_key }} - - - name: Load compressed images (on cache hit) - if: steps.cache-docker-images.outputs.cache-hit == 'true' - run: | - gzip -dc docker-image-cache.tar.gz | docker load + path: /tmp/docker-image-cache.tar.gz + key: ${{ runner.os }}-docker-images-${{ hashFiles('docker-compose.yml') + }} - - name: Save compressed images to cache (on cache miss) - if: steps.cache-docker-images.outputs.cache-hit != 'true' + - name: Pull and load/save compressed images run: | - images=$(docker compose config --images) - docker save $images | gzip -9 > docker-image-cache.tar.gz + if [ "${{ steps.cache-docker-images.outputs.cache-hit }}" == "true" ]; then + echo "Cache hit, loading images..." + gzip -dc /tmp/docker-image-cache.tar.gz | docker load + else + echo "Cache miss, pulling and saving images..." + docker compose pull + images=$(docker compose config --images) + docker save $images | gzip -9 > /tmp/docker-image-cache.tar.gz + fi - name: Set up backend run: | From 66331aae874c3a9cf59d4f533bc1813508696b6b Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 11:27:32 -0500 Subject: [PATCH 22/25] move tmp to mnt --- .github/workflows/CDA-testing.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 12902c47..45f696ca 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -36,6 +36,9 @@ jobs: }} - name: Pull and load/save compressed images + env: + # Set Docker's temporary directory to a location with more space + DOCKER_TMPDIR: /mnt run: | if [ "${{ steps.cache-docker-images.outputs.cache-hit }}" == "true" ]; then echo "Cache hit, loading images..." From c064d1ea400fc2a431eb2b136f9b07342e608b36 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 11:54:08 -0500 Subject: [PATCH 23/25] final try --- .github/workflows/CDA-testing.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 45f696ca..27186d4c 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -27,28 +27,29 @@ jobs: - name: Post-cleanup disk space check run: df -h - - name: Pull and Cache Docker images + # --- Docker Image Caching with Compression --- + - name: Cache Docker images id: cache-docker-images uses: actions/cache@v4 with: - path: /tmp/docker-image-cache.tar.gz + path: /mnt/docker-image-cache.tar.gz key: ${{ runner.os }}-docker-images-${{ hashFiles('docker-compose.yml') }} - - name: Pull and load/save compressed images + - name: Load compressed images from cache (on cache hit) + if: steps.cache-docker-images.outputs.cache-hit == 'true' + run: | + gzip -dc /mnt/docker-image-cache.tar.gz | docker load + + - name: Pull and save compressed images (on cache miss) + if: steps.cache-docker-images.outputs.cache-hit != 'true' env: - # Set Docker's temporary directory to a location with more space + # Use /mnt for Docker's temporary operations DOCKER_TMPDIR: /mnt run: | - if [ "${{ steps.cache-docker-images.outputs.cache-hit }}" == "true" ]; then - echo "Cache hit, loading images..." - gzip -dc /tmp/docker-image-cache.tar.gz | docker load - else - echo "Cache miss, pulling and saving images..." - docker compose pull - images=$(docker compose config --images) - docker save $images | gzip -9 > /tmp/docker-image-cache.tar.gz - fi + docker compose pull + images=$(docker compose config --images) + docker save $images | gzip -9 > /mnt/docker-image-cache.tar.gz - name: Set up backend run: | From 0126567715e79dbef8c376d8952fad5468cc22a1 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 12:08:32 -0500 Subject: [PATCH 24/25] move to mnt/docker-temp --- .github/workflows/CDA-testing.yml | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 27186d4c..01eed388 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -15,41 +15,38 @@ jobs: - uses: actions/checkout@v5 # --- Disk Space Management --- - # Check disk space before any operations - - name: Pre-cleanup disk space check - run: df -h - - # Free up disk space to prevent "no space left" errors - - name: Free up disk space on runner - run: docker system prune -af - - # Check disk space after cleanup - - name: Post-cleanup disk space check - run: df -h + # Create a writable directory in /mnt for Docker's temporary files + - name: Create writable /mnt directory + run: | + sudo mkdir -p /mnt/docker-tmp + sudo chown -R runner:runner /mnt/docker-tmp # --- Docker Image Caching with Compression --- - name: Cache Docker images id: cache-docker-images uses: actions/cache@v4 with: - path: /mnt/docker-image-cache.tar.gz + # Cache to the new writable directory in /mnt + path: /mnt/docker-tmp/docker-image-cache.tar.gz key: ${{ runner.os }}-docker-images-${{ hashFiles('docker-compose.yml') }} - name: Load compressed images from cache (on cache hit) if: steps.cache-docker-images.outputs.cache-hit == 'true' + env: + DOCKER_TMPDIR: /mnt/docker-tmp run: | - gzip -dc /mnt/docker-image-cache.tar.gz | docker load + gzip -dc /mnt/docker-tmp/docker-image-cache.tar.gz | docker load - name: Pull and save compressed images (on cache miss) if: steps.cache-docker-images.outputs.cache-hit != 'true' env: - # Use /mnt for Docker's temporary operations - DOCKER_TMPDIR: /mnt + # Use the new writable directory for Docker's temporary operations + DOCKER_TMPDIR: /mnt/docker-tmp run: | docker compose pull images=$(docker compose config --images) - docker save $images | gzip -9 > /mnt/docker-image-cache.tar.gz + docker save $images | gzip -9 > /mnt/docker-tmp/docker-image-cache.tar.gz - name: Set up backend run: | From 9535a742c5534ef0cf82bdabec6d3a72be26eda3 Mon Sep 17 00:00:00 2001 From: Eric Novotny Date: Tue, 21 Oct 2025 12:16:26 -0500 Subject: [PATCH 25/25] revert back to original --- .github/workflows/CDA-testing.yml | 36 +------------------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/.github/workflows/CDA-testing.yml b/.github/workflows/CDA-testing.yml index 01eed388..8d5960d7 100644 --- a/.github/workflows/CDA-testing.yml +++ b/.github/workflows/CDA-testing.yml @@ -14,43 +14,9 @@ jobs: steps: - uses: actions/checkout@v5 - # --- Disk Space Management --- - # Create a writable directory in /mnt for Docker's temporary files - - name: Create writable /mnt directory - run: | - sudo mkdir -p /mnt/docker-tmp - sudo chown -R runner:runner /mnt/docker-tmp - - # --- Docker Image Caching with Compression --- - - name: Cache Docker images - id: cache-docker-images - uses: actions/cache@v4 - with: - # Cache to the new writable directory in /mnt - path: /mnt/docker-tmp/docker-image-cache.tar.gz - key: ${{ runner.os }}-docker-images-${{ hashFiles('docker-compose.yml') - }} - - - name: Load compressed images from cache (on cache hit) - if: steps.cache-docker-images.outputs.cache-hit == 'true' - env: - DOCKER_TMPDIR: /mnt/docker-tmp - run: | - gzip -dc /mnt/docker-tmp/docker-image-cache.tar.gz | docker load - - - name: Pull and save compressed images (on cache miss) - if: steps.cache-docker-images.outputs.cache-hit != 'true' - env: - # Use the new writable directory for Docker's temporary operations - DOCKER_TMPDIR: /mnt/docker-tmp - run: | - docker compose pull - images=$(docker compose config --images) - docker save $images | gzip -9 > /mnt/docker-tmp/docker-image-cache.tar.gz - - name: Set up backend run: | - # The images are already pulled or loaded from cache + docker compose pull docker compose up -d - name: Set Up Python