From 1469c4025dbe4c9cff4e05b9533a9a48ec1a6e06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:13:10 -0400 Subject: [PATCH 1/5] chore(deps): bump the python-minor-patch group with 4 updates (#90) Bumps the python-minor-patch group with 4 updates: [tabulate](https://github.com/astanin/python-tabulate), [socketdev](https://github.com/SocketDev/socket-sdk-python), [pytest](https://github.com/pytest-dev/pytest) and [pytest-cov](https://github.com/pytest-dev/pytest-cov). Updates `tabulate` from 0.9.0 to 0.10.0 - [Changelog](https://github.com/astanin/python-tabulate/blob/master/CHANGELOG) - [Commits](https://github.com/astanin/python-tabulate/compare/v0.9.0...v0.10.0) Updates `socketdev` from 3.0.29 to 3.3.0 - [Release notes](https://github.com/SocketDev/socket-sdk-python/releases) - [Commits](https://github.com/SocketDev/socket-sdk-python/compare/v3.0.29...v3.3.0) Updates `pytest` from 9.0.3 to 9.1.1 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.3...9.1.1) Updates `pytest-cov` from 7.0.0 to 7.1.0 - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v7.0.0...v7.1.0) --- updated-dependencies: - dependency-name: tabulate dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-patch - dependency-name: socketdev dependency-version: 3.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-patch - dependency-name: pytest dependency-version: 9.1.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-patch - dependency-name: pytest-cov dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 8 ++++---- uv.lock | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bf82834..55f6404 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,18 +24,18 @@ classifiers = [ dependencies = [ # Keep runtime deps minimal and explicit. Remove unused packages (mdutils, PyGithub) "requests>=2.33.0", - "tabulate~=0.9.0", + "tabulate~=0.10.0", "light-s3-client~=0.0.30", "PyYAML>=6.0.0", "tomli; python_version < '3.11'", - "socketdev>=3.0.29", + "socketdev>=3.3.0", "jsonschema>=4.25.1" ] [project.optional-dependencies] dev = [ - "pytest>=7.0.0", - "pytest-cov>=4.0.0", + "pytest>=9.1.1", + "pytest-cov>=7.1.0", "black>=22.0.0", "flake8>=5.0.0", ] diff --git a/uv.lock b/uv.lock index 821d045..6a825ec 100644 --- a/uv.lock +++ b/uv.lock @@ -396,7 +396,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -407,23 +407,23 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] name = "pytest-cov" -version = "7.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] [[package]] @@ -649,12 +649,12 @@ requires-dist = [ { name = "flake8", marker = "extra == 'dev'", specifier = ">=5.0.0" }, { name = "jsonschema", specifier = ">=4.25.1" }, { name = "light-s3-client", specifier = "~=0.0.30" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.1.1" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.1.0" }, { name = "pyyaml", specifier = ">=6.0.0" }, { name = "requests", specifier = ">=2.33.0" }, - { name = "socketdev", specifier = ">=3.0.29" }, - { name = "tabulate", specifier = "~=0.9.0" }, + { name = "socketdev", specifier = ">=3.3.0" }, + { name = "tabulate", specifier = "~=0.10.0" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] provides-extras = ["dev"] @@ -664,24 +664,24 @@ dev = [] [[package]] name = "socketdev" -version = "3.0.29" +version = "3.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/f4/a4434843e0f01da684d0d330f1b4b744abfad1ee4d6b6d5fddfa9228b122/socketdev-3.0.29.tar.gz", hash = "sha256:be201a9bd186da6ddae4725294d3cbf11b00ec76c96e46be38d78a569fde4af3", size = 170751, upload-time = "2026-01-21T09:15:57.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/30/16155f7f27d18274f364b3bd3506ee45d17f53fc8938aaea9a618054449b/socketdev-3.3.0.tar.gz", hash = "sha256:3d60bd4ac3201e9d581b1fe02bf2e6aef1b90c13ae75d15a8664aa9ef966734e", size = 181519, upload-time = "2026-06-10T11:41:17.942Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/08/f1cea3b342d8b9109f5792257e3f6c31c3ff53a1e42a87726a2acac65440/socketdev-3.0.29-py3-none-any.whl", hash = "sha256:c2f832a703bd61eb88a5e3f9b8079e62f7cd1352ec206a20a946c6dd34fa788e", size = 66783, upload-time = "2026-01-21T09:15:55.909Z" }, + { url = "https://files.pythonhosted.org/packages/33/dd/25622e033182e8c744d2420bb4f056206edc096a1e5ce8e4af4b0a0c0791/socketdev-3.3.0-py3-none-any.whl", hash = "sha256:513c045ce42bdd6cc2bb66a527f5863e0c399e56dbdcb1832cd5d94a5fb1a5e4", size = 67956, upload-time = "2026-06-10T11:41:16.534Z" }, ] [[package]] name = "tabulate" -version = "0.9.0" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, ] [[package]] From cb45486810482789e00b662171fdddb7cce64412 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:16:09 -0400 Subject: [PATCH 2/5] ci(deps): bump the github-actions-minor-patch group across 2 directories with 5 updates (#89) Bumps the github-actions-minor-patch group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `4.1.0` | `4.2.0` | | [docker/login-action](https://github.com/docker/login-action) | `4.2.0` | `4.4.0` | | [docker/metadata-action](https://github.com/docker/metadata-action) | `6.1.0` | `6.2.0` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `7.2.0` | `7.3.0` | | [actions/setup-python](https://github.com/actions/setup-python) | `6.2.0` | `6.3.0` | Bumps the github-actions-minor-patch group with 1 update in the /.github/actions/setup-sfw directory: [actions/setup-python](https://github.com/actions/setup-python). Updates `docker/setup-buildx-action` from 4.1.0 to 4.2.0 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5...bb05f3f5519dd87d3ba754cc423b652a5edd6d2c) Updates `docker/login-action` from 4.2.0 to 4.4.0 - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/650006c6eb7dba73a995cc03b0b2d7f5ca915bee...af1e73f918a031802d376d3c8bbc3fe56130a9b0) Updates `docker/metadata-action` from 6.1.0 to 6.2.0 - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9...dc802804100637a589fabce1cb79ff13a1411302) Updates `docker/build-push-action` from 7.2.0 to 7.3.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/f9f3042f7e2789586610d6e8b85c8f03e5195baf...53b7df96c91f9c12dcc8a07bcb9ccacbed38856a) Updates `actions/setup-python` from 6.2.0 to 6.3.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v6.2.0...ece7cb06caefa5fff74198d8649806c4678c61a1) Updates `actions/setup-python` from 6.2.0 to 6.3.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v6.2.0...ece7cb06caefa5fff74198d8649806c4678c61a1) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-minor-patch - dependency-name: docker/login-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-minor-patch - dependency-name: docker/metadata-action dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-minor-patch - dependency-name: docker/build-push-action dependency-version: 7.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-minor-patch - dependency-name: actions/setup-python dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-minor-patch - dependency-name: actions/setup-python dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-minor-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/setup-sfw/action.yml | 2 +- .github/workflows/_docker-pipeline.yml | 12 ++++++------ .github/workflows/core-tool-watch.yml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index b580759..109149e 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -20,7 +20,7 @@ inputs: runs: using: "composite" steps: - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" diff --git a/.github/workflows/_docker-pipeline.yml b/.github/workflows/_docker-pipeline.yml index fde4334..7eb558e 100644 --- a/.github/workflows/_docker-pipeline.yml +++ b/.github/workflows/_docker-pipeline.yml @@ -70,12 +70,12 @@ jobs: persist-credentials: false - name: 🔨 Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 # GHCR login runs before the build — needed to pull ghcr.io/astral-sh/uv. - name: Login to GHCR if: inputs.push - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -90,7 +90,7 @@ jobs: - name: Extract image metadata if: inputs.push id: meta - uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 with: images: | ghcr.io/socketdev/${{ inputs.name }} @@ -113,7 +113,7 @@ jobs: # Loads image into the local Docker daemon without pushing. # Writes all layers to the GHA cache so the push step is just an upload. - name: 🔨 Build (load for testing) - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: # zizmor: ignore[template-injection] — safe: always hardcoded "." from same-repo callers; passed as array element to exec, not shell-interpolated context: ${{ inputs.context }} @@ -159,7 +159,7 @@ jobs: # with public image pulls during the build step. - name: Login to Docker Hub if: inputs.push - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -167,7 +167,7 @@ jobs: # All layers are in the GHA cache from step 1 — this is just an upload. - name: 🚀 Push to registries if: inputs.push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: # zizmor: ignore[template-injection] — safe: always hardcoded "." from same-repo callers; passed as array element to exec, not shell-interpolated context: ${{ inputs.context }} diff --git a/.github/workflows/core-tool-watch.yml b/.github/workflows/core-tool-watch.yml index 51dd985..c2401b6 100644 --- a/.github/workflows/core-tool-watch.yml +++ b/.github/workflows/core-tool-watch.yml @@ -99,7 +99,7 @@ jobs: persist-credentials: false - name: 🐍 Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" From def178e63b7062e907995ff81e0ff705f3b3b45f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:16:54 -0400 Subject: [PATCH 3/5] ci(deps): bump the github-actions-major group across 1 directory with 2 updates (#91) Bumps the github-actions-major group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [actions/upload-artifact](https://github.com/actions/upload-artifact). Updates `actions/checkout` from 6.0.2 to 7.0.0 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0) Updates `actions/upload-artifact` from 4.6.2 to 7.0.1 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/ea165f8d65b6e75b540449e92b4886f43607fa02...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions-major - dependency-name: actions/upload-artifact dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/_docker-pipeline.yml | 2 +- .github/workflows/core-tool-watch.yml | 6 +++--- .github/workflows/dependency-review.yml | 10 +++++----- .github/workflows/publish-docker.yml | 2 +- .github/workflows/python-tests.yml | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/_docker-pipeline.yml b/.github/workflows/_docker-pipeline.yml index 7eb558e..15fe12b 100644 --- a/.github/workflows/_docker-pipeline.yml +++ b/.github/workflows/_docker-pipeline.yml @@ -65,7 +65,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/core-tool-watch.yml b/.github/workflows/core-tool-watch.yml index c2401b6..ebe5e18 100644 --- a/.github/workflows/core-tool-watch.yml +++ b/.github/workflows/core-tool-watch.yml @@ -80,7 +80,7 @@ jobs: contents: read issues: write # upsert the drift tracking issue on scheduled runs steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 1 persist-credentials: false @@ -91,7 +91,7 @@ jobs: # already-merged, already-scored lockfile versions -- never from the PR # under review, whose freshly-bumped packages are the very thing being # judged. On push/schedule runs both checkouts are identical. - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: main path: .scan-env @@ -150,7 +150,7 @@ jobs: - name: Upload core-tool report if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: core-tools-report path: | diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 850317f..794a00f 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -75,7 +75,7 @@ jobs: workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }} is_trusted: ${{ steps.trust.outputs.is_trusted }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false @@ -165,7 +165,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 1 persist-credentials: false @@ -201,7 +201,7 @@ jobs: - name: Upload Socket Firewall report if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sfw-report-free path: | @@ -226,7 +226,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 1 persist-credentials: false @@ -265,7 +265,7 @@ jobs: - name: Upload Socket Firewall report if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sfw-report-enterprise path: | diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 82d10fb..47aab71 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -39,7 +39,7 @@ jobs: outputs: version: ${{ steps.version.outputs.clean }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} persist-credentials: false diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index a5d7482..99fce66 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 1 persist-credentials: false From fb86cda987106138b88959e43bf4d99e3560603c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 17:16:20 -0400 Subject: [PATCH 4/5] chore(deps): bump black in the python-major group across 1 directory (#92) Bumps the python-major group with 1 update in the / directory: [black](https://github.com/psf/black). Updates `black` from 25.1.0 to 26.5.1 - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/25.1.0...26.5.1) --- updated-dependencies: - dependency-name: black dependency-version: 26.5.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: python-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- uv.lock | 97 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 55f6404..fc8be92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ dev = [ "pytest>=9.1.1", "pytest-cov>=7.1.0", - "black>=22.0.0", + "black>=26.5.1", "flake8>=5.0.0", ] diff --git a/uv.lock b/uv.lock index 6a825ec..fe586c9 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "black" -version = "25.1.0" +version = "26.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -21,28 +21,38 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload-time = "2025-01-29T05:37:06.642Z" }, - { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload-time = "2025-01-29T05:37:09.321Z" }, - { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload-time = "2025-01-29T04:18:24.432Z" }, - { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload-time = "2025-01-29T04:19:04.296Z" }, - { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" }, - { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" }, - { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" }, - { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" }, - { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" }, - { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" }, - { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" }, - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/84/b3f55026206a9e8820a91503308075ca48eadc515e436731ca01dbe043b3/black-26.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9942db8888e06943c5dde66ca0037dcff82a2a4ec1ad0ada9e0d2ee9d9823893", size = 1987719, upload-time = "2026-05-18T17:05:02.757Z" }, + { url = "https://files.pythonhosted.org/packages/c6/34/7db312c5e5783d6e76cffd9d5ac8972a32badae4c6e3288dac0eed8d3bed/black-26.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:89c93167a74d3a75dfaa38a5c7cca015537d5820dd7f17d63267d674a61cae90", size = 1810083, upload-time = "2026-05-18T17:05:04.302Z" }, + { url = "https://files.pythonhosted.org/packages/33/e2/e0101e73c2c8727634e2efcb35e2b34bd23ad70dfa673789f5773a591b21/black-26.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f2cd76d069cc54c71f10360744ba8983fbb616903b4304a85b734915c8e1b4", size = 1860633, upload-time = "2026-05-18T17:05:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4c/e15c0c5b23cf3651035fe5addcce90e283af3548a3f91bb03d81b83106ab/black-26.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:87ed5c6f450580a2f6790bc7cbfb016dfc73bc750249762268a3695361315eef", size = 1477886, upload-time = "2026-05-18T17:05:07.96Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3f/59d43ade98d2ce5c8dc34a4e46cbecd177e6d55d7d4092969c6003ccc655/black-26.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:58b4bd92cf88aacf83d88479c8f9caee044b1ec55f2451a337354a7ea2590a22", size = 1277111, upload-time = "2026-05-18T17:05:09.473Z" }, + { url = "https://files.pythonhosted.org/packages/4b/96/3c3e09f09f44a37aac36b178a279cd19aa7001bd796187a7b162a294c81f/black-26.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96ae2c733b2aabdd9986e2c5df628ff3473676cd1c5faded1ff496cf6d74083c", size = 1970639, upload-time = "2026-05-18T17:05:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/5ad117b9ee3ecd933c712bcbae610006e5b7cc9f41c526cd7ed3b6c4124c/black-26.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e48b87e03bf109288e55cfceadcfa15ff5470aca2851a851950ed2926f450d7", size = 1792130, upload-time = "2026-05-18T17:05:12.983Z" }, + { url = "https://files.pythonhosted.org/packages/06/3a/7c448bc623fcdfa96672531beb5a616ea5e64f6975955254d7731ffb0ad9/black-26.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5119fa92ae61f786e8c3662fd60aece1d0a2dd5cca5d0c79417a95e7a4272a59", size = 1846134, upload-time = "2026-05-18T17:05:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5b/0b39b3a5917f0657ac014ad2edb58c139553a478adfe7f817abf1622ff6e/black-26.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:30d3c14661f2792e9142cce3eeeb1cbc175b3eb5f733be0c8eeb99651e52b0c3", size = 1478883, upload-time = "2026-05-18T17:05:16.542Z" }, + { url = "https://files.pythonhosted.org/packages/4c/48/dc222692e0f95030db1bbfb6c857e76858bad09058221ea7aae815255327/black-26.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:1ef92b76f7733f282fd096ea406200b5a286c42947412b0eaff3a74e3616cefe", size = 1277776, upload-time = "2026-05-18T17:05:18.029Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/7744b906703228264ef73bdd534df88ec1ef3de45c4e78f6d31b9e32d0c9/black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8", size = 2012518, upload-time = "2026-05-18T17:05:20.108Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/c5a3b1636dfd09c42534f2b3cf33506814f6d3e066fb0879ffa16c1ae860/black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217", size = 1816016, upload-time = "2026-05-18T17:05:21.84Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0e/36044316b65ca471d3bb6d3703fd06fb50c6b727c3562f6a5a3153634f88/black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d", size = 1884150, upload-time = "2026-05-18T17:05:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/dafc5808c2af43672912111d7c3354af1615f7e2be3bed7a878461abbe4d/black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264", size = 1486825, upload-time = "2026-05-18T17:05:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/b965ee6ad2a311f28bdbf692def3ee9848d2ae289dab28b27657fcee3e78/black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418", size = 1288646, upload-time = "2026-05-18T17:05:26.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, ] [[package]] @@ -342,11 +352,11 @@ wheels = [ [[package]] name = "pathspec" -version = "0.12.1" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] [[package]] @@ -426,6 +436,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -645,7 +694,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "black", marker = "extra == 'dev'", specifier = ">=22.0.0" }, + { name = "black", marker = "extra == 'dev'", specifier = ">=26.5.1" }, { name = "flake8", marker = "extra == 'dev'", specifier = ">=5.0.0" }, { name = "jsonschema", specifier = ">=4.25.1" }, { name = "light-s3-client", specifier = "~=0.0.30" }, From 0d3f141365f719f22c6e027e7ff45b5f976017ef Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:47:07 -0400 Subject: [PATCH 5/5] ci: publish multi-arch Docker image variants (#85) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: publish multi-arch Docker images Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * ci: publish socket-basics heavy image Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * docs(changelog): add 2.1.0 release notes Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): harden Docker release publishing Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): address Docker publish review findings Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): harden Docker manifest publishing Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): publish heavy variant as tag suffix in the shared repo All image variants now publish to the single socket-basics repository per registry, distinguished by tag suffix (2.1.0 vs 2.1.0-heavy) instead of a separate socket-basics-heavy repository. This follows the standard Docker variant convention (like :slim/:alpine), requires no new Docker Hub repo, token rescoping, or GHCR package visibility changes, and makes retiring the POC variant trivial. - _docker-pipeline.yml: new push_name input decouples the registry repo from the local build/artifact name - publish-docker.yml: merge-manifests iterates variants with a tag_suffix, tags via metadata-action flavor suffix, and inspects both suffixed tags Co-Authored-By: Claude Fable 5 * fix: move unreleased changelog entries out of the shipped 2.1.0 section v2.1.0 was released from main with different content; this PR's entries now sit under [Unreleased] and get stamped as 2.2.0 at release time. Co-Authored-By: Claude Fable 5 * fix: correct 2.1.0 changelog date to actual release date (2026-07-22) The 2026-06-02 date reflected when the bundled commits were authored, not when v2.1.0 was actually tagged and released. Co-Authored-By: Claude Fable 5 * chore(release): prep v2.2.0 — stamp changelog, bump version files and action image ref Co-Authored-By: Claude Fable 5 * ci: gate publishing on version files matching the release tag Restores the guarantee lost when .hooks/version-check.py was removed in #46: resolve-version now fails fast if version.py, pyproject.toml, or the action.yml image tag disagree with the tag being published. Co-Authored-By: Claude Fable 5 * feat(scripts): add prep_release.py for mechanical release-prep PRs One command bumps version.py, pyproject.toml, action.yml, refreshes uv.lock, and stamps the [Unreleased] changelog section — so the final release PR is a five-file diff that always satisfies the publish workflow's version gate. Validates everything before writing anything; a failure leaves the tree untouched. Co-Authored-By: Claude Fable 5 * fix(release): sync __init__.py version and derive bumps from pyproject The sync_release_version.py check from main caught socket_basics/ __init__.py still at 2.0.3 — a duplicate version field prep_release.py didn't know about. prep_release.py now bumps only pyproject.toml (the canonical source) and delegates derived files to sync_release_version.py so the two scripts can never disagree. The publish version gate also checks __init__.py now. Co-Authored-By: Claude Fable 5 * fix(core-tool-watch): opt into fail-closed purl batch semantics The batch purl endpoints default to fail-open: inputs with pending or failed resolution are silently omitted unless the caller opts in. Fresh pins (socketdev 3.3.0) fell into that omission path and tripped the unverified-pin guard with a misleading message. - purl.post now sends poll=true + timeoutSec=120 + alerts=true (extra kwargs pass through as query params on SDK 3.0.29 and 3.3.0) - client timeout raised 60->180s so the bounded server poll can finish - synthetic pendingScan/notFound rows are mapped to a status field before severity classification (never through MALWARE_ALERT_TYPES / CRITICAL_SEVERITIES) and fail closed with distinct, precise messages - OpenGrep's pkg:github coverage-gap exemption carries over: its pin now returns a notFound row instead of being omitted, and stays exempt - log the endpoint choice + org slug for forensics Co-Authored-By: Claude Fable 5 * docs: changelog entry for core-tool-watch fail-closed purl fix Co-Authored-By: Claude Fable 5 * fix(core-tool-watch): calibrate alert thresholds for the full alert set alerts=true exposed the complete informational alert firehose for the first time (the old fail-open responses carried no alert data, so the malware gate never actually saw alerts). Calibrated against real batch data from run 30504424787: - drop capability/heuristic signals from MALWARE_ALERT_TYPES: shellAccess fires on all four tools (security CLIs spawn subprocesses), gptMalware/gptSecurity/obfuscatedFile fire on the OpenGrep repo artifact (SAST engines bundle malicious-looking test fixtures by design) - hard-fail severity gate is critical-only; high-severity rows (cve on trivy, gpt heuristics) stay visible in the report for human review Verified: replaying the failing run's report through the new rules yields green while keeping true compromise signals fail-worthy. --------- Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .dockerignore | 5 +- .github/workflows/_docker-pipeline.yml | 134 +++++++------ .github/workflows/publish-docker.yml | 253 +++++++++++++++++++++++-- .github/workflows/smoke-test.yml | 43 ++++- CHANGELOG.md | 15 +- Dockerfile | 2 +- Dockerfile.heavy | 68 +++++++ action.yml | 2 +- pyproject.toml | 2 +- scripts/check_core_tools.py | 137 ++++++++++--- scripts/docker-heavy-entrypoint.sh | 20 ++ scripts/integration-test-docker.sh | 2 +- scripts/prep_release.py | 134 +++++++++++++ scripts/smoke-test-docker.sh | 38 +++- scripts/update_changelog.py | 8 +- socket_basics/__init__.py | 2 +- socket_basics/version.py | 2 +- uv.lock | 2 +- 18 files changed, 743 insertions(+), 126 deletions(-) create mode 100644 Dockerfile.heavy create mode 100644 scripts/docker-heavy-entrypoint.sh create mode 100755 scripts/prep_release.py diff --git a/.dockerignore b/.dockerignore index 9722a63..f04d40e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,9 +10,10 @@ tests/ app_tests/ -# Docs and scripts (not needed in image) +# Docs and scripts (not needed in image, except the heavy image entrypoint) docs/ -scripts/ +scripts/* +!scripts/docker-heavy-entrypoint.sh # Markdown (keep README.md — it's copied explicitly in the Dockerfile) *.md diff --git a/.github/workflows/_docker-pipeline.yml b/.github/workflows/_docker-pipeline.yml index 15fe12b..147485a 100644 --- a/.github/workflows/_docker-pipeline.yml +++ b/.github/workflows/_docker-pipeline.yml @@ -1,13 +1,16 @@ name: _docker-pipeline (reusable) -# Reusable workflow — the single lego brick for all Docker CI steps. +# Reusable workflow — the per-arch lego brick for all Docker CI steps. # -# Called by smoke-test.yml (push: false) and publish-docker.yml (push: true). -# Step visibility is controlled by the push/tag_push inputs; the caller sets permissions. +# Called by smoke-test.yml, dependency-review.yml (push: false) and +# publish-docker.yml (push: true, once per arch via matrix). # # Two modes: # push: false → build + smoke test + integration test (main image only) -# push: true → above + push exact version tags to GHCR/Docker Hub +# push: true → above + push the built image by digest to GHCR + Docker Hub, +# and upload the resulting digest as an artifact. The caller's +# merge-manifests job assembles per-arch digests into a +# multi-arch manifest list at the user-facing tags. # # Permissions required from the calling workflow: # push: false → contents: read @@ -33,25 +36,40 @@ on: description: "Smoke-test tool set: main or app-tests" type: string required: true - push: - description: "Push to GHCR and Docker Hub after testing" - type: boolean + runs_on: + description: "Runner label (e.g. ubuntu-latest, ubuntu-24.04-arm). The build/test steps run natively on this arch." + type: string required: false - default: false - tag_push: + default: "ubuntu-latest" + arch_label: + description: "Short arch identifier used for digest artifact name and cache scope (e.g. amd64, arm64). Required when push=true." + type: string + required: false + default: "" + push_name: description: > - True when the caller was triggered by a tag push (e.g. v2.0.0). - Controls semver metadata-action tagging for exact release tags. - Passed explicitly rather than relying on github.ref_type inside the callee, - since context propagation in reusable workflows can be ambiguous. + Registry repository to push to (defaults to name). Image variants share + the base repository and are distinguished by tag suffix (e.g. -heavy), + so the heavy build passes name=socket-basics-heavy (local tag, cache + scope, digest artifact) with push_name=socket-basics. + type: string + required: false + default: "" + push: + description: "Push to GHCR + Docker Hub by digest after testing. The publish workflow merges per-arch digests into a multi-arch manifest list." type: boolean required: false default: false version: - description: "Semver without v prefix (e.g. 2.0.0) — used for OCI labels and push tags" + description: "Semver without v prefix (e.g. 2.0.0) — passed as the SOCKET_BASICS_VERSION build-arg, baked into OCI labels" type: string required: false default: "dev" + ref: + description: "Git ref to check out. Publish mode passes the resolved release tag so builds use the release source." + type: string + required: false + default: "" secrets: DOCKERHUB_USERNAME: required: false @@ -60,15 +78,20 @@ on: jobs: pipeline: - runs-on: ubuntu-latest + runs-on: ${{ inputs.runs_on }} timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: + ref: ${{ inputs.ref }} persist-credentials: false + - name: Resolve source revision + id: source + run: echo "revision=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + - name: 🔨 Set up Docker Buildx uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 @@ -87,31 +110,10 @@ jobs: # requests including pulling public base images (python, trivy, trufflehog). # Those public images pull fine without auth; only the push needs credentials. - - name: Extract image metadata - if: inputs.push - id: meta - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 - with: - images: | - ghcr.io/socketdev/${{ inputs.name }} - ${{ secrets.DOCKERHUB_USERNAME }}/${{ inputs.name }} - # Disable the automatic :latest tag — metadata-action adds it by default - # for semver tag pushes. Mutable tags are inappropriate for a security tool. - flavor: | - latest=false - tags: | - # Tag push (v2.0.0) → exact immutable version tag only. - # Minor (2.0) and latest tags are intentionally omitted. - type=semver,pattern={{version}} - # workflow_dispatch re-publish → use the version input directly - type=raw,value=${{ inputs.version }},enable=${{ !inputs.tag_push }} - labels: | - org.opencontainers.image.title=${{ inputs.name }} - org.opencontainers.image.source=https://github.com/SocketDev/socket-basics - # ── Step 1: Build ────────────────────────────────────────────────────── # Loads image into the local Docker daemon without pushing. - # Writes all layers to the GHA cache so the push step is just an upload. + # Per-arch cache scope ensures amd64 and arm64 builds don't pollute each + # other's layer cache. arch_label defaults to "smoke" when push=false. - name: 🔨 Build (load for testing) uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: @@ -123,10 +125,10 @@ jobs: tags: ${{ inputs.name }}:pipeline-test build-args: | SOCKET_BASICS_VERSION=${{ inputs.version }} - VCS_REF=${{ github.sha }} + VCS_REF=${{ steps.source.outputs.revision }} BUILD_DATE=${{ github.event.repository.updated_at }} - cache-from: type=gha,scope=${{ inputs.name }} - cache-to: type=gha,mode=max,scope=${{ inputs.name }} + cache-from: type=gha,scope=${{ inputs.name }}-${{ inputs.arch_label || 'smoke' }} + cache-to: type=gha,mode=max,scope=${{ inputs.name }}-${{ inputs.arch_label || 'smoke' }} # Disable attestations for the test build — provenance/SBOM cause BuildKit # to pull docker/buildkit-syft-scanner from Docker Hub, which fails with a # repo-scoped token. Attestations are enabled on the push step only. @@ -144,16 +146,16 @@ jobs: --image-tag "$IMAGE_NAME:pipeline-test" \ --check-set "$CHECK_SET" - # ── Step 3: Integration test (main image only) ───────────────────────── + # ── Step 3: Integration test (socket-basics variants only) ───────────── - name: 🔬 Integration test - if: inputs.name == 'socket-basics' + if: inputs.name == 'socket-basics' || inputs.name == 'socket-basics-heavy' env: IMAGE_NAME: ${{ inputs.name }} run: | bash ./scripts/integration-test-docker.sh \ --image-tag "$IMAGE_NAME:pipeline-test" - # ── Step 4: Push to registries (publish mode only) ───────────────────── + # ── Step 4: Push by digest (publish mode only) ───────────────────────── # Docker Hub login happens here — after build and tests, immediately before # push. Keeping it here prevents the repo-scoped token from interfering # with public image pulls during the build step. @@ -164,30 +166,50 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - # All layers are in the GHA cache from step 1 — this is just an upload. - - name: 🚀 Push to registries + # Per-arch by-digest push to BOTH registries. No tags are written here; + # the publish workflow's merge-manifests job creates the multi-arch + # manifest list at user-facing tags via `docker buildx imagetools create`. + # Layer cache from step 1 means this is mostly a metadata write + push. + - name: 🚀 Build & push by digest if: inputs.push + id: build-digest uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: # zizmor: ignore[template-injection] — safe: always hardcoded "." from same-repo callers; passed as array element to exec, not shell-interpolated context: ${{ inputs.context }} file: ${{ inputs.dockerfile }} - load: false - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} build-args: | SOCKET_BASICS_VERSION=${{ inputs.version }} - VCS_REF=${{ github.sha }} + VCS_REF=${{ steps.source.outputs.revision }} BUILD_DATE=${{ github.event.repository.updated_at }} - cache-from: type=gha,scope=${{ inputs.name }} + # One `--output` per registry → blobs land in both, by digest. + # build-push-action splits this scalar on newlines into separate outputs. + outputs: | + type=image,name=ghcr.io/socketdev/${{ inputs.push_name || inputs.name }},push-by-digest=true,name-canonical=true,push=true + type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/${{ inputs.push_name || inputs.name }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=${{ inputs.name }}-${{ inputs.arch_label }} + cache-to: type=gha,mode=max,scope=${{ inputs.name }}-${{ inputs.arch_label }} # SBOM and provenance generation pull docker/buildkit-syft-scanner from # Docker Hub, which fails with a repo-scoped token. Disabled until a # token with broader Docker Hub read access is available. provenance: false sbom: false - # Floating major version tags (v2 → latest v2.x.y) have been intentionally - # removed. Mutable tags are structurally equivalent to :latest and are - # inappropriate for a security tool. Users should pin to an immutable - # version tag or digest and use Dependabot to manage upgrades. + # Persist the per-arch digest as an artifact so the merge-manifests job + # can reference it via `@sha256:` when creating the list. + - name: 📤 Export digest + if: inputs.push + env: + DIGEST: ${{ steps.build-digest.outputs.digest }} + run: | + mkdir -p /tmp/digests + touch "/tmp/digests/${DIGEST#sha256:}" + + - name: ⬆️ Upload digest artifact + if: inputs.push + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: digests-${{ inputs.name }}__${{ inputs.arch_label }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 47aab71..e9110a7 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -1,11 +1,18 @@ name: publish-docker -# Builds, tests, and publishes the socket-basics image to GHCR and Docker Hub. +# Builds, tests, and publishes multi-arch socket-basics image variants +# (linux/amd64 + linux/arm64) to GHCR and Docker Hub. # -# Flow: resolve-version → build-test-push → create-release +# Flow: +# resolve-version +# → build-test-push (matrix: image variant + native arch, pushes by digest) +# → merge-manifests (assembles per-image per-arch digests into manifest lists) +# → create-release (tag pushes only) # # Tag convention: # v2.0.0 — immutable exact release (floating major tags intentionally not published) +# All image variants publish to the single socket-basics repository per +# registry, distinguished by tag suffix: 2.0.0 (main), 2.0.0-heavy (heavy). # See docs/github-action.md → "Pinning strategies" for the full rationale. # # Required repository secrets: @@ -19,7 +26,7 @@ on: workflow_dispatch: inputs: tag: - description: "Full git tag to publish (e.g. v2.0.0 for new releases, 1.1.3 for old). Must exist in the repo." + description: "Full git tag to publish (e.g. v2.0.3 or 2.0.3). Must exist in the repo." required: true # Default: deny everything. Each job below grants only what it needs. @@ -38,56 +45,260 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.clean }} + ref: ${{ steps.version.outputs.ref }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} - persist-credentials: false - - name: 🏷️ Resolve version id: version env: EVENT_NAME: ${{ github.event_name }} INPUT_TAG: ${{ inputs.tag }} REF_NAME: ${{ github.ref_name }} + REPO_URL: https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git run: | if [ "$EVENT_NAME" = "workflow_dispatch" ]; then - CLEAN="$INPUT_TAG" # full tag as provided (e.g. 1.1.3 or v2.0.0) + RAW="${INPUT_TAG#refs/tags/}" # full tag as provided (e.g. 2.0.3 or v2.0.3) else - CLEAN="$REF_NAME" # e.g. v2.0.0 + RAW="$REF_NAME" # e.g. v2.0.3 + fi + CLEAN="${RAW#v}" # strip leading v if present → 2.0.3 + if [[ ! "$CLEAN" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid release tag: $CLEAN" >&2 + exit 1 fi - CLEAN="${CLEAN#v}" # strip leading v if present → 2.0.0 or 1.1.3 + + REF_TAG="$RAW" + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + REF_TAG="" + for candidate in "$RAW" "v$CLEAN" "$CLEAN"; do + if git ls-remote --exit-code --tags "$REPO_URL" "refs/tags/$candidate" >/dev/null 2>&1; then + REF_TAG="$candidate" + break + fi + done + if [ -z "$REF_TAG" ]; then + echo "No matching release tag found for input: $INPUT_TAG" >&2 + exit 1 + fi + fi + echo "clean=$CLEAN" >> "$GITHUB_OUTPUT" + echo "ref=refs/tags/$REF_TAG" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ steps.version.outputs.ref }} + persist-credentials: false + + # Guard: the release source must agree with the tag. The pre-commit + # version-check hook was removed in #46 with no automated replacement, + # which let v2.1.0 ship with version files still at 2.0.3. This is the + # release-time gate: mismatches fail here, before anything is built. + - name: 🔎 Verify source versions match release tag + env: + VERSION: ${{ steps.version.outputs.clean }} + run: | + set -euo pipefail + fail=0 + check() { + if [ "$2" != "$VERSION" ]; then + echo "::error::$1 declares version '$2' but the release tag resolves to '$VERSION'" + fail=1 + fi + } + check "socket_basics/version.py" "$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' socket_basics/version.py | head -1)" + check "socket_basics/__init__.py" "$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' socket_basics/__init__.py | head -1)" + check "pyproject.toml" "$(sed -n 's/^version = "\(.*\)"$/\1/p' pyproject.toml | head -1)" + check "action.yml image tag" "$(sed -n 's#.*docker://ghcr.io/socketdev/socket-basics:\([^"]*\)".*#\1#p' action.yml | head -1)" + if [ "$fail" -ne 0 ]; then + echo "::error::Bump the version files in the release PR, merge, then re-tag." + exit 1 + fi + echo "✅ version.py, pyproject.toml, and action.yml all agree on $VERSION" - # ── Job 2: Build → test → push ───────────────────────────────────────────── - # Delegates all Docker steps to the reusable _docker-pipeline workflow. + # ── Job 2: Build → test → push by digest (per image + arch) ──────────────── + # Each matrix entry runs the full build/smoke/integration pipeline on a + # native runner for its target arch and pushes the resulting image by digest + # to both registries. The digest is exported as an artifact for the merge job. build-test-push: - name: publish (socket-basics) + name: publish (${{ matrix.image }}, ${{ matrix.arch }}) needs: resolve-version permissions: contents: read packages: write # push images to GHCR + strategy: + fail-fast: false + matrix: + include: + - image: socket-basics + dockerfile: Dockerfile + check_set: main + arch: amd64 + runs_on: ubuntu-latest + - image: socket-basics + dockerfile: Dockerfile + check_set: main + arch: arm64 + runs_on: ubuntu-24.04-arm + - image: socket-basics-heavy + dockerfile: Dockerfile.heavy + check_set: heavy + arch: amd64 + runs_on: ubuntu-latest + - image: socket-basics-heavy + dockerfile: Dockerfile.heavy + check_set: heavy + arch: arm64 + runs_on: ubuntu-24.04-arm uses: ./.github/workflows/_docker-pipeline.yml with: - name: socket-basics - dockerfile: Dockerfile + name: ${{ matrix.image }} + dockerfile: ${{ matrix.dockerfile }} context: . - check_set: main + check_set: ${{ matrix.check_set }} + runs_on: ${{ matrix.runs_on }} + arch_label: ${{ matrix.arch }} + # All variants publish to the single socket-basics repository on each + # registry; variants are distinguished by tag suffix (e.g. -heavy), never + # by a separate repository. + push_name: socket-basics push: true - tag_push: ${{ github.ref_type == 'tag' }} version: ${{ needs.resolve-version.outputs.version }} + ref: ${{ needs.resolve-version.outputs.ref }} secrets: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - # ── Job 3: Create GitHub release ─────────────────────────────────────────── - # Runs once after the image is successfully pushed (not for workflow_dispatch + # ── Job 3: Merge per-arch digests into a multi-arch manifest list ────────── + # Floating major version tags (v2 → latest v2.x.y) are intentionally omitted. + # Mutable tags are structurally equivalent to :latest and inappropriate for a + # security tool. Users should pin to an exact version and use Dependabot. + merge-manifests: + name: merge-manifests (${{ matrix.variant }}) + needs: [resolve-version, build-test-push] + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # Both variants live in the single socket-basics repository per registry, + # distinguished by tag suffix (2.2.0 vs 2.2.0-heavy). `variant` selects + # the per-arch digest artifacts produced by build-test-push. + include: + - variant: socket-basics + tag_suffix: "" + - variant: socket-basics-heavy + tag_suffix: "-heavy" + steps: + - name: ⬇️ Download per-arch digest artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: /tmp/digests + pattern: digests-${{ matrix.variant }}__* + merge-multiple: true + + - name: 🔨 Set up Docker Buildx + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + + - name: Login to GHCR + uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Login to Docker Hub + uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract image metadata + id: meta + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + with: + images: | + ghcr.io/socketdev/socket-basics + ${{ secrets.DOCKERHUB_USERNAME }}/socket-basics + # Disable the automatic :latest tag — metadata-action adds it by default + # for semver tag pushes. Mutable tags are inappropriate for a security tool. + # The variant suffix yields 2.2.0 for the main image, 2.2.0-heavy for heavy. + flavor: | + latest=false + suffix=${{ matrix.tag_suffix }} + tags: | + # Tag push (v2.0.0) → exact immutable version tag only. + type=semver,pattern={{version}} + # workflow_dispatch re-publish → use the version input directly + type=raw,value=${{ needs.resolve-version.outputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }} + + - name: 🧬 Create multi-arch manifest list + working-directory: /tmp/digests + env: + GHCR_IMAGE: ghcr.io/socketdev/socket-basics + DH_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/socket-basics + META_TAGS: ${{ steps.meta.outputs.tags }} + EXPECTED_ARCHES: "2" + run: | + set -euo pipefail + shopt -s nullglob + digests=(*) + if [ "${#digests[@]}" -ne "$EXPECTED_ARCHES" ]; then + echo "::error::expected $EXPECTED_ARCHES per-arch digests for ${{ matrix.variant }}, found ${#digests[@]}" + ls -la + exit 1 + fi + # Each by-digest push from the matrix step landed blobs in BOTH + # registries, so per-registry imagetools-create only writes manifests. + for image in "$GHCR_IMAGE" "$DH_IMAGE"; do + tag_args=() + while IFS= read -r tag; do + [ -z "$tag" ] && continue + case "$tag" in + "$image:"*) tag_args+=(-t "$tag") ;; + esac + done <<< "$META_TAGS" + if [ ${#tag_args[@]} -eq 0 ]; then + echo "::error::no tags resolved for $image" + exit 1 + fi + sources=() + for digest in "${digests[@]}"; do + sources+=("${image}@sha256:${digest}") + done + echo "→ creating manifest list for $image with ${#sources[@]} arch sources" + docker buildx imagetools create "${tag_args[@]}" "${sources[@]}" + done + + - name: 🔍 Inspect published manifest + env: + GHCR_IMAGE: ghcr.io/socketdev/socket-basics + DH_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/socket-basics + VERSION: ${{ needs.resolve-version.outputs.version }} + TAG_SUFFIX: ${{ matrix.tag_suffix }} + run: | + set -euo pipefail + for image in "$GHCR_IMAGE" "$DH_IMAGE"; do + ref="${image}:${VERSION}${TAG_SUFFIX}" + inspect="$(docker buildx imagetools inspect "$ref")" + echo "$inspect" + for platform in linux/amd64 linux/arm64; do + if ! grep -q "Platform:[[:space:]]*$platform" <<< "$inspect"; then + echo "::error::$ref is missing $platform" + exit 1 + fi + done + done + + # ── Job 4: Create GitHub release ─────────────────────────────────────────── + # Runs once after the manifest is published (not for workflow_dispatch # re-publishes — those don't create new releases). # Generates categorised release notes from merged PR labels (.github/release.yml). # CHANGELOG updates are intentionally human-authored in the release PR so this # workflow never needs to push commits to the protected default branch. create-release: - needs: [resolve-version, build-test-push] + needs: [resolve-version, merge-manifests] if: github.ref_type == 'tag' permissions: contents: write # create GitHub release diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index 480e9db..025ae14 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -1,6 +1,6 @@ name: smoke-test -# Builds the main socket-basics image and verifies all baked-in tools respond. +# Builds socket-basics image variants and verifies all baked-in tools respond. # Calls _docker-pipeline.yml in smoke-only mode (no push to registries). on: @@ -8,12 +8,16 @@ on: branches: [main] paths: - 'Dockerfile' + - 'Dockerfile.heavy' + - 'scripts/docker-heavy-entrypoint.sh' - 'scripts/smoke-test-docker.sh' - '.github/workflows/smoke-test.yml' - '.github/workflows/_docker-pipeline.yml' pull_request: paths: - 'Dockerfile' + - 'Dockerfile.heavy' + - 'scripts/docker-heavy-entrypoint.sh' - 'scripts/smoke-test-docker.sh' - '.github/workflows/smoke-test.yml' - '.github/workflows/_docker-pipeline.yml' @@ -29,12 +33,41 @@ concurrency: cancel-in-progress: true jobs: + # Native build + smoke per arch. amd64 covers the standard runner; arm64 + # covers ubuntu-24.04-arm and Apple Silicon self-hosted runners (issue #69). + # Native runners build ~5x faster than QEMU and exercise the real binaries. smoke: - name: smoke (socket-basics) + name: smoke (${{ matrix.image }}, ${{ matrix.arch }}) + strategy: + fail-fast: false + matrix: + include: + - image: socket-basics + dockerfile: Dockerfile + check_set: main + arch: amd64 + runs_on: ubuntu-latest + - image: socket-basics + dockerfile: Dockerfile + check_set: main + arch: arm64 + runs_on: ubuntu-24.04-arm + - image: socket-basics-heavy + dockerfile: Dockerfile.heavy + check_set: heavy + arch: amd64 + runs_on: ubuntu-latest + - image: socket-basics-heavy + dockerfile: Dockerfile.heavy + check_set: heavy + arch: arm64 + runs_on: ubuntu-24.04-arm uses: ./.github/workflows/_docker-pipeline.yml with: - name: socket-basics - dockerfile: Dockerfile + name: ${{ matrix.image }} + dockerfile: ${{ matrix.dockerfile }} context: . - check_set: main + check_set: ${{ matrix.check_set }} + runs_on: ${{ matrix.runs_on }} + arch_label: ${{ matrix.arch }} push: false diff --git a/CHANGELOG.md b/CHANGELOG.md index ddc0828..2ed4a71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] -## [2.1.0] - 2026-06-02 +## [2.2.0] - 2026-07-29 + +### Added +- Publish multi-arch Docker images for `linux/amd64` and `linux/arm64`. +- Add a heavy image variant (`socket-basics:-heavy` tag suffix) bundling + Socket Basics with the pinned Python Socket CLI. + +### Fixed +- Normalize manual Docker release tag inputs before checkout. +- core-tool-watch now opts into fail-closed Socket purl batch semantics + (`poll` + `alerts`), so fresh-but-unanalyzed pins surface as labeled + pending/not-found failures instead of silently dropped rows. + +## [2.1.0] - 2026-07-22 ### Added - Diff-only scan scoping now applies to SAST/OpenGrep via `changed_files` and diff --git a/Dockerfile b/Dockerfile index ff11d58..6ea5bb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,4 +84,4 @@ LABEL org.opencontainers.image.title="Socket Basics" \ ENV PATH="/socket-basics/.venv/bin:/root/.opengrep/cli/latest:/usr/local/bin:$PATH" -ENTRYPOINT ["socket-basics"] \ No newline at end of file +ENTRYPOINT ["socket-basics"] diff --git a/Dockerfile.heavy b/Dockerfile.heavy new file mode 100644 index 0000000..9a180f9 --- /dev/null +++ b/Dockerfile.heavy @@ -0,0 +1,68 @@ +# Heavy POC image: socket-basics plus a pinned stable Python Socket CLI. +ARG PYTHON_VERSION=3.12 +ARG TRUFFLEHOG_VERSION=3.93.8 +ARG TRIVY_VERSION=0.69.3 +ARG UV_VERSION=0.10.11 +ARG OPENGREP_VERSION=v1.16.5 +ARG SOCKET_CLI_VERSION=2.5.0 + +# FROM aquasec/trivy:${TRIVY_VERSION} AS trivy +FROM trufflesecurity/trufflehog:${TRUFFLEHOG_VERSION} AS trufflehog +FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv + +FROM python:${PYTHON_VERSION}-slim AS opengrep-installer +ARG OPENGREP_VERSION +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && apt-get install -y --no-install-recommends \ + curl ca-certificates bash +RUN curl -fsSL https://raw.githubusercontent.com/opengrep/opengrep/main/install.sh \ + | bash -s -- -v "${OPENGREP_VERSION}" + +FROM python:${PYTHON_VERSION}-slim AS runtime + +WORKDIR /socket-basics + +COPY --from=uv /uv /uvx /bin/ +COPY --from=trufflehog /usr/bin/trufflehog /usr/local/bin/trufflehog +COPY --from=opengrep-installer /root/.opengrep /root/.opengrep + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && apt-get install -y --no-install-recommends \ + curl git wget ca-certificates +RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs +RUN --mount=type=cache,target=/root/.npm \ + npm install -g socket + +COPY socket_basics /socket-basics/socket_basics +COPY pyproject.toml README.md LICENSE uv.lock /socket-basics/ + +ENV UV_LINK_MODE=copy +ARG SOCKET_CLI_VERSION +RUN --mount=type=cache,target=/root/.cache/uv \ + pip install -e . \ + && uv sync --frozen --no-dev \ + && pip install --no-cache-dir "socketsecurity==${SOCKET_CLI_VERSION}" + +COPY scripts/docker-heavy-entrypoint.sh /usr/local/bin/docker-heavy-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-heavy-entrypoint.sh + +ARG SOCKET_BASICS_VERSION=dev +ARG VCS_REF=unknown +ARG BUILD_DATE=unknown +ARG TRUFFLEHOG_VERSION +ARG OPENGREP_VERSION +LABEL org.opencontainers.image.title="Socket Basics Heavy" \ + org.opencontainers.image.source="https://github.com/SocketDev/socket-basics" \ + org.opencontainers.image.version="${SOCKET_BASICS_VERSION}" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + com.socket.cli-version="${SOCKET_CLI_VERSION}" \ + com.socket.trufflehog-version="${TRUFFLEHOG_VERSION}" \ + com.socket.opengrep-version="${OPENGREP_VERSION}" + +ENV PATH="/socket-basics/.venv/bin:/root/.opengrep/cli/latest:/usr/local/bin:$PATH" + +ENTRYPOINT ["/usr/local/bin/docker-heavy-entrypoint.sh"] diff --git a/action.yml b/action.yml index 867c3f7..42d6660 100644 --- a/action.yml +++ b/action.yml @@ -4,7 +4,7 @@ author: "Socket" runs: using: "docker" - image: "docker://ghcr.io/socketdev/socket-basics:2.0.3" + image: "docker://ghcr.io/socketdev/socket-basics:2.2.0" env: # Core GitHub variables (these are automatically available, but we explicitly pass GITHUB_TOKEN) GITHUB_TOKEN: ${{ inputs.github_token }} diff --git a/pyproject.toml b/pyproject.toml index fc8be92..9a28003 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "socket_basics" -version = "2.0.3" +version = "2.2.0" description = "Socket Basics with integrated SAST, secret scanning, and container analysis" readme = "README.md" requires-python = ">=3.10" diff --git a/scripts/check_core_tools.py b/scripts/check_core_tools.py index 733e30d..e53b7b1 100644 --- a/scripts/check_core_tools.py +++ b/scripts/check_core_tools.py @@ -32,13 +32,16 @@ dependency-review.yml). Exit code is 0 unless --fail-on-malware is set AND a PINNED version trips the -(deliberately strict) thresholds: any alert type in MALWARE_ALERT_TYPES -- a -curated list that goes beyond outright malware to include strong risk signals -like install scripts, obfuscation, and telemetry -- OR any alert of high or -critical severity. With a token present, a Socket scoring error -- or a -covered pinned coordinate missing from the returned batch -- also fails -(fail-closed: unverified pins must not ship; OpenGrep's documented pkg:github -coverage gap is the one exemption). Drift alone never fails the run, +thresholds: any alert type in MALWARE_ALERT_TYPES -- a curated list of +compromise and compromise-adjacent signals -- OR any alert of critical +severity. With a token present, a Socket scoring error also fails, as +does a covered pinned coordinate that comes back still pending analysis +(synthetic pendingScan row), unresolvable (synthetic notFound row), or missing +from the returned batch entirely (fail-closed: unverified pins must not ship; +OpenGrep's documented pkg:github coverage gap is the one exemption). The batch +call opts into poll=true + alerts=true so fresh-but-unanalyzed versions +surface as labeled pendingScan rows instead of being silently omitted by the +endpoint's fail-open default. Drift alone never fails the run, and the discovered *latest* version is scored for reporting only; both are surfaced via the JSON report and the `drift`/`malware`/`critical` GitHub outputs so the workflow decides what to do. @@ -62,21 +65,19 @@ DOCKERFILES = [REPO_ROOT / "Dockerfile", REPO_ROOT / "app_tests" / "Dockerfile"] UV_LOCK = REPO_ROOT / "uv.lock" -# Alert types treated as fail-worthy on a pinned version. Deliberately broader -# than literal malware: alongside outright compromise (malware, trojan, -# backdoor) it includes strong risk signals (obfuscation, install scripts, -# shell access, telemetry, typosquat hints) -- for the four core tools we bake -# into the image, any of these deserves a hard stop and a human look, at the -# cost of occasional false positives. Trim this set rather than disabling -# --fail-on-malware if it proves too noisy. +# Alert types treated as fail-worthy on a pinned version: outright compromise +# signals plus typosquat/fake-popularity hints and compromise-adjacent +# behaviors (install scripts, telemetry). Calibrated against real batch data +# once alerts=true started returning the full alert set (run 30504424787): +# capability signals (shellAccess -- present on ALL four tools; they spawn +# subprocesses by design) and heuristic/static signals (gptMalware, +# gptSecurity, obfuscatedFile -- a SAST engine ships malicious-looking test +# fixtures on purpose) are informational there, not compromise evidence, and +# were removed. Trim further rather than disabling --fail-on-malware if new +# noise appears. MALWARE_ALERT_TYPES = { "malware", - "gptMalware", - "gptSecurity", "didYouMean", - "obfuscatedFile", - "obfuscatedRequire", - "shellAccess", "suspiciousStarActivity", "cryptoMiner", "installScript", @@ -84,8 +85,28 @@ "trojan", "backdoor", } -# Severities that count as fail-worthy: includes "high", not just "critical". -CRITICAL_SEVERITIES = {"critical", "high"} +# Severities that count as fail-worthy. "high" was included while the batch +# response carried no alert data (the pre-alerts=true fail-open default made +# this gate dead code); the full alert set carries high-severity heuristic and +# cve rows on perfectly healthy tools (gptMalware/obfuscatedFile on the +# OpenGrep repo artifact, cve on Trivy), so the hard gate is critical-only. +# High-severity findings still land in the report for human review. +CRITICAL_SEVERITIES = {"critical"} + +# Synthetic batch-status alert types the purl endpoints emit when called with +# alerts=true (added upstream ~2026-04, depscan #18990). They mark inputs whose +# analysis is incomplete (pendingScan) or whose coordinate could not be +# resolved (notFound) -- without alerts=true such inputs are SILENTLY OMITTED +# from the response (the endpoint's documented fail-open default), which is +# what made fresh pins like pkg:pypi/socketdev@3.3.0 trip the unverified-pin +# guard with a misleading "batch dropped rows" message. These are batch-status +# markers, not package risk signals: they must never be classified through +# MALWARE_ALERT_TYPES / CRITICAL_SEVERITIES regardless of the severity label +# they carry. +SYNTHETIC_STATUS_ALERTS = { + "pendingScan": "pending", + "notFound": "not_found", +} @dataclass @@ -265,7 +286,9 @@ def analyze_purls(purls: list[str], token: str) -> dict[str, dict[str, Any]]: from socketdev import socketdev # imported lazily; only needed with a token - client = socketdev(token=token, timeout=60) + # Client timeout must exceed the server-side poll bound (timeoutSec=120 + # below), or the HTTP call would abort before the server finishes waiting. + client = socketdev(token=token, timeout=180) # Prefer the org-scoped purl endpoint. socketdev >= 3.1 deprecates the # legacy POST /v0/purl (used when org_slug is absent) in favor of @@ -282,6 +305,7 @@ def analyze_purls(purls: list[str], token: str) -> dict[str, dict[str, Any]]: slug = next(iter(orgs.values())).get("slug") if len(orgs) == 1 else None if slug: kwargs["org_slug"] = slug + print(f" using org-scoped purl endpoint (org={slug})") else: print( f" ! org slug not resolvable ({len(orgs)} orgs on token); using legacy purl endpoint", @@ -289,7 +313,21 @@ def analyze_purls(purls: list[str], token: str) -> dict[str, dict[str, Any]]: ) components = [{"purl": p} for p in purls] - results = client.purl.post(license="false", components=components, **kwargs) or [] + # The batch purl endpoints default to fail-open: inputs whose analysis is + # pending or unresolvable are silently omitted from the response unless the + # caller opts in. Opt in to fail-closed semantics: poll=true waits (bounded + # by timeoutSec; the server may cap it) for pending analysis, and + # alerts=true materializes still-unresolved inputs as synthetic + # pendingScan/notFound rows instead of dropping them. The SDK passes these + # extra kwargs through as query params (verified on 3.0.29 and 3.3.0). + results = client.purl.post( + license="false", + components=components, + poll="true", + timeoutSec="120", + alerts="true", + **kwargs, + ) or [] if not results: raise RuntimeError( f"Socket purl API returned no results for {len(purls)} PURLs " @@ -304,9 +342,17 @@ def analyze_purls(purls: list[str], token: str) -> dict[str, dict[str, Any]]: norm_alerts = [] malware = [] critical = [] + status = None for a in alerts: a_type = a.get("type", "") a_sev = (a.get("severity") or "").lower() + # Synthetic batch-status markers (from alerts=true) are handled + # before severity classification: whatever severity/action labels + # they carry after org-policy application, they describe the batch + # row, not the package. + if a_type in SYNTHETIC_STATUS_ALERTS: + status = status or SYNTHETIC_STATUS_ALERTS[a_type] + continue norm_alerts.append({"type": a_type, "severity": a_sev}) if a_type in MALWARE_ALERT_TYPES: malware.append(a_type) @@ -317,6 +363,7 @@ def analyze_purls(purls: list[str], token: str) -> dict[str, dict[str, Any]]: "version": item.get("version"), "type": item.get("type"), "score": item.get("score"), + "status": status, "alerts": norm_alerts, "malware": sorted(set(malware)), "critical": sorted(set(critical)), @@ -376,6 +423,10 @@ def verdict(version: Optional[str]) -> str: suffix = f" _(via {t.proxy_label})_" if not a: return "no data" + if a.get("status") == "pending": + return "⏳ analysis pending upstream" + suffix + if a.get("status") == "not_found": + return "❓ coordinate not resolvable" + suffix if a.get("malware"): return "🚨 MALWARE: " + ", ".join(a["malware"]) + suffix if a.get("critical"): @@ -469,6 +520,8 @@ def main() -> int: any_malware = False any_critical = False unverified: list[str] = [] + pending: list[str] = [] + not_found: list[str] = [] findings: list[dict[str, Any]] = [] for t in tools: drift = bool(t.latest and any(_strip_v(p) != _strip_v(t.latest) for p in t.pinned)) @@ -490,6 +543,16 @@ def main() -> int: any_malware = True if a.get("critical"): any_critical = True + # Synthetic status on a covered pin: the batch answered, but + # not with analysis. Same fail-closed posture as unverified, + # tracked separately so the error names the actual condition. + # Coverage-gap tools (OpenGrep's pkg:github) are exempt: with + # alerts=true their known-uncovered pin now returns a notFound + # row instead of being silently omitted. + if t.socket_coverage and a.get("status") == "pending": + pending.append(f"{t.key} {t.purl(v)}") + elif t.socket_coverage and a.get("status") == "not_found": + not_found.append(f"{t.key} {t.purl(v)}") elif token_present and not scoring_error and t.socket_coverage: # Scoring "succeeded" but this pinned coordinate has no row -- # a partial batch or a purl/echo mismatch. The guard's job is @@ -527,6 +590,8 @@ def main() -> int: "token_present": token_present, "scoring_error": scoring_error, "unverified": unverified, + "pending": pending, + "not_found": not_found, "findings": findings, }, indent=2, @@ -556,8 +621,32 @@ def main() -> int: file=sys.stderr, ) return 1 + # Fail closed: Socket knows the coordinate but analysis was still + # running when the bounded poll expired. Distinct from a dropped row: + # this is upstream latency, not an API anomaly. + if pending: + print( + "::error::Socket analysis still pending after the bounded poll for pinned " + "coordinate(s): " + "; ".join(pending) + + ". Failing closed -- re-run later, or investigate Socket ingestion if it persists.", + file=sys.stderr, + ) + return 1 + # Fail closed: Socket could not resolve the coordinate at all. For a + # published package version this is a registry/ingestion bug with a + # one-line repro -- hand it to the API team. + if not_found: + print( + "::error::Socket cannot resolve pinned coordinate(s): " + + "; ".join(not_found) + + ". Failing closed -- likely a Socket registry/ingestion gap; report it upstream.", + file=sys.stderr, + ) + return 1 # Fail closed: scoring returned rows, but some covered pinned - # coordinate has none -- a partial batch is not a clean bill. + # coordinate has none -- with poll+alerts requested this should no + # longer happen for merely-fresh versions, so a missing row is a + # genuine anomaly (purl/echo mismatch or batch drop). if unverified: print( "::error::Socket scoring returned no analysis for pinned coordinate(s): " diff --git a/scripts/docker-heavy-entrypoint.sh b/scripts/docker-heavy-entrypoint.sh new file mode 100644 index 0000000..e7422aa --- /dev/null +++ b/scripts/docker-heavy-entrypoint.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +if [ "$#" -eq 0 ]; then + exec socket-basics -h +fi + +case "$1" in + socket-basics) + shift + exec socket-basics "$@" + ;; + socketcli) + shift + exec socketcli "$@" + ;; + *) + exec socket-basics "$@" + ;; +esac diff --git a/scripts/integration-test-docker.sh b/scripts/integration-test-docker.sh index 748242d..77545ff 100755 --- a/scripts/integration-test-docker.sh +++ b/scripts/integration-test-docker.sh @@ -8,7 +8,7 @@ # # Usage: # ./scripts/integration-test-docker.sh [--image-tag TAG] -# ./scripts/integration-test-docker.sh --image-tag socket-basics:1.1.3 +# ./scripts/integration-test-docker.sh --image-tag socket-basics:2.0.3 set -euo pipefail diff --git a/scripts/prep_release.py b/scripts/prep_release.py new file mode 100755 index 0000000..8208f53 --- /dev/null +++ b/scripts/prep_release.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +""" +prep_release.py — Prepare the final release-prep PR for a new version. + +Feature PRs never touch version files; they only add CHANGELOG entries under +[Unreleased]. When a release batch is complete, run this once on a fresh +branch: it bumps every version-bearing file and stamps the [Unreleased] +changelog section, so the release PR is a mechanical five-file diff. Tag the +release PR's merge commit and the publish workflow's version gate passes by +construction. + +Files updated: + pyproject.toml [project] version (canonical source) + socket_basics/version.py derived via sync_release_version.py + socket_basics/__init__.py derived via sync_release_version.py + action.yml derived via sync_release_version.py + uv.lock project entry (via `uv lock`) + CHANGELOG.md [Unreleased] -> [X.Y.Z] - YYYY-MM-DD + +Usage: + python scripts/prep_release.py --version 2.2.0 + python scripts/prep_release.py --version 2.2.0 --date 2026-07-29 + python scripts/prep_release.py --version 2.2.0 --dry-run +""" +from __future__ import annotations + +import argparse +import datetime +import re +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).parent.parent +PYPROJECT = ROOT / "pyproject.toml" +CHANGELOG = ROOT / "CHANGELOG.md" + +SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+$") + +# pyproject.toml is the canonical version source; version.py, __init__.py, and +# action.yml are derived from it by scripts/sync_release_version.py. +PYPROJECT_PATTERN = re.compile(r'^version = "(\d+\.\d+\.\d+)"$', re.M) + + +def _bump_file(path: Path, pattern: re.Pattern[str], replacement: str, version: str) -> tuple[str, str]: + """Return (old_version, new_content) without writing.""" + content = path.read_text() + match = pattern.search(content) + if not match: + sys.exit(f"error: no version pattern found in {path.relative_to(ROOT)}") + old = match.group(match.lastindex or 0) if match.lastindex else match.group(1) + new_content, count = pattern.subn(replacement.format(v=version), content, count=1) + if count != 1: + sys.exit(f"error: expected exactly one version in {path.relative_to(ROOT)}, replaced {count}") + return old, new_content + + +def _stamp_changelog(version: str, date: str) -> str: + """Return the stamped CHANGELOG content without writing.""" + content = CHANGELOG.read_text() + + if f"## [{version}]" in content: + sys.exit(f"error: CHANGELOG.md already has a [{version}] section") + + match = re.search(r"^## \[Unreleased\]\n(.*?)(?=^## \[)", content, re.M | re.S) + if not match: + sys.exit("error: could not find an [Unreleased] section followed by a release section") + + body = match.group(1).strip("\n") + if not body.strip(): + sys.exit("error: [Unreleased] is empty — nothing to release. " + "Feature PRs should add their entries there before release prep.") + + stamped = f"## [Unreleased]\n\n## [{version}] - {date}\n\n{body}\n\n" + return content[:match.start()] + stamped + content[match.end():] + + +def _refresh_lock(dry_run: bool) -> None: + if dry_run: + print("dry-run: skipping `uv lock`") + return + try: + subprocess.run(["uv", "lock"], cwd=ROOT, check=True, capture_output=True, text=True) + except FileNotFoundError: + sys.exit("error: `uv` not found — install uv or run `uv lock` manually before committing") + except subprocess.CalledProcessError as exc: + sys.exit(f"error: `uv lock` failed:\n{exc.stderr}") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Prepare version bumps and changelog for a release PR.") + parser.add_argument("--version", required=True, help="Release version without v prefix, e.g. 2.2.0") + parser.add_argument("--date", default=datetime.date.today().isoformat(), + help="Release date for the changelog section (default: today)") + parser.add_argument("--dry-run", action="store_true", help="Report changes without writing") + args = parser.parse_args() + + if not SEMVER_RE.match(args.version): + sys.exit(f"error: version must be X.Y.Z (got {args.version!r})") + + tags = subprocess.run(["git", "tag", "--list", f"v{args.version}", args.version], + cwd=ROOT, capture_output=True, text=True).stdout.split() + if tags: + sys.exit(f"error: tag for {args.version} already exists: {', '.join(tags)}") + + # Validate and render every change first; only write once all succeed, + # so a failure never leaves a half-modified tree. + old, pyproject_content = _bump_file(PYPROJECT, PYPROJECT_PATTERN, 'version = "{v}"', args.version) + changelog_content = _stamp_changelog(args.version, args.date) + + if not args.dry_run: + PYPROJECT.write_text(pyproject_content) + print(f"pyproject.toml: {old} -> {args.version}") + + if args.dry_run: + print("dry-run: skipping sync_release_version.py --write") + else: + subprocess.run([sys.executable, str(ROOT / "scripts" / "sync_release_version.py"), "--write"], + cwd=ROOT, check=True) + + if not args.dry_run: + CHANGELOG.write_text(changelog_content) + print(f"CHANGELOG.md: [Unreleased] -> [{args.version}] - {args.date}") + + _refresh_lock(args.dry_run) + if not args.dry_run: + print("uv.lock: refreshed") + + print("\nNext steps: commit these changes on a release branch, open the release PR,") + print(f"merge it last, then tag the merge commit as v{args.version} to trigger publish.") + + +if __name__ == "__main__": + main() diff --git a/scripts/smoke-test-docker.sh b/scripts/smoke-test-docker.sh index 2962951..14c2243 100644 --- a/scripts/smoke-test-docker.sh +++ b/scripts/smoke-test-docker.sh @@ -8,6 +8,8 @@ APP_TESTS_IMAGE_TAG="${APP_TESTS_IMAGE_TAG:-socket-basics-app-tests:smoke-test}" RUN_APP_TESTS=false SKIP_BUILD=false CHECK_SET="main" +DOCKERFILE="Dockerfile" +DOCKERFILE_SET=false BUILD_PROGRESS="${SMOKE_TEST_BUILD_PROGRESS:-}" MAIN_TOOLS=( @@ -23,6 +25,14 @@ APP_TESTS_TOOLS=( "command -v socket" ) +HEAVY_TOOLS=( + "socket-basics -h" + "socketcli --help" + "command -v socket" + "trufflehog --version" + "opengrep --version" +) + # TEMPORARY: trivy is being removed to assess impact. These checks FAIL if the # tool is still present in the image — ensures removal is complete. MUST_NOT_EXIST_TOOLS=( @@ -30,9 +40,10 @@ MUST_NOT_EXIST_TOOLS=( ) usage() { - echo "Usage: $0 [--image-tag TAG] [--app-tests] [--skip-build] [--check-set main|app-tests] [--build-progress MODE]" + echo "Usage: $0 [--image-tag TAG] [--app-tests] [--skip-build] [--check-set main|app-tests|heavy] [--dockerfile FILE] [--build-progress MODE]" echo " --skip-build: skip docker build; verify tools in a pre-built image" - echo " --check-set: which tool set to verify: main (default) or app-tests" + echo " --check-set: which tool set to verify: main (default), app-tests, or heavy" + echo " --dockerfile: Dockerfile to build in non-skip mode (default: Dockerfile)" echo " --build-progress: auto|plain|tty (default: auto locally, plain in CI)" } @@ -49,6 +60,10 @@ while [[ $# -gt 0 ]]; do [[ $# -lt 2 ]] && { echo "Error: --check-set requires a value"; exit 1; } CHECK_SET="$2"; shift 2 ;; + --dockerfile) + [[ $# -lt 2 ]] && { echo "Error: --dockerfile requires a value"; exit 1; } + DOCKERFILE="$2"; DOCKERFILE_SET=true; shift 2 + ;; --build-progress) [[ $# -lt 2 ]] && { echo "Error: --build-progress requires a value"; exit 1; } BUILD_PROGRESS="$2"; shift 2 @@ -58,10 +73,14 @@ while [[ $# -gt 0 ]]; do done case "$CHECK_SET" in - main|app-tests) ;; - *) echo "Error: invalid --check-set '$CHECK_SET' (must be 'main' or 'app-tests')"; exit 1 ;; + main|app-tests|heavy) ;; + *) echo "Error: invalid --check-set '$CHECK_SET' (must be 'main', 'app-tests', or 'heavy')"; exit 1 ;; esac +if [[ "$CHECK_SET" == "heavy" && "$DOCKERFILE_SET" == "false" ]]; then + DOCKERFILE="Dockerfile.heavy" +fi + if [[ -z "$BUILD_PROGRESS" ]]; then if [[ "${GITHUB_ACTIONS:-}" == "true" ]]; then BUILD_PROGRESS="plain" @@ -133,6 +152,8 @@ if $SKIP_BUILD; then echo "Check set: $CHECK_SET" if [[ "$CHECK_SET" == "app-tests" ]]; then run_checks "$IMAGE_TAG" "${APP_TESTS_TOOLS[@]}" + elif [[ "$CHECK_SET" == "heavy" ]]; then + run_checks "$IMAGE_TAG" "${HEAVY_TOOLS[@]}" else run_checks "$IMAGE_TAG" "${MAIN_TOOLS[@]}" fi @@ -141,15 +162,20 @@ else # ── Normal mode: build then verify ──────────────────────────────────────── echo "==> Build main image" echo "Image: $IMAGE_TAG" + echo "Dockerfile: $DOCKERFILE" echo "Docker build progress mode: $BUILD_PROGRESS" build_args_for_tag "$IMAGE_TAG" main_build_start="$(date +%s)" - docker build "${BUILD_ARGS[@]}" . + docker build -f "$DOCKERFILE" "${BUILD_ARGS[@]}" . main_build_end="$(date +%s)" echo "Main image build completed in $((main_build_end - main_build_start))s" echo "==> Verify tools in main image" - run_checks "$IMAGE_TAG" "${MAIN_TOOLS[@]}" + if [[ "$CHECK_SET" == "heavy" ]]; then + run_checks "$IMAGE_TAG" "${HEAVY_TOOLS[@]}" + else + run_checks "$IMAGE_TAG" "${MAIN_TOOLS[@]}" + fi run_must_not_exist_checks "$IMAGE_TAG" "${MUST_NOT_EXIST_TOOLS[@]}" if $RUN_APP_TESTS; then diff --git a/scripts/update_changelog.py b/scripts/update_changelog.py index 373f4cc..1787c1c 100755 --- a/scripts/update_changelog.py +++ b/scripts/update_changelog.py @@ -85,11 +85,11 @@ def _update_links(content: str, version: str, prev_tag: str) -> str: Update the comparison links block at the bottom of the changelog. Before: - [Unreleased]: .../compare/1.1.3...HEAD + [Unreleased]: .../compare/v2.0.3...HEAD - After publishing v2.0.1: - [Unreleased]: .../compare/v2.0.1...HEAD - [2.0.1]: .../compare/v2.0.0...v2.0.1 + After publishing v2.0.4: + [Unreleased]: .../compare/v2.0.4...HEAD + [2.0.4]: .../compare/v2.0.3...v2.0.4 """ new_tag = _tag(version) diff --git a/socket_basics/__init__.py b/socket_basics/__init__.py index 6dd634c..b00b4f6 100644 --- a/socket_basics/__init__.py +++ b/socket_basics/__init__.py @@ -12,7 +12,7 @@ from .socket_basics import SecurityScanner, main from .core.config import load_config_from_env, Config -__version__ = "2.0.3" +__version__ = "2.2.0" __author__ = "Socket.dev" __email__ = "support@socket.dev" diff --git a/socket_basics/version.py b/socket_basics/version.py index 5fa9130..8a124bf 100644 --- a/socket_basics/version.py +++ b/socket_basics/version.py @@ -1 +1 @@ -__version__ = "2.0.3" +__version__ = "2.2.0" diff --git a/uv.lock b/uv.lock index fe586c9..b03fc51 100644 --- a/uv.lock +++ b/uv.lock @@ -672,7 +672,7 @@ wheels = [ [[package]] name = "socket-basics" -version = "2.0.3" +version = "2.2.0" source = { editable = "." } dependencies = [ { name = "jsonschema" },