From 24971e1aacf6277a711ae47c7d8a2dbd91f96aea Mon Sep 17 00:00:00 2001 From: konstin Date: Mon, 28 Oct 2024 21:18:01 +0100 Subject: [PATCH] A complete, self-contained example for trusted publishing with uv --- .github/workflows/ci.yml | 34 ++++ .github/workflows/errors.yml | 177 ++++++++++++++++++++ .github/workflows/release.yml | 28 ++++ .gitignore | 10 ++ .python-version | 1 + README.md | 32 ++++ data/trusted-publishing-config-pypi.png | Bin 0 -> 23353 bytes pyproject.toml | 22 +++ src/trusted_publishing_examples/__init__.py | 7 + src/trusted_publishing_examples/py.typed | 0 tests/smoke_test.py | 12 ++ tests/test_example.py | 7 + uv.lock | 127 ++++++++++++++ 13 files changed, 457 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/errors.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 data/trusted-publishing-config-pypi.png create mode 100644 pyproject.toml create mode 100644 src/trusted_publishing_examples/__init__.py create mode 100644 src/trusted_publishing_examples/py.typed create mode 100644 tests/smoke_test.py create mode 100644 tests/test_example.py create mode 100644 uv.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6f95201 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +# Basic CI setup: Lint with ruff, run tests with pytest +name: Test + +on: + pull_request: + push: + branches: + - main + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v3 + - name: Ruff lint + run: uv run ruff check . + - name: Ruff format + run: uv run ruff format --diff . + # This isn't a general Python lint, this style is just used in this repository + - name: Prettier format + run: npx prettier --prose-wrap always --check "**/*.md" + + test: + name: Run tests + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v3 + - run: uv run pytest diff --git a/.github/workflows/errors.yml b/.github/workflows/errors.yml new file mode 100644 index 0000000..9a8f10d --- /dev/null +++ b/.github/workflows/errors.yml @@ -0,0 +1,177 @@ +# Test the error messages when the trusted publishing configuration is incorrect +name: Release wrong name + +on: + workflow_dispatch: + inputs: + ref: + description: "The commit SHA, tag, or branch of uv. Uses the last release if not specified." + default: "" + type: string + +jobs: + get-binary-linux: + runs-on: ubuntu-latest + name: Get binary + steps: + - if: ${{ inputs.ref }} + uses: actions/checkout@v4 + with: + repository: "astral-sh/uv" + ref: ${{ inputs.ref }} + + - if: ${{ inputs.ref }} + uses: rui314/setup-mold@v1 + - if: ${{ inputs.ref }} + name: Setup musl + run: | + sudo apt-get install musl-tools + rustup target add x86_64-unknown-linux-musl + - if: ${{ inputs.ref }} + uses: Swatinem/rust-cache@v2 + - if: ${{ inputs.ref }} + name: Build uv + run: cargo build --target x86_64-unknown-linux-musl + - if: ${{ inputs.ref }} + name: Strip uv + run: strip ./target/x86_64-unknown-linux-musl/debug/uv + + - if: ${{ inputs.ref }} + name: Upload uv + uses: actions/upload-artifact@v4 + with: + name: uv + path: ./target/x86_64-unknown-linux-musl/debug/uv + + - if: ${{ !inputs.ref }} + run: | + wget https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-musl.tar.gz + tar xf uv-x86_64-unknown-linux-musl.tar.gz + - if: ${{ !inputs.ref }} + name: Upload uv + uses: actions/upload-artifact@v4 + with: + name: uv + path: ./uv-x86_64-unknown-linux-musl/uv + + pypi-wrong-name: + name: Publish wrong name + needs: get-binary-linux + runs-on: ubuntu-latest + # Environment and permissions trusted publishing. + environment: + # Create this environment in the GitHub repository under Settings -> Environments + name: release + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Download uv + uses: actions/download-artifact@v4 + with: + name: uv + - name: Prepare uv + run: chmod +x ./uv + + - run: ./uv build + # Check that basic features work and we didn't miss to include crucial files + - name: Smoke test (wheel) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.whl tests/smoke_test.py + - name: Smoke test (source distribution) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.tar.gz tests/smoke_test.py + - run: ./uv publish --trusted-publishing always + # The part below with testpypi only because it's a demo repo, remove the next two lines for production usage + env: + UV_PUBLISH_URL: https://test.pypi.org/legacy/ + + # Fails because the workflow name is wrong, but without `--trusted-publishing always` + pypi-wrong-name-no-trusted-publishing: + name: Publish wrong name alt + needs: get-binary-linux + runs-on: ubuntu-latest + # Environment and permissions trusted publishing. + environment: + # Create this environment in the GitHub repository under Settings -> Environments + name: release + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Download uv + uses: actions/download-artifact@v4 + with: + name: uv + - name: Prepare uv + run: chmod +x ./uv + + - uses: astral-sh/setup-uv@v3 + - run: ./uv build + # Check that basic features work and we didn't miss to include crucial files + - name: Smoke test (wheel) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.whl tests/smoke_test.py + - name: Smoke test (source distribution) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.tar.gz tests/smoke_test.py + - run: ./uv publish + # The part below with testpypi only because it's a demo repo, remove the next two lines for production usage + env: + UV_PUBLISH_URL: https://test.pypi.org/legacy/ + + # Fails because the permission section is missing + pypi-missing-permissions: + name: Publish missing permissions + needs: get-binary-linux + runs-on: ubuntu-latest + # Environment trusted publishing. + environment: + # Create this environment in the GitHub repository under Settings -> Environments + name: release + # Here the permission section is skipped + steps: + - uses: actions/checkout@v4 + - name: Download uv + uses: actions/download-artifact@v4 + with: + name: uv + - name: Prepare uv + run: chmod +x ./uv + + - run: ./uv build + # Check that basic features work and we didn't miss to include crucial files + - name: Smoke test (wheel) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.whl tests/smoke_test.py + - name: Smoke test (source distribution) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.tar.gz tests/smoke_test.py + - run: ./uv publish --trusted-publishing always + # The part below with testpypi only because it's a demo repo, remove the next two lines for production usage + env: + UV_PUBLISH_URL: https://test.pypi.org/legacy/ + + # Fails because the environment section is missing + pypi-missing-environment: + name: Publish missing environment + needs: get-binary-linux + runs-on: ubuntu-latest + # Here the environment section is skipped + # Permissions trusted publishing. + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Download uv + uses: actions/download-artifact@v4 + with: + name: uv + - name: Prepare uv + run: chmod +x ./uv + + - uses: astral-sh/setup-uv@v3 + - run: ./uv build + # Check that basic features work and we didn't miss to include crucial files + - name: Smoke test (wheel) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.whl tests/smoke_test.py + - name: Smoke test (source distribution) + run: ./uv run --isolated --no-project -p 3.13 --with dist/*.tar.gz tests/smoke_test.py + - run: ./uv publish --trusted-publishing always + # The part below with testpypi only because it's a demo repo, remove the next two lines for production usage + env: + UV_PUBLISH_URL: https://test.pypi.org/legacy/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6ef73e5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: Release + +on: + push: + tags: + # Publish on any tag starting with a `v`, e.g. v1.2.3 + - v* + +jobs: + pypi: + name: Publish to PyPI + runs-on: ubuntu-latest + # Environment and permissions trusted publishing. + environment: + # Create this environment in the GitHub repository under Settings -> Environments + name: release + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v3 + - run: uv build + # Check that basic features work and we didn't miss to include crucial files + - name: Smoke test (wheel) + run: uv run --isolated --no-project -p 3.13 --with dist/*.whl tests/smoke_test.py + - name: Smoke test (source distribution) + run: uv run --isolated --no-project -p 3.13 --with dist/*.tar.gz tests/smoke_test.py + - run: uv publish --trusted-publishing always diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..505a3b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..3a4f41e --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0568c9 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# uv trusted publishing examples + +Trusted publishing allows uploading package from GitHub Action to PyPI without +manually setting a secret token. Instead, you specify on PyPI a GitHub Actions +workflow that is allowed to publish the package. + +This repository contains a full, self-contained example for trusted publishing +with uv. The release workflow can be found in +[.github/workflows/release.yml](.github/workflows/release.yml). On PyPI, the +matching configuration is set under +`https://pypi.org/manage/project//settings/publishing/`: + +![Screenshot from PyPI. +Manage current publishers. +Publisher: GitHub. +Details: +Repository: astral-sh/trusted-publishing-examples +Workflow: release.yml +Environment name: release](data/trusted-publishing-config-pypi.png) + +You can find the published package at +https://pypi.org/project/trusted-publishing-examples/. + +[.github/workflows/ci.yml](.github/workflows/ci.yml) is a minimal test and lint +workflow for a Python package, while +[.github/workflows/errors.yml](.github/workflows/errors.yml) is for testing uv +itself only. + +## Documentation + +- uv's side: https://docs.astral.sh/uv/guides/publish/ +- PyPI's side: https://docs.pypi.org/trusted-publishers/ diff --git a/data/trusted-publishing-config-pypi.png b/data/trusted-publishing-config-pypi.png new file mode 100644 index 0000000000000000000000000000000000000000..354ab55ba159ccbaf282e313f26355182fcae203 GIT binary patch literal 23353 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 ate9q4C7CXp5k_L)c%*5dQU1_A@_zuObDjtQ literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ebfa2f4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "trusted-publishing-examples" +version = "0.1.2" +description = "Add your description here" +readme = "README.md" +authors = [ + { name = "konstin", email = "konstin@mailbox.org" } +] +requires-python = ">=3.13" +dependencies = [ + "numpy>=2.1.2", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.uv] +dev-dependencies = [ + "pytest>=8.3.3", + "ruff>=0.7.1", +] diff --git a/src/trusted_publishing_examples/__init__.py b/src/trusted_publishing_examples/__init__.py new file mode 100644 index 0000000..e502801 --- /dev/null +++ b/src/trusted_publishing_examples/__init__.py @@ -0,0 +1,7 @@ +import numpy + + +def hello(n: int) -> str: + """Greet the sum from 0 to n (exclusive end).""" + sum_n = numpy.arange(n).sum() + return f"Hello {sum_n}!" diff --git a/src/trusted_publishing_examples/py.typed b/src/trusted_publishing_examples/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/smoke_test.py b/tests/smoke_test.py new file mode 100644 index 0000000..a6da013 --- /dev/null +++ b/tests/smoke_test.py @@ -0,0 +1,12 @@ +"""Check that basic features work. + +Catch cases where e.g. files are missing so the import doesn't work. It is +recommended to check that e.g. assets are included.""" + +from trusted_publishing_examples import hello + +message = hello(101) +if message == "Hello 5050!": + print("Smoke test succeeded") +else: + raise RuntimeError(message) diff --git a/tests/test_example.py b/tests/test_example.py new file mode 100644 index 0000000..c1d0457 --- /dev/null +++ b/tests/test_example.py @@ -0,0 +1,7 @@ +from trusted_publishing_examples import hello + + +def test_hello(): + assert hello(0) == "Hello 0!" + assert hello(1) == "Hello 0!" + assert hello(1000) == "Hello 499500!" diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..434dc68 --- /dev/null +++ b/uv.lock @@ -0,0 +1,127 @@ +version = 1 +requires-python = ">=3.13" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "numpy" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/d1/8a730ea07f4a37d94f9172f4ce1d81064b7a64766b460378be278952de75/numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c", size = 18878063 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/72/716fa1dbe92395a9a623d5049203ff8ddb0cfce65b9df9117c3696ccc011/numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d", size = 20834690 }, + { url = "https://files.pythonhosted.org/packages/1e/fb/3e85a39511586053b5c6a59a643879e376fae22230ebfef9cfabb0e032e2/numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf", size = 13507474 }, + { url = "https://files.pythonhosted.org/packages/35/eb/5677556d9ba13436dab51e129f98d4829d95cd1b6bd0e199c14485a4bdb9/numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e", size = 5074742 }, + { url = "https://files.pythonhosted.org/packages/3e/c5/6c5ef5ba41b65a7e51bed50dbf3e1483eb578055633dd013e811a28e96a1/numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3", size = 6606787 }, + { url = "https://files.pythonhosted.org/packages/08/ac/f2f29dd4fd325b379c7dc932a0ebab22f0e031dbe80b2f6019b291a3a544/numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8", size = 13601333 }, + { url = "https://files.pythonhosted.org/packages/44/26/63f5f4e5089654dfb858f4892215ed968cd1a68e6f4a83f9961f84f855cb/numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a", size = 16038090 }, + { url = "https://files.pythonhosted.org/packages/1d/21/015e0594de9c3a8d5edd24943d2bd23f102ec71aec026083f822f86497e2/numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98", size = 16410865 }, + { url = "https://files.pythonhosted.org/packages/df/01/c1bcf9e6025d79077fbf3f3ee503b50aa7bfabfcd8f4b54f5829f4c00f3f/numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe", size = 14078077 }, + { url = "https://files.pythonhosted.org/packages/ba/06/db9d127d63bd11591770ba9f3d960f8041e0f895184b9351d4b1b5b56983/numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a", size = 6234904 }, + { url = "https://files.pythonhosted.org/packages/a9/96/9f61f8f95b6e0ea0aa08633b704c75d1882bdcb331bdf8bfd63263b25b00/numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445", size = 12561910 }, + { url = "https://files.pythonhosted.org/packages/36/b8/033f627821784a48e8f75c218033471eebbaacdd933f8979c79637a1b44b/numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5", size = 20857719 }, + { url = "https://files.pythonhosted.org/packages/96/46/af5726fde5b74ed83f2f17a73386d399319b7ed4d51279fb23b721d0816d/numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0", size = 13518826 }, + { url = "https://files.pythonhosted.org/packages/db/6e/8ce677edf36da1c4dae80afe5529f47690697eb55b4864673af260ccea7b/numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17", size = 5115036 }, + { url = "https://files.pythonhosted.org/packages/6a/ba/3cce44fb1b8438042c11847048812a776f75ee0e7070179c22e4cfbf420c/numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6", size = 6628641 }, + { url = "https://files.pythonhosted.org/packages/59/c8/e722998720ccbd35ffbcf1d1b8ed0aa2304af88d3f1c38e06ebf983599b3/numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8", size = 13574803 }, + { url = "https://files.pythonhosted.org/packages/7c/8e/fc1fdd83a55476765329ac2913321c4aed5b082a7915095628c4ca30ea72/numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35", size = 16021174 }, + { url = "https://files.pythonhosted.org/packages/2a/b6/a790742aa88067adb4bd6c89a946778c1417d4deaeafce3ca928f26d4c52/numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62", size = 16400117 }, + { url = "https://files.pythonhosted.org/packages/48/6f/129e3c17e3befe7fefdeaa6890f4c4df3f3cf0831aa053802c3862da67aa/numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a", size = 14066202 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "ruff" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/21/5c6e05e0fd3fbb41be4fb92edbc9a04de70baf60adb61435ce0c6b8c3d55/ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4", size = 3181670 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/45/8a20a9920175c9c4892b2420f80ff3cf14949cf3067118e212f9acd9c908/ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89", size = 10389268 }, + { url = "https://files.pythonhosted.org/packages/1b/d3/2f8382db2cf4f9488e938602e33e36287f9d26cb283aa31f11c31297ce79/ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35", size = 10188348 }, + { url = "https://files.pythonhosted.org/packages/a2/31/7d14e2a88da351200f844b7be889a0845d9e797162cf76b136d21b832a23/ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99", size = 9841448 }, + { url = "https://files.pythonhosted.org/packages/db/99/738cafdc768eceeca0bd26c6f03e213aa91203d2278e1d95b1c31c4ece41/ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca", size = 10674864 }, + { url = "https://files.pythonhosted.org/packages/fe/12/bcf2836b50eab53c65008383e7d55201e490d75167c474f14a16e1af47d2/ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250", size = 10192105 }, + { url = "https://files.pythonhosted.org/packages/2b/71/261d5d668bf98b6c44e89bfb5dfa4cb8cb6c8b490a201a3d8030e136ea4f/ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c", size = 11194144 }, + { url = "https://files.pythonhosted.org/packages/90/1f/0926d18a3b566fa6e7b3b36093088e4ffef6b6ba4ea85a462d9a93f7e35c/ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565", size = 11917066 }, + { url = "https://files.pythonhosted.org/packages/cd/a8/9fac41f128b6a44ab4409c1493430b4ee4b11521e8aeeca19bfe1ce851f9/ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7", size = 11458821 }, + { url = "https://files.pythonhosted.org/packages/25/cd/59644168f086ab13fe4e02943b9489a0aa710171f66b178e179df5383554/ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a", size = 12700379 }, + { url = "https://files.pythonhosted.org/packages/fb/30/3bac63619eb97174661829c07fc46b2055a053dee72da29d7c304c1cd2c0/ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad", size = 11019813 }, + { url = "https://files.pythonhosted.org/packages/4b/af/f567b885b5cb3bcdbcca3458ebf210cc8c9c7a9f61c332d3c2a050c3b21e/ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112", size = 10662146 }, + { url = "https://files.pythonhosted.org/packages/bc/ad/eb930d3ad117a9f2f7261969c21559ebd82bb13b6e8001c7caed0d44be5f/ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378", size = 10256911 }, + { url = "https://files.pythonhosted.org/packages/20/d5/af292ce70a016fcec792105ca67f768b403dd480a11888bc1f418fed0dd5/ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8", size = 10767488 }, + { url = "https://files.pythonhosted.org/packages/24/85/cc04a3bd027f433bebd2a097e63b3167653c079f7f13d8f9a1178e693412/ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd", size = 11093368 }, + { url = "https://files.pythonhosted.org/packages/0b/fb/c39cbf32d1f3e318674b8622f989417231794926b573f76dd4d0ca49f0f1/ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9", size = 8594180 }, + { url = "https://files.pythonhosted.org/packages/5a/71/ec8cdea34ecb90c830ca60d54ac7b509a7b5eab50fae27e001d4470fe813/ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307", size = 9419751 }, + { url = "https://files.pythonhosted.org/packages/79/7b/884553415e9f0a9bf358ed52fb68b934e67ef6c5a62397ace924a1afdf9a/ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37", size = 8717402 }, +] + +[[package]] +name = "trusted-publishing-examples" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "numpy" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [{ name = "numpy", specifier = ">=2.1.2" }] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.3" }, + { name = "ruff", specifier = ">=0.7.1" }, +]