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" },
-]