From 57bc4907059b4d30ef9973219fc8171bf1122325 Mon Sep 17 00:00:00 2001 From: Hector Castejon Diaz Date: Mon, 21 Sep 2026 06:48:54 +0000 Subject: [PATCH] Validate build-time SDK generation with google-java-format Draft PR to validate the Databricks Universe build-time SDK generation against this repo's CI. The SDK surface and the generated test suite are rendered from the OpenAPI spec at build time and formatted in place with google-java-format (run to a fixed point), replacing the committed generated code and the Maven Spotless lint: - Surface + generated tests formatted by google-java-format; the test files come out byte-identical to the current formatting (0 test-file changes here), confirming the generate-time formatter matches Spotless's output. - Spotless removed from the build (pom, the CI "Check formatting" step, the Makefile targets, scripts/mvn-spotless-apply.sh): the generator is now authoritative, so a second formatter here would only risk drift. - Release/changelog tooling (tagging.py, the tagging and next-changelog workflows, NEXT_CHANGELOG.md) is dropped; releases move to the Universe pipeline. - Remaining source changes are ordinary API evolution since v0.156.0. Not for merge -- this validates formatting and CI only. Signed-off-by: Hector Castejon Diaz Co-authored-by: Isaac --- .codegen.json | 34 +- .codegen/_last_sha | 1 + .github/workflows/next-changelog.yml | 108 -- .github/workflows/push.yml | 4 - .github/workflows/tagging.yml | 122 -- .vscode/settings.json | 3 - CONTRIBUTING.md | 2 - Makefile | 14 - NEXT_CHANGELOG.md | 17 - .../com/databricks/sdk/service/apps/App.java | 10 +- .../sdk/service/apps/AppUpdate.java | 27 +- .../sdk/service/apps/ApplicationStatus.java | 2 +- .../sdk/service/apps/ComputeStatus.java | 2 +- .../sdk/service/catalog/ConnectionType.java | 1 + .../McpServiceConfigSourceConnection.java | 26 +- ...eConfigCustomProviderApiKeyHeaderAuth.java | 72 + ...rviceConfigCustomProviderDirectConfig.java | 29 +- .../sdk/service/catalog/SecurableType.java | 2 + .../cleanrooms/CleanRoomAssetJarAnalysis.java | 4 +- .../sdk/service/dashboards/LakeviewAPI.java | 139 +- .../service/dashboards/LakeviewService.java | 139 +- .../databricks/sdk/service/jobs/BaseRun.java | 7 +- .../sdk/service/jobs/CreateJob.java | 7 +- .../service/jobs/JobEmailNotifications.java | 38 + .../sdk/service/jobs/JobSettings.java | 7 +- .../sdk/service/jobs/RepairHistoryItem.java | 7 +- .../sdk/service/jobs/RepairRun.java | 7 +- .../com/databricks/sdk/service/jobs/Run.java | 7 +- .../databricks/sdk/service/jobs/RunNow.java | 7 +- .../databricks/sdk/service/jobs/RunTask.java | 7 +- .../sdk/service/jobs/SubmitRun.java | 7 +- .../service/jobs/TaskEmailNotifications.java | 38 + .../service/jobs/WebhookNotifications.java | 40 + .../service/ml/BackfillFeaturesRequest.java | 45 +- .../sdk/service/ml/CronSchedule.java | 30 +- .../ml/PurgeFeatureEntitiesRequest.java | 47 +- .../sdk/service/oauth2/FederationPolicy.java | 5 +- .../pipelines/IngestionSourceType.java | 2 + .../databricks/example/CliAuthAccount.java | 49 +- .../databricks/example/CliAuthWorkspace.java | 49 +- .../example/CliOAuthU2MExample.java | 22 +- .../example/GithubOIDCAuthExample.java | 54 +- .../databricks/example/HttpProxyExample.java | 8 +- .../databricks/example/M2MAuthExample.java | 72 +- .../example/TriggerJobToRunPythonProgram.java | 218 +-- .../example/UnifiedAuthWorkspace.java | 28 +- .../src/main/java/com/databricks/sdk/App.java | 25 +- .../com/databricks/sdk/RootController.java | 86 +- pom.xml | 35 - scripts/mvn-spotless-apply.sh | 70 - tagging.py | 1395 ----------------- tagging.py.lock | 302 ---- 52 files changed, 1019 insertions(+), 2460 deletions(-) create mode 100644 .codegen/_last_sha delete mode 100644 .github/workflows/next-changelog.yml delete mode 100644 .github/workflows/tagging.yml delete mode 100644 .vscode/settings.json delete mode 100644 NEXT_CHANGELOG.md create mode 100644 databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth.java delete mode 100755 scripts/mvn-spotless-apply.sh delete mode 100644 tagging.py delete mode 100755 tagging.py.lock diff --git a/.codegen.json b/.codegen.json index a62df179d..62e4908e9 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1,6 +1,38 @@ { "mode": "java_v0", "api_changelog": true, + "sync": { + "allow_from_packages": [ + "**/*.java", + "**/*.{xml,html,tmpl,properties}", + "**/*.md", + "**/*.sh", + "**/testdata/**", + ".codegen.json", + ".codegen/{_last_sha,_openapi_sha}", + ".gitattributes", + "**/.gitignore", + ".github/actions/**/*.yml", + ".github/conftest/**/*.rego", + ".github/dependabot.yml", + ".github/workflows/*.yml", + ".package.json", + ".release_metadata.json", + "DCO", + "LICENSE", + "Makefile", + "NOTICE", + "databricks-sdk-java/lockfile.json", + "lockfile.json" + ], + "skip_from_source": [ + ".nextchanges", + "BUILD.bazel", + "CLAUDE.md", + "OWNERS", + "changelog_validate" + ] + }, "version": { "pom.xml": "databricks-sdk-parent\n $VERSION", "databricks-sdk-java/pom.xml": "databricks-sdk-parent\n $VERSION", @@ -14,6 +46,6 @@ "toolchain": { "require": ["mvn", "java"], "setup": ["bash scripts/cleanup-services.sh"], - "post_generate": ["bash scripts/mvn-spotless-apply.sh","mvn --errors clean compile"] + "post_generate": ["mvn --errors clean compile"] } } diff --git a/.codegen/_last_sha b/.codegen/_last_sha new file mode 100644 index 000000000..f26953677 --- /dev/null +++ b/.codegen/_last_sha @@ -0,0 +1 @@ +463ed2cce38f6306936a13c3a51822d7999553c3 \ No newline at end of file diff --git a/.github/workflows/next-changelog.yml b/.github/workflows/next-changelog.yml deleted file mode 100644 index 475157d1f..000000000 --- a/.github/workflows/next-changelog.yml +++ /dev/null @@ -1,108 +0,0 @@ -# Generated file. DO NOT EDIT. -name: Check for NEXT_CHANGELOG.md Changes - -on: - # Use pull_request_target to have access to GitHub API - pull_request_target: - types: [opened, synchronize, reopened, edited] - -jobs: - check-next-changelog: - # Allow Dependabot PRs to pass without a changelog entry - if: github.actor != 'dependabot[bot]' - runs-on: - group: databricks-protected-runner-group - labels: linux-ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Fetch list of changed files - id: changed-files - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Use the GitHub API to fetch changed files - files=$(gh pr view ${{ github.event.pull_request.number }} --json files -q '.files[].path') - - # Sanitize to avoid code injection - sanitized_files=$(echo "$files" | sed 's/[^a-zA-Z0-9._/-]/_/g') - - # Store the sanitized list of files in a temporary file to avoid env variable issues - echo "$sanitized_files" > modified_files.txt - - - name: Fetch PR message - id: pr-message - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Use the GitHub API to fetch the PR message - pr_message=$(gh pr view ${{ github.event.pull_request.number }} --json body -q '.body') - - # Sanitize the PR message to avoid code injection, keeping the equal sign - sanitized_pr_message=$(echo "$pr_message" | sed 's/[^a-zA-Z0-9._/-=]/_/g') - - # Store the sanitized PR message - echo "$sanitized_pr_message" > pr_message.txt - - - name: Verify NEXT_CHANGELOG.md was modified or PR message contains NO_CHANGELOG=true - run: | - # Read the sanitized files and PR message from the temporary files - modified_files=$(cat modified_files.txt) - pr_message=$(cat pr_message.txt) - - # Check if NEXT_CHANGELOG.md exists in the list of changed files - echo "Changed files: $modified_files" - if ! echo "$modified_files" | grep -q "NEXT_CHANGELOG.md"; then - echo "NEXT_CHANGELOG.md not modified." - - # Check if PR message contains NO_CHANGELOG=true - if echo "$pr_message" | grep -q "NO_CHANGELOG=true"; then - echo "NO_CHANGELOG=true found in PR message. Skipping changelog check." - exit 0 - else - echo "WARNING: file NEXT_CHANGELOG.md not changed. If this is expected, add NO_CHANGELOG=true to the PR message." - exit 1 - fi - fi - - - name: Comment on PR with instructions if needed - if: failure() # This step will only run if the previous step fails (i.e., if NEXT_CHANGELOG.md was not modified and NO_CHANGELOG=true was not in the PR message) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Check if a comment exists with the instructions - previous_comment_ids=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ - --jq '.[] | select(.body | startswith("")) | .id') - echo "Previous comment IDs: $previous_comment_ids" - - # If no previous comment exists, add one with instructions - if [ -z "$previous_comment_ids" ]; then - echo "Adding instructions comment." - gh pr comment ${{ github.event.pull_request.number }} --body \ - " - Please ensure that the NEXT_CHANGELOG.md file is updated with any relevant changes. - If this is not necessary for your PR, please include the following in your PR description: - NO_CHANGELOG=true - and rerun the job." - fi - - - name: Delete instructions comment on success - if: success() # This step will only run if the previous check passed (i.e., if NEXT_CHANGELOG.md was modified or NO_CHANGELOG=true is in the PR message) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Check if there is a previous instructions comment - previous_comment_ids=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ - --jq '.[] | select(.body | startswith("")) | .id') - - # If a comment exists, delete it - if [ -n "$previous_comment_ids" ]; then - echo "Deleting previous instructions comment." - for comment_id in $previous_comment_ids; do - gh api "repos/${{ github.repository }}/issues/comments/$comment_id" --method DELETE - done - else - echo "No instructions comment found to delete." - fi diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 3ecb424a3..4f75d4526 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -30,12 +30,8 @@ jobs: - name: Setup build environment uses: ./.github/actions/setup-build-environment with: - # google-java-format 1.27.0 (pinned in pom.xml) requires JRE 17+ to run. java-version: 17 - - name: Check formatting - run: mvn --errors spotless:check - - name: Check for JFrog proxy URLs in lockfiles run: | make fix-lockfile diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml deleted file mode 100644 index 341df0776..000000000 --- a/.github/workflows/tagging.yml +++ /dev/null @@ -1,122 +0,0 @@ -# Generated file. DO NOT EDIT. -name: tagging - -on: - # Manual dispatch. - workflow_dispatch: - inputs: - packages: - description: 'Comma-separated list of packages to tag (e.g. "pkg1,pkg2"). Leave empty to tag all packages with pending releases.' - required: false - type: string - default: '' - - # NOTE: Temporarily disable automated releases. - # - # Runs at 8:00 UTC on Monday, Tuesday, Wednesday, and Thursday. To enable automated - # tagging for a repository, simply add it to the if block of the tag job. - # schedule: - # - cron: '0 8 * * MON,TUE,WED,THU' - -# Ensure that only a single instance of the workflow is running at a time. -concurrency: - group: "tagging" - - -jobs: - tag: - # Only run the tag job if the trigger is manual (workflow_dispatch) or - # the repository has been approved for automated releases. - # - # To disable release for a repository, simply exclude it from the if - # condition. - if: >- - github.event_name == 'workflow_dispatch' || - github.repository == 'databricks/databricks-sdk-go' || - github.repository == 'databricks/databricks-sdk-py' || - github.repository == 'databricks/databricks-sdk-java' - env: - # Ref the release is cut from. Releasing from a branch is currently - # supported in the terraform-provider-databricks and cli repositories: - # for those two this follows the dispatched branch - # (``github.ref_name``), so a release can be cut from a branch other - # than main; every other synced repo (SDK go/py/java, …) stays pinned - # to main, preserving the pre-existing hardcoded-main behaviour and - # blocking branch releases even via a manual non-main dispatch. - RELEASE_REF: ${{ (github.repository == 'databricks/terraform-provider-databricks' || github.repository == 'databricks/cli') && github.ref_name || 'main' }} - # Changelog source. databricks/cli builds the release changelog from - # per-PR ``.nextchanges/
/*.md`` fragments and a - # ``.nextchanges/version`` file rather than a single hand-maintained - # ``NEXT_CHANGELOG.md`` (see tagging.py's nextchanges mode). Setting this - # to the fragment directory name opts that repo in; every other synced - # repo leaves it empty and keeps the ``NEXT_CHANGELOG.md`` flow unchanged. - NEXTCHANGES_DIR: ${{ github.repository == 'databricks/cli' && '.nextchanges' || '' }} - environment: "release-is" - runs-on: - group: databricks-protected-runner-group - labels: linux-ubuntu-latest - steps: - - name: Generate GitHub App Token - id: generate-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 - with: - app-id: ${{ secrets.DECO_SDK_TAGGING_APP_ID }} - private-key: ${{ secrets.DECO_SDK_TAGGING_PRIVATE_KEY }} - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - # Check out the release ref. ``RELEASE_REF`` is the dispatched - # branch for terraform-provider-databricks and cli, and ``main`` for every - # other repo (see the job-level ``env``). For scheduled runs and - # manual dispatches left at defaults this is the default branch - # (main); dispatching terraform or cli with ``--ref `` cuts the - # release from that branch instead. - # - # Naming the ref explicitly (rather than omitting ``ref:``) - # forces re-resolution of the branch head at step time. Without - # it, checkout pins to ``github.sha`` — the SHA frozen at - # workflow_dispatch time — so re-running a stale dispatch would - # check out an older commit even when newer ones exist. - ref: ${{ env.RELEASE_REF }} - fetch-depth: 0 - token: ${{ steps.generate-token.outputs.token }} - - #NOTE: email must be the GitHub App email or the commit will not be verified. - - name: Set up Git configuration - run: | - git config user.name "Databricks SDK Release Bot" - git config user.email "DECO-SDK-Tagging[bot]@users.noreply.github.com" - - - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - - - name: Run script - env: - GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} - GITHUB_REPOSITORY: ${{ github.repository }} - PACKAGES: ${{ inputs.packages }} - # Branch the release is cut from. tagging.py commits the - # changelog bump and creates the tag on this branch, and its - # concurrent-advance guard checks this branch. Matches the ref - # checked out above (``RELEASE_REF``): the dispatched branch for - # terraform and cli, main everywhere else. - DECO_TAGGING_REF: ${{ env.RELEASE_REF }} - # Non-empty only for databricks/cli (see job-level env). When set, - # tagging.py renders the changelog from ``$NEXTCHANGES_DIR/`` fragments - # instead of ``NEXT_CHANGELOG.md``. - NEXTCHANGES_DIR: ${{ env.NEXTCHANGES_DIR }} - run: | - if [ -n "$PACKAGES" ]; then - uv run --locked tagging.py --package "$PACKAGES" - else - uv run --locked tagging.py - fi - - - name: Upload created tags artifact - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: created-tags - path: created_tags.json - if-no-files-found: ignore diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7b016a89f..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.compile.nullAnalysis.mode": "automatic" -} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c4f1a644..b696e6ac2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,8 +86,6 @@ We recommend using IntelliJ to develop the Java SDK. You can download the commun We welcome any contributions to improving documentation of the Databricks Java SDK. All documentation contributions should be as detailed as possible. Please note that many files of the SDK are autogenerated, including all files in the `databricks-sdk-java/src/main/java/com/databricks/sdk/service` directory, so manual edits to those files will be overwritten. -After adding any documentation, make sure to run the Spotless code formatting plugin, either from the Maven plugin in IntelliJ or from the command line: `mvn spotless:apply`. - ## Debugging The Databricks Java SDK uses the [SLF4J](https://www.slf4j.org/) logging framework. To enable debug logging, add the following line to your `log4j.properties` file: diff --git a/Makefile b/Makefile index 32c24823c..04b5c9280 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,3 @@ -fmt: - @mvn spotless:apply || ( \ - echo ""; \ - echo "==> default JDK could not run google-java-format (likely < JDK 17). Retrying with JDK 17..."; \ - echo ""; \ - $(MAKE) fmt-jdk17 \ - ) - -# Same as `fmt` but runs under JDK 17 explicitly. Use this when your default -# JDK is older than 17 (e.g. JDK 11), since google-java-format 1.27.0 — pinned -# in pom.xml for JDK 25 generator support — requires JRE 17+ to load. -fmt-jdk17: - JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 mvn spotless:apply - test: mvn test diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md deleted file mode 100644 index d5bd1f524..000000000 --- a/NEXT_CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -# NEXT CHANGELOG - -## Release v0.157.0 - -### New Features and Improvements - -### Breaking Changes - -### Bug Fixes - -### Security Vulnerabilities - -### Documentation - -### Internal Changes - -### API Changes diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/App.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/App.java index 5dde28892..a473ebe45 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/App.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/App.java @@ -25,11 +25,17 @@ public class App { @JsonProperty("budget_policy_id") private String budgetPolicyId; - /** Maximum number of app instances. Must be set together with `compute_min_instances`. */ + /** + * Maximum number of app instances the app is configured to run. Must be set together with + * `compute_min_instances`. + */ @JsonProperty("compute_max_instances") private Long computeMaxInstances; - /** Minimum number of app instances. Must be set together with `compute_max_instances`. */ + /** + * Minimum number of app instances the app is configured to run. Must be set together with + * `compute_max_instances`. + */ @JsonProperty("compute_min_instances") private Long computeMinInstances; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/AppUpdate.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/AppUpdate.java index b8a4825dd..59ed5d36a 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/AppUpdate.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/AppUpdate.java @@ -14,11 +14,17 @@ public class AppUpdate { @JsonProperty("budget_policy_id") private String budgetPolicyId; - /** Maximum number of app instances. Must be set together with `compute_min_instances`. */ + /** + * Maximum number of app instances the app is configured to run. Must be set together with + * `compute_min_instances`. + */ @JsonProperty("compute_max_instances") private Long computeMaxInstances; - /** Minimum number of app instances. Must be set together with `compute_max_instances`. */ + /** + * Minimum number of app instances the app is configured to run. Must be set together with + * `compute_max_instances`. + */ @JsonProperty("compute_min_instances") private Long computeMinInstances; @@ -49,6 +55,10 @@ public class AppUpdate { @JsonProperty("status") private AppUpdateUpdateStatus status; + /** */ + @JsonProperty("telemetry_export_destinations") + private Collection telemetryExportDestinations; + /** */ @JsonProperty("usage_policy_id") private String usagePolicyId; @@ -138,6 +148,16 @@ public AppUpdateUpdateStatus getStatus() { return status; } + public AppUpdate setTelemetryExportDestinations( + Collection telemetryExportDestinations) { + this.telemetryExportDestinations = telemetryExportDestinations; + return this; + } + + public Collection getTelemetryExportDestinations() { + return telemetryExportDestinations; + } + public AppUpdate setUsagePolicyId(String usagePolicyId) { this.usagePolicyId = usagePolicyId; return this; @@ -170,6 +190,7 @@ public boolean equals(Object o) { && Objects.equals(gitRepository, that.gitRepository) && Objects.equals(resources, that.resources) && Objects.equals(status, that.status) + && Objects.equals(telemetryExportDestinations, that.telemetryExportDestinations) && Objects.equals(usagePolicyId, that.usagePolicyId) && Objects.equals(userApiScopes, that.userApiScopes); } @@ -186,6 +207,7 @@ public int hashCode() { gitRepository, resources, status, + telemetryExportDestinations, usagePolicyId, userApiScopes); } @@ -202,6 +224,7 @@ public String toString() { .add("gitRepository", gitRepository) .add("resources", resources) .add("status", status) + .add("telemetryExportDestinations", telemetryExportDestinations) .add("usagePolicyId", usagePolicyId) .add("userApiScopes", userApiScopes) .toString(); diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ApplicationStatus.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ApplicationStatus.java index 1e9da9de5..ca64de094 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ApplicationStatus.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ApplicationStatus.java @@ -13,7 +13,7 @@ public class ApplicationStatus { @JsonProperty("message") private String message; - /** The number of running instances of this application. */ + /** The number of app instances whose application process is running. */ @JsonProperty("running_instances") private Long runningInstances; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ComputeStatus.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ComputeStatus.java index 19bc1f492..64a0a658c 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ComputeStatus.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/apps/ComputeStatus.java @@ -9,7 +9,7 @@ @Generated public class ComputeStatus { - /** The number of compute instances used and billed for this application. */ + /** The number of active compute instances currently used and billed for this application. */ @JsonProperty("active_instances") private Long activeInstances; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ConnectionType.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ConnectionType.java index 173dde824..515086420 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ConnectionType.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ConnectionType.java @@ -35,6 +35,7 @@ public enum ConnectionType { SQLDW, SQLSERVER, TERADATA, + TIKTOK_ADS, UNKNOWN_CONNECTION_TYPE, WORKDAY_RAAS, ZENDESK, diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/McpServiceConfigSourceConnection.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/McpServiceConfigSourceConnection.java index de61b9bd9..880dc5a23 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/McpServiceConfigSourceConnection.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/McpServiceConfigSourceConnection.java @@ -5,6 +5,7 @@ import com.databricks.sdk.support.Generated; import com.databricks.sdk.support.ToStringer; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; import java.util.Objects; /** @@ -30,6 +31,15 @@ public class McpServiceConfigSourceConnection { @JsonProperty("name") private String name; + /** + * Options needed to build the U2M authorize request, returned as a flat map. When set, it + * includes: `authorization_endpoint` (OAuth authorize URL), `token_endpoint` (token-exchange + * URL), `oauth_scope` (space-separated scopes to request), `client_id` (OAuth client id), and + * `oauth_provider` (the OAuth provider). + */ + @JsonProperty("options") + private Map options; + public McpServiceConfigSourceConnection setIsDeleted(Boolean isDeleted) { this.isDeleted = isDeleted; return this; @@ -48,17 +58,28 @@ public String getName() { return name; } + public McpServiceConfigSourceConnection setOptions(Map options) { + this.options = options; + return this; + } + + public Map getOptions() { + return options; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; McpServiceConfigSourceConnection that = (McpServiceConfigSourceConnection) o; - return Objects.equals(isDeleted, that.isDeleted) && Objects.equals(name, that.name); + return Objects.equals(isDeleted, that.isDeleted) + && Objects.equals(name, that.name) + && Objects.equals(options, that.options); } @Override public int hashCode() { - return Objects.hash(isDeleted, name); + return Objects.hash(isDeleted, name, options); } @Override @@ -66,6 +87,7 @@ public String toString() { return new ToStringer(McpServiceConfigSourceConnection.class) .add("isDeleted", isDeleted) .add("name", name) + .add("options", options) .toString(); } } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth.java new file mode 100644 index 000000000..86f51ac71 --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth.java @@ -0,0 +1,72 @@ +// Code generated from OpenAPI specs by Databricks SDK Generator. DO NOT EDIT. + +package com.databricks.sdk.service.catalog; + +import com.databricks.sdk.support.Generated; +import com.databricks.sdk.support.ToStringer; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + +/** + * Header-based API-key authentication for a custom provider: the secret is forwarded on outbound + * requests under a caller-chosen HTTP header, as `: `. + */ +@Generated +public class ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth { + /** + * HTTP header name that carries the API key on outbound requests (e.g., + * `Ocp-Apim-Subscription-Key`). The value forwarded under this header is supplied via + * `api_key_value`. + */ + @JsonProperty("api_key_name") + private String apiKeyName; + + /** + * Secret value forwarded under the `api_key_name` header on outbound requests. Supplied as inline + * plaintext via `ProviderSecret.plaintext`. + */ + @JsonProperty("api_key_value") + private ModelProviderServiceConfigProviderSecret apiKeyValue; + + public ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth setApiKeyName(String apiKeyName) { + this.apiKeyName = apiKeyName; + return this; + } + + public String getApiKeyName() { + return apiKeyName; + } + + public ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth setApiKeyValue( + ModelProviderServiceConfigProviderSecret apiKeyValue) { + this.apiKeyValue = apiKeyValue; + return this; + } + + public ModelProviderServiceConfigProviderSecret getApiKeyValue() { + return apiKeyValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth that = + (ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth) o; + return Objects.equals(apiKeyName, that.apiKeyName) + && Objects.equals(apiKeyValue, that.apiKeyValue); + } + + @Override + public int hashCode() { + return Objects.hash(apiKeyName, apiKeyValue); + } + + @Override + public String toString() { + return new ToStringer(ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth.class) + .add("apiKeyName", apiKeyName) + .add("apiKeyValue", apiKeyValue) + .toString(); + } +} diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderDirectConfig.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderDirectConfig.java index 9620a1ed0..017ab13c0 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderDirectConfig.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/ModelProviderServiceConfigCustomProviderDirectConfig.java @@ -8,8 +8,8 @@ import java.util.Objects; /** - * Direct form of a custom provider configuration. Set `api_key` to the bearer token sent in the - * `Authorization` header. + * Direct form of a custom provider configuration. Set `api_key` to send the secret as an + * `Authorization` bearer token, or `header_auth` to forward it under a caller-chosen HTTP header. */ @Generated public class ModelProviderServiceConfigCustomProviderDirectConfig { @@ -26,6 +26,14 @@ public class ModelProviderServiceConfigCustomProviderDirectConfig { @JsonProperty("base_url") private String baseUrl; + /** + * Header-based API-key auth: the secret is forwarded on outbound requests under a caller-chosen + * HTTP header rather than as an `Authorization` bearer token. Set this instead of `api_key` for + * header auth. + */ + @JsonProperty("header_auth") + private ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth headerAuth; + public ModelProviderServiceConfigCustomProviderDirectConfig setApiKey( ModelProviderServiceConfigProviderSecret apiKey) { this.apiKey = apiKey; @@ -45,18 +53,30 @@ public String getBaseUrl() { return baseUrl; } + public ModelProviderServiceConfigCustomProviderDirectConfig setHeaderAuth( + ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth headerAuth) { + this.headerAuth = headerAuth; + return this; + } + + public ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth getHeaderAuth() { + return headerAuth; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ModelProviderServiceConfigCustomProviderDirectConfig that = (ModelProviderServiceConfigCustomProviderDirectConfig) o; - return Objects.equals(apiKey, that.apiKey) && Objects.equals(baseUrl, that.baseUrl); + return Objects.equals(apiKey, that.apiKey) + && Objects.equals(baseUrl, that.baseUrl) + && Objects.equals(headerAuth, that.headerAuth); } @Override public int hashCode() { - return Objects.hash(apiKey, baseUrl); + return Objects.hash(apiKey, baseUrl, headerAuth); } @Override @@ -64,6 +84,7 @@ public String toString() { return new ToStringer(ModelProviderServiceConfigCustomProviderDirectConfig.class) .add("apiKey", apiKey) .add("baseUrl", baseUrl) + .add("headerAuth", headerAuth) .toString(); } } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/SecurableType.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/SecurableType.java index 055549976..03dbb51fe 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/SecurableType.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/catalog/SecurableType.java @@ -7,6 +7,7 @@ /** The type of Unity Catalog securable. */ @Generated public enum SecurableType { + AGENT_SERVICE, CATALOG, CLEAN_ROOM, CONNECTION, @@ -24,6 +25,7 @@ public enum SecurableType { RECIPIENT, SCHEMA, SHARE, + SKILL, STAGING_TABLE, STORAGE_CREDENTIAL, TABLE, diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/cleanrooms/CleanRoomAssetJarAnalysis.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/cleanrooms/CleanRoomAssetJarAnalysis.java index 1011c6401..25ecbfdf6 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/cleanrooms/CleanRoomAssetJarAnalysis.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/cleanrooms/CleanRoomAssetJarAnalysis.java @@ -23,8 +23,8 @@ public class CleanRoomAssetJarAnalysis { private String description; /** - * The serverless environment version used to execute the JAR analysis (e.g. "4"). Defaults to - * "4-scala-preview" if not specified. + * The serverless environment version used to execute the JAR analysis (e.g. "4"). If not + * specified, uses the service-configured JAR analysis default. */ @JsonProperty("environment_version") private String environmentVersion; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewAPI.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewAPI.java index deacceef8..b9dced31d 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewAPI.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewAPI.java @@ -30,18 +30,38 @@ public LakeviewAPI(LakeviewService mock) { /** * Create a draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public Dashboard create(CreateDashboardRequest request) { return impl.create(request); } - /** Create dashboard schedule. */ + /** + * Create dashboard schedule. + * + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements + */ public Schedule createSchedule(CreateScheduleRequest request) { return impl.createSchedule(request); } - /** Create schedule subscription. */ + /** + * Create schedule subscription. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ public Subscription createSubscription(CreateSubscriptionRequest request) { return impl.createSubscription(request); } @@ -51,7 +71,14 @@ public void deleteSchedule(String dashboardId, String scheduleId) { new DeleteScheduleRequest().setDashboardId(dashboardId).setScheduleId(scheduleId)); } - /** Delete dashboard schedule. */ + /** + * Delete dashboard schedule. + * + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements + */ public void deleteSchedule(DeleteScheduleRequest request) { impl.deleteSchedule(request); } @@ -64,7 +91,17 @@ public void deleteSubscription(String dashboardId, String scheduleId, String sub .setSubscriptionId(subscriptionId)); } - /** Delete schedule subscription. */ + /** + * Delete schedule subscription. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ public void deleteSubscription(DeleteSubscriptionRequest request) { impl.deleteSubscription(request); } @@ -76,7 +113,10 @@ public Dashboard get(String dashboardId) { /** * Get a draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public Dashboard get(GetDashboardRequest request) { return impl.get(request); @@ -104,7 +144,17 @@ public Schedule getSchedule(String dashboardId, String scheduleId) { new GetScheduleRequest().setDashboardId(dashboardId).setScheduleId(scheduleId)); } - /** Get dashboard schedule. */ + /** + * Get dashboard schedule. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ public Schedule getSchedule(GetScheduleRequest request) { return impl.getSchedule(request); } @@ -118,7 +168,17 @@ public Subscription getSubscription( .setSubscriptionId(subscriptionId)); } - /** Get schedule subscription. */ + /** + * Get schedule subscription. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ public Subscription getSubscription(GetSubscriptionRequest request) { return impl.getSubscription(request); } @@ -126,7 +186,10 @@ public Subscription getSubscription(GetSubscriptionRequest request) { /** * List dashboards. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public Iterable list(ListDashboardsRequest request) { return Paginator.newTokenPagination( @@ -146,7 +209,17 @@ public Iterable listSchedules(String dashboardId) { return listSchedules(new ListSchedulesRequest().setDashboardId(dashboardId)); } - /** List dashboard schedules. */ + /** + * List dashboard schedules. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ public Iterable listSchedules(ListSchedulesRequest request) { return Paginator.newTokenPagination( request, @@ -166,7 +239,17 @@ public Iterable listSubscriptions(String dashboardId, String sched new ListSubscriptionsRequest().setDashboardId(dashboardId).setScheduleId(scheduleId)); } - /** List schedule subscriptions. */ + /** + * List schedule subscriptions. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ public Iterable listSubscriptions(ListSubscriptionsRequest request) { return Paginator.newTokenPagination( request, @@ -192,7 +275,10 @@ public Dashboard migrate(MigrateDashboardRequest request) { /** * Publish the current draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public PublishedDashboard publish(PublishRequest request) { return impl.publish(request); @@ -201,7 +287,10 @@ public PublishedDashboard publish(PublishRequest request) { /** * Revert a dashboard's definition in draft mode to the last published version. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public RevertDashboardResponse revert(RevertDashboardRequest request) { return impl.revert(request); @@ -214,7 +303,10 @@ public void trash(String dashboardId) { /** * Trash a dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public void trash(TrashDashboardRequest request) { impl.trash(request); @@ -227,7 +319,10 @@ public void unpublish(String dashboardId) { /** * Unpublish the dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public void unpublish(UnpublishDashboardRequest request) { impl.unpublish(request); @@ -236,13 +331,23 @@ public void unpublish(UnpublishDashboardRequest request) { /** * Update a draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ public Dashboard update(UpdateDashboardRequest request) { return impl.update(request); } - /** Update dashboard schedule. */ + /** + * Update dashboard schedule. + * + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements + */ public Schedule updateSchedule(UpdateScheduleRequest request) { return impl.updateSchedule(request); } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewService.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewService.java index 8d9997452..54be52dff 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewService.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/dashboards/LakeviewService.java @@ -16,26 +16,66 @@ public interface LakeviewService { /** * Create a draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ Dashboard create(CreateDashboardRequest createDashboardRequest); - /** Create dashboard schedule. */ + /** + * Create dashboard schedule. + * + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements + */ Schedule createSchedule(CreateScheduleRequest createScheduleRequest); - /** Create schedule subscription. */ + /** + * Create schedule subscription. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ Subscription createSubscription(CreateSubscriptionRequest createSubscriptionRequest); - /** Delete dashboard schedule. */ + /** + * Delete dashboard schedule. + * + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements + */ void deleteSchedule(DeleteScheduleRequest deleteScheduleRequest); - /** Delete schedule subscription. */ + /** + * Delete schedule subscription. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ void deleteSubscription(DeleteSubscriptionRequest deleteSubscriptionRequest); /** * Get a draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ Dashboard get(GetDashboardRequest getDashboardRequest); @@ -50,23 +90,66 @@ public interface LakeviewService { */ PublishedDashboard getPublished(GetPublishedDashboardRequest getPublishedDashboardRequest); - /** Get dashboard schedule. */ + /** + * Get dashboard schedule. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ Schedule getSchedule(GetScheduleRequest getScheduleRequest); - /** Get schedule subscription. */ + /** + * Get schedule subscription. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ Subscription getSubscription(GetSubscriptionRequest getSubscriptionRequest); /** * List dashboards. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ ListDashboardsResponse list(ListDashboardsRequest listDashboardsRequest); - /** List dashboard schedules. */ + /** + * List dashboard schedules. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ ListSchedulesResponse listSchedules(ListSchedulesRequest listSchedulesRequest); - /** List schedule subscriptions. */ + /** + * List schedule subscriptions. + * + *

The caller must be a workspace user with one of the following [entitlements]: Workspace + * access, Databricks SQL access, or Consumer access. + * + *

Account-level users who are not members of the workspace cannot call this endpoint, even if + * the dashboard has been shared with them. + * + *

[entitlements]: https://docs.databricks.com/security/auth/entitlements + */ ListSubscriptionsResponse listSubscriptions(ListSubscriptionsRequest listSubscriptionsRequest); /** @@ -78,38 +161,60 @@ public interface LakeviewService { /** * Publish the current draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ PublishedDashboard publish(PublishRequest publishRequest); /** * Revert a dashboard's definition in draft mode to the last published version. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ RevertDashboardResponse revert(RevertDashboardRequest revertDashboardRequest); /** * Trash a dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ void trash(TrashDashboardRequest trashDashboardRequest); /** * Unpublish the dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ void unpublish(UnpublishDashboardRequest unpublishDashboardRequest); /** * Update a draft dashboard. * - *

Requires the Databricks SQL access entitlement. + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements */ Dashboard update(UpdateDashboardRequest updateDashboardRequest); - /** Update dashboard schedule. */ + /** + * Update dashboard schedule. + * + *

Requires the [Databricks SQL access] entitlement. Grant Databricks SQL access in addition to + * Workspace access. + * + *

[Databricks SQL access]: https://docs.databricks.com/security/auth/entitlements + */ Schedule updateSchedule(UpdateScheduleRequest updateScheduleRequest); } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/BaseRun.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/BaseRun.java index 8824fbcfb..d64419342 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/BaseRun.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/BaseRun.java @@ -64,9 +64,10 @@ public class BaseRun { * the client-set performance target on the request depending on whether the performance mode is * supported by the job type. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("effective_performance_target") private PerformanceTarget effectivePerformanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/CreateJob.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/CreateJob.java index 9559a9eff..d89d8a37e 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/CreateJob.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/CreateJob.java @@ -145,9 +145,10 @@ public class CreateJob { * performance or cost-efficiency for the run. The performance target does not apply to tasks that * run on Serverless GPU compute. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("performance_target") private PerformanceTarget performanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobEmailNotifications.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobEmailNotifications.java index 6c56a80cc..7140b4559 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobEmailNotifications.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobEmailNotifications.java @@ -35,6 +35,20 @@ public class JobEmailNotifications { @JsonProperty("on_failure") private Collection onFailure; + /** + * A list of email addresses to notify when platform-initiated maintenance completes for a + * continuous job. + */ + @JsonProperty("on_maintenance_complete") + private Collection onMaintenanceComplete; + + /** + * A list of email addresses to notify when platform-initiated maintenance starts for a continuous + * job. + */ + @JsonProperty("on_maintenance_start") + private Collection onMaintenanceStart; + /** * A list of email addresses to be notified when a run begins. If not specified on job creation, * reset, or update, the list is empty, and notifications are not sent. @@ -89,6 +103,24 @@ public Collection getOnFailure() { return onFailure; } + public JobEmailNotifications setOnMaintenanceComplete(Collection onMaintenanceComplete) { + this.onMaintenanceComplete = onMaintenanceComplete; + return this; + } + + public Collection getOnMaintenanceComplete() { + return onMaintenanceComplete; + } + + public JobEmailNotifications setOnMaintenanceStart(Collection onMaintenanceStart) { + this.onMaintenanceStart = onMaintenanceStart; + return this; + } + + public Collection getOnMaintenanceStart() { + return onMaintenanceStart; + } + public JobEmailNotifications setOnStart(Collection onStart) { this.onStart = onStart; return this; @@ -126,6 +158,8 @@ public boolean equals(Object o) { && Objects.equals( onDurationWarningThresholdExceeded, that.onDurationWarningThresholdExceeded) && Objects.equals(onFailure, that.onFailure) + && Objects.equals(onMaintenanceComplete, that.onMaintenanceComplete) + && Objects.equals(onMaintenanceStart, that.onMaintenanceStart) && Objects.equals(onStart, that.onStart) && Objects.equals(onStreamingBacklogExceeded, that.onStreamingBacklogExceeded) && Objects.equals(onSuccess, that.onSuccess); @@ -137,6 +171,8 @@ public int hashCode() { noAlertForSkippedRuns, onDurationWarningThresholdExceeded, onFailure, + onMaintenanceComplete, + onMaintenanceStart, onStart, onStreamingBacklogExceeded, onSuccess); @@ -148,6 +184,8 @@ public String toString() { .add("noAlertForSkippedRuns", noAlertForSkippedRuns) .add("onDurationWarningThresholdExceeded", onDurationWarningThresholdExceeded) .add("onFailure", onFailure) + .add("onMaintenanceComplete", onMaintenanceComplete) + .add("onMaintenanceStart", onMaintenanceStart) .add("onStart", onStart) .add("onStreamingBacklogExceeded", onStreamingBacklogExceeded) .add("onSuccess", onSuccess) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobSettings.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobSettings.java index ec3c688d8..9db2217e9 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobSettings.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/JobSettings.java @@ -141,9 +141,10 @@ public class JobSettings { * performance or cost-efficiency for the run. The performance target does not apply to tasks that * run on Serverless GPU compute. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("performance_target") private PerformanceTarget performanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairHistoryItem.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairHistoryItem.java index 7a0e6d77f..f52d4e92b 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairHistoryItem.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairHistoryItem.java @@ -15,9 +15,10 @@ public class RepairHistoryItem { * the client-set performance target on the request depending on whether the performance mode is * supported by the job type. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("effective_performance_target") private PerformanceTarget effectivePerformanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairRun.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairRun.java index c31d6f4fb..94217cf5b 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairRun.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RepairRun.java @@ -74,9 +74,10 @@ public class RepairRun { * compute performance or cost-efficiency for the run. This field overrides the performance target * defined on the job level. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("performance_target") private PerformanceTarget performanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/Run.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/Run.java index 619290fae..af0d6c6c4 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/Run.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/Run.java @@ -65,9 +65,10 @@ public class Run { * the client-set performance target on the request depending on whether the performance mode is * supported by the job type. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("effective_performance_target") private PerformanceTarget effectivePerformanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunNow.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunNow.java index 09f7343ac..d8cc4556b 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunNow.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunNow.java @@ -96,9 +96,10 @@ public class RunNow { * compute performance or cost-efficiency for the run. This field overrides the performance target * defined on the job level. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("performance_target") private PerformanceTarget performanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunTask.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunTask.java index e4a53f5bc..49bed2e70 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunTask.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/RunTask.java @@ -118,9 +118,10 @@ public class RunTask { * the client-set performance target on the request depending on whether the performance mode is * supported by the job type. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("effective_performance_target") private PerformanceTarget effectivePerformanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/SubmitRun.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/SubmitRun.java index 7793d8ad7..a9b7b7fb3 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/SubmitRun.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/SubmitRun.java @@ -76,9 +76,10 @@ public class SubmitRun { * performance or cost-efficiency for the run. The performance target does not apply to tasks that * run on Serverless GPU compute. * - *

* `STANDARD`: Enables cost-efficient execution of serverless workloads. * - * `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid scaling and - * optimized cluster performance. + *

* `PERFORMANCE_OPTIMIZED`: Prioritizes fast startup and execution times through rapid + * scaling and optimized cluster performance. * `STANDARD`: Enables cost-efficient execution of + * serverless workloads. * `COST_OPTIMIZED`: Enables lower job costs by optimizing compute for + * your selected target duration time. Must provide a duration target. */ @JsonProperty("performance_target") private PerformanceTarget performanceTarget; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/TaskEmailNotifications.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/TaskEmailNotifications.java index 440ad8cf8..2db79f819 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/TaskEmailNotifications.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/TaskEmailNotifications.java @@ -35,6 +35,20 @@ public class TaskEmailNotifications { @JsonProperty("on_failure") private Collection onFailure; + /** + * A list of email addresses to notify when platform-initiated maintenance completes for a + * continuous job. + */ + @JsonProperty("on_maintenance_complete") + private Collection onMaintenanceComplete; + + /** + * A list of email addresses to notify when platform-initiated maintenance starts for a continuous + * job. + */ + @JsonProperty("on_maintenance_start") + private Collection onMaintenanceStart; + /** * A list of email addresses to be notified when a run begins. If not specified on job creation, * reset, or update, the list is empty, and notifications are not sent. @@ -89,6 +103,24 @@ public Collection getOnFailure() { return onFailure; } + public TaskEmailNotifications setOnMaintenanceComplete(Collection onMaintenanceComplete) { + this.onMaintenanceComplete = onMaintenanceComplete; + return this; + } + + public Collection getOnMaintenanceComplete() { + return onMaintenanceComplete; + } + + public TaskEmailNotifications setOnMaintenanceStart(Collection onMaintenanceStart) { + this.onMaintenanceStart = onMaintenanceStart; + return this; + } + + public Collection getOnMaintenanceStart() { + return onMaintenanceStart; + } + public TaskEmailNotifications setOnStart(Collection onStart) { this.onStart = onStart; return this; @@ -126,6 +158,8 @@ public boolean equals(Object o) { && Objects.equals( onDurationWarningThresholdExceeded, that.onDurationWarningThresholdExceeded) && Objects.equals(onFailure, that.onFailure) + && Objects.equals(onMaintenanceComplete, that.onMaintenanceComplete) + && Objects.equals(onMaintenanceStart, that.onMaintenanceStart) && Objects.equals(onStart, that.onStart) && Objects.equals(onStreamingBacklogExceeded, that.onStreamingBacklogExceeded) && Objects.equals(onSuccess, that.onSuccess); @@ -137,6 +171,8 @@ public int hashCode() { noAlertForSkippedRuns, onDurationWarningThresholdExceeded, onFailure, + onMaintenanceComplete, + onMaintenanceStart, onStart, onStreamingBacklogExceeded, onSuccess); @@ -148,6 +184,8 @@ public String toString() { .add("noAlertForSkippedRuns", noAlertForSkippedRuns) .add("onDurationWarningThresholdExceeded", onDurationWarningThresholdExceeded) .add("onFailure", onFailure) + .add("onMaintenanceComplete", onMaintenanceComplete) + .add("onMaintenanceStart", onMaintenanceStart) .add("onStart", onStart) .add("onStreamingBacklogExceeded", onStreamingBacklogExceeded) .add("onSuccess", onSuccess) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/WebhookNotifications.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/WebhookNotifications.java index 72d92748d..a14ee51cd 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/WebhookNotifications.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/jobs/WebhookNotifications.java @@ -25,6 +25,22 @@ public class WebhookNotifications { @JsonProperty("on_failure") private Collection onFailure; + /** + * An optional list of system notification IDs to call when platform-initiated maintenance + * completes for a continuous job. A maximum of 3 destinations can be specified for the + * `on_maintenance_complete` property. + */ + @JsonProperty("on_maintenance_complete") + private Collection onMaintenanceComplete; + + /** + * An optional list of system notification IDs to call when platform-initiated maintenance starts + * for a continuous job. A maximum of 3 destinations can be specified for the + * `on_maintenance_start` property. + */ + @JsonProperty("on_maintenance_start") + private Collection onMaintenanceStart; + /** * An optional list of system notification IDs to call when the run starts. A maximum of 3 * destinations can be specified for the `on_start` property. @@ -69,6 +85,24 @@ public Collection getOnFailure() { return onFailure; } + public WebhookNotifications setOnMaintenanceComplete(Collection onMaintenanceComplete) { + this.onMaintenanceComplete = onMaintenanceComplete; + return this; + } + + public Collection getOnMaintenanceComplete() { + return onMaintenanceComplete; + } + + public WebhookNotifications setOnMaintenanceStart(Collection onMaintenanceStart) { + this.onMaintenanceStart = onMaintenanceStart; + return this; + } + + public Collection getOnMaintenanceStart() { + return onMaintenanceStart; + } + public WebhookNotifications setOnStart(Collection onStart) { this.onStart = onStart; return this; @@ -105,6 +139,8 @@ public boolean equals(Object o) { return Objects.equals( onDurationWarningThresholdExceeded, that.onDurationWarningThresholdExceeded) && Objects.equals(onFailure, that.onFailure) + && Objects.equals(onMaintenanceComplete, that.onMaintenanceComplete) + && Objects.equals(onMaintenanceStart, that.onMaintenanceStart) && Objects.equals(onStart, that.onStart) && Objects.equals(onStreamingBacklogExceeded, that.onStreamingBacklogExceeded) && Objects.equals(onSuccess, that.onSuccess); @@ -115,6 +151,8 @@ public int hashCode() { return Objects.hash( onDurationWarningThresholdExceeded, onFailure, + onMaintenanceComplete, + onMaintenanceStart, onStart, onStreamingBacklogExceeded, onSuccess); @@ -125,6 +163,8 @@ public String toString() { return new ToStringer(WebhookNotifications.class) .add("onDurationWarningThresholdExceeded", onDurationWarningThresholdExceeded) .add("onFailure", onFailure) + .add("onMaintenanceComplete", onMaintenanceComplete) + .add("onMaintenanceStart", onMaintenanceStart) .add("onStart", onStart) .add("onStreamingBacklogExceeded", onStreamingBacklogExceeded) .add("onSuccess", onSuccess) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/BackfillFeaturesRequest.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/BackfillFeaturesRequest.java index 6b8cbb010..9d81b1e61 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/BackfillFeaturesRequest.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/BackfillFeaturesRequest.java @@ -6,6 +6,7 @@ import com.databricks.sdk.support.ToStringer; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Collection; +import java.util.Map; import java.util.Objects; @Generated @@ -14,6 +15,13 @@ public class BackfillFeaturesRequest { @JsonProperty("backfill_ranges") private Collection backfillRanges; + /** + * The budget policy ID, in UUID format, used to attribute the serverless compute cost of this + * backfill. If not specified, a default budget policy may be applied. + */ + @JsonProperty("budget_policy_id") + private String budgetPolicyId; + /** Full names of the features to backfill. */ @JsonProperty("feature_full_names") private Collection featureFullNames; @@ -22,6 +30,17 @@ public class BackfillFeaturesRequest { @JsonProperty("request_id") private String requestId; + /** + * Custom tags to associate with this backfill. They are applied to the backfill job and forwarded + * to the underlying compute as Databricks resource tags, so backfill cost can be attributed in + * the billing system tables. These tags apply only to the backfill compute; they are not applied + * to the Unity Catalog Feature resources themselves, whose tags are managed separately through + * the Unity Catalog tagging API. A maximum of 25 tags is supported; keys and values are subject + * to the same limitations as Databricks resource tags. + */ + @JsonProperty("tags") + private Map tags; + public BackfillFeaturesRequest setBackfillRanges(Collection backfillRanges) { this.backfillRanges = backfillRanges; return this; @@ -31,6 +50,15 @@ public Collection getBackfillRanges() { return backfillRanges; } + public BackfillFeaturesRequest setBudgetPolicyId(String budgetPolicyId) { + this.budgetPolicyId = budgetPolicyId; + return this; + } + + public String getBudgetPolicyId() { + return budgetPolicyId; + } + public BackfillFeaturesRequest setFeatureFullNames(Collection featureFullNames) { this.featureFullNames = featureFullNames; return this; @@ -49,27 +77,40 @@ public String getRequestId() { return requestId; } + public BackfillFeaturesRequest setTags(Map tags) { + this.tags = tags; + return this; + } + + public Map getTags() { + return tags; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BackfillFeaturesRequest that = (BackfillFeaturesRequest) o; return Objects.equals(backfillRanges, that.backfillRanges) + && Objects.equals(budgetPolicyId, that.budgetPolicyId) && Objects.equals(featureFullNames, that.featureFullNames) - && Objects.equals(requestId, that.requestId); + && Objects.equals(requestId, that.requestId) + && Objects.equals(tags, that.tags); } @Override public int hashCode() { - return Objects.hash(backfillRanges, featureFullNames, requestId); + return Objects.hash(backfillRanges, budgetPolicyId, featureFullNames, requestId, tags); } @Override public String toString() { return new ToStringer(BackfillFeaturesRequest.class) .add("backfillRanges", backfillRanges) + .add("budgetPolicyId", budgetPolicyId) .add("featureFullNames", featureFullNames) .add("requestId", requestId) + .add("tags", tags) .toString(); } } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/CronSchedule.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/CronSchedule.java index 9e387c2d1..5788d8334 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/CronSchedule.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/CronSchedule.java @@ -12,9 +12,9 @@ public class CronSchedule { /** * The cron expression defining the schedule (e.g., "0 0 * * *" for daily at midnight). The - * schedule is interpreted in the UTC time zone. Required when mode is MANUAL (or unset). Left - * empty when mode is DERIVED, where the service computes it (aligned to UTC) from the features' - * window timing and fills it in on the response. + * schedule is interpreted in timezone_id (defaults to UTC). Required when mode is MANUAL (or + * unset). Left empty when mode is DERIVED, where the service computes it (aligned to UTC) from + * the features' window timing and fills it in on the response. */ @JsonProperty("cron_expression") private String cronExpression; @@ -23,6 +23,14 @@ public class CronSchedule { @JsonProperty("mode") private CronScheduleMode mode; + /** + * A Java timezone ID. The schedule is resolved with respect to this timezone. Defaults to UTC + * when omitted. Can only be configured for MANUAL schedules; DERIVED schedules are always aligned + * to UTC. + */ + @JsonProperty("timezone_id") + private String timezoneId; + public CronSchedule setCronExpression(String cronExpression) { this.cronExpression = cronExpression; return this; @@ -41,17 +49,28 @@ public CronScheduleMode getMode() { return mode; } + public CronSchedule setTimezoneId(String timezoneId) { + this.timezoneId = timezoneId; + return this; + } + + public String getTimezoneId() { + return timezoneId; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CronSchedule that = (CronSchedule) o; - return Objects.equals(cronExpression, that.cronExpression) && Objects.equals(mode, that.mode); + return Objects.equals(cronExpression, that.cronExpression) + && Objects.equals(mode, that.mode) + && Objects.equals(timezoneId, that.timezoneId); } @Override public int hashCode() { - return Objects.hash(cronExpression, mode); + return Objects.hash(cronExpression, mode, timezoneId); } @Override @@ -59,6 +78,7 @@ public String toString() { return new ToStringer(CronSchedule.class) .add("cronExpression", cronExpression) .add("mode", mode) + .add("timezoneId", timezoneId) .toString(); } } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/PurgeFeatureEntitiesRequest.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/PurgeFeatureEntitiesRequest.java index d31fc7668..0d2f53743 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/PurgeFeatureEntitiesRequest.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/ml/PurgeFeatureEntitiesRequest.java @@ -6,6 +6,7 @@ import com.databricks.sdk.support.ToStringer; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Collection; +import java.util.Map; import java.util.Objects; /** @@ -13,6 +14,13 @@ */ @Generated public class PurgeFeatureEntitiesRequest { + /** + * The budget policy ID, in UUID format, used to attribute the serverless compute cost of this + * purge. If not specified, a default budget policy may be applied. + */ + @JsonProperty("budget_policy_id") + private String budgetPolicyId; + /** * Fully qualified name of the Unity Catalog Delta table containing the entity keys to purge. The * table may contain a subset of each feature's entity-key columns. A partial key match deletes @@ -34,6 +42,26 @@ public class PurgeFeatureEntitiesRequest { @JsonProperty("request_id") private String requestId; + /** + * Custom tags to associate with this purge. They are applied to the purge job and forwarded to + * the underlying compute as Databricks resource tags, so purge cost can be attributed in the + * billing system tables. These tags apply only to the purge compute; they are not applied to the + * Unity Catalog Feature resources themselves, whose tags are managed separately through the Unity + * Catalog tagging API. A maximum of 25 tags is supported; keys and values are subject to the same + * limitations as Databricks resource tags. + */ + @JsonProperty("tags") + private Map tags; + + public PurgeFeatureEntitiesRequest setBudgetPolicyId(String budgetPolicyId) { + this.budgetPolicyId = budgetPolicyId; + return this; + } + + public String getBudgetPolicyId() { + return budgetPolicyId; + } + public PurgeFeatureEntitiesRequest setEntitiesTable(String entitiesTable) { this.entitiesTable = entitiesTable; return this; @@ -61,27 +89,40 @@ public String getRequestId() { return requestId; } + public PurgeFeatureEntitiesRequest setTags(Map tags) { + this.tags = tags; + return this; + } + + public Map getTags() { + return tags; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PurgeFeatureEntitiesRequest that = (PurgeFeatureEntitiesRequest) o; - return Objects.equals(entitiesTable, that.entitiesTable) + return Objects.equals(budgetPolicyId, that.budgetPolicyId) + && Objects.equals(entitiesTable, that.entitiesTable) && Objects.equals(features, that.features) - && Objects.equals(requestId, that.requestId); + && Objects.equals(requestId, that.requestId) + && Objects.equals(tags, that.tags); } @Override public int hashCode() { - return Objects.hash(entitiesTable, features, requestId); + return Objects.hash(budgetPolicyId, entitiesTable, features, requestId, tags); } @Override public String toString() { return new ToStringer(PurgeFeatureEntitiesRequest.class) + .add("budgetPolicyId", budgetPolicyId) .add("entitiesTable", entitiesTable) .add("features", features) .add("requestId", requestId) + .add("tags", tags) .toString(); } } diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/oauth2/FederationPolicy.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/oauth2/FederationPolicy.java index 6bcf8c410..83ef8eeb6 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/oauth2/FederationPolicy.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/oauth2/FederationPolicy.java @@ -29,7 +29,10 @@ public class FederationPolicy { @JsonProperty("name") private String name; - /** */ + /** + * audit_mode INCLUDE is required on both this message field and its leaf fields so the OIDC + * policy configuration is captured in create/update audit logs (see go/auditlogs). + */ @JsonProperty("oidc_policy") private OidcFederationPolicy oidcPolicy; diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/pipelines/IngestionSourceType.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/pipelines/IngestionSourceType.java index e314ce506..d99ddfae2 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/service/pipelines/IngestionSourceType.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/service/pipelines/IngestionSourceType.java @@ -23,8 +23,10 @@ public enum IngestionSourceType { SALESFORCE, SERVICENOW, SHAREPOINT, + SMARTSHEET, SQLSERVER, TERADATA, + TIKTOK_ADS, WORKDAY_RAAS, ZENDESK, } diff --git a/examples/docs/src/main/java/com/databricks/example/CliAuthAccount.java b/examples/docs/src/main/java/com/databricks/example/CliAuthAccount.java index 012e7aa10..76937f647 100644 --- a/examples/docs/src/main/java/com/databricks/example/CliAuthAccount.java +++ b/examples/docs/src/main/java/com/databricks/example/CliAuthAccount.java @@ -5,35 +5,32 @@ import com.databricks.sdk.service.provisioning.Workspace; /** - Example for authenticating with Databricks Account through CLI. - The authentication type can be set to either "databricks-cli" or "azure-cli". - For details on authenticating via bricks cli, please see: ... + * Example for authenticating with Databricks Account through CLI. The authentication type can be + * set to either "databricks-cli" or "azure-cli". For details on authenticating via bricks cli, + * please see: ... */ public class CliAuthAccount { - /** - Get config used for authenticating with Databricks. - @return DatabricksConfig object used for authentication - */ - private static DatabricksConfig getConfig() { - // Change to "azure-cli" if you want to authenticate through azure cli - // Please authenticate using cli before using them in SDK. - // Example: $ databricks auth login --host --account-id - String authType = "databricks-cli"; - String profile = ""; - return new DatabricksConfig() - .setAuthType(authType) - .setProfile(profile); - } + /** + * Get config used for authenticating with Databricks. + * + * @return DatabricksConfig object used for authentication + */ + private static DatabricksConfig getConfig() { + // Change to "azure-cli" if you want to authenticate through azure cli + // Please authenticate using cli before using them in SDK. + // Example: $ databricks auth login --host --account-id + String authType = "databricks-cli"; + String profile = ""; + return new DatabricksConfig().setAuthType(authType).setProfile(profile); + } - /** - Authenticate and retrieve the list of workspaces from account - */ - public static void main(String[] args) { - DatabricksConfig config = getConfig(); + /** Authenticate and retrieve the list of workspaces from account */ + public static void main(String[] args) { + DatabricksConfig config = getConfig(); - AccountClient account = new AccountClient(config); - for (Workspace w : account.workspaces().list()) { - System.out.println(w.getWorkspaceName()); - } + AccountClient account = new AccountClient(config); + for (Workspace w : account.workspaces().list()) { + System.out.println(w.getWorkspaceName()); } + } } diff --git a/examples/docs/src/main/java/com/databricks/example/CliAuthWorkspace.java b/examples/docs/src/main/java/com/databricks/example/CliAuthWorkspace.java index 2f646f243..8f263b8e4 100644 --- a/examples/docs/src/main/java/com/databricks/example/CliAuthWorkspace.java +++ b/examples/docs/src/main/java/com/databricks/example/CliAuthWorkspace.java @@ -6,35 +6,32 @@ import com.databricks.sdk.service.compute.ListClustersRequest; /** - Example for authenticating with Databricks Workspace through CLI. - The authentication type can be set to either "databricks-cli" or "azure-cli". - For details on authenticating via bricks cli, please see: ... + * Example for authenticating with Databricks Workspace through CLI. The authentication type can be + * set to either "databricks-cli" or "azure-cli". For details on authenticating via bricks cli, + * please see: ... */ public class CliAuthWorkspace { - /** - Get config used for authenticating with Databricks. - @return DatabricksConfig object used for authentication - */ - private static DatabricksConfig getConfig() { - // Change to "azure-cli" if you want to authenticate through azure cli - // Please authenticate using cli before using them in SDK. - // Example: $ databricks auth login --host - String authType = "databricks-cli"; - String profile = ""; - return new DatabricksConfig() - .setAuthType(authType) - .setProfile(profile); - } + /** + * Get config used for authenticating with Databricks. + * + * @return DatabricksConfig object used for authentication + */ + private static DatabricksConfig getConfig() { + // Change to "azure-cli" if you want to authenticate through azure cli + // Please authenticate using cli before using them in SDK. + // Example: $ databricks auth login --host + String authType = "databricks-cli"; + String profile = ""; + return new DatabricksConfig().setAuthType(authType).setProfile(profile); + } - /** - Authenticate and retrieve the list of clusters from the workspace - */ - public static void main(String[] args) { - DatabricksConfig config = getConfig(); + /** Authenticate and retrieve the list of clusters from the workspace */ + public static void main(String[] args) { + DatabricksConfig config = getConfig(); - WorkspaceClient workspace = new WorkspaceClient(config); - for (ClusterDetails c : workspace.clusters().list(new ListClustersRequest())) { - System.out.println(c.getClusterName()); - } + WorkspaceClient workspace = new WorkspaceClient(config); + for (ClusterDetails c : workspace.clusters().list(new ListClustersRequest())) { + System.out.println(c.getClusterName()); } + } } diff --git a/examples/docs/src/main/java/com/databricks/example/CliOAuthU2MExample.java b/examples/docs/src/main/java/com/databricks/example/CliOAuthU2MExample.java index c0eb53704..4552d33d2 100644 --- a/examples/docs/src/main/java/com/databricks/example/CliOAuthU2MExample.java +++ b/examples/docs/src/main/java/com/databricks/example/CliOAuthU2MExample.java @@ -6,19 +6,19 @@ import com.databricks.sdk.service.compute.ListClustersRequest; /** - * Example for authenticating with Databricks Workspace through CLI using the external-browser auth type. + * Example for authenticating with Databricks Workspace through CLI using the external-browser auth + * type. * - *

Before running this example, make sure to configure the host and client ID in the {@code DatabricksConfig} object. + *

Before running this example, make sure to configure the host and client ID in the {@code + * DatabricksConfig} object. */ public class CliOAuthU2MExample { - public static void main(String[] args) { - DatabricksConfig config = new DatabricksConfig() - .setAuthType("external-browser") - .setClientId("") - .setHost(""); - WorkspaceClient workspace = new WorkspaceClient(config); - for (ClusterDetails c : workspace.clusters().list(new ListClustersRequest())) { - System.out.println(c.getClusterName()); - } + public static void main(String[] args) { + DatabricksConfig config = + new DatabricksConfig().setAuthType("external-browser").setClientId("").setHost(""); + WorkspaceClient workspace = new WorkspaceClient(config); + for (ClusterDetails c : workspace.clusters().list(new ListClustersRequest())) { + System.out.println(c.getClusterName()); } + } } diff --git a/examples/docs/src/main/java/com/databricks/example/GithubOIDCAuthExample.java b/examples/docs/src/main/java/com/databricks/example/GithubOIDCAuthExample.java index 478d4a21a..c0eed6658 100644 --- a/examples/docs/src/main/java/com/databricks/example/GithubOIDCAuthExample.java +++ b/examples/docs/src/main/java/com/databricks/example/GithubOIDCAuthExample.java @@ -2,41 +2,41 @@ import com.databricks.sdk.AccountClient; import com.databricks.sdk.core.DatabricksConfig; -import com.databricks.sdk.service.iam.User; -import com.databricks.sdk.service.iam.ListAccountGroupsRequest; import com.databricks.sdk.service.iam.Group; +import com.databricks.sdk.service.iam.ListAccountGroupsRequest; /** * Example demonstrating how to use GitHub OIDC authentication with Databricks. - * - * IMPORTANT: This example only works when running within GitHub Actions. + * + *

IMPORTANT: This example only works when running within GitHub Actions. */ public class GithubOIDCAuthExample { - public static void main(String[] args) { - // Create Databricks configuration with GitHub OIDC authentication - // Note: This configuration assumes the code is running in GitHub Actions - DatabricksConfig config = new DatabricksConfig() - .setAuthType("github-oidc") // Specifies GitHub OIDC as the authentication method - .setHost("") // Databricks account URL - .setAccountId("") // Your Databricks account ID - .setClientId(""); // Service Principal ID + public static void main(String[] args) { + // Create Databricks configuration with GitHub OIDC authentication + // Note: This configuration assumes the code is running in GitHub Actions + DatabricksConfig config = + new DatabricksConfig() + .setAuthType("github-oidc") // Specifies GitHub OIDC as the authentication method + .setHost("") // Databricks account URL + .setAccountId("") // Your Databricks account ID + .setClientId(""); // Service Principal ID - // Initialize the Account client with the OIDC configuration - AccountClient account = new AccountClient(config); + // Initialize the Account client with the OIDC configuration + AccountClient account = new AccountClient(config); - try { - // Example: List all groups in the Databricks account - // This demonstrates that the OIDC authentication is working - System.out.println("\nListing account groups:"); - Iterable groups = account.groups().list(new ListAccountGroupsRequest()); - for (Group group : groups) { - System.out.println("- Group: " + group.getDisplayName() + " (ID: " + group.getId() + ")"); - } + try { + // Example: List all groups in the Databricks account + // This demonstrates that the OIDC authentication is working + System.out.println("\nListing account groups:"); + Iterable groups = account.groups().list(new ListAccountGroupsRequest()); + for (Group group : groups) { + System.out.println("- Group: " + group.getDisplayName() + " (ID: " + group.getId() + ")"); + } - } catch (Exception e) { - System.err.println("Authentication failed: " + e.getMessage()); - e.printStackTrace(); - } + } catch (Exception e) { + System.err.println("Authentication failed: " + e.getMessage()); + e.printStackTrace(); } -} \ No newline at end of file + } +} diff --git a/examples/docs/src/main/java/com/databricks/example/HttpProxyExample.java b/examples/docs/src/main/java/com/databricks/example/HttpProxyExample.java index 8ab8a2360..4849a75fa 100644 --- a/examples/docs/src/main/java/com/databricks/example/HttpProxyExample.java +++ b/examples/docs/src/main/java/com/databricks/example/HttpProxyExample.java @@ -7,11 +7,9 @@ /** * This example demonstrates how to use the Databricks Java SDK with an HTTP proxy. * - * To run this example, you must set the following system properties: - * -Dhttps.proxyHost= - * The host name of the HTTP proxy server. - * -Dhttps.proxyPort= - * The port number of the HTTP proxy server. + *

To run this example, you must set the following system properties: -Dhttps.proxyHost= The host name of the HTTP proxy server. -Dhttps.proxyPort= The port number of + * the HTTP proxy server. */ class HttpProxyExample { public static void main(String[] args) { diff --git a/examples/docs/src/main/java/com/databricks/example/M2MAuthExample.java b/examples/docs/src/main/java/com/databricks/example/M2MAuthExample.java index 60f3e8e9c..1ddc8b81c 100644 --- a/examples/docs/src/main/java/com/databricks/example/M2MAuthExample.java +++ b/examples/docs/src/main/java/com/databricks/example/M2MAuthExample.java @@ -8,45 +8,47 @@ import com.databricks.sdk.service.provisioning.Workspace; /** - Example for authenticating with Databricks Account through CLI. - The authentication type can be set to either "databricks-cli" or "azure-cli". - For details on authenticating via bricks cli, please see: ... + * Example for authenticating with Databricks Account through CLI. The authentication type can be + * set to either "databricks-cli" or "azure-cli". For details on authenticating via bricks cli, + * please see: ... */ public class M2MAuthExample { - /** - Get config used for authenticating with Databricks. - @return DatabricksConfig object used for authentication - */ - private static DatabricksConfig getConfig() { - return new DatabricksConfig() - .setHost("https://accounts.cloud.databricks.com") - // Fill in your E2 account ID. Click on your username in the top-right corner of the accounts console to - // display your account ID. - .setAccountId("") - // Create a service principal in the Account console at "User Management" -> "Service Principals" and - // click "Create service principal". Generate a secret and paste the client ID and secret below. - .setClientId("") - .setClientSecret(""); - } + /** + * Get config used for authenticating with Databricks. + * + * @return DatabricksConfig object used for authentication + */ + private static DatabricksConfig getConfig() { + return new DatabricksConfig() + .setHost("https://accounts.cloud.databricks.com") + // Fill in your E2 account ID. Click on your username in the top-right corner of the + // accounts console to + // display your account ID. + .setAccountId("") + // Create a service principal in the Account console at "User Management" -> "Service + // Principals" and + // click "Create service principal". Generate a secret and paste the client ID and secret + // below. + .setClientId("") + .setClientSecret(""); + } - /** - Authenticate and retrieve the list of workspaces from account - */ - public static void main(String[] args) { - DatabricksConfig config = getConfig(); + /** Authenticate and retrieve the list of workspaces from account */ + public static void main(String[] args) { + DatabricksConfig config = getConfig(); - AccountClient account = new AccountClient(config); - Workspace firstWorkspace = null; - for (Workspace w : account.workspaces().list()) { - if (w.getDeploymentName().equals("dbc-a39a1eb1-ef95")) { - firstWorkspace = w; - } - System.out.println(w.getWorkspaceName()); - } + AccountClient account = new AccountClient(config); + Workspace firstWorkspace = null; + for (Workspace w : account.workspaces().list()) { + if (w.getDeploymentName().equals("dbc-a39a1eb1-ef95")) { + firstWorkspace = w; + } + System.out.println(w.getWorkspaceName()); + } - WorkspaceClient w = account.getWorkspaceClient(firstWorkspace); - for (ClusterDetails c : w.clusters().list(new ListClustersRequest())) { - System.out.println(c.getClusterName()); - } + WorkspaceClient w = account.getWorkspaceClient(firstWorkspace); + for (ClusterDetails c : w.clusters().list(new ListClustersRequest())) { + System.out.println(c.getClusterName()); } + } } diff --git a/examples/docs/src/main/java/com/databricks/example/TriggerJobToRunPythonProgram.java b/examples/docs/src/main/java/com/databricks/example/TriggerJobToRunPythonProgram.java index 0ed094992..89ea2f2d1 100644 --- a/examples/docs/src/main/java/com/databricks/example/TriggerJobToRunPythonProgram.java +++ b/examples/docs/src/main/java/com/databricks/example/TriggerJobToRunPythonProgram.java @@ -7,7 +7,6 @@ import com.databricks.sdk.service.iam.MeRequest; import com.databricks.sdk.service.jobs.*; import com.databricks.sdk.support.Wait; - import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; @@ -15,120 +14,127 @@ import java.util.*; import java.util.concurrent.TimeoutException; -/** - An example class that triggers a Databricks job using the Databricks Java SDK. - */ +/** An example class that triggers a Databricks job using the Databricks Java SDK. */ public class TriggerJobToRunPythonProgram { - /** - Returns a WorkspaceClient object initialized with the configuration. - @return The WorkspaceClient object. - */ - private static WorkspaceClient getWorkspace() { - DatabricksConfig config = new DatabricksConfig(); - return new WorkspaceClient(config); + /** + * Returns a WorkspaceClient object initialized with the configuration. + * + * @return The WorkspaceClient object. + */ + private static WorkspaceClient getWorkspace() { + DatabricksConfig config = new DatabricksConfig(); + return new WorkspaceClient(config); + } + + /** + * Returns a sample Python program that sleeps for 1 second and prints a message containing the + * current UTC time. + * + * @return The sample Python program as a string. + */ + private static String getSamplePythonProgram() { + return "import time; time.sleep(1); print(\"This is a test run executed at: \" + time.strftime(\"%Y-%m-%d %H:%M:%S\", time.gmtime()) + \" UTC\" );"; + } + + /** + * Creates a Databricks cluster with the given specifications and returns the corresponding + * ClusterDetails object. + * + * @param testWorkspace The Databricks workspace where the cluster will be created. + * @return The ClusterDetails object representing the newly created cluster, or null if the + * creation process timed out. + */ + private static ClusterDetails getCluster(WorkspaceClient testWorkspace) { + try { + CreateCluster request = + new CreateCluster() + .setClusterName("test-cluster") + .setSparkVersion("13.0.x-scala2.12") + .setNodeTypeId("i3.xlarge") + .setAutoterminationMinutes(10L) + .setNumWorkers(1L); + return testWorkspace.clusters().create(request).get(Duration.ofMinutes(10)); + } catch (TimeoutException e) { + System.err.println( + "Timeout: Didn't get cluster within 10 minutes. Error Message: " + e.getMessage()); + System.exit(1); } - - /** - Returns a sample Python program that sleeps for 1 second and prints a message containing the current UTC time. - @return The sample Python program as a string. - */ - private static String getSamplePythonProgram() { - return "import time; time.sleep(1); print(\"This is a test run executed at: \" + time.strftime(\"%Y-%m-%d %H:%M:%S\", time.gmtime()) + \" UTC\" );"; + return null; + } + + /** + * Triggers a job on a specified Databricks cluster using the provided Databricks workspace. The + * method first creates a Python file on Databricks DBFS that contains a sample Python program. + * The program is then encoded using Base64, and added to the file created on DBFS. The method + * then creates a task for the job, which specifies the Python file on DBFS as the file to be + * executed, and the ID of the existing cluster to be used for the job. The task is then submitted + * to the workspace to trigger the job. + * + * @param testWorkspace the Databricks workspace to be used for triggering the job + * @param cluster the cluster on which to trigger the job + * @return a list of strings that contains the results of the job for each task in the job + */ + private static List triggerJobOn(WorkspaceClient testWorkspace, ClusterDetails cluster) { + String pyFileOnDBFS = + String.format( + "/home/%s/java-sdk-test-sample.py", + testWorkspace.currentUser().me(new MeRequest()).getUserName()); + String pyProgram = getSamplePythonProgram(); + + try { + testWorkspace + .dbfs() + .write(Paths.get(pyFileOnDBFS), pyProgram.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + System.err.println("Couldn't write DBFS file: " + e.getMessage()); + System.exit(1); } - /** - Creates a Databricks cluster with the given specifications and returns the corresponding ClusterDetails object. - @param testWorkspace The Databricks workspace where the cluster will be created. - @return The ClusterDetails object representing the newly created cluster, or null if the creation process timed out. - */ - private static ClusterDetails getCluster(WorkspaceClient testWorkspace) { - try { - CreateCluster request = new CreateCluster() - .setClusterName("test-cluster") - .setSparkVersion("13.0.x-scala2.12") - .setNodeTypeId("i3.xlarge") - .setAutoterminationMinutes(10L) - .setNumWorkers(1L); - return testWorkspace.clusters().create(request).get(Duration.ofMinutes(10)); - } catch (TimeoutException e) { - System.err.println("Timeout: Didn't get cluster within 10 minutes. Error Message: " + e.getMessage()); - System.exit(1); - } - return null; + String runName = String.format("java-sdk-run-%s", System.currentTimeMillis() / 1000.0); + + SubmitTask task = + new SubmitTask() + .setTaskKey("test-task") + .setSparkPythonTask( + new SparkPythonTask().setPythonFile(String.format("dbfs:%s", pyFileOnDBFS))) + .setExistingClusterId(cluster.getClusterId()); + + Wait submit = + testWorkspace + .jobs() + .submit(new SubmitRun().setRunName(runName).setTasks(Collections.singletonList(task))); + + Long runId = submit.getResponse().getRunId(); + System.out.printf("Starting to poll for JobID: %s%n%n", runId); + + List results = new ArrayList<>(); + try { + Collection tasks = submit.get().getTasks(); + for (RunTask eachTask : tasks) { + Long taskRunId = eachTask.getRunId(); + String logs = testWorkspace.jobs().getRunOutput(taskRunId).getLogs(); + results.add(String.format("Result for taskID: %s = %s", taskRunId, logs)); + } + } catch (TimeoutException e) { + results.add(e.getMessage()); } - /** - Triggers a job on a specified Databricks cluster using the provided Databricks workspace. - The method first creates a Python file on Databricks DBFS that contains a sample Python program. - The program is then encoded using Base64, and added to the file created on DBFS. - The method then creates a task for the job, which specifies the Python file on DBFS as the - file to be executed, and the ID of the existing cluster to be used for the job. The task is - then submitted to the workspace to trigger the job. - @param testWorkspace the Databricks workspace to be used for triggering the job - @param cluster the cluster on which to trigger the job - @return a list of strings that contains the results of the job for each task in the job - */ - private static List triggerJobOn(WorkspaceClient testWorkspace, ClusterDetails cluster) { - String pyFileOnDBFS = String.format("/home/%s/java-sdk-test-sample.py", testWorkspace.currentUser().me(new MeRequest()).getUserName()); - String pyProgram = getSamplePythonProgram(); - - try { - testWorkspace.dbfs().write(Paths.get(pyFileOnDBFS), pyProgram.getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - System.err.println("Couldn't write DBFS file: " + e.getMessage()); - System.exit(1); - } - - String runName = String.format("java-sdk-run-%s", System.currentTimeMillis()/1000.0); - - SubmitTask task = new SubmitTask() - .setTaskKey("test-task") - .setSparkPythonTask( - new SparkPythonTask() - .setPythonFile(String.format("dbfs:%s", pyFileOnDBFS)) - ) - .setExistingClusterId(cluster.getClusterId()); - - Wait submit = testWorkspace.jobs().submit( - new SubmitRun() - .setRunName(runName) - .setTasks(Collections.singletonList(task)) - ); - - Long runId = submit.getResponse().getRunId(); - System.out.printf("Starting to poll for JobID: %s%n%n", runId); - - List results = new ArrayList<>(); - try { - Collection tasks = submit.get().getTasks(); - for(RunTask eachTask: tasks) { - Long taskRunId = eachTask.getRunId(); - String logs = testWorkspace.jobs().getRunOutput(taskRunId).getLogs(); - results.add(String.format("Result for taskID: %s = %s", taskRunId, logs)); - } - } catch (TimeoutException e) { - results.add(e.getMessage()); - } - - return results; - } + return results; + } - /** - The main method to trigger the Databricks job execution and display the result of the job. - */ - public static void main(String[] args) { - // Get your workspace - WorkspaceClient testWorkspace = getWorkspace(); + /** The main method to trigger the Databricks job execution and display the result of the job. */ + public static void main(String[] args) { + // Get your workspace + WorkspaceClient testWorkspace = getWorkspace(); - // Get cluster you want to run the job on - ClusterDetails cluster = getCluster(testWorkspace); + // Get cluster you want to run the job on + ClusterDetails cluster = getCluster(testWorkspace); - // Trigger a job - List results = triggerJobOn(testWorkspace, cluster); - - // Print the result of job - results.forEach(System.out::println); - } + // Trigger a job + List results = triggerJobOn(testWorkspace, cluster); + // Print the result of job + results.forEach(System.out::println); + } } diff --git a/examples/docs/src/main/java/com/databricks/example/UnifiedAuthWorkspace.java b/examples/docs/src/main/java/com/databricks/example/UnifiedAuthWorkspace.java index 5b372b827..bddffd794 100644 --- a/examples/docs/src/main/java/com/databricks/example/UnifiedAuthWorkspace.java +++ b/examples/docs/src/main/java/com/databricks/example/UnifiedAuthWorkspace.java @@ -6,22 +6,22 @@ import com.databricks.sdk.service.compute.ListClustersRequest; /** - Example for authenticating with Databricks Workspace through Databricks' Unified Authentication. Unified - Authentication takes care of inspecting your environment to determine the best way to authenticate. - -

- For more details, please see Authenticate with Databricks SDK for Java. + * Example for authenticating with Databricks Workspace through Databricks' Unified Authentication. + * Unified Authentication takes care of inspecting your environment to determine the best way to + * authenticate. + * + *

For more details, please see Authenticate + * with Databricks SDK for Java. */ public class UnifiedAuthWorkspace { - /** - Authenticate and retrieve the list of clusters from the workspace - */ - public static void main(String[] args) { - DatabricksConfig config = new DatabricksConfig(); + /** Authenticate and retrieve the list of clusters from the workspace */ + public static void main(String[] args) { + DatabricksConfig config = new DatabricksConfig(); - WorkspaceClient workspace = new WorkspaceClient(config); - for (ClusterDetails c : workspace.clusters().list(new ListClustersRequest())) { - System.out.println(c.getClusterName()); - } + WorkspaceClient workspace = new WorkspaceClient(config); + for (ClusterDetails c : workspace.clusters().list(new ListClustersRequest())) { + System.out.println(c.getClusterName()); } + } } diff --git a/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/App.java b/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/App.java index 875de6e77..ae49d710d 100644 --- a/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/App.java +++ b/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/App.java @@ -12,20 +12,19 @@ @SpringBootApplication @EnableWebSecurity public class App { - public static void main(String[] args) { - SpringApplication.run(App.class, args); - } + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } - @Bean - public HttpClient getHttpClient() { - return new CommonsHttpClient.Builder().withTimeoutSeconds(30).build(); - } + @Bean + public HttpClient getHttpClient() { + return new CommonsHttpClient.Builder().withTimeoutSeconds(30).build(); + } - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.authorizeHttpRequests((requests) -> requests - .anyRequest().permitAll()); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); - return http.build(); - } + return http.build(); + } } diff --git a/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/RootController.java b/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/RootController.java index bb83f8466..ee0172f3d 100644 --- a/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/RootController.java +++ b/examples/spring-boot-oauth-u2m-demo/src/main/java/com/databricks/sdk/RootController.java @@ -10,14 +10,6 @@ import com.databricks.sdk.service.compute.ListClustersRequest; import com.databricks.sdk.service.oauth2.CreateCustomAppIntegration; import com.databricks.sdk.service.oauth2.CreateCustomAppIntegrationOutput; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; - import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.net.MalformedURLException; @@ -25,11 +17,17 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; @Controller public class RootController { - @Autowired - private HttpClient hc; + @Autowired private HttpClient hc; // Initialized by initializeApp(). This should be initialized in a more Spring-friendly way. private OAuthClient client; @@ -61,28 +59,33 @@ public String setupOAuthApplication(Model model) { @PostMapping("/initialize-app") public String initializeApp( - @RequestParam(name="client_id") String clientId, - @RequestParam(name="client_secret") String clientSecret, - @RequestParam(name="hostname") String hostname) throws IOException { + @RequestParam(name = "client_id") String clientId, + @RequestParam(name = "client_secret") String clientSecret, + @RequestParam(name = "hostname") String hostname) + throws IOException { DatabricksConfig config = new DatabricksConfig().setHost(hostname).setHttpClient(hc).resolve(); OpenIDConnectEndpoints oidcEndpoints = config.getDatabricksOidcEndpoints(); - client = new OAuthClient.Builder() - .withClientId(clientId) - .withClientSecret(clientSecret) - .withHost(hostname) - .withRedirectUrl(getRedirectUrl()) - .withHttpClient(hc) - .withOpenIDConnectEndpoints(oidcEndpoints) - .withScopes(List.of("all-apis", "offline_access")) - .build(); + client = + new OAuthClient.Builder() + .withClientId(clientId) + .withClientSecret(clientSecret) + .withHost(hostname) + .withRedirectUrl(getRedirectUrl()) + .withHttpClient(hc) + .withOpenIDConnectEndpoints(oidcEndpoints) + .withScopes(List.of("all-apis", "offline_access")) + .build(); return "redirect:/"; } private String getAccountsHost(String cloud) { switch (cloud) { - case "aws": return "https://accounts.cloud.databricks.com"; - case "azure": return "https://accounts.azuredatabricks.net"; - case "gcp": return "https://accounts.gcp.databricks.com"; + case "aws": + return "https://accounts.cloud.databricks.com"; + case "azure": + return "https://accounts.azuredatabricks.net"; + case "gcp": + return "https://accounts.gcp.databricks.com"; } throw new RuntimeException("Unexpected cloud: " + cloud); } @@ -93,18 +96,23 @@ public String makeNewApp( @RequestParam String password, @RequestParam String cloud, @RequestParam("account_id") String accountId, - @RequestParam String hostname) throws IOException { - DatabricksConfig c = new DatabricksConfig() - .setUsername(username) - .setPassword(password) - .setHost(getAccountsHost(cloud)) - .setAccountId(accountId) - .setHttpClient(hc); + @RequestParam String hostname) + throws IOException { + DatabricksConfig c = + new DatabricksConfig() + .setUsername(username) + .setPassword(password) + .setHost(getAccountsHost(cloud)) + .setAccountId(accountId) + .setHttpClient(hc); AccountClient account = new AccountClient(c); - CreateCustomAppIntegrationOutput result = account.customAppIntegration().create( - new CreateCustomAppIntegration() - .setName("java-sdk-demo") - .setRedirectUrls(Collections.singletonList(getRedirectUrl()))); + CreateCustomAppIntegrationOutput result = + account + .customAppIntegration() + .create( + new CreateCustomAppIntegration() + .setName("java-sdk-demo") + .setRedirectUrls(Collections.singletonList(getRedirectUrl()))); return initializeApp(result.getClientId(), result.getClientSecret(), hostname); } @@ -122,7 +130,8 @@ public String authenticate(HttpSession session, Model model) throws MalformedURL } @GetMapping("/callback") - public String callback(HttpSession session, @RequestParam Map allParams) throws IOException { + public String callback(HttpSession session, @RequestParam Map allParams) + throws IOException { Consent consent = (Consent) session.getAttribute("consent"); consent.setHttpClient(hc); SessionCredentials creds = consent.exchangeCallbackParameters(allParams); @@ -136,7 +145,8 @@ public String callback(HttpSession session, @RequestParam Map al @GetMapping("/list-clusters") public String listClusters(Model model) { - Iterable clustersIterable = workspace.clusters().list(new ListClustersRequest()); + Iterable clustersIterable = + workspace.clusters().list(new ListClustersRequest()); List clusterNames = new ArrayList<>(); clustersIterable.forEach(c -> clusterNames.add(c.getClusterName())); model.addAttribute("clusterNames", clusterNames); diff --git a/pom.xml b/pom.xml index 1054ad783..159bbeca0 100644 --- a/pom.xml +++ b/pom.xml @@ -75,41 +75,6 @@ maven-archetype-plugin 3.3.0 - - com.diffplug.spotless - spotless-maven-plugin - - 2.30.0 - - - - - - 1.27.0 - - - - - - - - pom.xml - - - false - false - - - true - true - true - - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/scripts/mvn-spotless-apply.sh b/scripts/mvn-spotless-apply.sh deleted file mode 100755 index 14f6e245d..000000000 --- a/scripts/mvn-spotless-apply.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -# Wrapper for `mvn spotless:apply` that exports the javac-internals flags required by -# google-java-format on JDK 16+. Without these, GJF fails on JDK 17+ with either -# IllegalAccessError or NoSuchMethodError when it reaches into com.sun.tools.javac.*. -# On JDK <= 15 the flags are unrecognized and would break Maven startup, so we only -# set them when the detected JDK major version is >= 16. -# -# If that JDK can't run google-java-format (most often because it's older than JDK 17, -# which the pinned GJF 1.27.0 requires), we retry under an explicit JDK 17 install — -# mirroring the `make fmt` -> `make fmt-jdk17` fallback. Override the fallback location -# with JDK17_HOME, or just point JAVA_HOME at a JDK 17+ and re-run. - -set -euo pipefail - -# Location used by the JDK 17 fallback. Override for non-Debian layouts, e.g. -# `JDK17_HOME=$(/usr/libexec/java_home -v 17) bash scripts/mvn-spotless-apply.sh`. -JDK17_HOME="${JDK17_HOME:-/usr/lib/jvm/java-17-openjdk-amd64}" - -# The --add-exports flags google-java-format needs to reach into com.sun.tools.javac.* -# on JDK 16+. -GJF_ADD_EXPORTS="\ ---add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" - -# Detect the major version of the JDK Maven will actually use: $JAVA_HOME/bin/java when -# JAVA_HOME is set, otherwise `java` from PATH. mvn resolves its JVM the same way, so the -# add-exports gating below matches what the first attempt really runs (and re-running with -# JAVA_HOME pointed at a JDK 17+ makes that attempt succeed without hitting the fallback). -JAVA_BIN="java" -if [ -n "${JAVA_HOME:-}" ] && [ -x "${JAVA_HOME}/bin/java" ]; then - JAVA_BIN="${JAVA_HOME}/bin/java" -fi - -JDK_VERSION_OUTPUT=$("$JAVA_BIN" -version 2>&1 | head -1) -# Matches `"1.8.0_xxx"` (legacy) and `"17.0.1"` / `"25"` (modern) forms. -JDK_MAJOR=$(echo "$JDK_VERSION_OUTPUT" | sed -E 's/.*version "([0-9]+)(\.[0-9]+)?.*/\1/') -if [ "$JDK_MAJOR" = "1" ]; then - JDK_MAJOR=$(echo "$JDK_VERSION_OUTPUT" | sed -E 's/.*version "1\.([0-9]+).*/\1/') -fi - -DEFAULT_OPTS="${MAVEN_OPTS:-}" -if [ "${JDK_MAJOR:-0}" -ge 16 ]; then - DEFAULT_OPTS="$DEFAULT_OPTS $GJF_ADD_EXPORTS" -fi - -# First attempt: the JDK selected above (JAVA_HOME or PATH). -if MAVEN_OPTS="$DEFAULT_OPTS" mvn spotless:apply; then - exit 0 -fi - -echo "" -echo "==> default JDK could not run google-java-format (likely < JDK 17). Retrying with JDK 17..." -echo "" - -if [ ! -x "$JDK17_HOME/bin/java" ]; then - echo "error: JDK 17 not found at $JDK17_HOME" >&2 - echo " install it, set JDK17_HOME to a JDK 17 install, or point JAVA_HOME" >&2 - echo " at a JDK 17+ and re-run." >&2 - exit 1 -fi - -# JDK 17 is always >= 16, so the add-exports flags are always required here. -exec env JAVA_HOME="$JDK17_HOME" \ - MAVEN_OPTS="${MAVEN_OPTS:-} $GJF_ADD_EXPORTS" \ - mvn spotless:apply diff --git a/tagging.py b/tagging.py deleted file mode 100644 index 46f0fdae3..000000000 --- a/tagging.py +++ /dev/null @@ -1,1395 +0,0 @@ -# /// script -# dependencies = ["PyGithub>=2,<3", "pyjwt<2.12.0", "charset-normalizer<3.4.6"] -# /// - -import os -import re -import argparse -from typing import Optional, List, Callable, Dict -from dataclasses import dataclass, replace -import subprocess -import time -import json -from github import Auth, Github, Repository, InputGitTreeElement, InputGitAuthor -from datetime import datetime, timezone - -NEXT_CHANGELOG_FILE_NAME = "NEXT_CHANGELOG.md" -CHANGELOG_FILE_NAME = "CHANGELOG.md" -PACKAGE_FILE_NAME = ".package.json" -CODEGEN_FILE_NAME = ".codegen.json" -CREATED_TAGS_FILE_NAME = "created_tags.json" - -# Presence of this env var switches the changelog source from a single -# hand-maintained ``NEXT_CHANGELOG.md`` to per-PR ``

/
/*.md`` -# fragments, and the release version from the ``## Release vX.Y.Z`` header to a -# ``/version`` file. Its value is the fragment directory name (e.g. -# ``.nextchanges``). Unset for every SDK repo, so their behavior is unchanged; -# a repo opts in by setting it in the tagging workflow (see the databricks/cli -# release workflow). The per-section ``(slug, header)`` mapping is read from -# ``.codegen.json``'s ``nextchanges_sections`` key. -NEXTCHANGES_DIR_ENV = "NEXTCHANGES_DIR" - -# File inside the fragment directory tracking the next release's version — -# read at release time and bumped afterward, the role the ``## Release vX.Y.Z`` -# header plays in the ``NEXT_CHANGELOG.md`` flow. -NEXTCHANGES_VERSION_FILE = "version" - -# ``README.md`` in a section slug is documentation (e.g. "put CLI changelog -# fragments here"), not a changelog fragment. It is excluded from rendering and -# preserved across releases, so teams can keep per-slug guidance in place. -NEXTCHANGES_README_FILE = "README.md" -""" -This script tags the release of the SDKs using a combination of the GitHub API and Git commands. -It reads the local repository to determine necessary changes, updates changelogs, and creates tags. - -### How it Works: -- It does **not** modify the local repository directly. -- Instead of committing and pushing changes locally, it uses the **GitHub API** to create commits and tags. -""" - - -@dataclass(frozen=True) -class Version: - """ - A semver 2.0.0-compliant version (https://semver.org). - - Mirrors the API of the `semver` PyPI package so this implementation can be - swapped for that library if it is ever added to the wheelhouse. Supports - parsing, stringification, and the two bumps we need: minor (for stable - releases) and prerelease (for release trains). - """ - - # Permissive pattern for locating a semver version string inside larger - # text (e.g. a changelog header). Callers use it in f-strings; strict - # validation happens via Version.parse. - PATTERN = r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?" - - # Strict anchored regex per https://semver.org. Rejects leading zeros in - # numeric identifiers and invalid pre-release/build identifier charsets. - _PARSE_REGEX = re.compile( - r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)" - r"(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)" - r"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" - r"(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" - ) - - major: int - minor: int - patch: int - prerelease: str = "" - build: str = "" - - @classmethod - def parse(cls, text: str) -> "Version": - """Parse a semver string, raising ValueError on malformed input.""" - match = cls._PARSE_REGEX.match(text) - if not match: - raise ValueError(f"Invalid semver version: {text!r}") - major, minor, patch, prerelease, build = match.groups() - return cls( - major=int(major), - minor=int(minor), - patch=int(patch), - prerelease=prerelease or "", - build=build or "", - ) - - def __str__(self) -> str: - result = f"{self.major}.{self.minor}.{self.patch}" - if self.prerelease: - result += f"-{self.prerelease}" - if self.build: - result += f"+{self.build}" - return result - - def bump_minor(self) -> "Version": - """ - Bump the minor version and reset patch. - - Per semver item 9, a pre-release version has lower precedence than - the same MAJOR.MINOR.PATCH, so bumping to a new minor drops any - pre-release and build metadata. - """ - return Version(major=self.major, minor=self.minor + 1, patch=0) - - def bump_prerelease(self) -> "Version": - """ - Increment the rightmost numeric identifier in the pre-release. - - Matches the npm `prerelease` bump semantics: - 0.0.0-alpha.1 -> 0.0.0-alpha.2 - 0.0.0-alpha -> 0.0.0-alpha.1 - 0.0.0-rc.1.2 -> 0.0.0-rc.1.3 - - Raises ValueError if the version has no pre-release to bump. - Build metadata is dropped since it does not affect precedence. - """ - if not self.prerelease: - raise ValueError(f"Cannot bump prerelease of {self}: no pre-release component") - parts = self.prerelease.split(".") - for i in range(len(parts) - 1, -1, -1): - if parts[i].isdigit(): - parts[i] = str(int(parts[i]) + 1) - return replace(self, prerelease=".".join(parts), build="") - # No numeric identifier exists; append ".1" to start a counter. - return replace(self, prerelease=f"{self.prerelease}.1", build="") - - def next_release_version(self) -> "Version": - """ - Default next version for the changelog after this one is released. - - If on a pre-release track, stay on it by bumping the pre-release - identifier (npm convention). Otherwise, bump the minor version, - the script's historical default for stable releases. Teams can - override the default in the release PR. - """ - if self.prerelease: - return self.bump_prerelease() - return self.bump_minor() - - -def _read_local_head_sha() -> str: - """ - Returns the SHA of the local working tree's HEAD via ``git rev-parse``. - """ - return subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() - - -def _release_branch() -> str: - """ - Returns the branch this release is being cut from. - - The tagging workflow sets ``DECO_TAGGING_REF`` to the branch it was - dispatched on (``github.ref_name``) so a release can be cut from a - branch other than main. It is unset for local runs and the historical - main-only release path, so we default to ``main`` and every existing - caller is unaffected. - """ - return os.environ.get("DECO_TAGGING_REF", "").strip() or "main" - - -class MainAdvancedError(Exception): - """ - Raised when the release branch (``origin/main`` by default; see - ``_release_branch``) has advanced since the workflow's checkout — - i.e., another commit landed during this run. The local working tree - is now stale, so any commit produced from it would silently revert - whatever the concurrent push added. - """ - - -# GitHub does not support signing commits for GitHub Apps directly. -# This class replaces usages for git commands such as "git add", "git commit", and "git push". -@dataclass -class GitHubRepo: - def __init__(self, repo: Repository): - self.repo = repo - self.changed_files: list[InputGitTreeElement] = [] - # Branch the changelog-bump commit + tag land on. Defaults to - # ``heads/main``; ``DECO_TAGGING_REF`` overrides it for a branch - # release. See ``_release_branch``. - self.ref = f"heads/{_release_branch()}" - # Anchor ``self.sha`` to the **local checkout** rather than a - # live API call. ``actions/checkout`` populates the working tree - # at this SHA, and every subsequent file read in this run is - # against that tree; the API HEAD is only relevant when we go - # to push. - self.sha = _read_local_head_sha() - - # Replaces "git add file" - def add_file(self, loc: str, content: str): - local_path = os.path.relpath(loc, os.getcwd()) - print(f"Adding file {local_path}") - blob = self.repo.create_git_blob(content=content, encoding="utf-8") - element = InputGitTreeElement(path=local_path, mode="100644", type="blob", sha=blob.sha) - self.changed_files.append(element) - - # Replaces "git rm file" - def delete_file(self, loc: str): - """``git rm`` equivalent for GitHubRepo: stage a tree deletion (sha=None).""" - local_path = os.path.relpath(loc, os.getcwd()) - print(f"Deleting file {local_path}") - self.changed_files.append(InputGitTreeElement(path=local_path, mode="100644", type="blob", sha=None)) - - # Replaces "git commit && git push" - def commit_and_push(self, message: str): - head_ref = self.repo.get_git_ref(self.ref) - if head_ref.object.sha != self.sha: - raise MainAdvancedError( - f"{self.ref} advanced from {self.sha} to {head_ref.object.sha} " - f"during this run. Local working tree is stale; aborting before " - f"the commit would silently revert the new content. Re-run the " - f"workflow." - ) - base_tree = self.repo.get_git_tree(sha=head_ref.object.sha) - new_tree = self.repo.create_git_tree(self.changed_files, base_tree) - parent_commit = self.repo.get_git_commit(head_ref.object.sha) - - new_commit = self.repo.create_git_commit(message=message, tree=new_tree, parents=[parent_commit]) - # Update branch reference. - head_ref.edit(new_commit.sha) - self.sha = new_commit.sha - - def reset(self, sha: Optional[str] = None): - self.changed_files = [] - if sha: - self.sha = sha - else: - self.sha = _read_local_head_sha() - - def tag(self, tag_name: str, tag_message: str): - # Create a tag pointing to the new commit - # The email MUST be the GitHub Apps email. - # Otherwise, the tag will not be verified. - tagger = InputGitAuthor( - name="Databricks SDK Release Bot", email="DECO-SDK-Tagging[bot]@users.noreply.github.com" - ) - - tag = self.repo.create_git_tag(tag=tag_name, message=tag_message, object=self.sha, type="commit", tagger=tagger) - # Create a Git ref (the actual reference for the tag in the repo) - self.repo.create_git_ref(ref=f"refs/tags/{tag_name}", sha=tag.sha) - - -gh: Optional[GitHubRepo] = None - - -@dataclass -class Package: - """ - Represents a package in the repository. - :name: The package name. - :path: The path to the package relative to the repository root. - """ - - name: str - path: str - - -@dataclass -class TagInfo: - """ - Represents all changes on a release. - :package: package info. - :version: release version for the package. Format: v.. - :content: changes for the release, as they appear in the changelog. - When written to CHANGELOG.md, the current date (YYYY-MM-DD) is automatically added. - - Example (from NEXT_CHANGELOG.md): - - ## Release v0.56.0 - - ### New Features and Improvements - * Feature - * Some improvement - - ### Bug Fixes - * Bug fix - - ### Documentation - * Doc Changes - - ### Internal Changes - * More Changes - - ### API Changes - * Add new Service - - Note: When written to CHANGELOG.md, the header becomes: ## Release v0.56.0 (YYYY-MM-DD) - - """ - - package: Package - version: str - content: str - - def tag_name(self) -> str: - return f"{self.package.name}/v{self.version}" if self.package.name else f"v{self.version}" - - -def get_package_name(package_path: str) -> str: - """ - Returns the package name from the package path. - The name is found inside the .package.json file: - { - "package": "package_name" - } - """ - filepath = os.path.join(os.getcwd(), package_path, PACKAGE_FILE_NAME) - with open(filepath, "r") as file: - content = json.load(file) - if "package" in content: - return content["package"] - # Legacy SDKs have no packages. - return "" - - -def stage_version_updates(tag_infos: List[TagInfo], packages: List[Package]) -> None: - """ - Stages all version-related edits for the release in a single pass over - every package the workspace already opts in via ``.package.json``. - """ - - # Load patterns from '.codegen.json' at the top level of the repository. - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - with open(package_file_path, "r") as file: - codegen = json.load(file) - - version_patterns = codegen.get("version", {}) - dep_patterns = codegen.get("dependency_pattern", {}) - name_template = codegen.get("dependency_name_template", "") - - if not version_patterns and not dep_patterns: - print("Neither `version` nor `dependency_pattern` found in .codegen.json. Nothing to update.") - return - - bumped_by_dir: Dict[str, TagInfo] = {info.package.path: info for info in tag_infos} - new_dep_versions = compute_dependency_rewrites(tag_infos, name_template) - - files = sorted(set(version_patterns.keys()) | set(dep_patterns.keys())) - - for pkg in packages: - for filename in files: - loc = os.path.join(os.getcwd(), pkg.path, filename) - - with open(loc, "r") as file: - content = file.read() - original = content - - # Own version (only when this package is being released and the - # file has a version pattern declared). - info = bumped_by_dir.get(pkg.path) - if info is not None and filename in version_patterns: - pattern = version_patterns[filename] - previous_version = pattern.replace("$VERSION", Version.PATTERN) - new_version = pattern.replace("$VERSION", info.version) - content = re.sub(previous_version, new_version, content) - - # Sibling dependency rewrites (only when the file has a - # dependency pattern and there is at least one bumped sibling). - if filename in dep_patterns and new_dep_versions: - content = rewrite_dependencies(content, dep_patterns[filename], new_dep_versions) - - if content != original: - gh.add_file(loc, content) - - -def compute_dependency_rewrites( - tag_infos: List[TagInfo], - name_template: str, -) -> Dict[str, str]: - """ - Returns a map of dependency-name to the new semver string for each - bumped package. - """ - if not name_template: - return {} - rewrites: Dict[str, str] = {} - for info in tag_infos: - # Skip legacy releases that don't have a per-package name; their - # tag_info has an empty package.name and they can't be referenced - # as a sibling dep anyway. - if not info.package.name: - continue - dep_name = name_template.replace("$PACKAGE", info.package.name) - rewrites[dep_name] = info.version - return rewrites - - -def rewrite_dependencies(content: str, pattern: str, new_versions: Dict[str, str]) -> str: - """ - Apply ``pattern`` (with ``$DEPENDENCY`` and ``$VERSION`` placeholders) to - rewrite every entry in ``content`` whose dependency name appears in - ``new_versions``. - """ - # Sentinel strings used to protect the placeholders through re.escape: - # we substitute them in, escape the whole template, then swap them out - # for the dep-name literal and Version.PATTERN. Control characters so - # they can't collide with anything in real .codegen.json patterns. - dep_sentinel = "\x01DEPENDENCY\x01" - ver_sentinel = "\x01VERSION\x01" - - for dep_name, new_value in new_versions.items(): - regex = pattern.replace("$DEPENDENCY", dep_sentinel).replace("$VERSION", ver_sentinel) - regex = re.escape(regex) - regex = regex.replace(re.escape(dep_sentinel), re.escape(dep_name)) - regex = regex.replace(re.escape(ver_sentinel), Version.PATTERN) - - # Build the literal replacement text by substituting the same - # placeholders directly. A lambda is used instead of a string to - # avoid re.sub interpreting \1, \g<...>, etc. inside the value. - replacement_text = pattern.replace("$DEPENDENCY", dep_name).replace("$VERSION", new_value) - content = re.sub(regex, lambda _m, text=replacement_text: text, content) - return content - - -def clean_next_changelog(package_path: str) -> None: - """ - Cleans the "NEXT_CHANGELOG.md" file. It performs 2 operations: - * Increase the version to the next minor version. - * Remove release notes. Sections names are kept to - keep consistency in the section names between releases. - """ - - file_path = os.path.join(os.getcwd(), package_path, NEXT_CHANGELOG_FILE_NAME) - with open(file_path, "r") as file: - content = file.read() - - # Remove content between ### sections. - cleaned_content = re.sub(r"(### [^\n]+\n)(?:.*?\n?)*?(?=###|$)", r"\1", content) - # Ensure there is exactly one empty line before each section. - cleaned_content = re.sub(r"(\n*)(###[^\n]+)", r"\n\n\2", cleaned_content) - # Find the version number and compute the default next release version. - # Teams can adjust the version in the PR if the default is not desired. - # For stable versions, bump minor (historical default since minor releases - # are more common than patch or major). For pre-release versions, stay on - # the same track by bumping the pre-release identifier (npm convention). - version_match = re.search(rf"Release v({Version.PATTERN})", cleaned_content) - if not version_match: - raise Exception("Version not found in the changelog") - current = Version.parse(version_match.group(1)) - new_header = f"Release v{current.next_release_version()}" - cleaned_content = cleaned_content.replace(version_match.group(0), new_header) - - # Update file with cleaned content - gh.add_file(file_path, cleaned_content) - - -def get_previous_tag_info(package: Package) -> Optional[TagInfo]: - """ - Extracts the previous tag info from the "CHANGELOG.md" file. - Used for failure recovery purposes. - """ - changelog_path = os.path.join(os.getcwd(), package.path, CHANGELOG_FILE_NAME) - - with open(changelog_path, "r") as f: - changelog = f.read() - - # Extract the latest release section using regex. - match = re.search( - rf"## (\[Release\] )?Release v{Version.PATTERN}.*?(?=\n## (\[Release\] )?Release v|\Z)", - changelog, - re.S, - ) - - # E.g., for new packages. - if not match: - return None - - latest_release = match.group(0) - version_match = re.search(rf"## (\[Release\] )?Release v({Version.PATTERN})", latest_release) - - if not version_match: - raise Exception("Version not found in the changelog") - - # Validate the extracted string is spec-compliant; fail loudly otherwise. - version = str(Version.parse(version_match.group(2))) - return TagInfo(package=package, version=version, content=latest_release) - - -def _load_codegen_config(package_path: str = "") -> Dict: - """ - Loads ``.codegen.json`` for a package: prefers ``/.codegen.json`` - and falls back to the repo-root file, returning an empty dict when neither - exists. ``package_path=""`` (the default) reads the repo root, matching the - single-package / root-config layout. - - Package-local lookup keeps the section taxonomy and other codegen options in - lockstep with the package-relative directories the nextchanges helpers - resolve, so a multi-package repo can give each package its own config - instead of every package sharing the root file. - """ - candidates = [os.path.join(os.getcwd(), package_path, CODEGEN_FILE_NAME)] - root_config = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - if root_config not in candidates: - candidates.append(root_config) - for candidate in candidates: - if os.path.exists(candidate): - with open(candidate, "r") as file: - return json.load(file) - return {} - - -def _nextchanges_dir() -> Optional[str]: - """ - Returns the fragment directory name when nextchanges mode is enabled - (``NEXTCHANGES_DIR`` set to a non-empty value), else ``None``. In the - ``None`` case the historical ``NEXT_CHANGELOG.md`` flow is used, so every - repo that doesn't set the env var is unaffected. - """ - return os.environ.get(NEXTCHANGES_DIR_ENV, "").strip() or None - - -def _nextchanges_sections(package_path: str = "") -> List[tuple]: - """ - Returns the ordered ``(slug, header)`` section list from the package's - ``.codegen.json`` ``nextchanges_sections`` — the mapping of ``//`` - subdirectories to the ``###
`` blocks they render into, in changelog - order. Read via ``_load_codegen_config(package_path)`` so it matches the - package-relative fragment directories (a multi-package repo can scope the - taxonomy per package). - - Declared as a JSON object ``{"": "
", ...}`` so the per-repo - section taxonomy stays out of this shared script; insertion order in the - object is the changelog order (JSON objects preserve order in Python 3.7+). - Raises when nextchanges mode is on but the key is absent/empty, or is not an - object, since there would be nothing sensible to render. - """ - sections = _load_codegen_config(package_path).get("nextchanges_sections", {}) - if not sections: - raise Exception( - f"nextchanges mode is enabled ({NEXTCHANGES_DIR_ENV} set) but " - f"`nextchanges_sections` is missing or empty in {CODEGEN_FILE_NAME}." - ) - if not isinstance(sections, dict): - raise Exception( - f"`nextchanges_sections` in {CODEGEN_FILE_NAME} must be a JSON object " - f'mapping section slug to header (e.g. {{"cli": "CLI"}}), got ' - f"{type(sections).__name__}." - ) - return list(sections.items()) - - -def _render_fragment(text: str) -> str: - """ - Render one fragment body into changelog bullets. Each line that starts with - a ``* ``/``- `` marker (ignoring leading whitespace) becomes its own `` * `` - bullet; a line without a marker is a continuation of the preceding bullet - and is kept as authored. A markerless first line is itself a single bullet. - - So a fragment with multiple ``* ``/``- `` lines renders as multiple bullets, - while a bullet followed by plain lines stays one bullet spanning those - lines. Every bullet gets the leading-space ``*`` that matches CHANGELOG.md. - """ - lines = [] - for line in text.split("\n"): - marker = line.lstrip() - if marker.startswith(("* ", "- ")): - lines.append(f" * {marker[2:]}") - elif lines: - lines.append(line) - else: - lines.append(f" * {line}") - return "\n".join(lines) - - -def render_nextchanges(package_path: str) -> Optional[str]: - """ - Render ``//
/*.md`` fragments into the changelog - body: one ``###
`` block per non-empty section in - ``nextchanges_sections`` order, fragments sorted by filename. Returns - ``None`` when there are no fragments. - - Every ``.md`` file under a section directory is a fragment, except - ``README.md`` which is treated as per-slug documentation and skipped (see - ``NEXTCHANGES_README_FILE``). Empty/whitespace-only files contribute - nothing. Files at the ```` root or under a slug not listed in - ``nextchanges_sections`` are ignored. Each fragment renders per - ``_render_fragment``. Link expansion (e.g. ``(#1234)`` → markdown link) is - assumed to have happened before release, so none here. - """ - base = os.path.join(os.getcwd(), package_path, _nextchanges_dir()) - if not os.path.isdir(base): - return None - - blocks = [] - for slug, header in _nextchanges_sections(package_path): - section_dir = os.path.join(base, slug) - if not os.path.isdir(section_dir): - continue - entries = [] - for name in sorted(os.listdir(section_dir)): - if not name.endswith(".md") or name == NEXTCHANGES_README_FILE: - continue - with open(os.path.join(section_dir, name)) as f: - text = f.read().strip() - if not text: - continue - entries.append(_render_fragment(text)) - if entries: - # Blank line after the heading, matching CHANGELOG.md. - blocks.append(f"### {header}\n\n" + "\n".join(entries)) - - if not blocks: - return None - return "\n\n".join(blocks) - - -def _nextchanges_version_path(package_path: str) -> str: - return os.path.join(os.getcwd(), package_path, _nextchanges_dir(), NEXTCHANGES_VERSION_FILE) - - -def read_nextchanges_version(package: Package) -> str: - """ - Release version for this run, read from the package's own ``/version`` - (resolved under ``package.path``, so each package in a multi-package repo - keeps its own version source). In nextchanges mode this file — not the - ``## Release v…`` changelog header — is the source of truth. To cut a patch - or major release, edit it in the PR; otherwise its default (bumped to the - next minor after the previous release by ``clean_nextchanges``) applies. - - Raises with an actionable message when the file is absent, so a package that - opts into nextchanges mode without a version file fails loudly instead of - with a bare ``FileNotFoundError``. - """ - version_path = _nextchanges_version_path(package.path) - if not os.path.exists(version_path): - raise Exception( - f"nextchanges mode is enabled ({NEXTCHANGES_DIR_ENV} set) but the version " - f"file {os.path.relpath(version_path, os.getcwd())} is missing; each package " - f"in nextchanges mode must provide its own /version file." - ) - with open(version_path) as f: - return str(Version.parse(f.read().strip().lstrip("v"))) - - -def get_next_tag_info_from_nextchanges(package: Package) -> Optional[TagInfo]: - """ - nextchanges-mode counterpart of ``get_next_tag_info``: build the release - TagInfo from ``/`` fragments. Returns ``None`` when there are no - entries (nothing to release), unless ``allow_empty_changelog`` is set in - ``.codegen.json`` — matching the ``NEXT_CHANGELOG.md`` skip behavior. - """ - body = render_nextchanges(package.path) - if body is None: - if not _load_codegen_config(package.path).get("allow_empty_changelog", False): - print(f"No {_nextchanges_dir()}/ entries. No changes will be made to the changelog.") - return None - if not _source_changed_since_last_release(package): - print(f"No {_nextchanges_dir()}/ entries and no source changes since the last release; skipping.") - return None - - version = read_nextchanges_version(package) - # write_changelog() keys off the "## Release v…" header, so include it. - content = f"## Release v{version}\n" + (f"\n{body}\n" if body else "") - return TagInfo(package=package, version=version, content=content) - - -def clean_nextchanges(package_path: str) -> None: - """ - nextchanges-mode counterpart of ``clean_next_changelog``: stage deletion of - the ``/`` fragments consumed by this release and bump ``/version`` - to the next minor (its post-release default; teams can still override it in - a PR). Deletes every ``.md`` under each section directory — the same set - ``render_nextchanges`` consumed, so ``README.md`` is preserved — leaving the - section directories in place. - """ - base = os.path.join(os.getcwd(), package_path, _nextchanges_dir()) - for slug, _ in _nextchanges_sections(package_path): - section_dir = os.path.join(base, slug) - if not os.path.isdir(section_dir): - continue - # Deletion order is irrelevant, so listdir as-is (no sort needed). - for name in os.listdir(section_dir): - if name.endswith(".md") and name != NEXTCHANGES_README_FILE: - gh.delete_file(os.path.join(section_dir, name)) - - version_path = _nextchanges_version_path(package_path) - with open(version_path) as f: - released = Version.parse(f.read().strip().lstrip("v")) - gh.add_file(version_path, f"{released.next_release_version()}\n") - - -def get_next_tag_info(package: Package) -> Optional[TagInfo]: - """ - Extracts the changes for the next release. In nextchanges mode (see - ``_nextchanges_dir``) it reads ``/`` fragments; otherwise it reads the - package's ``NEXT_CHANGELOG.md``. The result is already processed. - """ - if _nextchanges_dir() is not None: - return get_next_tag_info_from_nextchanges(package) - - next_changelog_path = os.path.join(os.getcwd(), package.path, NEXT_CHANGELOG_FILE_NAME) - # Read NEXT_CHANGELOG.md - with open(next_changelog_path, "r") as f: - next_changelog = f.read() - - # Remove "# NEXT CHANGELOG" line - next_changelog = re.sub(r"^# NEXT CHANGELOG(\n+)", "", next_changelog, flags=re.MULTILINE) - - # Remove empty sections - next_changelog = re.sub(r"###[^\n]+\n+(?=##|\Z)", "", next_changelog) - # Ensure there is exactly one empty line before each section - next_changelog = re.sub(r"(\n*)(###[^\n]+)", r"\n\n\2", next_changelog) - - # By default, packages whose NEXT_CHANGELOG.md has no populated - # sections are skipped — there's nothing meaningful to release. - # Repos like sdk-js which are still in development can opt in - # by setting ``allow_empty_changelog: true`` in .codegen.json — but - # even then, only when the package's source actually changed. - if not re.search(r"###", next_changelog): - if not _load_codegen_config(package.path).get("allow_empty_changelog", False): - print("All sections are empty. No changes will be made to the changelog.") - return None - if not _source_changed_since_last_release(package): - print("All sections are empty and no source changes since the last release; skipping.") - return None - - version_match = re.search(rf"## Release v({Version.PATTERN})", next_changelog) - - if not version_match: - raise Exception("Version not found in the changelog") - - # Validate the extracted string is spec-compliant; fail loudly otherwise. - version = str(Version.parse(version_match.group(1))) - return TagInfo(package=package, version=version, content=next_changelog) - - -def write_changelog(tag_info: TagInfo) -> None: - """ - Updates the changelog with a new tag info. - """ - changelog_path = os.path.join(os.getcwd(), tag_info.package.path, CHANGELOG_FILE_NAME) - with open(changelog_path, "r") as f: - changelog = f.read() - - # Add current date to the release header. - current_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") - content_with_date = re.sub( - rf"## Release v({Version.PATTERN})", - rf"## Release v\1 ({current_date})", - tag_info.content.strip(), - ) - - updated_changelog = re.sub(r"(# Version changelog\n\n)", f"\\1{content_with_date}\n\n\n", changelog) - gh.add_file(changelog_path, updated_changelog) - - -def process_package(package: Package) -> TagInfo: - """ - Processes a package's changelog scaffolding for the release. - """ - print(f"Processing package {package.name}") - tag_info = get_next_tag_info(package) - - # If there are no updates, skip. - if tag_info is None: - return - - write_changelog(tag_info) - if _nextchanges_dir() is not None: - clean_nextchanges(package.path) - else: - clean_next_changelog(package.path) - return tag_info - - -def find_packages() -> List[Package]: - """ - Returns all directories which contains a ".package.json" file. - """ - paths = _find_directories_with_file(PACKAGE_FILE_NAME) - return [Package(name=get_package_name(path), path=path) for path in paths] - - -def _find_directories_with_file(target_file: str) -> List[str]: - root_path = os.getcwd() - matching_directories = [] - - for dirpath, _, filenames in os.walk(root_path): - if target_file in filenames: - path = os.path.relpath(dirpath, root_path) - # If the path is the root directory (e.g., SDK V0), set it to an empty string. - if path == ".": - path = "" - matching_directories.append(path) - - return matching_directories - - -def is_tag_applied(tag: TagInfo) -> bool: - """ - Returns whether a tag is already applied in the repository. - - :param tag: The tag to check. - :return: True if the tag is applied, False otherwise. - :raises Exception: If the git command fails. - """ - try: - # Check if the specific tag exists - result = subprocess.check_output(["git", "tag", "--list", tag.tag_name()], stderr=subprocess.PIPE, text=True) - return result.strip() == tag.tag_name() - except subprocess.CalledProcessError as e: - # Raise a exception for git command errors - raise Exception(f"Git command failed: {e.stderr.strip() or e}") from e - - -def find_last_release_tag(package: Package) -> Optional[str]: - """ - Returns the most recent ``/v*`` tag in the repository, or - ``None`` if no such tag exists. Tags are sorted by semver ordering - (``--sort=-v:refname``) so pre-releases sort below their stable - counterparts. - - :raises Exception: If the git command fails. - """ - pattern = f"{package.name}/v*" if package.name else "v*" - try: - output = subprocess.check_output( - ["git", "tag", "--list", pattern, "--sort=-v:refname"], - stderr=subprocess.PIPE, - text=True, - ).strip() - except subprocess.CalledProcessError as e: - raise Exception(f"Git command failed: {e.stderr.strip() or e}") from e - if not output: - return None - return output.split("\n")[0].strip() - - -def has_commits_since_tag(tag: str, path: str) -> bool: - """ - Returns True iff at least one commit reachable from HEAD but not from - ``tag`` touches ``path``. Detects both that a sibling dependency has - unreleased changes that would ship stale (freshness) and, via - ``_source_changed_since_last_release``, whether a package has anything to - release under allow_empty_changelog. - - :raises Exception: If the git command fails. - """ - args = ["git", "log", "--oneline", f"{tag}..HEAD", "--", path or "."] - try: - output = subprocess.check_output(args, stderr=subprocess.PIPE, text=True).strip() - except subprocess.CalledProcessError as e: - raise Exception(f"Git command failed: {e.stderr.strip() or e}") from e - return bool(output) - - -def _source_changed_since_last_release(package: Package) -> bool: - """ - True when the package has any commits since its last release tag. A - never-released package (no tag) counts as changed. Gates the - ``allow_empty_changelog`` path so a package with no changelog entries - releases only when something actually changed since it was last released. - - Uses the same commit-based check as ``check_dependency_freshness`` — a - package's release bookkeeping is committed *at* its release tag, not after - it, so it never falls in the ``tag..HEAD`` window. Sharing the check keeps - the two consistent: a package this skips is exactly one freshness will not - flag as stale. - """ - tag = find_last_release_tag(package) - if tag is None: - return True - return has_commits_since_tag(tag, package.path) - - -def check_dependency_freshness(tag_infos: List[TagInfo], all_packages: List[Package]) -> None: - """ - Hard-fails when a package being released depends on a sibling package - that has unreleased commits since its last tag. - - Why: dependency rewrites (``stage_version_updates``) only fire for - siblings that are *also* being released. Without this check, releasing - package_a alone — when package_b has commits since its last tag — - publishes ``package_a@new`` pinning the *old* package_b artifact, which - won't have the changes package_a's source depends on. The check is - commit-based (not changelog-based) so a missing ``NEXT_CHANGELOG.md`` - entry on package_b is still caught. - - No-op when ``.codegen.json`` declares no dependency pattern (legacy - SDKs without per-package wiring). - """ - if not tag_infos: - return - - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - with open(package_file_path, "r") as file: - codegen = json.load(file) - - name_template = codegen.get("dependency_name_template", "") - dep_patterns = codegen.get("dependency_pattern", {}) - if not name_template or not dep_patterns: - return - - releasing_paths = {info.package.path for info in tag_infos} - by_dep_name: Dict[str, Package] = {} - for pkg in all_packages: - if not pkg.name: - continue - by_dep_name[name_template.replace("$PACKAGE", pkg.name)] = pkg - - issues: List[str] = [] - for info in tag_infos: - for filename, pattern in dep_patterns.items(): - loc = os.path.join(os.getcwd(), info.package.path, filename) - if not os.path.exists(loc): - continue - with open(loc, "r") as f: - content = f.read() - - for dep_name, dep_pkg in by_dep_name.items(): - if dep_pkg.path == info.package.path: - continue - if dep_pkg.path in releasing_paths: - continue - - # Same regex construction used by ``rewrite_dependencies``, - # so "is this dep referenced?" matches "would the rewrite - # touch it?". Keeps the two in lockstep. - regex = ( - re.escape(pattern) - .replace(re.escape("$DEPENDENCY"), re.escape(dep_name)) - .replace(re.escape("$VERSION"), Version.PATTERN) - ) - if not re.search(regex, content): - continue - - last_tag = find_last_release_tag(dep_pkg) - if last_tag is None: - # No prior tag means the dep was never released; we - # can't reason about staleness. Surface it anyway so - # the human resolves it explicitly. - issues.append( - f"{info.package.name} depends on {dep_pkg.name}, " - f"which has never been released. Release " - f"{dep_pkg.name} first or include it in this run." - ) - continue - if has_commits_since_tag(last_tag, dep_pkg.path): - issues.append( - f"{info.package.name} depends on {dep_pkg.name}, " - f"which has commits since {last_tag} but is not " - f"being released. Either release {dep_pkg.name} " - f"as well, or hold this release until its changes " - f"are reverted." - ) - - if issues: - raise Exception("Dependency freshness check failed:\n - " + "\n - ".join(issues)) - - -def find_last_tags() -> List[TagInfo]: - """ - Finds the last tags for each package. - - Returns a list of TagInfo objects for each package with a non-None changelog. - """ - packages = find_packages() - - return [info for info in (get_previous_tag_info(package) for package in packages) if info is not None] - - -def find_pending_tags() -> List[TagInfo]: - """ - Finds all tags that are pending to be applied. - """ - tag_infos = find_last_tags() - return [tag for tag in tag_infos if not is_tag_applied(tag)] - - -def generate_commit_message(tag_infos: List[TagInfo]) -> str: - """ - Generates a commit message for the release. - """ - if not tag_infos: - raise Exception("No tag infos provided to generate commit message") - - info = tag_infos[0] - # Legacy mode for SDKs without per service packaging - if not info.package.name: - if len(tag_infos) > 1: - raise Exception("Multiple packages found in legacy mode") - return f"[Release] Release v{info.version}\n\n{info.content}" - - # Sort tag_infos by package name for consistency. - tag_infos.sort(key=lambda info: info.package.name) - titles = ", ".join(f"{info.package.name}/v{info.version}" for info in tag_infos) - body = "\n\n".join(f"## {info.package.name}/v{info.version}\n\n{info.content}" for info in tag_infos) - return f"[Release] {titles}\n\n{body}" - - -def push_changes(tag_infos: List[TagInfo]) -> None: - """Pushes changes to the remote repository after handling possible merge conflicts.""" - - commit_message = generate_commit_message(tag_infos) - - # Create the release metadata file - file_name = os.path.join(os.getcwd(), ".release_metadata.json") - metadata = {"timestamp": datetime.now(tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S%z")} - content = json.dumps(metadata, indent=4) - gh.add_file(file_name, content) - - gh.commit_and_push(commit_message) - - -def reset_repository(hash: Optional[str] = None) -> None: - """ - Reset git to the specified commit. Defaults to HEAD. - - :param hash: The commit hash to reset to. If None, it resets to HEAD. - """ - # Fetch the latest changes from the remote repository. - subprocess.run(["git", "fetch"]) - - # Determine the commit hash (default to the release branch's remote - # head if none is provided). ``_release_branch`` is ``main`` unless - # ``DECO_TAGGING_REF`` selects a branch release. - commit_hash = hash or f"origin/{_release_branch()}" - - # ``git reset --hard`` must land before ``gh.reset(None)``, since - # ``gh.reset(None)`` reads ``git rev-parse HEAD`` to anchor - # ``self.sha`` to the local working tree. - subprocess.run(["git", "reset", "--hard", commit_hash], check=True) - gh.reset(hash) - - -def retry_function( - func: Callable[[], List[TagInfo]], cleanup: Callable[[], None], max_attempts: int = 5, delay: int = 5 -) -> List[TagInfo]: - """ - Calls a function call up to `max_attempts` times if an exception occurs. - - :param func: The function to call. - :param cleanup: Cleanup function in between retries - :param max_attempts: The maximum number of retries. - :param delay: The delay between retries in seconds. - :return: The return value of the function, or None if all retries fail. - """ - attempts = 0 - while attempts <= max_attempts: - try: - return func() # Call the function - except MainAdvancedError: - # Permanent failure: another commit landed on main during - # this run, so the local tree is stale. Retrying with the - # same stale tree would just hit the same mismatch — only - # a fresh workflow run against the new main can recover. - raise - except Exception as e: - attempts += 1 - print(f"Attempt {attempts} failed: {e}") - if attempts < max_attempts: - time.sleep(delay) # Wait before retrying - cleanup() - else: - print("All retry attempts failed.") - raise e # Re-raise the exception after max retries - - -def update_changelogs(selected_packages: List[Package], all_packages: List[Package]) -> List[TagInfo]: - """ - Updates changelogs and pushes the commits. - - ``selected_packages`` are the packages whose ``NEXT_CHANGELOG.md`` is - consulted to decide what gets released this run. ``all_packages`` is - the full repo inventory used for cross-package dep rewrites. - - The freshness check is deliberately *not* called here. ``process`` - runs it before entering the retry loop so a freshness violation - fails fast — the check is deterministic against the same git state, - so wrapping it in retry would just delay the same failure five - times. - """ - tag_infos = [info for info in (process_package(package) for package in selected_packages) if info is not None] - # If any package was changed, stage version updates and push. - if tag_infos: - stage_version_updates(tag_infos, all_packages) - push_changes(tag_infos) - return tag_infos - - -def preview_tag_infos(packages: List[Package]) -> List[TagInfo]: - """ - Read-only sibling of ``process_package``: returns the TagInfos that - would be released for ``packages`` without writing any changelog - edits. ``process`` calls this before the retry loop so the freshness - check has a snapshot to validate against. ``process_package`` will - re-derive the same TagInfos when ``update_changelogs`` runs; the - duplication is just a couple of NEXT_CHANGELOG.md reads. - """ - return [info for info in (get_next_tag_info(package) for package in packages) if info is not None] - - -def order_tag_infos_by_dependency(tag_infos: List[TagInfo]) -> List[TagInfo]: - """ - Returns ``tag_infos`` in topological order: every package appears - after every sibling it depends on. - """ - if not tag_infos: - return list(tag_infos) - - if any(not info.package.name for info in tag_infos) and len(tag_infos) > 1: - raise Exception("Multiple packages found in legacy mode") - - package_file_path = os.path.join(os.getcwd(), CODEGEN_FILE_NAME) - with open(package_file_path, "r") as file: - codegen = json.load(file) - - name_template = codegen.get("dependency_name_template", "") - dep_patterns = codegen.get("dependency_pattern", {}) - if not name_template or not dep_patterns: - return list(tag_infos) - - by_dep_name: Dict[str, TagInfo] = { - name_template.replace("$PACKAGE", info.package.name): info for info in tag_infos if info.package.name - } - - # Adjacency: path -> set of paths it depends on (within tag_infos). - deps: Dict[str, set] = {info.package.path: set() for info in tag_infos} - for info in tag_infos: - for filename, pattern in dep_patterns.items(): - loc = os.path.join(os.getcwd(), info.package.path, filename) - if not os.path.exists(loc): - continue - with open(loc, "r") as f: - content = f.read() - for dep_name, dep_info in by_dep_name.items(): - if dep_info.package.path == info.package.path: - continue - regex = ( - re.escape(pattern) - .replace(re.escape("$DEPENDENCY"), re.escape(dep_name)) - .replace(re.escape("$VERSION"), Version.PATTERN) - ) - if re.search(regex, content): - deps[info.package.path].add(dep_info.package.path) - - # Stable topological sort: at each step, emit every node whose deps - # are already emitted, alphabetically by package name. Ties broken - # alphabetically so the manifest is reproducible across runs. - emitted: set = set() - ordered: List[TagInfo] = [] - while len(ordered) < len(tag_infos): - ready = sorted( - ( - info - for info in tag_infos - if info.package.path not in emitted and deps[info.package.path].issubset(emitted) - ), - key=lambda info: info.package.name, - ) - if not ready: - remaining = [info.package.name for info in tag_infos if info.package.path not in emitted] - raise Exception(f"Cyclic dependency detected among packages: {remaining}") - for info in ready: - ordered.append(info) - emitted.add(info.package.path) - return ordered - - -def push_tags(tag_infos: List[TagInfo]) -> None: - """ - Creates and pushes tags to the repository. - - Tags are emitted in topological order — dependencies before - dependents — so downstream publishing pipelines reading - ``created_tags.json`` can walk it sequentially without re-deriving - the dependency graph. See ``order_tag_infos_by_dependency``. - - As a side effect, writes the names of successfully created tags to - ``./created_tags.json`` so that workflows triggering this script can - discover what was produced (the GitHub Actions workflow uploads this - file as the ``created-tags`` artifact). - - Schema: - {"tags": ["service-a/v1.2.3", "service-b/v0.4.0"]} - - The manifest is written even if tag creation fails partway through: - tags that succeeded before the failure are flushed before the - exception is re-raised, so recovery-mode runs still surface their - output. - """ - tag_infos = order_tag_infos_by_dependency(tag_infos) - created: List[str] = [] - try: - for tag_info in tag_infos: - gh.tag(tag_info.tag_name(), tag_info.content) - created.append(tag_info.tag_name()) - finally: - manifest_path = os.path.join(os.getcwd(), CREATED_TAGS_FILE_NAME) - with open(manifest_path, "w") as f: - json.dump({"tags": created}, f) - - -def run_command(command: List[str]) -> str: - """ - Runs a command and returns the output - """ - output = subprocess.check_output(command) - print(f'Running command: {" ".join(command)}') - return output.decode() - - -def pull_last_release_commit() -> None: - """ - Reset the repository to the last release. - Uses commit for last change to .release_metadata.json, since it's only updated on releases. - """ - commit_hash = subprocess.check_output( - ["git", "log", "-n", "1", "--format=%H", "--", ".release_metadata.json"], text=True - ).strip() - - # If no commit is found, raise an exception - if not commit_hash: - raise ValueError("No commit found for .release_metadata.json") - - # Reset the repository to the commit - reset_repository(commit_hash) - - -def _build_arg_parser() -> argparse.ArgumentParser: - """ - Builds the CLI parser. Shared by ``get_packages_from_args`` and - ``args_request_preview`` so both accept the same flags — parsing either one - with an unknown flag present would otherwise error. - """ - parser = argparse.ArgumentParser(description="Update changelogs and tag the release.") - parser.add_argument( - "--package", - "-p", - type=str, - default="", - help="Comma-separated list of packages to tag. Leave empty to tag all packages with pending releases.", - ) - parser.add_argument( - "--preview", - action="store_true", - help=( - "Print the changelog section the next release would add, then exit " - "(no writes, no network). Only supported in nextchanges mode." - ), - ) - return parser - - -def get_packages_from_args() -> List[str]: - """ - Retrieves the list of packages to tag. - - python3 ./tagging.py --package # single package - python3 ./tagging.py --package , # multiple packages - - Returns an empty list when --package is omitted, which means all packages - with pending releases will be tagged. - """ - args = _build_arg_parser().parse_args() - return [name.strip() for name in args.package.split(",") if name.strip()] - - -def _select_packages(all_packages: List[Package]) -> List[Package]: - """ - Narrow ``all_packages`` to the ``--package`` selection, or return all of - them when ``--package`` is omitted. Shared by ``process`` and ``preview`` so - the read-only preview covers exactly the release scope the same CLI - arguments would execute — ``tagging.py --package foo --preview`` previews - only ``foo``, not every package in the repo. - """ - package_names = get_packages_from_args() - if not package_names: - return all_packages - return [package for package in all_packages if package.name in package_names] - - -def args_request_preview() -> bool: - """Returns whether ``--preview`` was passed.""" - return _build_arg_parser().parse_args().preview - - -def preview() -> None: - """ - Print the ``## Release vX.Y.Z`` section(s) the next release would prepend to - CHANGELOG.md, rendered from the current ``/`` fragments — without - touching git, GitHub, or any file. Mirrors ``write_changelog``'s date stamp - so the output matches what would land. Read-only: safe to run anytime, no - credentials. Only meaningful in nextchanges mode. - """ - if _nextchanges_dir() is None: - raise Exception(f"--preview requires nextchanges mode ({NEXTCHANGES_DIR_ENV} must be set).") - - current_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") - printed = False - for package in _select_packages(find_packages()): - tag_info = get_next_tag_info(package) - if tag_info is None: - continue - dated = re.sub( - rf"## Release v({Version.PATTERN})", - rf"## Release v\1 ({current_date})", - tag_info.content.strip(), - ) - print(dated) - printed = True - if not printed: - print(f"No {_nextchanges_dir()}/ entries — the next release would add no changelog section.") - - -def init_github(): - token = os.environ["GITHUB_TOKEN"] - repo_name = os.environ["GITHUB_REPOSITORY"] - g = Github(auth=Auth.Token(token)) - repo = g.get_repo(repo_name) - global gh - gh = GitHubRepo(repo) - - -def process(): - """ - Main entry point for tagging process. - - Tagging process consist of multiple steps: - * For each package, update the corresponding CHANGELOG.md file based on the contents of NEXT_CHANGELOG.md file - * If any package has been updated, commit and push the changes. - * Apply and push the new tags matching the version. - - If a specific pagkage is provided as a parameter, only that package will be tagged. - - If any tag are pending from an early process, it will skip updating the CHANGELOG.md files and only apply the tags. - """ - - package_names = get_packages_from_args() - pending_tags = find_pending_tags() - - # pending_tags is non-empty only when the tagging process previously failed or interrupted. - # We must complete the interrupted tagging process before starting a new one to avoid inconsistent states and missing changelog entries. - # Therefore, we don't support specifying packages until the previously started process has been successfully completed. - if pending_tags and package_names: - pending_packages = [tag.package.name for tag in pending_tags] - raise Exception(f"Cannot release packages {package_names}. Pending release for {pending_packages}") - - if pending_tags: - print("Found pending tags from previous executions, entering recovery mode.") - pull_last_release_commit() - push_tags(pending_tags) - return - - all_packages = find_packages() - # If packages are specified as an argument, only release those — but - # dep rewrites and the freshness check still operate over the full - # set. Shared with preview() so both scope identically. - selected_packages = _select_packages(all_packages) - - # Run the freshness check against a read-only preview before the - # retry loop, since the check is deterministic. A freshness - # violation fails the run immediately, with no commits, no tags, no - # retry storm. - check_dependency_freshness(preview_tag_infos(selected_packages), all_packages) - - pending_tags = retry_function( - func=lambda: update_changelogs(selected_packages, all_packages), - cleanup=reset_repository, - ) - push_tags(pending_tags) - - -def validate_git_root(): - """ - Validate that the script is run from the root of the repository. - """ - repo_root = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).strip().decode("utf-8") - current_dir = subprocess.check_output(["pwd"]).strip().decode("utf-8") - if repo_root != current_dir: - raise Exception("Please run this script from the root of the repository.") - - -def main(): - validate_git_root() - # Preview is read-only: no GitHub credentials, no network, no commits. - if args_request_preview(): - preview() - return - init_github() - process() - - -if __name__ == "__main__": - main() diff --git a/tagging.py.lock b/tagging.py.lock deleted file mode 100755 index 2bd746a66..000000000 --- a/tagging.py.lock +++ /dev/null @@ -1,302 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[manifest] -requirements = [ - { name = "charset-normalizer", specifier = "<3.4.6" }, - { name = "pygithub", specifier = ">=2,<3" }, - { name = "pyjwt", specifier = "<2.12.0" }, -] - -[[package]] -name = "certifi" -version = "2026.2.25" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/b6/9ee9c1a608916ca5feae81a344dffbaa53b26b90be58cc2159e3332d44ec/charset_normalizer-3.4.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed97c282ee4f994ef814042423a529df9497e3c666dca19be1d4cd1129dc7ade", size = 280976, upload-time = "2026-03-06T06:01:15.276Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d8/a54f7c0b96f1df3563e9190f04daf981e365a9b397eedfdfb5dbef7e5c6c/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0294916d6ccf2d069727d65973c3a1ca477d68708db25fd758dd28b0827cff54", size = 189356, upload-time = "2026-03-06T06:01:16.511Z" }, - { url = "https://files.pythonhosted.org/packages/42/69/2bf7f76ce1446759a5787cb87d38f6a61eb47dbbdf035cfebf6347292a65/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc57a0baa3eeedd99fafaef7511b5a6ef4581494e8168ee086031744e2679467", size = 206369, upload-time = "2026-03-06T06:01:17.853Z" }, - { url = "https://files.pythonhosted.org/packages/10/9c/949d1a46dab56b959d9a87272482195f1840b515a3380e39986989a893ae/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ed1a9a204f317ef879b32f9af507d47e49cd5e7f8e8d5d96358c98373314fc60", size = 203285, upload-time = "2026-03-06T06:01:19.473Z" }, - { url = "https://files.pythonhosted.org/packages/67/5c/ae30362a88b4da237d71ea214a8c7eb915db3eec941adda511729ac25fa2/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad83b8f9379176c841f8865884f3514d905bcd2a9a3b210eaa446e7d2223e4d", size = 196274, upload-time = "2026-03-06T06:01:20.728Z" }, - { url = "https://files.pythonhosted.org/packages/b2/07/c9f2cb0e46cb6d64fdcc4f95953747b843bb2181bda678dc4e699b8f0f9a/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:a118e2e0b5ae6b0120d5efa5f866e58f2bb826067a646431da4d6a2bdae7950e", size = 184715, upload-time = "2026-03-06T06:01:22.194Z" }, - { url = "https://files.pythonhosted.org/packages/36/64/6b0ca95c44fddf692cd06d642b28f63009d0ce325fad6e9b2b4d0ef86a52/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:754f96058e61a5e22e91483f823e07df16416ce76afa4ebf306f8e1d1296d43f", size = 193426, upload-time = "2026-03-06T06:01:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/50/bc/a730690d726403743795ca3f5bb2baf67838c5fea78236098f324b965e40/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c300cefd9b0970381a46394902cd18eaf2aa00163f999590ace991989dcd0fc", size = 191780, upload-time = "2026-03-06T06:01:25.053Z" }, - { url = "https://files.pythonhosted.org/packages/97/4f/6c0bc9af68222b22951552d73df4532b5be6447cee32d58e7e8c74ecbb7b/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c108f8619e504140569ee7de3f97d234f0fbae338a7f9f360455071ef9855a95", size = 185805, upload-time = "2026-03-06T06:01:26.294Z" }, - { url = "https://files.pythonhosted.org/packages/dd/b9/a523fb9b0ee90814b503452b2600e4cbc118cd68714d57041564886e7325/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d1028de43596a315e2720a9849ee79007ab742c06ad8b45a50db8cdb7ed4a82a", size = 208342, upload-time = "2026-03-06T06:01:27.55Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/c59e761dee4464050713e50e27b58266cc8e209e518c0b378c1580c959ba/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:19092dde50335accf365cce21998a1c6dd8eafd42c7b226eb54b2747cdce2fac", size = 193661, upload-time = "2026-03-06T06:01:29.051Z" }, - { url = "https://files.pythonhosted.org/packages/1c/43/729fa30aad69783f755c5ad8649da17ee095311ca42024742701e202dc59/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4354e401eb6dab9aed3c7b4030514328a6c748d05e1c3e19175008ca7de84fb1", size = 204819, upload-time = "2026-03-06T06:01:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/87/33/d9b442ce5a91b96fc0840455a9e49a611bbadae6122778d0a6a79683dd31/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68766a3c58fde7f9aaa22b3786276f62ab2f594efb02d0a1421b6282e852e98", size = 198080, upload-time = "2026-03-06T06:01:31.478Z" }, - { url = "https://files.pythonhosted.org/packages/56/5a/b8b5a23134978ee9885cee2d6995f4c27cc41f9baded0a9685eabc5338f0/charset_normalizer-3.4.5-cp312-cp312-win32.whl", hash = "sha256:1827734a5b308b65ac54e86a618de66f935a4f63a8a462ff1e19a6788d6c2262", size = 132630, upload-time = "2026-03-06T06:01:33.056Z" }, - { url = "https://files.pythonhosted.org/packages/70/53/e44a4c07e8904500aec95865dc3f6464dc3586a039ef0df606eb3ac38e35/charset_normalizer-3.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:728c6a963dfab66ef865f49286e45239384249672cd598576765acc2a640a636", size = 142856, upload-time = "2026-03-06T06:01:34.489Z" }, - { url = "https://files.pythonhosted.org/packages/ea/aa/c5628f7cad591b1cf45790b7a61483c3e36cf41349c98af7813c483fd6e8/charset_normalizer-3.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:75dfd1afe0b1647449e852f4fb428195a7ed0588947218f7ba929f6538487f02", size = 132982, upload-time = "2026-03-06T06:01:35.641Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac59c15e3f1465f722607800c68713f9fbc2f672b9eb649fe831da4019ae9b23", size = 280788, upload-time = "2026-03-06T06:01:37.126Z" }, - { url = "https://files.pythonhosted.org/packages/0e/09/6003e7ffeb90cc0560da893e3208396a44c210c5ee42efff539639def59b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:165c7b21d19365464e8f70e5ce5e12524c58b48c78c1f5a57524603c1ab003f8", size = 188890, upload-time = "2026-03-06T06:01:38.73Z" }, - { url = "https://files.pythonhosted.org/packages/42/1e/02706edf19e390680daa694d17e2b8eab4b5f7ac285e2a51168b4b22ee6b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:28269983f25a4da0425743d0d257a2d6921ea7d9b83599d4039486ec5b9f911d", size = 206136, upload-time = "2026-03-06T06:01:40.016Z" }, - { url = "https://files.pythonhosted.org/packages/c7/87/942c3def1b37baf3cf786bad01249190f3ca3d5e63a84f831e704977de1f/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d27ce22ec453564770d29d03a9506d449efbb9fa13c00842262b2f6801c48cce", size = 202551, upload-time = "2026-03-06T06:01:41.522Z" }, - { url = "https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0625665e4ebdddb553ab185de5db7054393af8879fb0c87bd5690d14379d6819", size = 195572, upload-time = "2026-03-06T06:01:43.208Z" }, - { url = "https://files.pythonhosted.org/packages/20/ea/dfb1792a8050a8e694cfbde1570ff97ff74e48afd874152d38163d1df9ae/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:c23eb3263356d94858655b3e63f85ac5d50970c6e8febcdde7830209139cc37d", size = 184438, upload-time = "2026-03-06T06:01:44.755Z" }, - { url = "https://files.pythonhosted.org/packages/72/12/c281e2067466e3ddd0595bfaea58a6946765ace5c72dfa3edc2f5f118026/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e6302ca4ae283deb0af68d2fbf467474b8b6aedcd3dab4db187e07f94c109763", size = 193035, upload-time = "2026-03-06T06:01:46.051Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4f/3792c056e7708e10464bad0438a44708886fb8f92e3c3d29ec5e2d964d42/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e51ae7d81c825761d941962450f50d041db028b7278e7b08930b4541b3e45cb9", size = 191340, upload-time = "2026-03-06T06:01:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/e7/86/80ddba897127b5c7a9bccc481b0cd36c8fefa485d113262f0fe4332f0bf4/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:597d10dec876923e5c59e48dbd366e852eacb2b806029491d307daea6b917d7c", size = 185464, upload-time = "2026-03-06T06:01:48.764Z" }, - { url = "https://files.pythonhosted.org/packages/4d/00/b5eff85ba198faacab83e0e4b6f0648155f072278e3b392a82478f8b988b/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5cffde4032a197bd3b42fd0b9509ec60fb70918d6970e4cc773f20fc9180ca67", size = 208014, upload-time = "2026-03-06T06:01:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/c8/11/d36f70be01597fd30850dde8a1269ebc8efadd23ba5785808454f2389bde/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2da4eedcb6338e2321e831a0165759c0c620e37f8cd044a263ff67493be8ffb3", size = 193297, upload-time = "2026-03-06T06:01:51.933Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1d/259eb0a53d4910536c7c2abb9cb25f4153548efb42800c6a9456764649c0/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:65a126fb4b070d05340a84fc709dd9e7c75d9b063b610ece8a60197a291d0adf", size = 204321, upload-time = "2026-03-06T06:01:53.887Z" }, - { url = "https://files.pythonhosted.org/packages/84/31/faa6c5b9d3688715e1ed1bb9d124c384fe2fc1633a409e503ffe1c6398c1/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7a80a9242963416bd81f99349d5f3fce1843c303bd404f204918b6d75a75fd6", size = 197509, upload-time = "2026-03-06T06:01:56.439Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a5/c7d9dd1503ffc08950b3260f5d39ec2366dd08254f0900ecbcf3a6197c7c/charset_normalizer-3.4.5-cp313-cp313-win32.whl", hash = "sha256:f1d725b754e967e648046f00c4facc42d414840f5ccc670c5670f59f83693e4f", size = 132284, upload-time = "2026-03-06T06:01:57.812Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:e37bd100d2c5d3ba35db9c7c5ba5a9228cbcffe5c4778dc824b164e5257813d7", size = 142630, upload-time = "2026-03-06T06:01:59.062Z" }, - { url = "https://files.pythonhosted.org/packages/31/41/1c4b7cc9f13bd9d369ce3bc993e13d374ce25fa38a2663644283ecf422c1/charset_normalizer-3.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:93b3b2cc5cf1b8743660ce77a4f45f3f6d1172068207c1defc779a36eea6bb36", size = 133254, upload-time = "2026-03-06T06:02:00.281Z" }, - { url = "https://files.pythonhosted.org/packages/43/be/0f0fd9bb4a7fa4fb5067fb7d9ac693d4e928d306f80a0d02bde43a7c4aee/charset_normalizer-3.4.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8197abe5ca1ffb7d91e78360f915eef5addff270f8a71c1fc5be24a56f3e4873", size = 280232, upload-time = "2026-03-06T06:02:01.508Z" }, - { url = "https://files.pythonhosted.org/packages/28/02/983b5445e4bef49cd8c9da73a8e029f0825f39b74a06d201bfaa2e55142a/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2aecdb364b8a1802afdc7f9327d55dad5366bc97d8502d0f5854e50712dbc5f", size = 189688, upload-time = "2026-03-06T06:02:02.857Z" }, - { url = "https://files.pythonhosted.org/packages/d0/88/152745c5166437687028027dc080e2daed6fe11cfa95a22f4602591c42db/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a66aa5022bf81ab4b1bebfb009db4fd68e0c6d4307a1ce5ef6a26e5878dfc9e4", size = 206833, upload-time = "2026-03-06T06:02:05.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0f/ebc15c8b02af2f19be9678d6eed115feeeccc45ce1f4b098d986c13e8769/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77f97e515688bd615c1d1f795d540f32542d514242067adcb8ef532504cb9ee", size = 202879, upload-time = "2026-03-06T06:02:06.446Z" }, - { url = "https://files.pythonhosted.org/packages/38/9c/71336bff6934418dc8d1e8a1644176ac9088068bc571da612767619c97b3/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01a1ed54b953303ca7e310fafe0fe347aab348bd81834a0bcd602eb538f89d66", size = 195764, upload-time = "2026-03-06T06:02:08.763Z" }, - { url = "https://files.pythonhosted.org/packages/b7/95/ce92fde4f98615661871bc282a856cf9b8a15f686ba0af012984660d480b/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:b2d37d78297b39a9eb9eb92c0f6df98c706467282055419df141389b23f93362", size = 183728, upload-time = "2026-03-06T06:02:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/1c/e7/f5b4588d94e747ce45ae680f0f242bc2d98dbd4eccfab73e6160b6893893/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e71bbb595973622b817c042bd943c3f3667e9c9983ce3d205f973f486fec98a7", size = 192937, upload-time = "2026-03-06T06:02:11.663Z" }, - { url = "https://files.pythonhosted.org/packages/f9/29/9d94ed6b929bf9f48bf6ede6e7474576499f07c4c5e878fb186083622716/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cd966c2559f501c6fd69294d082c2934c8dd4719deb32c22961a5ac6db0df1d", size = 192040, upload-time = "2026-03-06T06:02:13.489Z" }, - { url = "https://files.pythonhosted.org/packages/15/d2/1a093a1cf827957f9445f2fe7298bcc16f8fc5e05c1ed2ad1af0b239035e/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d5e52d127045d6ae01a1e821acfad2f3a1866c54d0e837828538fabe8d9d1bd6", size = 184107, upload-time = "2026-03-06T06:02:14.83Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7d/82068ce16bd36135df7b97f6333c5d808b94e01d4599a682e2337ed5fd14/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:30a2b1a48478c3428d047ed9690d57c23038dac838a87ad624c85c0a78ebeb39", size = 208310, upload-time = "2026-03-06T06:02:16.165Z" }, - { url = "https://files.pythonhosted.org/packages/84/4e/4dfb52307bb6af4a5c9e73e482d171b81d36f522b21ccd28a49656baa680/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d8ed79b8f6372ca4254955005830fd61c1ccdd8c0fac6603e2c145c61dd95db6", size = 192918, upload-time = "2026-03-06T06:02:18.144Z" }, - { url = "https://files.pythonhosted.org/packages/08/a4/159ff7da662cf7201502ca89980b8f06acf3e887b278956646a8aeb178ab/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c5af897b45fa606b12464ccbe0014bbf8c09191e0a66aab6aa9d5cf6e77e0c94", size = 204615, upload-time = "2026-03-06T06:02:19.821Z" }, - { url = "https://files.pythonhosted.org/packages/d6/62/0dd6172203cb6b429ffffc9935001fde42e5250d57f07b0c28c6046deb6b/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1088345bcc93c58d8d8f3d783eca4a6e7a7752bbff26c3eee7e73c597c191c2e", size = 197784, upload-time = "2026-03-06T06:02:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/c7/5e/1aab5cb737039b9c59e63627dc8bbc0d02562a14f831cc450e5f91d84ce1/charset_normalizer-3.4.5-cp314-cp314-win32.whl", hash = "sha256:ee57b926940ba00bca7ba7041e665cc956e55ef482f851b9b65acb20d867e7a2", size = 133009, upload-time = "2026-03-06T06:02:23.289Z" }, - { url = "https://files.pythonhosted.org/packages/40/65/e7c6c77d7aaa4c0d7974f2e403e17f0ed2cb0fc135f77d686b916bf1eead/charset_normalizer-3.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4481e6da1830c8a1cc0b746b47f603b653dadb690bcd851d039ffaefe70533aa", size = 143511, upload-time = "2026-03-06T06:02:26.195Z" }, - { url = "https://files.pythonhosted.org/packages/ba/91/52b0841c71f152f563b8e072896c14e3d83b195c188b338d3cc2e582d1d4/charset_normalizer-3.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:97ab7787092eb9b50fb47fa04f24c75b768a606af1bcba1957f07f128a7219e4", size = 133775, upload-time = "2026-03-06T06:02:27.473Z" }, - { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, -] - -[[package]] -name = "cryptography" -version = "46.0.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, - { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, - { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pygithub" -version = "2.8.1" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -dependencies = [ - { name = "pyjwt", extra = ["crypto"] }, - { name = "pynacl" }, - { name = "requests" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c1/74/e560bdeffea72ecb26cff27f0fad548bbff5ecc51d6a155311ea7f9e4c4c/pygithub-2.8.1.tar.gz", hash = "sha256:341b7c78521cb07324ff670afd1baa2bf5c286f8d9fd302c1798ba594a5400c9", size = 2246994, upload-time = "2025-09-02T17:41:54.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/ba/7049ce39f653f6140aac4beb53a5aaf08b4407b6a3019aae394c1c5244ff/pygithub-2.8.1-py3-none-any.whl", hash = "sha256:23a0a5bca93baef082e03411bf0ce27204c32be8bfa7abc92fe4a3e132936df0", size = 432709, upload-time = "2025-09-02T17:41:52.947Z" }, -] - -[[package]] -name = "pyjwt" -version = "2.11.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, -] - -[package.optional-dependencies] -crypto = [ - { name = "cryptography" }, -] - -[[package]] -name = "pynacl" -version = "1.6.2" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/79/0e3c34dc3c4671f67d251c07aa8eb100916f250ee470df230b0ab89551b4/pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl", hash = "sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594", size = 390064, upload-time = "2026-01-01T17:31:57.264Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/23a26e931736e13b16483795c8a6b2f641bf6a3d5238c22b070a5112722c/pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0", size = 809370, upload-time = "2026-01-01T17:31:59.198Z" }, - { url = "https://files.pythonhosted.org/packages/87/74/8d4b718f8a22aea9e8dcc8b95deb76d4aae380e2f5b570cc70b5fd0a852d/pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9", size = 1408304, upload-time = "2026-01-01T17:32:01.162Z" }, - { url = "https://files.pythonhosted.org/packages/fd/73/be4fdd3a6a87fe8a4553380c2b47fbd1f7f58292eb820902f5c8ac7de7b0/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574", size = 844871, upload-time = "2026-01-01T17:32:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/55/ad/6efc57ab75ee4422e96b5f2697d51bbcf6cdcc091e66310df91fbdc144a8/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634", size = 1446356, upload-time = "2026-01-01T17:32:04.452Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/928ee9c4779caa0a915844311ab9fb5f99585621c5d6e4574538a17dca07/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88", size = 826814, upload-time = "2026-01-01T17:32:06.078Z" }, - { url = "https://files.pythonhosted.org/packages/f7/a9/1bdba746a2be20f8809fee75c10e3159d75864ef69c6b0dd168fc60e485d/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14", size = 1411742, upload-time = "2026-01-01T17:32:07.651Z" }, - { url = "https://files.pythonhosted.org/packages/f3/2f/5e7ea8d85f9f3ea5b6b87db1d8388daa3587eed181bdeb0306816fdbbe79/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444", size = 801714, upload-time = "2026-01-01T17:32:09.558Z" }, - { url = "https://files.pythonhosted.org/packages/06/ea/43fe2f7eab5f200e40fb10d305bf6f87ea31b3bbc83443eac37cd34a9e1e/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b", size = 1372257, upload-time = "2026-01-01T17:32:11.026Z" }, - { url = "https://files.pythonhosted.org/packages/4d/54/c9ea116412788629b1347e415f72195c25eb2f3809b2d3e7b25f5c79f13a/pynacl-1.6.2-cp314-cp314t-win32.whl", hash = "sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145", size = 231319, upload-time = "2026-01-01T17:32:12.46Z" }, - { url = "https://files.pythonhosted.org/packages/ce/04/64e9d76646abac2dccf904fccba352a86e7d172647557f35b9fe2a5ee4a1/pynacl-1.6.2-cp314-cp314t-win_amd64.whl", hash = "sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590", size = 244044, upload-time = "2026-01-01T17:32:13.781Z" }, - { url = "https://files.pythonhosted.org/packages/33/33/7873dc161c6a06f43cda13dec67b6fe152cb2f982581151956fa5e5cdb47/pynacl-1.6.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2", size = 188740, upload-time = "2026-01-01T17:32:15.083Z" }, - { url = "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", size = 388458, upload-time = "2026-01-01T17:32:16.829Z" }, - { url = "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", size = 800020, upload-time = "2026-01-01T17:32:18.34Z" }, - { url = "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", size = 1399174, upload-time = "2026-01-01T17:32:20.239Z" }, - { url = "https://files.pythonhosted.org/packages/68/f7/322f2f9915c4ef27d140101dd0ed26b479f7e6f5f183590fd32dfc48c4d3/pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87", size = 835085, upload-time = "2026-01-01T17:32:22.24Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d0/f301f83ac8dbe53442c5a43f6a39016f94f754d7a9815a875b65e218a307/pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c", size = 1437614, upload-time = "2026-01-01T17:32:23.766Z" }, - { url = "https://files.pythonhosted.org/packages/c4/58/fc6e649762b029315325ace1a8c6be66125e42f67416d3dbd47b69563d61/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130", size = 818251, upload-time = "2026-01-01T17:32:25.69Z" }, - { url = "https://files.pythonhosted.org/packages/c9/a8/b917096b1accc9acd878819a49d3d84875731a41eb665f6ebc826b1af99e/pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6", size = 1402859, upload-time = "2026-01-01T17:32:27.215Z" }, - { url = "https://files.pythonhosted.org/packages/85/42/fe60b5f4473e12c72f977548e4028156f4d340b884c635ec6b063fe7e9a5/pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e", size = 791926, upload-time = "2026-01-01T17:32:29.314Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f9/e40e318c604259301cc091a2a63f237d9e7b424c4851cafaea4ea7c4834e/pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577", size = 1363101, upload-time = "2026-01-01T17:32:31.263Z" }, - { url = "https://files.pythonhosted.org/packages/48/47/e761c254f410c023a469284a9bc210933e18588ca87706ae93002c05114c/pynacl-1.6.2-cp38-abi3-win32.whl", hash = "sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa", size = 227421, upload-time = "2026-01-01T17:32:33.076Z" }, - { url = "https://files.pythonhosted.org/packages/41/ad/334600e8cacc7d86587fe5f565480fde569dfb487389c8e1be56ac21d8ac/pynacl-1.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0", size = 239754, upload-time = "2026-01-01T17:32:34.557Z" }, - { url = "https://files.pythonhosted.org/packages/29/7d/5945b5af29534641820d3bd7b00962abbbdfee84ec7e19f0d5b3175f9a31/pynacl-1.6.2-cp38-abi3-win_arm64.whl", hash = "sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c", size = 184801, upload-time = "2026-01-01T17:32:36.309Z" }, -] - -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi-proxy.cloud.databricks.com/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -]