diff --git a/.bumpversion.toml b/.bumpversion.toml new file mode 100644 index 000000000..fa70769ec --- /dev/null +++ b/.bumpversion.toml @@ -0,0 +1,58 @@ +[tool.bumpversion] +current_version = "5.1.1" +commit = true +tag = true +tag_name = "{new_version}" + +[[tool.bumpversion.files]] +filename = "Dockerfile" +search = "LABEL org.opencontainers.image.version=\"{current_version}\"" +replace = "LABEL org.opencontainers.image.version=\"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "Dockerfile" +search = "LABEL org.opencontainers.image.created=\"\\d{{4}}-\\d{{2}}-\\d{{2}}T\\d{{2}}:\\d{{2}}:\\d{{2}}Z\"" +replace = "LABEL org.opencontainers.image.created=\"{utcnow:%Y-%m-%dT%H:%M:%SZ}\"" +regex = true + +[[tool.bumpversion.files]] +filename = "Dockerfile.adapter" +search = "LABEL org.opencontainers.image.version=\"{current_version}\"" +replace = "LABEL org.opencontainers.image.version=\"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "Dockerfile.adapter" +search = "LABEL org.opencontainers.image.created=\"\\d{{4}}-\\d{{2}}-\\d{{2}}T\\d{{2}}:\\d{{2}}:\\d{{2}}Z\"" +replace = "LABEL org.opencontainers.image.created=\"{utcnow:%Y-%m-%dT%H:%M:%SZ}\"" +regex = true + +[[tool.bumpversion.files]] +filename = "README.rst" +search = "{current_version}" +replace = "{new_version}" + +[[tool.bumpversion.files]] +filename = "Makefile" +search = "APP_VERSION ?= {current_version}" +replace = "APP_VERSION ?= {new_version}" + +[[tool.bumpversion.files]] +filename = "magpie/__meta__.py" +search = "__version__ = \"{current_version}\"" +replace = "__version__ = \"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "CHANGES.rst" +search = """ +`Unreleased `_ (latest) +------------------------------------------------------------------------------------""" +replace = """ +`Unreleased `_ (latest) +------------------------------------------------------------------------------------ + +* Nothing new for the moment. + +.. _changes_{new_version}: + +`{new_version} `_ ({now:%%Y-%%m-%%d}) +------------------------------------------------------------------------------------""" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d21d5dcde..c0379be7b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -29,6 +29,8 @@ updates: commit-message: prefix: "ci" include: "scope" + cooldown: + default-days: 7 # Python dependencies (pip) # Group patch/minor updates to reduce PR count; major updates get individual PRs @@ -58,6 +60,8 @@ updates: commit-message: prefix: "pip" include: "scope" + cooldown: + default-days: 7 # Docker dependencies # No grouping for Docker dependencies - all updates need manual review @@ -85,6 +89,8 @@ updates: - "version-update:semver-minor" versions: - ">= 3.14.0" # limit to Python 3.13.x, but allow any extra OS version tag + cooldown: + default-days: 7 # NPM dependencies - package-ecosystem: "npm" @@ -106,3 +112,5 @@ updates: commit-message: prefix: "npm" include: "scope" + cooldown: + default-days: 7 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 000000000..3bf874c99 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,117 @@ +name: Docker Hub Publishing + +on: + push: + branches: + - master + paths: + - ".bumpversion.toml" + - "Dockerfile" + - "Dockerfile.adapter" + tags: + - "[0-9]+.[0-9]+.[0-9]+" + +concurrency: + # For a given workflow, if we push to the same branch, cancel all previous builds on that branch except on master. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref == 'refs/heads/master' }} + +permissions: + contents: read + +jobs: + build-publish-magpie: + name: Build and publish (Magpie) + runs-on: ubuntu-latest + env: + IMAGE_NAME: "pavics/magpie" + steps: + - name: Harden Runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + disable-sudo: true + egress-policy: audit + + - name: Check out Repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Gather Docker metadata + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=tag + type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }} + + - name: Login to Docker Hub + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + + - name: Build Docker image and publish to Docker Hub + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + file: Dockerfile + labels: ${{ steps.meta.outputs.labels }} + push: true + tags: ${{ steps.meta.outputs.tags }} + + - name: Inspect labels + run: | + docker inspect --format='{{json .Config.Labels}}' ${{ env.IMAGE_NAME }}:latest + + build-publish-twitcher: + name: Build and publish (twitcher w/MagpieAdapter) + runs-on: ubuntu-latest + env: + IMAGE_NAME: "pavics/twitcher" + steps: + - name: Harden Runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + disable-sudo: true + egress-policy: audit + + - name: Check out Repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Gather Docker metadata + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest,enable={{is_default_branch}},prefix=magpie- + type=ref,event=tag,prefix=magpie- + type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }},prefix=magpie- + + - name: Login to Docker Hub + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + + - name: Build Docker image and publish to Docker Hub + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + file: Dockerfile.adapter + labels: ${{ steps.meta.outputs.labels }} + push: true + tags: ${{ steps.meta.outputs.tags }} + + - name: Inspect labels + run: | + docker inspect --format='{{json .Config.Labels}}' ${{ env.IMAGE_NAME }}:magpie-latest diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index 7a47b0d2d..56146f385 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -1,19 +1,28 @@ name: Greetings -on: [pull_request, issues] +on: + - pull_request + - issues + +permissions: + contents: read jobs: greeting: if: github.actor != 'dependabot[bot]' runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - - uses: actions/first-interaction@v3 + - name: Greet Contributor + uses: actions/first-interaction@1c4688942c71f71d4f5502a26ea67c331730fa4d # v3.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: > + issue_message: > Thanks for submitting an issue. Make sure you have checked for similar issues. Also, provide enough details for us to be able to replicate the problem. - pr-message: > + pr_message: > Thanks for submitting a PR. Make sure you have looked at the contribution guidelines. Also, look for quick check/tests operations that you can run locally for early verification of errors. Travis will be happier if it doesn't need to run too many times with problematic code. diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 3620c7e06..9e06b0a96 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -9,7 +9,14 @@ name: Pull Request Labeler on: # https://github.com/actions/labeler#recommended-permissions pull_request_target: - types: [opened, edited, synchronize, reopened] + types: + - edited + - opened + - reopened + - synchronize + +permissions: + contents: read jobs: label: @@ -17,15 +24,16 @@ jobs: if: github.actor != 'dependabot[bot]' permissions: # see: https://github.com/actions/labeler/issues/870 - contents: read pull-requests: write issues: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - name: Check out Repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: "0" - - uses: actions/labeler@v7.0.0 + - name: Label + uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 with: sync-labels: false repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 85ca72842..5babe2053 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -14,6 +14,9 @@ on: - pull_request - push +permissions: + contents: read + jobs: # ref: https://github.com/svdarren/secrets-workflow/blob/9633bc1195a1ca1d4d70415aa4eff6cf55d706de/.github/workflows/secrets.yml gitleak: @@ -24,10 +27,12 @@ jobs: if: github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v7 + - name: Check out Repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: '0' - - uses: gitleaks/gitleaks-action@v3.0.0 + - name: Run gitleaks + uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9667c3b8b..4ef9ec8bd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: # see: https://github.com/fkirc/skip-duplicate-actions skip_duplicate: @@ -114,12 +117,14 @@ jobs: echo "should_run=true" >> "$GITHUB_OUTPUT" fi - - uses: actions/checkout@v7 + - name: Check out Repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: ${{ steps.execution_guard.outputs.should_run == 'true' }} with: fetch-depth: "0" + - name: Setup Python - uses: actions/setup-python@v7 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 if: ${{ steps.execution_guard.outputs.should_run == 'true' && matrix.python-version != 'none' }} with: python-version: ${{ matrix.python-version }} @@ -128,31 +133,38 @@ jobs: - name: Install Package Managers if: ${{ steps.execution_guard.outputs.should_run == 'true' && matrix.python-version != 'none' }} run: make install-sys + - name: Install Dependencies if: ${{ steps.execution_guard.outputs.should_run == 'true' && matrix.python-version != 'none' }} run: make install-pkg install-req install-dev version + - name: Display Packages if: ${{ steps.execution_guard.outputs.should_run == 'true' && matrix.python-version != 'none' }} run: pip freeze + - name: Setup Environment Variables if: ${{ steps.execution_guard.outputs.should_run == 'true' }} uses: c-py/action-dotenv-to-setenv@v5 with: env-file: ./ci/magpie.env + - name: Display Environment Variables if: ${{ steps.execution_guard.outputs.should_run == 'true' }} run: | hash -r env | sort + - name: Create private key for network testing if: ${{ steps.execution_guard.outputs.should_run == 'true' && matrix.test-case == 'test-local' }} run: ${{ matrix.test-option }} make create-private-key # run '-only' test variations since dependencies are preinstalled, skip some resolution time + - name: Run Tests if: ${{ steps.execution_guard.outputs.should_run == 'true' }} run: ${{ matrix.test-option }} make stop ${{ matrix.test-case }}-only + - name: Upload coverage report - uses: codecov/codecov-action@v7 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 if: ${{ steps.execution_guard.outputs.should_run == 'true' && success() && matrix.test-case == 'coverage' }} with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.readthedocs.yml b/.readthedocs.yml index ee5f692c0..a813b0147 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,11 +1,14 @@ # configuration to setup readthedocs version: 2 + build: - os: ubuntu-22.04 + os: ubuntu-26.04 tools: python: "3.12" + sphinx: configuration: docs/conf.py + # note: # disable PDF and EPUB which break during LaTeX # generation because of `redoc.js` script not found @@ -14,6 +17,7 @@ formats: - htmlzip #- pdf #- epub + python: install: - requirements: requirements-sys.txt diff --git a/Dockerfile b/Dockerfile index 72e4bad1b..7637b443a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,15 @@ # version pin defined via '.github/dependabot.yml' FROM python:3.13-alpine3.23 -LABEL Description="Runs Magpie AuthN/AuthZ service for REST-API and UI interfaces." -LABEL Maintainer="Francis Charette-Migneault " -LABEL Vendor="CRIM" +ARG DEBIAN_FRONTEND=noninteractive +ENV PIP_ROOT_USER_ACTION=ignore +LABEL org.opencontainers.image.authors="Francis Charette-Migneault " +LABEL org.opencontainers.image.created="2026-07-30T20:37:38Z" +LABEL org.opencontainers.image.description="Runs Magpie AuthN/AuthZ service for REST-API and UI interfaces." +LABEL org.opencontainers.image.licenses="Apache-2.0" +LABEL org.opencontainers.image.source="https://github.com/Ouranosinc/Magpie" +LABEL org.opencontainers.image.title="Magpie" +LABEL org.opencontainers.image.vendor="CRIM and Ouranosinc" +LABEL org.opencontainers.image.version="5.1.1" # the cron service depends on the $MAGPIE_DIR environment variable ENV MAGPIE_DIR=/opt/local/src/magpie @@ -40,6 +47,7 @@ RUN apk upgrade --no-cache \ COPY ./config/magpie.ini $MAGPIE_CONFIG_DIR/magpie.ini COPY ./env/*.env.example $MAGPIE_ENV_DIR/ COPY ./magpie $MAGPIE_DIR/magpie/ + # equivalent of `make install` without conda env and pre-installed packages RUN pip install --no-dependencies -e $MAGPIE_DIR diff --git a/Dockerfile.adapter b/Dockerfile.adapter index c57faba9e..39415e25b 100644 --- a/Dockerfile.adapter +++ b/Dockerfile.adapter @@ -4,9 +4,16 @@ # using config 'twitcher.adapter = magpie.adapter.MagpieAdapter' # FROM birdhouse/twitcher:v0.11.1 -LABEL Description="Configures MagpieAdapter on top of Twitcher application." -LABEL Maintainer="Francis Charette-Migneault " -LABEL Vendor="CRIM" +ARG DEBIAN_FRONTEND=noninteractive +ENV PIP_ROOT_USER_ACTION=ignore +LABEL org.opencontainers.image.authors="Francis Charette-Migneault " +LABEL org.opencontainers.image.created="2026-07-30T20:37:38Z" +LABEL org.opencontainers.image.description="Configures MagpieAdapter on top of Twitcher application." +LABEL org.opencontainers.image.licenses="Apache-2.0" +LABEL org.opencontainers.image.source="https://github.com/Ouranosinc/Magpie" +LABEL org.opencontainers.image.title="Twitcher with MagpieAdapter" +LABEL org.opencontainers.image.vendor="CRIM and Ouranosinc" +LABEL org.opencontainers.image.version="5.1.1" ENV MAGPIE_DIR=/opt/local/src/magpie COPY magpie/__init__.py magpie/__meta__.py $MAGPIE_DIR/magpie/ diff --git a/Makefile b/Makefile index 000d5a099..590eff489 100644 --- a/Makefile +++ b/Makefile @@ -346,8 +346,8 @@ endif bump: ## bump version using VERSION specified as user input (make VERSION= bump) @-echo "Updating package version ..." @[ "${VERSION}" ] || ( echo ">> 'VERSION' is not set"; exit 1 ) - @-bash -c '$(CONDA_CMD) test -f "$(CONDA_ENV_PATH)/bin/bump2version" || pip install $(PIP_XARGS) bump2version' - @-bash -c '$(CONDA_CMD) bump2version $(BUMP_XARGS) --new-version "${VERSION}" patch;' + @-bash -c '$(CONDA_CMD) test -f "$(CONDA_ENV_PATH)/bin/bump-my-version" || pip install $(PIP_XARGS) bump-my-version' + @-bash -c '$(CONDA_CMD) bump-my-version bump $(BUMP_XARGS) --new-version "${VERSION}" patch;' ## --- Installation targets --- ## diff --git a/hooks/README.md b/hooks/README.md deleted file mode 100644 index 623b90481..000000000 --- a/hooks/README.md +++ /dev/null @@ -1,2 +0,0 @@ -These files are for additional dockerhub auto-build hook procedures. -see: https://docs.docker.com/docker-hub/builds/advanced/ diff --git a/hooks/post_push b/hooks/post_push deleted file mode 100644 index 3a4aa8c44..000000000 --- a/hooks/post_push +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -echo "=> Information of Magpie Adapter image for Twitcher (docker tag: $DOCKER_TAG)" -make APP_VERSION=${DOCKER_TAG} docker-info - -echo "=> Building Magpie Adapter image for Twitcher (docker tag: $DOCKER_TAG)" -make APP_VERSION=${DOCKER_TAG} docker-build-adapter - -echo "=> Pushing Magpie Adapter image for Twitcher (docker tag: $DOCKER_TAG)" -make APP_VERSION=${DOCKER_TAG} docker-push-adapter diff --git a/magpie/__meta__.py b/magpie/__meta__.py index 8369d0d9a..51f6f36ee 100644 --- a/magpie/__meta__.py +++ b/magpie/__meta__.py @@ -5,8 +5,8 @@ __version__ = "5.1.1" __title__ = "Magpie" __package__ = "magpie" # pylint: disable=W0622 -__author__ = "Francois-Xavier Derue, Francis Charette-Migneault" -__maintainer__ = "Francis Charette-Migneault" +__author__ = """Francois-Xavier Derue, Francis Charette-Migneault""" +__maintainer__ = """Francis Charette-Migneault""" __email__ = "francis.charette-migneault@crim.ca" __url__ = "https://github.com/Ouranosinc/Magpie" __docker__ = "https://hub.docker.com/r/pavics/magpie" diff --git a/requirements-dev.txt b/requirements-dev.txt index 86746e7de..df087711a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,8 +1,10 @@ -r requirements-sys.txt -r requirements-doc.txt +# pin authlib to patch broken safety dependency chain +authlib<1.8.0 autopep8>=1.5.4 bandit==1.9.4 -bump2version==1.0.1 +bump-my-version==1.5.1 codacy-coverage>=1.3.11 coverage>=5.5 doc8>=1.1.2 diff --git a/setup.cfg b/setup.cfg index 118baef4a..0497a882f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,36 +1,3 @@ -[bumpversion] -current_version = 5.1.1 -commit = True -tag = True -tag_name = {new_version} - -[bumpversion:file:README.rst] -search = {current_version} -replace = {new_version} - -[bumpversion:file:Makefile] -search = APP_VERSION ?= {current_version} -replace = APP_VERSION ?= {new_version} - -[bumpversion:file:magpie/__meta__.py] -search = __version__ = "{current_version}" -replace = __version__ = "{new_version}" - -[bumpversion:file:CHANGES.rst] -search = - `Unreleased `_ (latest) - ------------------------------------------------------------------------------------ -replace = - `Unreleased `_ (latest) - ------------------------------------------------------------------------------------ - - * Nothing new for the moment. - - .. _changes_{new_version}: - - `{new_version} `_ ({now:%%Y-%%m-%%d}) - ------------------------------------------------------------------------------------ - [wheel] universal = 1