From 689aba26c2b30c437b394821647fab5dcc82158c Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 09:24:20 +0000 Subject: [PATCH 01/14] Create release notes --- CHANGELOG.rst | 4 +++ script/create_release_notes.py | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 script/create_release_notes.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bee8b814..65f9f309 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,10 @@ Release 0.11.0 (unreleased) ==================================== +.. note:: + + This is the latest unreleased version and may change + * Support python 3.14 * Drop python 3.7, 3.8 support (#801) * Don't show animation when running in CI (#702) diff --git a/script/create_release_notes.py b/script/create_release_notes.py new file mode 100644 index 00000000..20101c46 --- /dev/null +++ b/script/create_release_notes.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +""" +create_release_notes.py + +Extracts the latest section from CHANGELOG.rst. +If --latest is provided, adds an admonition that it is unreleased. +""" + +import argparse +import re +from pathlib import Path + + +def extract_latest_section(changelog_path: Path) -> str: + """ + Extract the latest release section from a CHANGELOG.rst file. + """ + content = changelog_path.read_text(encoding="utf-8") + lines = content.splitlines() + + # Find lines starting with version headings like: 'v1.2.3' or '.. version' + # dfetch uses "vX.Y.Z" style headings in CHANGELOG.rst + version_header_pattern = re.compile(r"^Release \d+\.\d+\.\d+") + + start_idx = None + end_idx = None + + for idx, line in enumerate(lines): + if version_header_pattern.match(line.strip()): + start_idx = idx + break + + if start_idx is None: + raise ValueError("No release section found in CHANGELOG.rst") + + # Find the next version header to determine the end + for idx in range(start_idx + 1, len(lines)): + if version_header_pattern.match(lines[idx].strip()): + end_idx = idx + break + + section_lines = lines[start_idx:end_idx] + return "\n".join(section_lines).strip() + + +def main(): + """Main CLI entry.""" + parser = argparse.ArgumentParser( + description="Create release notes from CHANGELOG.rst" + ) + parser.add_argument( + "--changelog", default="CHANGELOG.rst", help="Path to CHANGELOG.rst" + ) + args = parser.parse_args() + + changelog_path = Path(args.changelog) + if not changelog_path.exists(): + print(f"Error: {changelog_path} not found.") + return + + release_notes = extract_latest_section(changelog_path) + print(release_notes) + + +if __name__ == "__main__": + main() From bc93d4481d004c8d4eb0566e729ebe41ef2d40a1 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 09:29:01 +0000 Subject: [PATCH 02/14] Create release workflow --- .github/workflows/release.yml | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..68490f89 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,64 @@ +name: Releases + +on: + push: + branches: + - main + tags: + - 'v*.*.*' + +jobs: + release: + runs-on: ubuntu-latest + needs: build_artifacts # previous jobs that create artifacts + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + + - name: Determine release info + id: release_info + run: | + if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then + TAG="latest" + RELEASE_NAME="Latest Release" + LATEST_FLAG="--latest" + else + TAG="${GITHUB_REF#refs/tags/}" + RELEASE_NAME="Release $TAG" + LATEST_FLAG="" + fi + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "release_name=$RELEASE_NAME" >> $GITHUB_OUTPUT + echo "latest_flag=$LATEST_FLAG" >> $GITHUB_OUTPUT + + - name: Update latest tag + if: github.ref == 'refs/heads/main' + uses: EndBug/latest-tag@v9 + with: + tag-name: latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate release notes + id: notes + run: | + notes=$(python create_release_notes.py ${{ steps.release_info.outputs.latest_flag }}) + echo "release_notes<> $GITHUB_OUTPUT + echo "$notes" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Create/update latest release + if: github.ref == 'refs/heads/main' + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.release_info.outputs.tag }} + name: ${{ steps.release_info.outputs.release_name }} + body: ${{ steps.notes.outputs.release_notes }} + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b72df382b2d865b2d6be08f7d1557d67c610383a Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 13:00:21 +0000 Subject: [PATCH 03/14] Automate --- .github/workflows/build.yml | 1 + .github/workflows/python-publish.yml | 1 + .github/workflows/release.yml | 50 +++++++++++++--------------- .gitignore | 1 + script/create_release_notes.py | 4 ++- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8bc4a2e6..0db3acd1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,6 +13,7 @@ permissions: jobs: build: + needs: prepare-release strategy: matrix: platform: [ubuntu-latest, macos-latest, windows-latest] diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index e9497f82..7197fed4 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -17,6 +17,7 @@ permissions: jobs: build: + needs: prepare-release name: Build distribution 📦 runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68490f89..c68aefe0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,61 +4,57 @@ on: push: branches: - main + - automate-release tags: - - 'v*.*.*' + - '*.*.*' + +permissions: + contents: read jobs: - release: + prepare-release: runs-on: ubuntu-latest - needs: build_artifacts # previous jobs that create artifacts + permissions: + contents: write + security-events: write + steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - name: Set up Python - uses: actions/setup-python@v4 + - name: Setup Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: "3.x" + python-version: '3.13' - name: Determine release info id: release_info run: | - if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then + if [[ "${GITHUB_REF}" == "refs/heads/automate-release" ]]; then TAG="latest" - RELEASE_NAME="Latest Release" - LATEST_FLAG="--latest" else TAG="${GITHUB_REF#refs/tags/}" - RELEASE_NAME="Release $TAG" - LATEST_FLAG="" fi echo "tag=$TAG" >> $GITHUB_OUTPUT - echo "release_name=$RELEASE_NAME" >> $GITHUB_OUTPUT - echo "latest_flag=$LATEST_FLAG" >> $GITHUB_OUTPUT - name: Update latest tag - if: github.ref == 'refs/heads/main' - uses: EndBug/latest-tag@v9 + if: github.ref == 'refs/heads/automate-release' + uses: EndBug/latest-tag@52ce15b2695f86a4ce47b72387dee54e47f6356c # v1.6.2 with: - tag-name: latest + ref: latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Generate release notes id: notes run: | - notes=$(python create_release_notes.py ${{ steps.release_info.outputs.latest_flag }}) - echo "release_notes<> $GITHUB_OUTPUT - echo "$notes" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + python script/create_release_notes.py - - name: Create/update latest release - if: github.ref == 'refs/heads/main' - uses: softprops/action-gh-release@v1 + - name: Create release + uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 with: tag_name: ${{ steps.release_info.outputs.tag }} - name: ${{ steps.release_info.outputs.release_name }} - body: ${{ steps.notes.outputs.release_notes }} + name: ${{ steps.release_info.outputs.tag }} + body_path: release_notes.txt draft: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index f420ad5d..efb8b658 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ doc/landing-page/_build example/Tests/ venv* *.cdx.json +release_notes.txt diff --git a/script/create_release_notes.py b/script/create_release_notes.py index 20101c46..bffa0e4b 100644 --- a/script/create_release_notes.py +++ b/script/create_release_notes.py @@ -59,7 +59,9 @@ def main(): return release_notes = extract_latest_section(changelog_path) - print(release_notes) + + with open("release_notes.txt", "w", encoding="UTF-8") as notes: + print(release_notes, file=notes) if __name__ == "__main__": From 3c146a7b1bfec9c0db51390f461a8ad8dc36af41 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 13:26:40 +0000 Subject: [PATCH 04/14] Upload wheels to release --- .github/workflows/python-publish.yml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 7197fed4..c45a8b06 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -17,7 +17,6 @@ permissions: jobs: build: - needs: prepare-release name: Build distribution 📦 runs-on: ubuntu-latest @@ -94,3 +93,22 @@ jobs: path: dist/ - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@03f86fee9ac21f854951f5c6e2a02c2a1324aec7 # v1 + + release: + if: github.ref_type == 'tag' + runs-on: ubuntu-latest + needs: + - build + permissions: + contents: write + security-events: write + steps: + - name: Upload wheels to release + uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 + with: + tag_name: ${{ github.ref_name }} + name: ${{ github.ref_name }} + draft: true + files: dist/ + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From bbda770a35bfb4c4011a401b567124c7ae1f68a1 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 14:09:43 +0000 Subject: [PATCH 05/14] reorder workflows --- .github/workflows/build.yml | 29 ++++++++++++---- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++++++ .github/workflows/docs.yml | 10 ++---- .github/workflows/landing-page.yml | 10 ++---- .github/workflows/python-publish.yml | 29 +++++++++------- .github/workflows/release.yml | 44 ++++++++++++++++-------- .github/workflows/run.yml | 12 +++---- .github/workflows/test.yml | 8 ++--- 8 files changed, 133 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0db3acd1..c41c2fd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,11 +1,11 @@ name: Build on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened] + workflow_call: + inputs: + release_id: + required: true + type: string permissions: contents: read @@ -13,13 +13,12 @@ permissions: jobs: build: - needs: prepare-release strategy: matrix: platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} permissions: - contents: read + contents: write security-events: write steps: @@ -96,6 +95,22 @@ jobs: build/dfetch-package/*.msi build/dfetch-package/*.cdx.json + - name: Upload artifacts to release + if: ${{ inputs.release_id }} + uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 + with: + tag_name: ${{ inputs.release_id }} + files: | + build/dfetch-package/*.deb + build/dfetch-package/*.rpm + build/dfetch-package/*.pkg + build/dfetch-package/*.msi + build/dfetch-package/*.cdx.json + overwrite_files: false + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + test-binary: name: test binary needs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..fbeaae8e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI & Release Orchestration + +on: + push: + branches: + - main + tags: + - '*.*.*' + pull_request: + types: [opened, synchronize, reopened] + + # Allows to run this workflow manually + workflow_dispatch: + +permissions: + contents: read + +jobs: + draft-release: + uses: ./.github/workflows/release.yml + permissions: + contents: write + security-events: write + + build-binaries: + needs: draft-release + uses: ./.github/workflows/build.yml + permissions: + contents: write + security-events: write + with: + release_id: ${{ needs.draft-release.outputs.release_id }} + + run: + needs: draft-release + uses: ./.github/workflows/run.yml + permissions: + contents: read + security-events: write + with: + release_id: ${{ needs.draft-release.outputs.release_id }} + + python-publish: + needs: draft-release + uses: ./.github/workflows/python-publish.yml + permissions: + contents: write + security-events: write + id-token: write + with: + release_id: ${{ needs.draft-release.outputs.release_id }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3c3861a5..366d7db2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,17 +9,13 @@ jobs: docs: runs-on: ubuntu-latest steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - - name: Install Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: '3.x' + python-version: '3.13' - name: Install documentation requirements run: "pip install .[docs] && pip install sphinx_design" diff --git a/.github/workflows/landing-page.yml b/.github/workflows/landing-page.yml index ea56d26c..ec1eb7d5 100644 --- a/.github/workflows/landing-page.yml +++ b/.github/workflows/landing-page.yml @@ -13,17 +13,13 @@ jobs: publish: runs-on: ubuntu-latest steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - - name: Setup Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: "3.13" + python-version: '3.13' - name: Install dependencies run: | diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index c45a8b06..6e1b2e05 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -5,12 +5,13 @@ name: Upload Python Package on: release: - types: [created] - pull_request: - types: [opened, synchronize, reopened] + types: [published] - # Allows to run this workflow manually - workflow_dispatch: + workflow_call: + inputs: + release_id: + required: true + type: string permissions: contents: read @@ -95,20 +96,24 @@ jobs: uses: pypa/gh-action-pypi-publish@03f86fee9ac21f854951f5c6e2a02c2a1324aec7 # v1 release: - if: github.ref_type == 'tag' runs-on: ubuntu-latest - needs: - - build + if: ${{ inputs.release_id }} + needs: build permissions: contents: write security-events: write steps: - - name: Upload wheels to release + - name: Download all the dists + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5 + with: + name: python-package-distributions + path: dist/ + - name: Upload artifacts to release uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 with: - tag_name: ${{ github.ref_name }} - name: ${{ github.ref_name }} + tag_name: ${{ inputs.release_id }} + files: dist/* + overwrite_files: false draft: true - files: dist/ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c68aefe0..057546eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,11 @@ name: Releases on: - push: - branches: - - main - - automate-release - tags: - - '*.*.*' + workflow_call: + outputs: + release_id: + description: "Tag name to use of release, empty if not needed" + value: ${{ jobs.prepare-release.outputs.release_id }} permissions: contents: read @@ -18,23 +17,36 @@ jobs: contents: write security-events: write + outputs: + release_id: ${{ steps.release_info.outputs.tag }} + steps: + - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - - name: Setup Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.13' - name: Determine release info id: release_info run: | - if [[ "${GITHUB_REF}" == "refs/heads/automate-release" ]]; then - TAG="latest" - else - TAG="${GITHUB_REF#refs/tags/}" - fi - echo "tag=$TAG" >> $GITHUB_OUTPUT + if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then + BRANCH="${GITHUB_HEAD_REF}" + else + BRANCH="${GITHUB_REF#refs/heads/}" + fi + + if [[ "$BRANCH" == "main" || "$BRANCH" == "automate-release" ]]; then + TAG="latest" + elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then + TAG="${GITHUB_REF#refs/tags/}" + else + TAG="" + fi + echo "tag=$TAG" + echo "tag=$TAG" >> $GITHUB_OUTPUT - name: Update latest tag if: github.ref == 'refs/heads/automate-release' @@ -45,11 +57,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Generate release notes + if: ${{ steps.release_info.outputs.tag != '' }} id: notes run: | python script/create_release_notes.py - name: Create release + if: ${{ steps.release_info.outputs.tag != '' }} uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 with: tag_name: ${{ steps.release_info.outputs.tag }} diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index 348576e1..ef9a5108 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -1,11 +1,11 @@ name: Run on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened] + workflow_call: + inputs: + release_id: + required: true + type: string permissions: contents: read @@ -58,7 +58,7 @@ jobs: dfetch update dfetch report - test: + run: strategy: matrix: platform: [ubuntu-latest, macos-latest, windows-latest] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 51bf8dfb..4bb174f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,15 +14,11 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - - name: Setup Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.13' From 284293680be2befd700ac7fbabaf1493bace2ae8 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 16:54:11 +0000 Subject: [PATCH 06/14] Make sure zig is installed --- .github/workflows/build.yml | 4 ++++ .github/workflows/run.yml | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c41c2fd4..e67e29e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,10 @@ jobs: with: python-version: '3.13' + - name: Install Zig (Windows) + if: runner.os == 'Windows' + run: choco install zig -y + - name: Set up Ruby if: ${{ matrix.platform != 'windows-latest' }} uses: ruby/setup-ruby@4c24fa5ec04b2e79eb40571b1cee2a0d2b705771 #v1.278.0 diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index ef9a5108..f4ea4c63 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -35,6 +35,9 @@ jobs: echo "C:\Program Files (x86)\Subversion\bin" >> $env:GITHUB_PATH svn --version # Verify installation + - name: Install Zig (Windows) + run: choco install zig -y + - name: Install dfetch run: pip install . @@ -102,6 +105,10 @@ jobs: echo "C:\Program Files (x86)\Subversion\bin" >> $env:GITHUB_PATH svn --version # Verify installation + - name: Install Zig (Windows) + if: runner.os == 'Windows' + run: choco install zig -y + - name: Install dfetch run: pip install . From d2b25ef2573d639240165ef64cfa7bf43522926e Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 17:06:48 +0000 Subject: [PATCH 07/14] Make tag annotated --- .github/workflows/build.yml | 55 +++++++++++++++++++++++++++- .github/workflows/ci.yml | 10 ----- .github/workflows/python-publish.yml | 9 ++--- .github/workflows/release.yml | 20 ++++++++-- README.md | 3 +- pyproject.toml | 1 + script/create_sbom.py | 40 +++++++++++++++----- script/package.py | 29 +++++++++++---- 8 files changed, 129 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e67e29e4..e2d60d30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,9 @@ jobs: egress-policy: audit - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 + with: + persist-credentials: false + fetch-depth: 0 # Fetches all history and tags - name: Setup Python uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 @@ -110,7 +113,6 @@ jobs: build/dfetch-package/*.pkg build/dfetch-package/*.msi build/dfetch-package/*.cdx.json - overwrite_files: false draft: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -166,3 +168,54 @@ jobs: - run: dfetch update - run: dfetch update - run: dfetch report -t sbom + + + build-whl: + name: Build wheel 📦 + runs-on: ubuntu-latest + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 + with: + persist-credentials: false + fetch-depth: 0 # Fetches all history and tags + - name: Set up Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.13' + - name: Install dependencies + run: python -m pip install --upgrade pip build --user + - name: Build a binary wheel and a source tarball + run: python3 -m build + - name: Store the distribution packages + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: python-package-distributions + path: dist/ + + release: + runs-on: ubuntu-latest + if: ${{ inputs.release_id }} + needs: build-whl + permissions: + contents: write + security-events: write + steps: + - name: Download all the dists + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5 + with: + name: python-package-distributions + path: dist/ + - name: Upload artifacts to release + uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 + with: + tag_name: ${{ inputs.release_id }} + files: dist/* + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbeaae8e..73c55f57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,13 +39,3 @@ jobs: security-events: write with: release_id: ${{ needs.draft-release.outputs.release_id }} - - python-publish: - needs: draft-release - uses: ./.github/workflows/python-publish.yml - permissions: - contents: write - security-events: write - id-token: write - with: - release_id: ${{ needs.draft-release.outputs.release_id }} diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 6e1b2e05..06252557 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -7,11 +7,9 @@ on: release: types: [published] - workflow_call: - inputs: - release_id: - required: true - type: string + # No support for reusable workflows (yet): https://github.com/pypi/warehouse/issues/11096 + pull_request: + types: [opened, synchronize, reopened] permissions: contents: read @@ -113,7 +111,6 @@ jobs: with: tag_name: ${{ inputs.release_id }} files: dist/* - overwrite_files: false draft: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 057546eb..b6ca09c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,26 +49,40 @@ jobs: echo "tag=$TAG" >> $GITHUB_OUTPUT - name: Update latest tag - if: github.ref == 'refs/heads/automate-release' + if: ${{ steps.release_info.outputs.tag }} == 'latest' uses: EndBug/latest-tag@52ce15b2695f86a4ce47b72387dee54e47f6356c # v1.6.2 with: ref: latest + description: Last state in main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Generate release notes - if: ${{ steps.release_info.outputs.tag != '' }} + if: ${{ steps.release_info.outputs.tag }} id: notes run: | python script/create_release_notes.py + - name: Delete existing release assets + if: ${{ steps.release_info.outputs.tag }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.release_info.outputs.tag }} + run: | + if gh release view "$TAG" >/dev/null 2>&1; then + gh release delete "$TAG" --yes + else + echo "No release found for $TAG." + fi + - name: Create release - if: ${{ steps.release_info.outputs.tag != '' }} + if: ${{ steps.release_info.outputs.tag }} uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 with: tag_name: ${{ steps.release_info.outputs.tag }} name: ${{ steps.release_info.outputs.tag }} body_path: release_notes.txt draft: true + files: LICENSE env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 28ffcdd6..f7320850 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,7 @@ pip install git+https://github.com/dfetch-org/dfetch.git#egg=dfetch ### Binary distributions -The [build.yml](https://github.com/dfetch-org/dfetch/actions/workflows/build.yml) produces installers for all major platforms. -See the artifacts in the run. +Each release on the [releases page](https://github.com/dfetch-org/dfetch/releases) provides installers for all major platforms. - Linux `.deb` & `.rpm` - macOS `.pkg` diff --git a/pyproject.toml b/pyproject.toml index b2a782a5..8250a286 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,6 +104,7 @@ casts = ['asciinema==2.4.0'] build = [ 'nuitka==2.8.9', "tomli; python_version < '3.11'", # Tomllib is default in 3.11, required for letting codespell read the pyproject.toml] + "setuptools-scm==9.2.2", # For determining version ] sbom = ["cyclonedx-bom==7.2.1"] diff --git a/script/create_sbom.py b/script/create_sbom.py index ddf3b5cf..0bc9bafa 100644 --- a/script/create_sbom.py +++ b/script/create_sbom.py @@ -8,21 +8,22 @@ import venv from pathlib import Path -from dfetch import __version__ - logging.basicConfig(level=logging.INFO) PROJECT_DIR = Path(__file__).parent.parent.resolve() -OUTPUT_FILE = ( - PROJECT_DIR - / "build" - / "dfetch-package" - / f"dfetch-{__version__}.{sys.platform}.cdx.json" -) + DEPS = f"{PROJECT_DIR}[sbom]" +PLATFORM_NAME = "nix" + +if sys.platform.startswith("darwin"): + PLATFORM_NAME = "osx" +elif sys.platform.startswith("win"): + PLATFORM_NAME = "win" + + @contextlib.contextmanager def temporary_venv(): """Create a temporary virtual environment and clean it up on exit.""" @@ -40,8 +41,29 @@ def temporary_venv(): with temporary_venv() as python: - OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True) subprocess.check_call([python, "-m", "pip", "install", DEPS]) # nosec + + __version__ = ( + subprocess.run( # nosec + [ + python, + "-c", + "from importlib.metadata import version; print(version('dfetch'))", + ], + check=True, + capture_output=True, + ) + .stdout.decode("UTF-8") + .strip() + ) + + OUTPUT_FILE = ( + PROJECT_DIR + / "build" + / "dfetch-package" + / f"dfetch-{__version__}-{PLATFORM_NAME}.cdx.json" + ) + OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True) subprocess.check_call( # nosec [python, "-m", "cyclonedx_py", "environment", "-o", str(OUTPUT_FILE)] ) diff --git a/script/package.py b/script/package.py index fd88afaa..2435bb6f 100644 --- a/script/package.py +++ b/script/package.py @@ -7,7 +7,14 @@ import xml.etree.ElementTree as ET # nosec (only used for XML generation, not parsing untrusted input) from pathlib import Path -from dfetch import __version__ +from setuptools_scm import get_version + +from dfetch import __version__ as __digit_only_version__ # Used inside the installers + +__version__ = get_version( # Used to name the installers + version_scheme="guess-next-dev", + local_scheme="no-local-version", +) # Configuration loading with open("pyproject.toml", "rb") as pyproject_file: @@ -38,6 +45,14 @@ WINDOWS_ICO_PATH = Path(WINDOWS_ICO).resolve() if WINDOWS_ICO else None +PLATFORM_NAME = "nix" + +if sys.platform.startswith("darwin"): + PLATFORM_NAME = "osx" +elif sys.platform.startswith("win"): + PLATFORM_NAME = "win" + + def run_command(command: list[str]) -> None: """Run a system command and handle errors.""" resolved_cmd = shutil.which(command[0]) @@ -53,7 +68,7 @@ def run_command(command: list[str]) -> None: def package_linux() -> None: """Package the build directory into .deb and .rpm installers.""" for target in ("deb", "rpm"): - output = f"{OUTPUT_DIR}/{PACKAGE_NAME}_{__version__}.{target}" + output = f"{OUTPUT_DIR}/{PACKAGE_NAME}-{__version__}-{PLATFORM_NAME}.{target}" cmd = [ "fpm", "-s", @@ -63,7 +78,7 @@ def package_linux() -> None: "-n", PACKAGE_NAME, "-v", - __version__, + __digit_only_version__, "-C", str(BUILD_DIR), "--prefix", @@ -94,7 +109,7 @@ def package_macos() -> None: "-n", PACKAGE_NAME, "-v", - __version__, + __digit_only_version__, "-C", str(BUILD_DIR), # https://github.com/jordansissel/fpm/issues/1996 This prefix results in /opt/dfetch/opt/dfetch @@ -109,7 +124,7 @@ def package_macos() -> None: "--license", LICENSE, "-p", - f"{OUTPUT_DIR}/{PACKAGE_NAME}_{__version__}.pkg", + f"{OUTPUT_DIR}/{PACKAGE_NAME}-{__version__}-{PLATFORM_NAME}.pkg", ".", ] run_command(cmd) @@ -135,7 +150,7 @@ def generate_wix_xml(build_dir: Path, output_wxs: Path) -> None: "Package", Name=PACKAGE_NAME, Manufacturer=MAINTAINER, - Version=__version__, + Version=__digit_only_version__, UpgradeCode=UPGRADE_CODE, ) @@ -232,7 +247,7 @@ def package_windows() -> None: OUTPUT_DIR.mkdir(parents=True, exist_ok=True) wix_file = OUTPUT_DIR / f"{PACKAGE_NAME}.wxs" wix_proj = OUTPUT_DIR / f"{PACKAGE_NAME}.wixproj" - msi_file = OUTPUT_DIR / f"{PACKAGE_NAME}_{__version__}.msi" + msi_file = OUTPUT_DIR / f"{PACKAGE_NAME}-{__version__}-{PLATFORM_NAME}.msi" generate_wix_xml(BUILD_DIR, wix_file) generate_wix_proj(wix_proj, wix_file) From b6fc50ce796de1dfb6be469765a635e2afb79f2d Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 21:37:33 +0000 Subject: [PATCH 08/14] cleanup script --- script/create_release_notes.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/script/create_release_notes.py b/script/create_release_notes.py index bffa0e4b..975d2596 100644 --- a/script/create_release_notes.py +++ b/script/create_release_notes.py @@ -3,7 +3,6 @@ create_release_notes.py Extracts the latest section from CHANGELOG.rst. -If --latest is provided, adds an admonition that it is unreleased. """ import argparse @@ -12,18 +11,14 @@ def extract_latest_section(changelog_path: Path) -> str: - """ - Extract the latest release section from a CHANGELOG.rst file. - """ + """Extract the latest release section from a CHANGELOG file.""" + content = changelog_path.read_text(encoding="utf-8") lines = content.splitlines() - # Find lines starting with version headings like: 'v1.2.3' or '.. version' - # dfetch uses "vX.Y.Z" style headings in CHANGELOG.rst version_header_pattern = re.compile(r"^Release \d+\.\d+\.\d+") - start_idx = None - end_idx = None + start_idx, end_idx = None, None for idx, line in enumerate(lines): if version_header_pattern.match(line.strip()): @@ -31,9 +26,8 @@ def extract_latest_section(changelog_path: Path) -> str: break if start_idx is None: - raise ValueError("No release section found in CHANGELOG.rst") + raise ValueError(f"No release section found in {changelog_path}") - # Find the next version header to determine the end for idx in range(start_idx + 1, len(lines)): if version_header_pattern.match(lines[idx].strip()): end_idx = idx @@ -58,10 +52,9 @@ def main(): print(f"Error: {changelog_path} not found.") return - release_notes = extract_latest_section(changelog_path) - - with open("release_notes.txt", "w", encoding="UTF-8") as notes: - print(release_notes, file=notes) + Path("release_notes.txt").write_text( + extract_latest_section(changelog_path), encoding="UTF-8" + ) if __name__ == "__main__": From 45696fe49934649279a83f98a44e1fe64f9bcbc8 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 21:48:53 +0000 Subject: [PATCH 09/14] Cleanup --- .github/workflows/build.yml | 7 ++++--- .github/workflows/ci.yml | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2d60d30..ef488dc9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: path: ${{ github.workspace }}\.clcache key: ${{ github.job }}-${{ matrix.platform }} - - name: Create binary + - name: Create sbom, binary & package env: CCACHE_BASEDIR: ${{ github.workspace }} CCACHE_NOHASHDIR: true @@ -102,7 +102,7 @@ jobs: build/dfetch-package/*.msi build/dfetch-package/*.cdx.json - - name: Upload artifacts to release + - name: Upload installer to release if: ${{ inputs.release_id }} uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 with: @@ -118,7 +118,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} test-binary: - name: test binary + name: Test dfetch from installer needs: - build strategy: @@ -199,6 +199,7 @@ jobs: path: dist/ release: + name: Upload wheel to release 📦 runs-on: ubuntu-latest if: ${{ inputs.release_id }} needs: build-whl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73c55f57..7c6e9ec0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,26 +16,26 @@ permissions: contents: read jobs: - draft-release: + prep-release: uses: ./.github/workflows/release.yml permissions: contents: write security-events: write - build-binaries: - needs: draft-release + build: + needs: prep-release uses: ./.github/workflows/build.yml permissions: contents: write security-events: write with: - release_id: ${{ needs.draft-release.outputs.release_id }} + release_id: ${{ needs.prep-release.outputs.release_id }} run: - needs: draft-release + needs: prep-release uses: ./.github/workflows/run.yml permissions: contents: read security-events: write with: - release_id: ${{ needs.draft-release.outputs.release_id }} + release_id: ${{ needs.prep-release.outputs.release_id }} From 07fbbf3bc36364cbf5e884604c712a560fb42a13 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 22:16:05 +0000 Subject: [PATCH 10/14] Review comments --- .github/workflows/build.yml | 2 ++ .github/workflows/ci.yml | 4 +--- .github/workflows/python-publish.yml | 24 +----------------------- .github/workflows/release.yml | 6 +++--- .github/workflows/run.yml | 4 ---- pyproject.toml | 2 +- script/create_release_notes.py | 4 +++- script/package.py | 1 + 8 files changed, 12 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef488dc9..32dfd4d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -114,6 +114,7 @@ jobs: build/dfetch-package/*.msi build/dfetch-package/*.cdx.json draft: true + preserve_order: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -218,5 +219,6 @@ jobs: tag_name: ${{ inputs.release_id }} files: dist/* draft: true + preserve_order: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c6e9ec0..c681b036 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: branches: - main tags: - - '*.*.*' + - '[0-9]+.[0-9]+.[0-9]+' pull_request: types: [opened, synchronize, reopened] @@ -37,5 +37,3 @@ jobs: permissions: contents: read security-events: write - with: - release_id: ${{ needs.prep-release.outputs.release_id }} diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 06252557..8c19f27a 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -5,7 +5,7 @@ name: Upload Python Package on: release: - types: [published] + types: [published] # Once manually verified, draft is released # No support for reusable workflows (yet): https://github.com/pypi/warehouse/issues/11096 pull_request: @@ -92,25 +92,3 @@ jobs: path: dist/ - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@03f86fee9ac21f854951f5c6e2a02c2a1324aec7 # v1 - - release: - runs-on: ubuntu-latest - if: ${{ inputs.release_id }} - needs: build - permissions: - contents: write - security-events: write - steps: - - name: Download all the dists - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5 - with: - name: python-package-distributions - path: dist/ - - name: Upload artifacts to release - uses: softprops/action-gh-release@5122b4edc95f85501a71628a57dc180a03ec7588 # v2.5.0 - with: - tag_name: ${{ inputs.release_id }} - files: dist/* - draft: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6ca09c1..30ab5504 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: echo "tag=$TAG" >> $GITHUB_OUTPUT - name: Update latest tag - if: ${{ steps.release_info.outputs.tag }} == 'latest' + if: ${{ steps.release_info.outputs.tag == 'latest' }} uses: EndBug/latest-tag@52ce15b2695f86a4ce47b72387dee54e47f6356c # v1.6.2 with: ref: latest @@ -63,8 +63,8 @@ jobs: run: | python script/create_release_notes.py - - name: Delete existing release assets - if: ${{ steps.release_info.outputs.tag }} + - name: Delete existing release + if: ${{ steps.release_info.outputs.tag == 'latest' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ steps.release_info.outputs.tag }} diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index f4ea4c63..670ed882 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -2,10 +2,6 @@ name: Run on: workflow_call: - inputs: - release_id: - required: true - type: string permissions: contents: read diff --git a/pyproject.toml b/pyproject.toml index 8250a286..38afeeef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "setuptools-scm", "wheel"] +requires = ["setuptools", "setuptools-scm==9.2.2", "wheel"] build-backend = "setuptools.build_meta" [project] diff --git a/script/create_release_notes.py b/script/create_release_notes.py index 975d2596..5241a47a 100644 --- a/script/create_release_notes.py +++ b/script/create_release_notes.py @@ -7,6 +7,7 @@ import argparse import re +import sys from pathlib import Path @@ -33,6 +34,7 @@ def extract_latest_section(changelog_path: Path) -> str: end_idx = idx break + # If end_idx is None, capture all lines to the end (single release section) section_lines = lines[start_idx:end_idx] return "\n".join(section_lines).strip() @@ -50,7 +52,7 @@ def main(): changelog_path = Path(args.changelog) if not changelog_path.exists(): print(f"Error: {changelog_path} not found.") - return + sys.exit(1) Path("release_notes.txt").write_text( extract_latest_section(changelog_path), encoding="UTF-8" diff --git a/script/package.py b/script/package.py index 2435bb6f..bd82d296 100644 --- a/script/package.py +++ b/script/package.py @@ -12,6 +12,7 @@ from dfetch import __version__ as __digit_only_version__ # Used inside the installers __version__ = get_version( # Used to name the installers + root=".", version_scheme="guess-next-dev", local_scheme="no-local-version", ) From 14b8b207d5b6bef3cf8bdf9e2594825692038cb3 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 22:19:35 +0000 Subject: [PATCH 11/14] Update contributing manual --- doc/contributing.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index bdb601c7..8b9fbb75 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -108,10 +108,8 @@ Releasing git tag -a '0.5.0' -m "Release version 0.5.0" git push --tags -- If all tests ok, create release in the `GitHub webui `_. -- Make sure all dependencies in ``pyproject.toml`` are pinned. -- Copy the CHANGELOG entry of the release to github. -- When the release is created, a new package is automatically pushed to `PyPi `_. +- The ``ci.yml`` job will automatically create a draft release in `GitHub Releases `_ with all artifacts. +- Once the release is published, a new package is automatically pushed to `PyPi `_. - After release, add new header to ``CHANGELOG.rst``: From 13f10e3f88cccad65718f4b88003f1c2e10380c8 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 22:23:55 +0000 Subject: [PATCH 12/14] Restore step titles --- .github/workflows/docs.yml | 8 ++++++-- .github/workflows/landing-page.yml | 10 +++++++--- .github/workflows/test.yml | 8 ++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 366d7db2..d762166a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,11 +9,15 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + + - name: Install Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.13' diff --git a/.github/workflows/landing-page.yml b/.github/workflows/landing-page.yml index ec1eb7d5..ea56d26c 100644 --- a/.github/workflows/landing-page.yml +++ b/.github/workflows/landing-page.yml @@ -13,13 +13,17 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + + - name: Setup Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: '3.13' + python-version: "3.13" - name: Install dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4bb174f7..51bf8dfb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,11 +14,15 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0 - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + + - name: Setup Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.13' From 39d87a19964c68d8a4f3527dd2eab44d8d83d34f Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 22:32:42 +0000 Subject: [PATCH 13/14] Review fixes --- .github/workflows/ci.yml | 1 - .github/workflows/release.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c681b036..1a43091a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,6 @@ jobs: release_id: ${{ needs.prep-release.outputs.release_id }} run: - needs: prep-release uses: ./.github/workflows/run.yml permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 30ab5504..4b922896 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,7 +38,7 @@ jobs: BRANCH="${GITHUB_REF#refs/heads/}" fi - if [[ "$BRANCH" == "main" || "$BRANCH" == "automate-release" ]]; then + if [[ "$BRANCH" == "main" ]]; then TAG="latest" elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then TAG="${GITHUB_REF#refs/tags/}" From d7655f7255db5b099c6db99047d765d1e528f97d Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Jan 2026 22:55:08 +0000 Subject: [PATCH 14/14] Pin zig --- .github/workflows/run.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index 670ed882..ca4ce67d 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -32,7 +32,7 @@ jobs: svn --version # Verify installation - name: Install Zig (Windows) - run: choco install zig -y + run: choco install zig --version=0.15.2 -y - name: Install dfetch run: pip install . @@ -103,7 +103,7 @@ jobs: - name: Install Zig (Windows) if: runner.os == 'Windows' - run: choco install zig -y + run: choco install zig --version=0.15.2 -y - name: Install dfetch run: pip install .