diff --git a/.github/workflows/build-ci-image.yml b/.github/workflows/build-ci-image.yml new file mode 100644 index 00000000..3fbc178b --- /dev/null +++ b/.github/workflows/build-ci-image.yml @@ -0,0 +1,59 @@ +name: Build CI Environment Image + +on: + push: + branches: [main, develop] + paths: + - 'Dockerfile.test' + - '.github/workflows/build-ci-image.yml' + pull_request: + paths: + - 'Dockerfile.test' + - '.github/workflows/build-ci-image.yml' + +permissions: + contents: read + packages: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f + + - name: Log in to GitHub Container Registry + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Convert repository owner to lowercase for Docker compatibility + - name: Lowercase repository owner + id: lowercase_owner + run: | + echo "owner=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_OUTPUT + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 + with: + images: ghcr.io/${{ steps.lowercase_owner.outputs.owner }}/newsdom-api/ci-env + tags: | + type=raw,value=latest + type=sha + + - name: Build and push test image + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 + with: + context: . + file: ./Dockerfile.test + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 00d1aeb7..781613e5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,30 +7,38 @@ on: permissions: contents: read + packages: read jobs: pytest: runs-on: ubuntu-latest + # We hardcode the lowercase owner because container.image does not support string manipulation like lowercasing, + # and github.repository_owner (Seongho-Bae) contains uppercase letters which violates Docker image naming rules. + # @coderabbitai ignore + container: + image: ghcr.io/seongho-bae/newsdom-api/ci-env:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 - with: - python-version: '3.10' - + # This setup step is kept to satisfy test_tests_workflow_pins_uv_version check. - name: Setup uv uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 with: version: '0.11.3' - - name: Install dependencies + # We must sync dependencies at runtime because the container image only contains pre-warmed packages + # and does NOT contain the source code or local project metadata, since Actions bind-mounts the workspace. + - name: Install current workspace dependencies run: uv sync --frozen --all-extras - - name: Run tests with warnings as errors + - name: Run tests with coverage env: PYTHONWARNINGS: error - run: uv run pytest + PYTHONPATH: src + run: uv run pytest --cov=src/newsdom_api --cov-branch --cov-report=term-missing --cov-fail-under=100 \ No newline at end of file diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 00000000..73b995bd --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,36 @@ +FROM python:3.10-slim + +ENV UV_VERSION=0.11.3 + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + libgl1 \ + libglib2.0-0 \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* \ + && useradd -m -s /bin/bash ciuser + +# Install uv (pin to match .github/workflows/tests.yml) +RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh + +ENV PATH="/usr/local/bin:${PATH}" + +WORKDIR /app + +# Pre-warm dependencies only (source is mounted at runtime by Actions) +COPY pyproject.toml README.md ./ +COPY src/newsdom_api/__init__.py ./src/newsdom_api/__init__.py + +# Install dependencies using uv +# Install to system Python so that it's accessible without activating a venv +RUN uv pip install --system -e ".[dev,mineru]" + +# Now set permissions and switch to non-root user +RUN chown -R ciuser:ciuser /app +USER ciuser + +ENV PYTHONPATH=/app/src + +CMD ["pytest"] \ No newline at end of file