From 84db91e6113f65c084bdb1c42934945079753586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sun, 18 Oct 2026 02:21:49 -0700 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=91=B7=20ci(release):=20gate=20tagged?= =?UTF-8?q?=20publishing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backfill the tagged history and require PR news in the aggregate. Commit release notes before an App pushes the tag, then check the tag and built distributions before publishing to PyPI. --- .github/workflows/check.yaml | 32 ++++++ .github/workflows/prepare-release.yaml | 88 +++++++++++++++ .github/workflows/release.yaml | 14 +++ CHANGELOG.md | 145 +++++++++++++++++++++++++ README.md | 2 + changelog.d/.gitkeep | 0 pyproject.toml | 1 + 7 files changed, 282 insertions(+) create mode 100644 .github/workflows/prepare-release.yaml create mode 100644 CHANGELOG.md create mode 100644 changelog.d/.gitkeep diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index d42671c..a0fb100 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -5,6 +5,7 @@ on: branches: ["main"] tags-ignore: ["**"] pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] schedule: - cron: "0 8 * * *" concurrency: @@ -52,3 +53,34 @@ jobs: env: PYTEST_ADDOPTS: "-vv --durations=20" DIFF_AGAINST: HEAD + news: + name: News fragment + if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip news') + runs-on: ubuntu-26.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Require a changelog entry + run: | + found=0 + while IFS= read -r path; do + if [[ "$path" =~ ^changelog\.d/[0-9]+\.(feature|bugfix|doc)\.md$ && -s "$path" ]]; then + found=1 + fi + done < <(git diff --name-only "origin/$GITHUB_BASE_REF"...HEAD) + if (( found == 0 )); then + echo "::error::add a nonempty changelog.d/..md fragment or apply skip news" + exit 1 + fi + gate: + name: Required checks pass + if: always() + needs: [test, news] + runs-on: ubuntu-26.04 + steps: + - env: + NEEDS_JSON: ${{ toJSON(needs) }} + run: | + jq -e 'all(to_entries[]; .value.result == "success" or (.key == "news" and .value.result == "skipped"))' <<< "$NEEDS_JSON" diff --git a/.github/workflows/prepare-release.yaml b/.github/workflows/prepare-release.yaml new file mode 100644 index 0000000..067d172 --- /dev/null +++ b/.github/workflows/prepare-release.yaml @@ -0,0 +1,88 @@ +name: Prepare release +on: + workflow_dispatch: + inputs: + bump: + description: Version increment + required: true + type: choice + options: [patch, minor, major] + default: patch +permissions: {} +concurrency: + group: prepare-release + cancel-in-progress: false +jobs: + prepare: + if: github.repository == 'pytest-dev/pytest-env' && github.ref == 'refs/heads/main' + runs-on: ubuntu-26.04 + environment: release-auth + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Assemble the release notes + id: version + env: + BUMP: ${{ inputs.bump }} + run: | + set -euo pipefail + current=$(git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) + IFS=. read -r major minor patch <<< "$current" + case "$BUMP" in + major) next="$((major + 1)).0.0" ;; + minor) next="$major.$((minor + 1)).0" ;; + patch) next="$major.$minor.$((patch + 1))" ;; + esac + if [[ -n "$(git tag --list "$next")" ]]; then + echo "::error::tag $next already exists" + exit 1 + fi + mapfile -d '' fragments < <(find changelog.d -maxdepth 1 -type f -name '*.md' -print0 | sort -z) + if (( ${#fragments[@]} == 0 )); then + echo "::error::no news fragments to release" + exit 1 + fi + for fragment in "${fragments[@]}"; do + if [[ ! "$fragment" =~ ^changelog\.d/[0-9]+\.(feature|bugfix|doc)\.md$ || ! -s "$fragment" ]]; then + echo "::error::invalid or empty news fragment: $fragment" + exit 1 + fi + done + { + printf '# Changelog\n\n## %s (%s)\n\n' "$next" "$(date -u +%F)" + for fragment in "${fragments[@]}"; do + printf '%s\n\n' "$(cat "$fragment")" + done + tail -n +3 CHANGELOG.md + } > "$RUNNER_TEMP/changelog.md" + mv "$RUNNER_TEMP/changelog.md" CHANGELOG.md + git rm -- "${fragments[@]}" + echo "next=$next" >> "$GITHUB_OUTPUT" + - id: token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.RELEASE_APP_CLIENT_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-contents: write + - name: Commit and tag the release + env: + GH_TOKEN: ${{ steps.token.outputs.token }} + APP_SLUG: ${{ steps.token.outputs.app-slug }} + NEXT: ${{ steps.version.outputs.next }} + run: | + set -euo pipefail + app_user_id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id) + git config user.name "${APP_SLUG}[bot]" + git config user.email "${app_user_id}+${APP_SLUG}[bot]@users.noreply.github.com" + git add CHANGELOG.md changelog.d + git commit -m "Prepare $NEXT" + git tag -a "$NEXT" -m "Release $NEXT" + gh auth setup-git --hostname github.com --force + git push origin HEAD:main + git push origin "$NEXT" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index af959e7..7a1d10e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,6 +14,14 @@ jobs: with: fetch-depth: 0 persist-credentials: false + - name: Require a versioned changelog entry + env: + TAG: ${{ github.ref_name }} + run: | + if [[ ! "$TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || ! grep -Fq "## $TAG (" CHANGELOG.md; then + echo "::error::$TAG has no matching changelog entry" + exit 1 + fi - name: Install the latest version of uv uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 with: @@ -23,6 +31,12 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Build package run: uv build --python 3.15 --python-preference only-managed --sdist --wheel . --out-dir dist + - name: Match distributions to the tag + env: + TAG: ${{ github.ref_name }} + run: | + test -f "dist/pytest_env-${TAG}.tar.gz" + compgen -G "dist/pytest_env-${TAG}-*.whl" >/dev/null - name: Store the distribution packages uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4fd47d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,145 @@ +# Changelog + +## 1.8.1 (2026-10-18) + +- Respect explicit empty native env_files over INI fallback ([#273](https://github.com/pytest-dev/pytest-env/pull/273)) + +## 1.8.0 (2026-10-10) + +- Document [pytest] env configuration in pytest.toml / .pytest.toml + ([#262](https://github.com/pytest-dev/pytest-env/pull/262)) +- 👷 ci: pin ubuntu-26.04 and bump dependencies ([#268](https://github.com/pytest-dev/pytest-env/pull/268)) +- feat(python): require 3.11 and default to 3.15 ([#270](https://github.com/pytest-dev/pytest-env/pull/270)) + +## 1.7.1 (2026-09-08) + +- 🔧 chore: batch dependency updates weekly on Tuesday ([#256](https://github.com/pytest-dev/pytest-env/pull/256)) +- Keep pyproject environment settings visible beside native pytest configuration + ([#260](https://github.com/pytest-dev/pytest-env/pull/260)) + +## 1.7.0 (2026-07-21) + +- 🔒 ci(workflows): add zizmor security auditing ([#216](https://github.com/pytest-dev/pytest-env/pull/216)) +- 📦 build: move the testing extra to a dependency group ([#243](https://github.com/pytest-dev/pytest-env/pull/243)) +- 👷 ci: test against Python 3.15 beta ([#245](https://github.com/pytest-dev/pytest-env/pull/245)) + +## 1.6.0 (2026-03-12) + +- ✨ feat(env): preserve existing env values ([#213](https://github.com/pytest-dev/pytest-env/pull/213)) + +## 1.5.1 (2026-03-12) + +- Add permissions to workflows ([#203](https://github.com/pytest-dev/pytest-env/pull/203)) +- Add SECURITY.md to .github/ ([#206](https://github.com/pytest-dev/pytest-env/pull/206)) +- Add missing .github config files ([#207](https://github.com/pytest-dev/pytest-env/pull/207)) +- Standardize .github files to .yaml suffix ([#208](https://github.com/pytest-dev/pytest-env/pull/208)) +- Fix verbose source attribution when falling back to INI env + ([#211](https://github.com/pytest-dev/pytest-env/pull/211)) + +## 1.5.0 (2026-02-17) + +- ✨ feat(plugin): add --pytest-env-verbose for debugging env assignments + ([#199](https://github.com/pytest-dev/pytest-env/pull/199)) + +## 1.4.0 (2026-02-17) + +- ✨ feat(cli): add --envfile option for runtime environment switching + ([#197](https://github.com/pytest-dev/pytest-env/pull/197)) + +## 1.3.2 (2026-02-11) + +[Release history](https://github.com/pytest-dev/pytest-env/releases/tag/1.3.2). + +## 1.3.1 (2026-02-11) + +- 🔧 build(type): migrate from mypy to ty ([#192](https://github.com/pytest-dev/pytest-env/pull/192)) +- ♻️ refactor(plugin): consolidate TOML config parsing ([#193](https://github.com/pytest-dev/pytest-env/pull/193)) +- ✅ test(plugin): verify env vars available during conftest imports + ([#194](https://github.com/pytest-dev/pytest-env/pull/194)) + +## 1.3.0 (2026-02-11) + +- Support pytest.toml and .pytest.toml ([#180](https://github.com/pytest-dev/pytest-env/pull/180)) +- ✨ feat(config): support [tool.pytest] in pyproject.toml ([#189](https://github.com/pytest-dev/pytest-env/pull/189)) +- ✨ feat(plugin): add U flag to unset env variables ([#188](https://github.com/pytest-dev/pytest-env/pull/188)) +- 🐛 fix(config): use inipath for TOML file discovery ([#190](https://github.com/pytest-dev/pytest-env/pull/190)) +- ✨ feat(plugin): add .env file loading support ([#191](https://github.com/pytest-dev/pytest-env/pull/191)) + +## 1.2.0 (2025-10-09) + +- Add support for 3.14 and drop 3.9 ([#163](https://github.com/pytest-dev/pytest-env/pull/163)) + +## 1.1.5 (2024-09-17) + +- Improve the CI ([#101](https://github.com/pytest-dev/pytest-env/pull/101)) + +## 1.1.4 (2024-09-07) + +[Release history](https://github.com/pytest-dev/pytest-env/releases/tag/1.1.4). + +## 1.1.3 (2023-11-28) + +- fix README typo ([#63](https://github.com/pytest-dev/pytest-env/pull/63)) + +## 1.1.2 (2023-11-28) + +[Release history](https://github.com/pytest-dev/pytest-env/releases/tag/1.1.2). + +## 1.1.1 (2023-10-30) + +- Fix native toml documentation does not match code ([#60](https://github.com/pytest-dev/pytest-env/pull/60)) + +## 1.1.0 (2023-10-24) + +- Fix type checker ([#50](https://github.com/pytest-dev/pytest-env/pull/50)) +- Drop the pytest dot ([#54](https://github.com/pytest-dev/pytest-env/pull/54)) +- Add native toml support ([#59](https://github.com/pytest-dev/pytest-env/pull/59)) + +## 1.0.1 (2023-08-24) + +- Disable plugin autoloading in the test suite ([#49](https://github.com/pytest-dev/pytest-env/pull/49)) + +## 1.0.0 (2023-08-22) + +- Exclude dependabot and pre-commit ci from release notes ([#37](https://github.com/pytest-dev/pytest-env/pull/37)) +- chore: typo fix in pyproject.toml ([#42](https://github.com/pytest-dev/pytest-env/pull/42)) + +## 0.8.2 (2023-06-15) + +- Bump tools and deps ([#13](https://github.com/pytest-dev/pytest-env/pull/13)) +- Bump pypa/gh-action-pypi-publish from 1.5.1 to 1.5.2 ([#14](https://github.com/pytest-dev/pytest-env/pull/14)) +- Bump pypa/gh-action-pypi-publish from 1.5.2 to 1.6.1 ([#15](https://github.com/pytest-dev/pytest-env/pull/15)) +- Bump pypa/gh-action-pypi-publish from 1.6.1 to 1.6.4 ([#16](https://github.com/pytest-dev/pytest-env/pull/16)) +- Bump deps and tools ([#18](https://github.com/pytest-dev/pytest-env/pull/18)) +- Add pyproject-fmt ([#19](https://github.com/pytest-dev/pytest-env/pull/19)) +- Bump pypa/gh-action-pypi-publish from 1.6.4 to 1.7.1 ([#20](https://github.com/pytest-dev/pytest-env/pull/20)) +- Bump pypa/gh-action-pypi-publish from 1.7.1 to 1.8.1 ([#21](https://github.com/pytest-dev/pytest-env/pull/21)) +- Bump deps and tools ([#22](https://github.com/pytest-dev/pytest-env/pull/22)) +- Bump deps and tools ([#23](https://github.com/pytest-dev/pytest-env/pull/23)) +- Bump pypa/gh-action-pypi-publish from 1.8.1 to 1.8.3 ([#24](https://github.com/pytest-dev/pytest-env/pull/24)) +- Bump pypa/gh-action-pypi-publish from 1.8.3 to 1.8.5 ([#25](https://github.com/pytest-dev/pytest-env/pull/25)) +- Bump deps and tools ([#26](https://github.com/pytest-dev/pytest-env/pull/26)) +- Bump deps and tools ([#28](https://github.com/pytest-dev/pytest-env/pull/28)) +- Bump deps and tools ([#30](https://github.com/pytest-dev/pytest-env/pull/30)) +- Add trusted-publish ([#31](https://github.com/pytest-dev/pytest-env/pull/31)) +- Add 3.12 support ([#32](https://github.com/pytest-dev/pytest-env/pull/32)) +- Bump pypa/gh-action-pypi-publish from 1.8.5 to 1.8.6 ([#33](https://github.com/pytest-dev/pytest-env/pull/33)) +- git ls-files -z -- .github/workflows/check.yml | xargs -0 sed -i 's|3.12.0-alpha.7|3.12.0-beta.1|g' + ([#34](https://github.com/pytest-dev/pytest-env/pull/34)) +- Add ruff ([#35](https://github.com/pytest-dev/pytest-env/pull/35)) + +## 0.8.1 (2022-10-23) + +- Add mention of pyproject.toml in the header of README.md ([#10](https://github.com/pytest-dev/pytest-env/pull/10)) + +## 0.8.0 (2022-10-23) + +- Add R: prefix implementation and unit tests ([#9](https://github.com/pytest-dev/pytest-env/pull/9)) + +## 0.7.0 (2022-10-22) + +[Release history](https://github.com/pytest-dev/pytest-env/releases/tag/0.7.0). + +## 0.6.2 (2022-10-10) + +[Release history](https://github.com/pytest-dev/pytest-env/releases/tag/0.6.2). diff --git a/README.md b/README.md index 2947e84..ead958f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ A `pytest` plugin that sets environment variables from `pyproject.toml`, `pytest.toml`, `.pytest.toml`, or `pytest.ini` configuration files. It can also load variables from `.env` files. +[Changelog](CHANGELOG.md). + - [Installation](#installation) diff --git a/changelog.d/.gitkeep b/changelog.d/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index d42451e..c4f7901 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ dependencies = [ "pytest>=9.1.1", "python-dotenv>=1.2.4", ] +urls.Changelog = "https://github.com/pytest-dev/pytest-env/blob/main/CHANGELOG.md" urls.Homepage = "https://github.com/pytest-dev/pytest-env" urls.Source = "https://github.com/pytest-dev/pytest-env" urls.Tracker = "https://github.com/pytest-dev/pytest-env/issues" From d1c56213844b4be4c7b869874aff34f80a4f3127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sun, 18 Oct 2026 03:44:40 -0700 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=94=92=20Push=20release=20refs=20atom?= =?UTF-8?q?ically?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/prepare-release.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/prepare-release.yaml b/.github/workflows/prepare-release.yaml index 067d172..0521cb6 100644 --- a/.github/workflows/prepare-release.yaml +++ b/.github/workflows/prepare-release.yaml @@ -84,5 +84,4 @@ jobs: git commit -m "Prepare $NEXT" git tag -a "$NEXT" -m "Release $NEXT" gh auth setup-git --hostname github.com --force - git push origin HEAD:main - git push origin "$NEXT" + git push --atomic origin HEAD:main "refs/tags/$NEXT" From 983429193ce19bbf09a930650d429d415331014f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sun, 18 Oct 2026 09:21:49 -0700 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20fix(release):=20refresh=20ma?= =?UTF-8?q?in=20before=20push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/prepare-release.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/prepare-release.yaml b/.github/workflows/prepare-release.yaml index 0521cb6..884a6ba 100644 --- a/.github/workflows/prepare-release.yaml +++ b/.github/workflows/prepare-release.yaml @@ -30,6 +30,8 @@ jobs: BUMP: ${{ inputs.bump }} run: | set -euo pipefail + git fetch origin main + git reset --hard origin/main current=$(git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) IFS=. read -r major minor patch <<< "$current" case "$BUMP" in From 67f9327ceee90c3df6969f72641cfd2aca6f4f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sun, 18 Oct 2026 09:21:49 -0700 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=91=B7=20ci:=20label=20automation=20P?= =?UTF-8?q?Rs=20skip=20news?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 69d0cfd..fccacae 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -2,6 +2,10 @@ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" + labels: + - dependencies + - github_actions + - skip news schedule: interval: "weekly" day: "tuesday"