From 67f9ffc3ad871fe51f5dddbd84079ba721903029 Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Tue, 26 May 2026 17:36:56 -0400 Subject: [PATCH] Secure CI with cooldowns etc, based on secure-ci skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Assisted-by: glm-4.7-flash pi --- .github/dependabot.yml | 4 ++++ .github/workflows/changelog.yml | 2 +- .github/workflows/codeql.yml | 13 +++++------ .github/workflows/pr-check.yml | 7 ++++-- .github/workflows/python-publish.yml | 13 +++++++---- .github/workflows/ruff-checks.yml | 7 ++++-- .github/workflows/unit-tests.yml | 10 +++++---- .pre-commit-config.yaml | 16 +++++++------- pyproject.toml | 4 ++++ uv.lock | 32 ++++++++++++++++++++++++++++ 10 files changed, 81 insertions(+), 27 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a893675..b38439f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,8 @@ updates: actions: patterns: - "*" + cooldown: + default-days: 7 # Enable version updates for pre-commit hooks - package-ecosystem: "pre-commit" @@ -27,3 +29,5 @@ updates: # Group all pre-commit hook version updates together patterns: - "*" + cooldown: + default-days: 7 diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 9951e4c..5d33bec 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -23,6 +23,6 @@ jobs: || ! contains(github.event.action, 'label') steps: - name: Check changelog - uses: tarides/changelog-check-action@v3 + uses: tarides/changelog-check-action@0189fc7eedec3ef3e9648c713908f6f2a6e99057 # v3 with: changelog: CHANGELOG.md diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0bea103..6fe6583 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,6 @@ on: permissions: contents: read - security-events: write pull-requests: read concurrency: @@ -40,14 +39,14 @@ jobs: runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} permissions: # required for all workflows - security-events: write + security-events: write # to create upload of SARIF results # required to fetch internal or private CodeQL packs packages: read # only required for workflows in private repositories actions: read - contents: read + contents: read # to checkout the repository strategy: fail-fast: false @@ -67,7 +66,9 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false # Add any setup steps before running the `github/codeql-action/init` action. # This includes steps like installing compilers or runtimes (`actions/setup-node` @@ -77,7 +78,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -106,7 +107,7 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 1277f2b..604c12a 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -18,11 +18,14 @@ jobs: name: Validate GitFlow branch rules runs-on: ubuntu-latest + env: + HEAD_REF: ${{ github.head_ref }} + steps: - name: Check PRs to main if: github.base_ref == 'main' run: | - if [[ ${{ github.head_ref }} =~ "release/*" && ${{ github.head_ref }} =~ "hotfix/*" ]]; then + if [[ ${HEAD_REF} =~ "release/*" && ${HEAD_REF} =~ "hotfix/*" ]]; then echo "ERROR: PRs targeting main must come from a release or hotfix branch" exit 1 fi @@ -30,7 +33,7 @@ jobs: - name: Check PRs to develop if: github.base_ref == 'develop' run: | - if [[ ${{ github.head_ref }} =~ "main" && ${{ github.head_ref }} =~ "feature/*" ]]; then + if [[ ${HEAD_REF} =~ "main" && ${HEAD_REF} =~ "feature/*" ]]; then echo "ERROR: PRs targeting develop must come from main or a feature branch" exit 1 fi diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 9d226ec..54147dd 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: permissions: - contents: read + contents: read # required to checkout the repository jobs: pypi-publish: @@ -23,10 +23,15 @@ jobs: permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.ref == 'main') + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.11" - name: Install dependencies @@ -36,4 +41,4 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/.github/workflows/ruff-checks.yml b/.github/workflows/ruff-checks.yml index f50f3a8..834c71c 100644 --- a/.github/workflows/ruff-checks.yml +++ b/.github/workflows/ruff-checks.yml @@ -16,11 +16,14 @@ concurrency: jobs: ruff: + name: Ruff check and formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Run Ruff linter - uses: astral-sh/ruff-action@v3 + uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0 with: args: "check --output-format=github" - name: Run Ruff formatter diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 20ef0f9..ceb0e2c 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -25,10 +25,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install uv and Python version - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: ${{ matrix.python }} @@ -40,7 +42,7 @@ jobs: # Only upload test coverage for Python 3.12 - name: Upload test coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 if: matrix.python == '3.12' with: token: ${{ secrets.CODECOV_TOKEN }} @@ -60,7 +62,7 @@ jobs: # at the organization level and available to all repositories. Only run on # scheduled builds & pushes, since PRs automatically report to Slack. - name: Report status to Slack - uses: rtCamp/action-slack-notify@v2 + uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 if: ${{ always() && (github.event_name == 'schedule' || github.event_name == 'push') }} continue-on-error: true env: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a58352..6159a50 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: # Ruff Python linter and formatter (configs in pyproject.toml) - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.12 + rev: 0c7b6c989466a93942def1f84baf36ddfcd60c83 # frozen: v0.15.14 hooks: # Run the linter - id: ruff-check @@ -14,7 +14,7 @@ repos: - id: ruff-format # mdformat for formatting Markdown files - repo: https://github.com/hukkin/mdformat - rev: 1.0.0 + rev: 2d496dbc18e31b83a1596685347ffe0b6041daf0 # frozen: 1.0.0 hooks: - id: mdformat # Optionally add plugins @@ -23,19 +23,19 @@ repos: - mdformat-frontmatter # support GitHub front-matter # yamlfmt for formatting YAML files - repo: https://github.com/google/yamlfmt - rev: v0.21.0 + rev: b5ca1890231d5e1e5181fef75a1be609d1e25029 # frozen: v0.21.0 hooks: - id: yamlfmt # Codespell for spell checking - repo: https://github.com/codespell-project/codespell - rev: v2.4.2 + rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2 hooks: - id: codespell additional_dependencies: - tomli # Some out-of-the-box file checks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: # Check for files with merge conflict strings - id: check-merge-conflict @@ -53,16 +53,16 @@ repos: # Check uv.lock file is up to date - repo: https://github.com/astral-sh/uv-pre-commit # uv version. - rev: 0.11.11 + rev: f19149a31a5aad375f89df042b796688201d6840 # frozen: 0.11.16 hooks: - id: uv-lock # Validate Github Actions schema - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.37.2 + rev: 943377262562a12b57292fc98fabd7dbf81451fe # frozen: 0.37.2 hooks: - id: check-github-workflows # Validate Github Actions workflow files - repo: https://github.com/mpalmer/action-validator - rev: v0.9.0 + rev: 76a805bbfcba3506d6cdb4bba1810ab504e0d72b # frozen: v0.9.0 hooks: - id: action-validator diff --git a/pyproject.toml b/pyproject.toml index 5eeffbf..d507783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ dependencies = [ "addict", "cached-property", "requests", + "uv>=0.4.0", ] license = {text = "Apache License, Version 2.0"} requires-python = ">=3.8" @@ -106,3 +107,6 @@ end_of_line = "lf" [tool.codespell] ignore-words-list = "pres" # shorthand for presentation + +[tool.uv] +exclude-newer = "7 days" diff --git a/uv.lock b/uv.lock index 4244f83..87b2411 100644 --- a/uv.lock +++ b/uv.lock @@ -7,6 +7,10 @@ resolution-markers = [ "python_full_version < '3.9'", ] +[options] +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer-span = "P7D" + [[package]] name = "addict" version = "2.4.0" @@ -669,6 +673,7 @@ dependencies = [ { name = "cached-property" }, { name = "requests", version = "2.32.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "uv" }, ] [package.optional-dependencies] @@ -713,6 +718,7 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'dev'" }, { name = "pytest-cov", marker = "extra == 'test'" }, { name = "requests" }, + { name = "uv", specifier = ">=0.4.0" }, ] provides-extras = ["dev", "test"] @@ -1182,6 +1188,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "uv" +version = "0.11.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/609d5d01ba21dc8f0974610ca7802fbb2c946a0c38665cfe5c5aeddbefb5/uv-0.11.15.tar.gz", hash = "sha256:755f959ec6a2fd8ccb6ee76ad90ab759d2eb1f4797444078645dd1ee4bca92d6", size = 4159545, upload-time = "2026-05-18T19:57:48.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/7c/dcc230c5911884d8848145dabcac8fb95a5ed6f9fe1c57fae8242618f28a/uv-0.11.15-py3-none-linux_armv6l.whl", hash = "sha256:83b04ab49514a0a761ffedb36a748ee81f87746671e72088e5f32c9585e5f1a9", size = 23110183, upload-time = "2026-05-18T19:57:23.051Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/efd4e044b60eb9c3c12ee386be098d56c335538ccec7caa49349cfba9344/uv-0.11.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cae61f737be075b90be9e3f07d961072aed7019f4c9b8ed5c5d41c4d6cade3", size = 22637941, upload-time = "2026-05-18T19:57:26.752Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b8/48627f895a1569e576822e0a8416aa4797eb4a4551de21a4ad97b9b5819d/uv-0.11.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9accae33619a9166e5c48531deb455d672cfb89f9357a00975e669c76b0bd49f", size = 21258803, upload-time = "2026-05-18T19:57:05.473Z" }, + { url = "https://files.pythonhosted.org/packages/af/50/4bc8a148274feabee2d9c9f1fa15009e10c0228dfe57981ee3ea2ef1d481/uv-0.11.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c0cf52cd6d50bb9e05e2d968f45f80761107e4cbc8d4a26d9758f9d8274aaec1", size = 23066178, upload-time = "2026-05-18T19:57:33.058Z" }, + { url = "https://files.pythonhosted.org/packages/a9/56/139fc3bec9a8b0a25bfe2196123adb9f16124da437bf4fbcf0d21cfcafb2/uv-0.11.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:49dc6ed70bff00937384f96cdc4b1a4742d18e5504ec2c4a1214dba2dee5687a", size = 22705332, upload-time = "2026-05-18T19:57:36.714Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b0/b18b3dd204f8c213236a1ebd148e009861637129a8cce34df0e9aa22ed40/uv-0.11.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:adb9a89352539fdd8f7cd5f9966cf9f94fc5b98e0ccdf5003a04123dc6423bec", size = 22707534, upload-time = "2026-05-18T19:58:04.117Z" }, + { url = "https://files.pythonhosted.org/packages/76/36/3ca09f95572df99d361b49c96b1297149e96e120d8d1ecf074095a4b6da4/uv-0.11.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40ff67e3f8e8a7533781a2e892a534975a93acb83ea35460e64e7b2bf2111774", size = 24096607, upload-time = "2026-05-18T19:58:11.625Z" }, + { url = "https://files.pythonhosted.org/packages/64/be/3bdee21a296bbf5336a526e3613d0e7d4538dacc39c62d7fcba55d15f6b0/uv-0.11.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6463a299ed7e6b5a800ed6f108af8e1588352629424133ddef7572b0e1e1118", size = 25082562, upload-time = "2026-05-18T19:57:40.69Z" }, + { url = "https://files.pythonhosted.org/packages/cd/73/f371f3689ffe741066468d001d85f739fc4b5574de83b639ef19b5e8a7f4/uv-0.11.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68c1e62d4b78578b90b833553286b65d6a7e327537716441068583ba652ec4f5", size = 24253391, upload-time = "2026-05-18T19:57:18.47Z" }, + { url = "https://files.pythonhosted.org/packages/d3/16/fe392d618af6b00c064b3e718d585dcf791546a77c5123a5bec07ce53a0a/uv-0.11.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98edf1bdaf82447014852051d93e3ee95012509c567bf057fd117e6bdbd9a807", size = 24415871, upload-time = "2026-05-18T19:58:19.651Z" }, + { url = "https://files.pythonhosted.org/packages/6e/24/2e92a052fb6334fcd746d1c7cb57847c204b118c84f5da53c0f9e129f7b7/uv-0.11.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:be8f76d25bcf4c92bb384240ac1bf9aa7f51063d0bdeca4c9cf0ec3ed8b145e0", size = 23159007, upload-time = "2026-05-18T19:57:10.653Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2e/6923d0658d164bb2c435ed1868aa2d49b3074594679917a001ff92dc95bb/uv-0.11.15-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:f9f4fbbf4fe485522054f3c7496c6e8e932d6436e4200ff3daf718db0b7c7bd5", size = 23769385, upload-time = "2026-05-18T19:58:15.856Z" }, + { url = "https://files.pythonhosted.org/packages/a4/99/7e34cd949e57360814e8064cc9fb7104df445d0f6a663504e5f7473480aa/uv-0.11.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0ed920e896b2fd13a35031707e307e42fbb2681458b967440a17272d86d49137", size = 23860973, upload-time = "2026-05-18T19:57:55.575Z" }, + { url = "https://files.pythonhosted.org/packages/28/98/8fe1f5f9d816e94569a0298dd8e0936801097625fa1952162951f0d628b6/uv-0.11.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:41d907611f3e6a13262807fd7f0a17849f76285ca80f536f6b3943732bdc6656", size = 23431392, upload-time = "2026-05-18T19:57:59.814Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6b/76a1ce2fa860026913a5941700cdc7d715fce9c3277a3fa3489cf2523ca0/uv-0.11.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e3b68f8bf1a4568710f77e5bda9182ce7682811d89a8e7468c22460e032b234d", size = 24519478, upload-time = "2026-05-18T19:57:51.165Z" }, + { url = "https://files.pythonhosted.org/packages/43/60/1d58e8a05718cb50494763115710b73846cacb651fd735d285233fd72c59/uv-0.11.15-py3-none-win32.whl", hash = "sha256:8e2da3076761086a5b76869c3f38ef0509c836046ef41ddd19485dfd7271dca9", size = 22020178, upload-time = "2026-05-18T19:58:07.64Z" }, + { url = "https://files.pythonhosted.org/packages/55/53/40fcefcb348af660488597ed3c01363df7344e60611f8883750dc596f5c6/uv-0.11.15-py3-none-win_amd64.whl", hash = "sha256:cc3915ab291a1ecaf31de05f5d8bd70d09c66fe9911a53f70d9efa62ff0dbd8a", size = 24668779, upload-time = "2026-05-18T19:57:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7d/fa3a9960c95af9bbe2a629048760d0b9b4fead8ccd4f2235af747ec7cdf0/uv-0.11.15-py3-none-win_arm64.whl", hash = "sha256:4f39426a13dee24897aed60c4b98058c66f18bd983885ac5f4a54a04b24fbddf", size = 23198178, upload-time = "2026-05-18T19:57:14.68Z" }, +] + [[package]] name = "virtualenv" version = "21.2.0"