diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e83b24786d..b82b04afc6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,8 @@ updates: directory: / schedule: interval: daily + cooldown: + default-days: 7 commit-message: prefix: ⬆ labels: [dependencies, internal] @@ -12,7 +14,9 @@ updates: - package-ecosystem: uv directory: / schedule: - interval: weekly + interval: daily + cooldown: + default-days: 7 commit-message: prefix: ⬆ labels: [dependencies, internal] @@ -20,7 +24,9 @@ updates: - package-ecosystem: bun directory: / schedule: - interval: weekly + interval: daily + cooldown: + default-days: 7 commit-message: prefix: ⬆ labels: [dependencies, internal] @@ -32,7 +38,9 @@ updates: - /backend - /frontend schedule: - interval: weekly + interval: daily + cooldown: + default-days: 7 commit-message: prefix: ⬆ labels: [dependencies, internal] @@ -40,7 +48,18 @@ updates: - package-ecosystem: docker-compose directory: / schedule: - interval: weekly + interval: daily + cooldown: + default-days: 7 + commit-message: + prefix: ⬆ + labels: [dependencies, internal] + - package-ecosystem: "pre-commit" + directory: "/" + schedule: + interval: "daily" + cooldown: + default-days: 7 commit-message: prefix: ⬆ labels: [dependencies, internal] diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml index 0308d7a07f..01a0824449 100644 --- a/.github/workflows/add-to-project.yml +++ b/.github/workflows/add-to-project.yml @@ -1,12 +1,14 @@ name: Add to Project on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] issues: types: - opened - reopened +permissions: {} + jobs: add-to-project: name: Add to project @@ -15,4 +17,4 @@ jobs: - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 with: project-url: https://github.com/orgs/fastapi/projects/2 - github-token: ${{ secrets.PROJECTS_TOKEN }} + github-token: ${{ secrets.PROJECTS_TOKEN }} # zizmor: ignore[secrets-outside-env] diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 76965a4c45..84257bbcdf 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -5,8 +5,11 @@ on: types: - published +permissions: {} + jobs: deploy: + environment: production # Do not deploy in the main repository, only in user projects if: github.repository_owner != 'fastapi' runs-on: @@ -28,5 +31,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_PRODUCTION }} build - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_PRODUCTION }} up -d diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 30e3c1bda0..bb0c1dc286 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -5,8 +5,11 @@ on: branches: - master +permissions: {} + jobs: deploy: + environment: staging # Do not deploy in the main repository, only in user projects if: github.repository_owner != 'fastapi' runs-on: @@ -28,5 +31,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_STAGING }} build - run: docker compose -f compose.yml --project-name ${{ secrets.STACK_NAME_STAGING }} up -d diff --git a/.github/workflows/detect-conflicts.yml b/.github/workflows/detect-conflicts.yml index 3ac6f65e2f..38d526bd9b 100644 --- a/.github/workflows/detect-conflicts.yml +++ b/.github/workflows/detect-conflicts.yml @@ -1,9 +1,11 @@ name: "Conflict detector" on: push: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] types: [synchronize] +permissions: {} + jobs: main: permissions: diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 99a87eddfb..2dd7802d5b 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -9,19 +9,20 @@ on: issues: types: - labeled - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - labeled workflow_dispatch: -permissions: - issues: write - pull-requests: write +permissions: {} jobs: issue-manager: if: github.repository_owner == 'fastapi' runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - name: Dump GitHub context env: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 6ba567399b..58c6892375 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,6 @@ name: Labels on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - opened - synchronize @@ -9,6 +9,8 @@ on: - labeled - unlabeled +permissions: {} + jobs: labeler: permissions: diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml index 5424ca228c..dbf7d26a0a 100644 --- a/.github/workflows/latest-changes.yml +++ b/.github/workflows/latest-changes.yml @@ -1,7 +1,7 @@ name: Latest Changes on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] branches: - master types: @@ -16,11 +16,14 @@ on: required: false default: "false" +permissions: {} + jobs: latest-changes: runs-on: ubuntu-latest permissions: pull-requests: read + if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true steps: - name: Dump GitHub context env: @@ -29,7 +32,8 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # To allow latest-changes to commit to the main branch - token: ${{ secrets.LATEST_CHANGES }} + token: ${{ secrets.LATEST_CHANGES }} # zizmor: ignore[secrets-outside-env] + persist-credentials: true # required by tiangolo/latest-changes - uses: tiangolo/latest-changes@c9d329cb147f0ddf4fb631214e3f838ff17ccbbd # 0.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 639ac9fc47..0001fd12ad 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -15,6 +15,8 @@ on: required: false default: 'false' +permissions: {} + jobs: changes: runs-on: ubuntu-latest @@ -23,6 +25,8 @@ jobs: changed: ${{ steps.filter.outputs.changed }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false # For pull requests it's not necessary to checkout the code but for the main branch it is - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 id: filter @@ -48,7 +52,9 @@ jobs: fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: oven-sh/setup-bun@v2 + with: + persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.10' @@ -59,6 +65,8 @@ jobs: limit-access-to-actor: true - name: Install uv uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + version: "0.11.4" - run: uv sync working-directory: backend - run: bun ci @@ -87,7 +95,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: oven-sh/setup-bun@v2 + with: + persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - name: Install dependencies run: bun ci - name: Download blob reports from GitHub Actions Artifacts diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index d3bb89e3ba..78eec1cdbe 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,6 +6,8 @@ on: - opened - synchronize +permissions: {} + env: # Forks and Dependabot don't have access to secrets HAS_SECRETS: ${{ secrets.PRE_COMMIT != '' }} @@ -28,7 +30,8 @@ jobs: # And it needs the full history to be able to compute diffs fetch-depth: 0 # A token other than the default GITHUB_TOKEN is needed to be able to trigger CI - token: ${{ secrets.PRE_COMMIT }} + token: ${{ secrets.PRE_COMMIT }} # zizmor: ignore[secrets-outside-env] + persist-credentials: true # Required for `git push` command # pre-commit lite ci needs the default checkout configs to work - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 name: Checkout PR for fork @@ -37,7 +40,8 @@ jobs: # To be able to commit it needs the head branch of the PR, the remote one ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - - uses: oven-sh/setup-bun@v2 + persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -45,6 +49,7 @@ jobs: - name: Setup uv uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: + version: "0.11.4" cache-dependency-glob: | requirements**.txt pyproject.toml @@ -55,7 +60,7 @@ jobs: run: bun ci - name: Run prek - pre-commit id: precommit - run: uvx prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure + run: uv run prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure continue-on-error: true - name: Commit and push changes if: env.HAS_SECRETS == 'true' diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index c9b4da97fd..75f07ac6bf 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -1,10 +1,12 @@ name: Smokeshow on: - workflow_run: + workflow_run: # zizmor: ignore[dangerous-triggers] workflows: [Test Backend] types: [completed] +permissions: {} + jobs: smokeshow: runs-on: ubuntu-latest @@ -14,10 +16,19 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" - - run: pip install smokeshow + - name: Setup uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + version: "0.11.4" + cache-dependency-glob: | + pyproject.toml + uv.lock + - run: uv sync --all-packages --no-dev --group github-actions - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: coverage-html @@ -31,4 +42,4 @@ jobs: SMOKESHOW_GITHUB_CONTEXT: coverage SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} - SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }} + SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }} # zizmor: ignore[secrets-outside-env] diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 9558ab72d3..92faaa9dbf 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -9,18 +9,24 @@ on: - opened - synchronize +permissions: {} + jobs: test-backend: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.10" - name: Install uv uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + version: "0.11.4" - run: docker compose down -v --remove-orphans - run: docker compose up -d db mailcatcher - name: Migrate DB diff --git a/.github/workflows/test-docker-compose.yml b/.github/workflows/test-docker-compose.yml index add87212c6..d7407cd321 100644 --- a/.github/workflows/test-docker-compose.yml +++ b/.github/workflows/test-docker-compose.yml @@ -9,6 +9,8 @@ on: - opened - synchronize +permissions: {} + jobs: test-docker-compose: @@ -16,6 +18,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - run: docker compose build - run: docker compose down -v --remove-orphans - run: docker compose up -d --wait backend frontend adminer diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f18ae2250..7889ad84f8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,3 +67,11 @@ repos: entry: uv run python scripts/add_latest_release_date.py files: ^release-notes\.md$ pass_filenames: false + + - id: zizmor + name: zizmor + language: python + entry: uv run zizmor . + files: ^\.github\/workflows\/ + require_serial: true + pass_filenames: false diff --git a/deployment.md b/deployment.md index 4b8ebc1988..996512db04 100644 --- a/deployment.md +++ b/deployment.md @@ -288,9 +288,15 @@ cd /home/github/actions-runner You can read more about it in the official guide: [Configuring the self-hosted runner application as a service](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service). +### Configure GitHub Environments + +The deployment workflows use [GitHub Environments](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) for `staging` and `production`. This enables environment-specific secrets, deployment protection rules (e.g. required reviewers, wait timers), and deployment status tracking. + +To configure them, go to your repository's **Settings** > **Environments** and create the `staging` and `production` environments. + ### Set Secrets -On your repository, configure secrets for the environment variables you need, the same ones described above, including `SECRET_KEY`, etc. Follow the [official GitHub guide for setting repository secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository). +For each GitHub Environment (`staging` and `production`), configure the required secrets as [environment secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment). Environment secrets are preferred over [repository secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) because they are scoped to the specific environment, reducing exposure and aligning with any protection rules you configure. The current Github Actions workflows expect these secrets: @@ -313,6 +319,8 @@ There are GitHub Action workflows in the `.github/workflows` directory already c * `staging`: after pushing (or merging) to the branch `master`. * `production`: after publishing a release. +Both workflows are associated with their respective GitHub Environments, so deployments will be visible in the repository's **Environments** section and will respect any protection rules you configure. + If you need to add extra environments you could use those as a starting point. ## URLs diff --git a/pyproject.toml b/pyproject.toml index 3935549783..3d7745e75e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,11 @@ [tool.uv.workspace] members = ["backend"] + +[dependency-groups] +dev = [ + "zizmor>=1.23.1", +] + +github-actions = [ + "smokeshow >=0.5.0", +] diff --git a/uv.lock b/uv.lock index 312035345c..f5adf8b2c2 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,10 @@ members = [ "app", ] +[manifest.dependency-groups] +dev = [{ name = "zizmor", specifier = ">=1.23.1" }] +github-actions = [{ name = "smokeshow", specifier = ">=0.5.0" }] + [[package]] name = "alembic" version = "1.18.1" @@ -2034,6 +2038,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "smokeshow" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/94/c99b76517c268ef8d5c2ff88faba5a019664bd69e4754944afa294b4f24c/smokeshow-0.5.0.tar.gz", hash = "sha256:91dcabc29ac3116bff59b4d8a7bda4ae3ccc4c70742a38cec7127b8162e4a0f6", size = 101349, upload-time = "2025-01-07T19:41:51.732Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/10/0d23e4953eb7c1e1ad848084b3115f19234f34f907658ed11bed0d826aee/smokeshow-0.5.0-py3-none-any.whl", hash = "sha256:da12a960fc7cb525efc4035a0c3c9363b6217ea7e66bc39b9ed3cd8bed6eeedc", size = 8389, upload-time = "2025-01-07T19:41:49.194Z" }, +] + [[package]] name = "sqlalchemy" version = "2.0.45" @@ -2488,3 +2505,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] + +[[package]] +name = "zizmor" +version = "1.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/58/d0228b1332f001f905d3cdd288a878d339e740ef8a92c321696a7359bdcd/zizmor-1.23.1.tar.gz", hash = "sha256:eb9871f1de004d8c6e35ff403bd6a41c495062736e78b9c4a98988970c598639", size = 463942, upload-time = "2026-03-08T16:57:29.065Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/57/32893d3370aa39f140934ee346a77aff1bc38d1de5248b9385dfcea612b7/zizmor-1.23.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:85f222eb610379aeeea76e4dc616621fdae9f21db77d1b006820452cafa739eb", size = 9085239, upload-time = "2026-03-08T16:57:32.241Z" }, + { url = "https://files.pythonhosted.org/packages/e3/43/037b68a2d173a44286f27c5c47e219d8beba758a323e1642770956831732/zizmor-1.23.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:82a7925bbdbc69713cbeb19ec90012cba3b92e3ace65ae60088e9604c5724182", size = 8657180, upload-time = "2026-03-08T16:57:23.078Z" }, + { url = "https://files.pythonhosted.org/packages/e5/37/322ec0e8b8d39a7de30290b754bd564c0b1c432d72f7b7aa011eca87cc7b/zizmor-1.23.1-py3-none-manylinux_2_24_aarch64.whl", hash = "sha256:19af913bb4bcd6dfeea41477fcf203d69e053f4b14a2b35690485c44ffa6c4a7", size = 8788247, upload-time = "2026-03-08T16:57:18.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e7/5ca6f7d56741b190c6d7d3721eb98c66e23fb68d64e6886c92993e049f36/zizmor-1.23.1-py3-none-manylinux_2_28_armv7l.whl", hash = "sha256:08ae0d8f4d665f6cf9b475913c64d2193d52ffc6f02ce66d4dcfd1b92daf4f82", size = 8374212, upload-time = "2026-03-08T16:57:25.437Z" }, + { url = "https://files.pythonhosted.org/packages/d4/a5/a3784392aeaca14d65c5e5efa2795d887ba24db4871a942e06a99f90a3c8/zizmor-1.23.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:08233d0d25947e43ac92374f22383c04e43f351f44bc44d60b3c0695157c0f3e", size = 9230697, upload-time = "2026-03-08T16:57:34.425Z" }, + { url = "https://files.pythonhosted.org/packages/b6/0d/4475ded1664262af70525700e158c3156653391770159d65cd80245fb68e/zizmor-1.23.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:795e04dff47ca1d1b0af2d7a5d3a96909a18d5fa80548534951efb24af6ec83e", size = 8820009, upload-time = "2026-03-08T16:57:36.865Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/818c68d9b407e3d02fbe7e39ad73750846d19afad50c4c9ad86455214fc2/zizmor-1.23.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c62059c75100d0bc1a19cd95a6dce9b93ac5ab2e7d7bcdd974c51b2c5eb503e3", size = 8331336, upload-time = "2026-03-08T16:57:20.825Z" }, + { url = "https://files.pythonhosted.org/packages/28/bb/1c984e1474fcf5f08e5847838007668d2682e1fcbc109d481967736ab18f/zizmor-1.23.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf0dc93171e9ae7b822041471715ea7a9f5ebefa6865ceb6d1a39729a982d770", size = 9314682, upload-time = "2026-03-08T16:57:27.361Z" }, + { url = "https://files.pythonhosted.org/packages/fb/26/10f597f9b19ecd7bece2a1eb7d1ca1bd09d089d750d70365c76118056ec1/zizmor-1.23.1-py3-none-win32.whl", hash = "sha256:229c6b275941a18b03eef0ba5d24089dfbbe4fc34633a6b22bf924294ef69cde", size = 7464678, upload-time = "2026-03-08T16:57:30.569Z" }, + { url = "https://files.pythonhosted.org/packages/04/25/14071ea8ab5ebde85391d27e9de060d8a31a44eea448aba8d8bdd30693b3/zizmor-1.23.1-py3-none-win_amd64.whl", hash = "sha256:dc9befe3c08fea7d0fa3a0bc98073fadf31a77f0572b1f7931e1ff300337fe11", size = 8506938, upload-time = "2026-03-08T16:57:15.787Z" }, +]