diff --git a/.gitattributes b/.gitattributes index 5e0f547..0f1461e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,2 @@ docs/** linguist-documentation -docs.ps1 linguist-vendored -build.ps1 linguist-vendored +*.ps1 linguist-vendored diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 681cad8..ca0fb4f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,15 +2,25 @@ name: "Build Package" on: workflow_call: - inputs: + inputs: &inputs + version: + description: "Build Version" + type: string + required: true + #version-file: + # description: "Version File" + # type: string + # default: "src/*/_version.py" name: description: "Artifact Name" type: string - required: true + default: "dist" path: description: "Build Path" type: string - default: dist + default: "dist" + workflow_dispatch: + inputs: *inputs jobs: build: @@ -19,7 +29,7 @@ jobs: timeout-minutes: 5 permissions: - contents: read + contents: write steps: - name: "Checkout" @@ -28,16 +38,14 @@ jobs: - name: "Debug event.json" continue-on-error: true run: cat "${GITHUB_EVENT_PATH}" - - name: "Debug CTX github" continue-on-error: true env: GITHUB_CTX: ${{ toJSON(github) }} run: echo "$GITHUB_CTX" - - #- name: "Debug Environment" - # continue-on-error: true - # run: env + - name: "Debug Environment" + continue-on-error: true + run: env - name: "Setup Python 3.13" uses: actions/setup-python@v6 @@ -50,19 +58,33 @@ jobs: python -m pip install -U pip python -m pip install --group dev + #- name: "Update Version" + # if: ${{ inputs.version-file }} + # uses: justalemon/VersionPatcher@master + # with: + # version: ${{ inputs.version }} + # initpy-files: ${{ inputs.version-file }} + # + #- name: "Debug Version" + # if: ${{ inputs.version-file }} + # continue-on-error: true + # run: | + # cat ${{ inputs.version-file }} + - name: "Build" run: | python -m build - name: "List Artifacts" + continue-on-error: true working-directory: ${{ inputs.path }} run: | - results="$(tree . || ls -lAhR .)" + results="$(tree .)" echo "::group::results" echo "${results}" echo "::endgroup::" - markdown='Artifacts: `${{ inputs.path }}`\n```text\n'"${results}"'\n```' - echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY + md="Artifacts: \`${{ inputs.path }}\`\n\`\`\`text\n${results}\n\`\`\`" + echo -e "${md}" >> "$GITHUB_STEP_SUMMARY" - name: "Upload to Actions" uses: actions/upload-artifact@v5 @@ -70,9 +92,17 @@ jobs: name: ${{ inputs.name }} path: ${{ inputs.path }} + - name: "Upload Release" + if: ${{ github.event_name == 'release' }} + uses: cssnr/upload-release-action@latest + with: + globs: | + dist/*.whl + dist/*.tar.gz + - name: "Send Failure Notification" if: ${{ failure() }} - uses: sarisia/actions-status-discord@v1 + uses: sarisia/actions-status-discord@b8381b25576cb341b2af39926ab42c5056cc44ed # v1.15.5 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} - description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} + description: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 3ec6a5e..a654056 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -50,12 +50,13 @@ jobs: - name: "Build Docs" run: | python -m pip install -U pip + python -m pip install --group dev python -m pip install -U pdoc - python -m pip install -e . + #python -m pip install -e . python -m pdoc -t docs -o "${{ inputs.path }}" \ --logo "${{ env.logo }}" \ --logo-link "${{ env.link }}" \ - vultr + src/vultr - name: "Fix Docs" working-directory: ${{ inputs.path }} @@ -75,8 +76,8 @@ jobs: echo "::group::results" echo "${results}" echo "::endgroup::" - markdown='Artifacts: `${{ inputs.path }}`\n```text\n'"${results}"'\n```' - echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY + markdown="Artifacts: \`${{ inputs.path }}\`\n\`\`\`text\n${results}\n\`\`\`" + echo -e "${markdown}" >> "$GITHUB_STEP_SUMMARY" - name: "Upload Pages Artifact" if: ${{ inputs.name == 'github-pages' }} diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml new file mode 100644 index 0000000..6f280f5 --- /dev/null +++ b/.github/workflows/labeler.yaml @@ -0,0 +1,52 @@ +name: "PR Labeler" + +on: + pull_request_target: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + labeler: + name: "Labeler" + runs-on: ubuntu-latest + timeout-minutes: 5 + + permissions: + pull-requests: write + issues: write + + steps: + - name: "Checkout Configs" + uses: actions/checkout@v6 + with: + repository: cssnr/configs + ref: master + path: .configs + sparse-checkout-cone-mode: false + sparse-checkout: | + labels/** + + - name: "Debug" + continue-on-error: true + run: | + echo "::group::labels.yaml" + cat .configs/labels/labels.yaml + echo "::endgroup::" + + echo "::group::labeler.yaml" + cat .configs/labels/labeler.yaml + echo "::endgroup::" + + - name: "Label Creator" + continue-on-error: true + uses: cssnr/label-creator-action@latest + with: + file: .configs/labels/labels.yaml + + - name: "Labeler" + uses: actions/labeler@v6 + with: + sync-labels: true + configuration-path: .configs/labels/labeler.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index d33fd46..3013e2b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -5,10 +5,12 @@ on: push: branches: [master] paths-ignore: + - ".gitattributes" - ".gitignore" - ".prettierignore" - "MANIFEST.in" pull_request: + branches: [master] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -30,34 +32,22 @@ jobs: - name: "Debug event.json" continue-on-error: true run: cat "${GITHUB_EVENT_PATH}" - - name: "Debug CTX github" continue-on-error: true env: GITHUB_CTX: ${{ toJSON(github) }} run: echo "$GITHUB_CTX" - - name: "Debug Environment" continue-on-error: true run: env - #- name: "Check Changed Files" - # id: changed - # uses: tj-actions/changed-files@v47 - # with: - # files: | - # src/** - # tests/** - # pyproject.toml - - name: "Setup Python 3.13" uses: actions/setup-python@v6 with: python-version: "3.13" cache: "pip" - - name: "Install" - id: install + - name: "Install Project" run: | python -m pip install -U pip python -m pip install --group dev @@ -75,7 +65,7 @@ jobs: - name: "astral-sh/ruff" if: ${{ !cancelled() }} - uses: astral-sh/ruff-action@v3 + uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1 with: version: latest @@ -90,7 +80,7 @@ jobs: - name: "isort" if: ${{ !cancelled() }} - uses: isort/isort-action@v1 + uses: isort/isort-action@24d8a7a51d33ca7f36c3f23598dafa33f7071326 # v1.1.1 - name: "mypy" if: ${{ !cancelled() }} @@ -107,39 +97,32 @@ jobs: run: | validate-pyproject pyproject.toml - - name: "prettier" + - name: "tombi" if: ${{ !cancelled() }} run: | - echo "::group::Install" - npm install prettier - echo "::endgroup::" - npx prettier --check . + tombi lint - name: "yamllint" if: ${{ !cancelled() }} - env: - CONFIG: "{extends: relaxed, ignore: [node_modules/], rules: {line-length: {max: 119}}}" run: | echo "::group::List Files" - yamllint -d '${{ env.CONFIG }}' --list-files . + yamllint -d .github/yamllint.yaml --list-files . echo "::endgroup::" - yamllint -d '${{ env.CONFIG }}' . + yamllint -d .github/yamllint.yaml . - - name: "actionlint" + - name: "prettier" if: ${{ !cancelled() }} run: | - echo "::group::Download" - loc=$(curl -sI https://github.com/rhysd/actionlint/releases/latest | grep -i '^location:') - echo "loc: ${loc}" - tag=$(echo "${loc}" | sed -E 's|.*/tag/v?(.*)|\1|' | tr -d '\t\r\n') - echo "tag: ${tag}" - url="https://github.com/rhysd/actionlint/releases/latest/download/actionlint_${tag}_linux_amd64.tar.gz" - echo "url: ${url}" - curl -sL "${url}" | tar xz -C "${RUNNER_TEMP}" actionlint - file "${RUNNER_TEMP}/actionlint" - "${RUNNER_TEMP}/actionlint" --version + echo "::group::Install" + npm install prettier echo "::endgroup::" - "${RUNNER_TEMP}/actionlint" -color -verbose -shellcheck= -pyflakes= + npx prettier --check . + + - name: "actionlint" + if: ${{ !cancelled() }} + uses: cssnr/actionlint-action@v1 + with: + shellcheck_opts: -e SC2012 - name: "pytest" if: ${{ !cancelled() }} @@ -151,21 +134,6 @@ jobs: - name: "codecov" if: ${{ !cancelled() && steps.coverage.outcome == 'success' }} - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: token: ${{ secrets.CODECOV_TOKEN }} - - #- name: "hadolint" - # if: ${{ !cancelled() }} - # uses: hadolint/hadolint-action@v3.3.0 - # with: - # dockerfile: Dockerfile - - #- name: "Vale" - # if: ${{ !cancelled() }} - # uses: errata-ai/vale-action@v2.1.1 - - #- name: "SonarQube" - # uses: SonarSource/sonarqube-scan-action@v4 - # env: - # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4b6e6b9..e467bc4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -5,8 +5,7 @@ on: types: [published] env: - url: "https://${{ github.event.release.prerelease && 'test.' || '' }}pypi.org/p/vultr-python" - repository-url: "https://${{ github.event.release.prerelease && 'test' || 'upload' }}.pypi.org/legacy/" + name: "vultr-python" jobs: build: @@ -14,10 +13,9 @@ jobs: uses: ./.github/workflows/build.yaml secrets: inherit with: - name: "release" - + version: ${{ github.ref_name }} permissions: - contents: read + contents: write publish: name: "Publish" @@ -30,54 +28,39 @@ jobs: id-token: write environment: - name: ${{ github.event.release.prerelease && 'test' || 'pypi' }} - url: ${{ env.url }} + name: "pypi" + url: "https://pypi.org/p/vultr-python" steps: - - name: "Debug event.json" - continue-on-error: true - run: cat "${GITHUB_EVENT_PATH}" - - - name: "Debug CTX github" - continue-on-error: true - env: - GITHUB_CTX: ${{ toJSON(github) }} - run: echo "$GITHUB_CTX" - - #- name: "Debug Environment" - # continue-on-error: true - # run: env - - name: "Debug" run: | echo "github.ref_name: ${{ github.ref_name }}" - echo "name: ${{ github.event.release.prerelease && 'test' || 'pypi' }}" - echo "env.url: ${{ env.url }}" - echo "env.repository-url: ${{ env.repository-url }}" + echo "env.name: ${{ env.name }}" - name: "Download Artifact" uses: actions/download-artifact@v6 with: - name: "release" + name: "dist" path: "dist" - - name: "Verify Artifacts" + - name: "Verify Artifact" env: path: "dist" + name: ${{ env.name }} tag: ${{ github.ref_name }} run: | - ls -lAh ${path}/* - results="$(ls -lAh ${path}/* | awk '{print $9" - "$5}')" - markdown='Artifacts:\n```text\n'"${results}"'\n```' - echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY - ls ${path} | grep -- "-${tag}-" > /dev/null + ls -lAh "${path}"/* + results="$(ls -lAh "${path}"/* | awk '{print $9" - "$5}')" + md="Artifacts:\n\`\`\`text\n${results}\n\`\`\`" + echo -e "${md}" >> "$GITHUB_STEP_SUMMARY" + echo "Verification String: '${name//-/_}-${tag}-'" + find "${path}" -type f | grep -- "${name//-/_}-${tag}-" > /dev/null - name: "Publish to PyPI" id: publish uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: true - repository-url: ${{ env.repository-url }} #attestations: false - name: "Summary" @@ -85,24 +68,22 @@ jobs: env: tag: ${{ github.ref_name }} url: ${{ env.url }} - repository-url: ${{ env.repository-url }} run: | if [ "${{ steps.publish.outcome }}" == "success" ];then - markdown="🎉 Published Version: \`${tag}\`\n\n[${url}](${url})\n\n" + md="🎉 Published Version: \`${tag}\`\n\n[${url}](${url})\n\n" else - markdown="⛔ Error Publishing Version: \`${tag}\`\n\n" + md="⛔ Error Publishing Version: \`${tag}\`\n\n" fi - markdown+="Repository URL: \`${repository-url}\`" - echo -e "${markdown}" >> $GITHUB_STEP_SUMMARY + echo -e "${md}" >> "$GITHUB_STEP_SUMMARY" - name: "Send Notification" if: ${{ !cancelled() }} - uses: sarisia/actions-status-discord@v1 + uses: sarisia/actions-status-discord@b8381b25576cb341b2af39926ab42c5056cc44ed # v1.15.5 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} description: | - ${{ env.url }} - ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} + - ${{ format('https://pypi.org/p/{0}/{1}', env.name, github.ref_name) }} + - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} release: name: "Release" @@ -114,51 +95,24 @@ jobs: contents: write steps: - - name: "Debug" - continue-on-error: true - run: | - echo "github.ref_name: ${{ github.ref_name }}" - echo "github.event.release.prerelease: ${{ github.event.release.prerelease }}" - - name: "Debug event.json" continue-on-error: true run: cat "${GITHUB_EVENT_PATH}" - - name: "Debug CTX github" continue-on-error: true env: GITHUB_CTX: ${{ toJSON(github) }} run: echo "$GITHUB_CTX" + - name: "Debug Environment" + continue-on-error: true + run: env - #- name: "Debug Environment" - # continue-on-error: true - # run: env - - #- name: "Update Latest Tag" - # if: ${{ !github.event.release.prerelease }} - # uses: cssnr/update-version-tags-action@latest - # continue-on-error: true - # with: - # major: false - # minor: false - # tags: latest - # #tag: ${{ github.ref_name }} - - - name: "Download Artifact" - uses: actions/download-artifact@v6 - with: - name: "release" - path: "dist" - - - name: "List Artifacts" + - name: "Debug" + continue-on-error: true run: | - ls -lAh dist - ls dist | grep -- "-${{ github.ref_name }}-" > /dev/null - - - name: "Upload Release" - uses: cssnr/upload-release-action@latest - with: - globs: dist/* + echo "github.ref_name: ${{ github.ref_name }}" + echo "github.event_name: ${{ github.event_name }}" + echo "github.event.release.prerelease: ${{ github.event.release.prerelease }}" - name: "Update Release Notes Action" continue-on-error: true @@ -166,12 +120,12 @@ jobs: with: location: tail pypi: | - name: vultr-python + name: ${{ env.name }} prerelease: ${{ github.event.release.prerelease }} - name: "Send Failure Notification" if: ${{ failure() }} - uses: sarisia/actions-status-discord@v1 + uses: sarisia/actions-status-discord@b8381b25576cb341b2af39926ab42c5056cc44ed # v1.15.5 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} - description: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} + description: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e61026f..32f679a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,30 +9,36 @@ on: - "src/**" - "tests/**" - "pyproject.toml" + - "uv.lock" pull_request: + branches: [master] paths: *paths +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: name: "Build" if: ${{ !contains(github.event.head_commit.message, '#notest') }} uses: ./.github/workflows/build.yaml with: - name: test - + version: "" + #version-file: "" permissions: - contents: read + contents: write test: - runs-on: ubuntu-22.04 + name: "Test" + runs-on: ${{ matrix.os }} timeout-minutes: 5 needs: [build] strategy: fail-fast: false matrix: + os: ["ubuntu-22.04"] version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] - name: "Test ${{ matrix.version }}" - permissions: contents: read @@ -41,26 +47,21 @@ jobs: uses: actions/checkout@v6 - name: "Debug event.json" - if: ${{ !github.event.act }} continue-on-error: true run: cat "${GITHUB_EVENT_PATH}" - - name: "Debug CTX github" - if: ${{ !github.event.act }} continue-on-error: true env: GITHUB_CTX: ${{ toJSON(github) }} run: echo "$GITHUB_CTX" - - name: "Debug Environment" - if: ${{ !github.event.act }} continue-on-error: true run: env - name: "Download Artifact" uses: actions/download-artifact@v6 with: - name: test + name: dist path: dist - name: "Setup Python ${{ matrix.version }}" @@ -75,11 +76,6 @@ jobs: python -m pip install -U pip pytest python -m pip install dist/*.whl - #- name: "Debug ${{ matrix.version }}" - # run: | - # ls -lAhR dist/ - - name: "Test ${{ matrix.version }}" - #continue-on-error: ${{ contains('dev', matrix.version) }} run: | pytest -s diff --git a/.github/yamllint.yaml b/.github/yamllint.yaml new file mode 100644 index 0000000..964f421 --- /dev/null +++ b/.github/yamllint.yaml @@ -0,0 +1,8 @@ +extends: relaxed + +ignore: + - "node_modules/" + +rules: + line-length: + max: 119 diff --git a/.gitignore b/.gitignore index 46aea92..72a00b4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,12 +8,11 @@ __pycache__/ *.egg-info/ build/ dist/ +.*cache/ *.log *.pyc .coverage coverage.xml -# Zensical -site/ -.cache/ # App -.secrets +site/ +output.txt diff --git a/.prettierignore b/.prettierignore index 5b4267c..0ea6e24 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,16 +1,2 @@ -# IDE -.idea/ -.vscode/ - -# Build -dist/ -node_modules/ - -# Tools -.ruff_cache/ -.mypy_cache/ -.pytest_cache/ - -# Files package-lock.json *.html diff --git a/README.md b/README.md index 48fd2e1..9ee3c08 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ Vultr Python -- [Install](#Install) -- [Usage](#Usage) -- [Support](#Support) -- [Contributing](#Contributing) +- [Install](#install) +- [Usage](#usage) +- [Support](#support) +- [Contributing](#contributing) Python 3 wrapper for the Vultr API v2. @@ -44,7 +44,7 @@ Vultr API Reference: [https://www.vultr.com/api](https://www.vultr.com/api/?ref= For more details visit [www.vultr.com](https://www.vultr.com/?ref=6905748). -## Install +## Install From PyPi: @@ -59,7 +59,7 @@ git clone https://github.com/cssnr/vultr-python.git python -m pip install vultr-python ``` -## Usage +## Usage You will need to create an api key and whitelist your IP address for most functions. @@ -144,7 +144,7 @@ Full Documentation: [https://cssnr.github.io/vultr-python](https://cssnr.github. Vultr API Reference: [https://www.vultr.com/api](https://www.vultr.com/api/?ref=6905748) -# Support +# Support For general help or to request a feature, see: @@ -158,7 +158,7 @@ If you are experiencing an issue/bug or getting unexpected results, you can: - Provide General Feedback: [https://cssnr.github.io/feedback/](https://cssnr.github.io/feedback/?app=vultr-python) - Chat with us on Discord: -# Contributing +# Contributing If you would like to submit a PR, please review the [CONTRIBUTING.md](#contributing-ov-file). diff --git a/docs.ps1 b/docs.ps1 index 7fed3bc..5113e3e 100644 --- a/docs.ps1 +++ b/docs.ps1 @@ -1,12 +1,14 @@ param ( [switch]$c, - [switch]$b + [switch]$b, + [switch]$w ) $ErrorActionPreference = "Stop" write-output "Clean: $c" write-output "Build: $b" +write-output "Watch: $w" if ($c) { Write-Host -ForegroundColor Yellow "Cleaning Docs..." @@ -22,18 +24,24 @@ if ($c) { } } -if ($b) { +$logo = "https://raw.githubusercontent.com/cssnr/vultr-python/refs/heads/master/.github/assets/logo.svg" +$logoLink = "https://github.com/cssnr/vultr-python" + +if ($b) +{ Write-Host -ForegroundColor Yellow "Building Docs..." python -m pdoc -t .\docs\ -o site ` - --logo "https://raw.githubusercontent.com/cssnr/vultr-python/refs/heads/master/.github/assets/logo.svg" ` - --logo-link "https://github.com/cssnr/vultr-python" ` - vultr + --logo $logo --logo-link $logoLink vultr +} +elseif ($w) { + Write-Host -ForegroundColor Green "Watching Docs..." + watchmedo auto-restart -R -d .\docs\ -p "*.*" -- ` + python -m pdoc -t .\docs\ -p 8000 -h 0.0.0.0 ` + --logo $logo --logo-link $logoLink .\src\vultr\ } else { Write-Host -ForegroundColor Green "Serving Docs..." python -m pdoc -t .\docs\ -p 8000 -h 0.0.0.0 ` - --logo "https://raw.githubusercontent.com/cssnr/vultr-python/refs/heads/master/.github/assets/logo.svg" ` - --logo-link "https://github.com/cssnr/vultr-python" ` - vultr + --logo $logo --logo-link $logoLink .\src\vultr\ } #--favicon "https://df.cssnr.com/raw/logo128.png" ` diff --git a/pyproject.toml b/pyproject.toml index 46d7e71..3d30ee3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Operating System :: OS Independent", "License :: OSI Approved :: MIT License", "Natural Language :: English", + "Typing :: Typed", ] license = { text = "MIT" } #license = { file = "LICENSE" } @@ -39,23 +40,26 @@ dev = [ "requests", "ruff", "setuptools", + "tombi; python_version>='3.10'", "ty", "types-requests", "validate-pyproject[all]", + "watchdog[watchmedo]", ] [project.urls] -Homepage = "https://cssnr.com/" -Documentation = "https://cssnr.github.io/vultr-python" -Source = "https://github.com/cssnr/vultr-python" +GitHub = "https://github.com/cssnr/vultr-python" Issues = "https://github.com/cssnr/vultr-python/issues" Changelog = "https://github.com/cssnr/vultr-python/releases" +Documentation = "https://cssnr.github.io/vultr-python/" + +Homepage = "https://cssnr.com/" Funding = "https://ko-fi.com/cssnr" Discord = "https://discord.gg/wXy6m2X8wY" # Setup Tools -#[tool.setuptools.package-data] -#vultr = ["py.typed"] +[tool.setuptools.package-data] +vultr = ["py.typed"] [tool.setuptools.dynamic] version = { attr = "vultr.version.__version__" }