diff --git a/.dockerignore b/.dockerignore index 60cc45ec..11a0c5d9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,12 @@ .dockerignore .venv +.git +.github +.history +.pytest_cache +__pycache__ +*.pyc +*.log +dist +docs/build +docs/source/_build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60e0cfff..0e1c6ddc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: version: - description: New semantic version (for example, 2.3.1) + description: New semantic version (for example, 2.3.2) required: true permissions: @@ -17,7 +17,7 @@ jobs: VERSION: ${{ inputs.version }} steps: - name: Check out the repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -34,7 +34,7 @@ jobs: fi - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.12" @@ -48,27 +48,17 @@ jobs: poetry run pytest poetry build - - name: Commit and tag the version - run: | - git config user.name "GitHub Actions" - git config user.email "actions@github.com" - git add pyproject.toml - git commit -m "chore: release $VERSION" - git tag "$VERSION" - git push origin "HEAD:${GITHUB_REF_NAME}" - git push origin "$VERSION" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and push Docker image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . file: ./Dockerfile @@ -78,6 +68,16 @@ jobs: ${{ vars.DOCKER_IMAGE_NAME }}:${{ env.VERSION }} ${{ vars.DOCKER_IMAGE_NAME }}:latest + - name: Commit and tag the version + run: | + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git add pyproject.toml + git commit -m "chore: release $VERSION" + git tag "$VERSION" + git push origin "HEAD:${GITHUB_REF_NAME}" + git push origin "$VERSION" + - name: Create GitHub release and upload Python artifacts env: GH_TOKEN: ${{ github.token }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 45dcfb3f..7546e5ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ ### Fixed +- Pinned the Docker build to the Debian 12 package repositories used by the + DSSAT base image, preventing the floating `stable` suite from mixing Debian + 13 packages into a Debian 12 image. +- Removed exact Debian revision pins for GDAL and isolated Poetry from the + system-managed Python environment. +- Reordered the release workflow so a failed Docker build cannot publish a + version commit and Git tag before the release artifacts are ready. +- Updated GitHub and Docker actions to their Node.js 24 generations. - Restored backward compatibility with legacy one-band GHR soil rasters. - Automatically detect one-band legacy and two-band encoded soil rasters. - Resolve legacy raster IDs through `GHR.db` and provide actionable errors for diff --git a/Dockerfile b/Dockerfile index 6a7129e7..a535c128 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,43 @@ FROM dssat/dssat-csm:v4.8.2.0 -RUN apt-get update && apt-get install -y \ - python3.11 \ - python3.11-dev \ - python3-pip \ - python3-venv \ - python3-poetry \ - python3-virtualenv \ - curl \ - gdal-bin=3.6.2+dfsg-1+b2 \ - libgdal-dev=3.6.2+dfsg-1+b2 \ +# The DSSAT 4.8.2 image is Debian 12, but its APT sources use the floating +# "stable" suite. Pin the suite to bookworm so a future Debian release cannot +# mix packages from a different operating-system version into this image. +RUN sed -i \ + -e 's/Suites: stable stable-updates/Suites: bookworm bookworm-updates/' \ + -e 's/Suites: stable-security/Suites: bookworm-security/' \ + /etc/apt/sources.list.d/debian.sources \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-venv \ + curl \ + gdal-bin \ + libgdal-dev \ && rm -rf /var/lib/apt/lists/* -ENV GDAL_VERSION 3.6.2 -ENV C_INCLUDE_PATH=/usr/include/python3.11/cpython -ENV CPLUS_INCLUDE_PATH=/usr/include/python3.11/cpython +ENV VIRTUAL_ENV="/opt/pythia-venv" \ + PATH="/opt/pythia-venv/bin:${PATH}" \ + PIP_NO_CACHE_DIR=1 \ + POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_CREATE=false + +# Install Poetry in an isolated environment instead of depending on the +# distribution's Poetry package or modifying Debian's managed Python. +RUN python3 -m venv /opt/pythia-venv \ + && python -m pip install --upgrade pip \ + && python -m pip install "poetry>=1.8,<3" WORKDIR /app/pythia +# Cache third-party dependencies separately from the application source. COPY pyproject.toml poetry.toml poetry.lock ./ -RUN POETRY_VIRTUALENVS_CREATE=false poetry install --no-interaction --no-ansi +RUN poetry install --only main --no-root --no-ansi COPY . ./ +RUN poetry install --only main --no-ansi + ENV PATH="${PATH}:/app/pythia/bin" ENTRYPOINT ["pythia"] diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 648f47b4..0c79a87e 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -2,8 +2,12 @@ Pythia uses [semantic versioning](https://semver.org/): increment `MAJOR` for incompatible changes, `MINOR` for backward-compatible features, and `PATCH` for -backward-compatible fixes. The one-band soil compatibility correction should -therefore be a patch release from 2.3.0 (for example, 2.3.1). +backward-compatible fixes. + +The failed 2.3.1 workflow already published its version commit and tag before +the Docker step failed. Do not reuse or move that tag. After merging the Docker +release fix, publish 2.3.2 so every artifact is built from the same immutable +source revision. ## Before triggering the release @@ -35,7 +39,7 @@ change. ## GitHub release workflow Open [Actions → Release](https://github.com/DSSAT/pythia/actions/workflows/release.yml), -choose **Run workflow**, enter a plain semantic version such as `2.3.1`, and +choose **Run workflow**, enter a plain semantic version such as `2.3.2`, and run it from the intended branch. Do not add a `v` prefix. The workflow validates the version, updates `pyproject.toml`, builds and tests diff --git a/pythia/example_release.py b/pythia/example_release.py index 0f8b35ea..bf6efc44 100644 --- a/pythia/example_release.py +++ b/pythia/example_release.py @@ -66,6 +66,8 @@ def build_example_archive(data_root, output, package_readme=None): readme = Path(package_readme).expanduser().resolve() archive.write(readme, f"{archive_root}/README.md") for source, relative in _release_files(root): + if package_readme and relative == Path("README.md"): + continue archive.write(source, (Path(archive_root) / relative).as_posix()) size = destination.stat().st_size diff --git a/pythia/tests/example_data_test.py b/pythia/tests/example_data_test.py index 191848da..117584f3 100644 --- a/pythia/tests/example_data_test.py +++ b/pythia/tests/example_data_test.py @@ -102,6 +102,7 @@ def test_release_archive_is_portable_and_excludes_local_files(tmp_path): (root / "OUTPUT" / "old-run").mkdir(parents=True) (root / "OUTPUT" / "old-run" / "result.csv").write_text("result", encoding="utf-8") (root / ".DS_Store").write_bytes(b"local") + (root / "README.md").write_text("old readme", encoding="utf-8") executable = tmp_path / "dscsm048" executable.write_text("executable", encoding="utf-8") @@ -115,6 +116,7 @@ def test_release_archive_is_portable_and_excludes_local_files(tmp_path): assert checksum.is_file() assert manifest["excluded_members"] == [] assert "Simulation_Data/OUTPUT/" in manifest["members"] + assert manifest["members"].count("Simulation_Data/README.md") == 1 assert "Simulation_Data/OUTPUT/old-run/result.csv" not in manifest["members"] for config in manifest["configs"].values(): serialized = json.dumps(config) diff --git a/release/RELEASE_NOTES_2.3.2.md b/release/RELEASE_NOTES_2.3.2.md new file mode 100644 index 00000000..b439b235 --- /dev/null +++ b/release/RELEASE_NOTES_2.3.2.md @@ -0,0 +1,14 @@ +# Pythia 2.3.2 + +Pythia 2.3.2 delivers the soil compatibility, Sri Lanka example, validation, +documentation, and packaging improvements prepared for 2.3.1, together with a +corrected Docker and GitHub release pipeline. + +The Docker build now keeps the DSSAT base image on Debian 12 repositories, +installs current Bookworm package revisions without fragile build-number pins, +and installs Poetry inside an isolated virtual environment. The GitHub Actions +used by the workflow now run on Node.js 24. Release commits and tags are +published only after tests, Python packaging, and the Docker build succeed. + +Users of one-band GHR soil rasters should follow the compatibility and example +instructions in the 2.3.1 notes. No soil raster conversion is required.