Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add allow_realtime and window config to subscriptions

Add allow_realtime (boolean) and window (days, time_start, time_end, tz)
fields to subscription schema on both client and server sides, with
migration to persist them. These fields flow through subscriptions:sync
from garden to server.

SOW-4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+88 -4
+35 -3
apps/sower/lib/sower/orchestration/subscription.ex
··· 26 26 field :timezone, :string 27 27 field :activation_args, {:array, :string}, default: [] 28 28 field :reboot_policy, :string, default: "never" 29 + field :allow_realtime, :boolean, default: false 29 30 embeds_many :rules, __MODULE__.Rule 31 + embeds_one :window, __MODULE__.Window 30 32 31 33 timestamps(type: :utc_datetime) 32 34 end ··· 42 44 :schedule, 43 45 :timezone, 44 46 :activation_args, 45 - :reboot_policy 47 + :reboot_policy, 48 + :allow_realtime 46 49 ]) 47 50 |> cast_embed(:rules, with: &__MODULE__.Rule.changeset/2) 51 + |> cast_embed(:window, with: &__MODULE__.Window.changeset/2) 48 52 |> unique_constraint([:garden_id, :org_id, :name]) 49 53 end 50 54 ··· 158 162 :schedule, 159 163 :timezone, 160 164 :activation_args, 161 - :reboot_policy 165 + :reboot_policy, 166 + :allow_realtime, 167 + :window 162 168 ]}, 163 169 conflict_target: [:garden_id, :org_id, :name], 164 170 returning: true ··· 180 186 schedule: sub.schedule, 181 187 timezone: sub.timezone, 182 188 activation_args: sub.activation_args, 183 - reboot_policy: sub.reboot_policy 189 + reboot_policy: sub.reboot_policy, 190 + allow_realtime: sub.allow_realtime, 191 + window: sub.window 184 192 }) do 185 193 {:ok, subscription} -> 186 194 {:ok, SowerClient.Orchestration.Subscription.cast!(subscription)} ··· 351 359 rule 352 360 |> cast(attrs, [:key, :op, :value]) 353 361 |> validate_required([:key, :op, :value]) 362 + end 363 + end 364 + 365 + defmodule Window do 366 + use Ecto.Schema 367 + import Ecto.Changeset 368 + 369 + @derive {Jason.Encoder, only: [:days, :time_start, :time_end, :tz]} 370 + 371 + embedded_schema do 372 + field :days, {:array, :string} 373 + field :time_start, :string 374 + field :time_end, :string 375 + field :tz, :string 376 + end 377 + 378 + def changeset(window, attrs) when is_struct(attrs) do 379 + changeset(window, Map.from_struct(attrs)) 380 + end 381 + 382 + def changeset(window, attrs) do 383 + window 384 + |> cast(attrs, [:days, :time_start, :time_end, :tz]) 385 + |> validate_required([:days, :time_start, :time_end, :tz]) 354 386 end 355 387 end 356 388 end
+10
apps/sower/priv/repo/migrations/20260331120000_add_realtime_and_window_to_subscriptions.exs
··· 1 + defmodule Sower.Repo.Migrations.AddRealtimeAndWindowToSubscriptions do 2 + use Ecto.Migration 3 + 4 + def change do 5 + alter table(:subscriptions) do 6 + add :allow_realtime, :boolean, default: false 7 + add :window, :map 8 + end 9 + end 10 + end
+1
apps/sower_client/lib/sower_client.ex
··· 39 39 SowerClient.Orchestration.SeedDeploymentResult, 40 40 SowerClient.Orchestration.SeedDeploymentStatus, 41 41 SowerClient.Orchestration.Subscription, 42 + SowerClient.Orchestration.Subscription.Window, 42 43 SowerClient.Orchestration.SubscriptionSync, 43 44 SowerClient.Storage.PresignedUploadReply, 44 45 SowerClient.Storage.DeploymentLogUploadRequest,
+7 -1
apps/sower_client/lib/sower_client/orchestration/subscription.ex
··· 64 64 description: "Whether deployment can trigger automated reboots", 65 65 enum: ["never", "when-required", "always"], 66 66 default: "never" 67 - } 67 + }, 68 + allow_realtime: %Schema{ 69 + type: :boolean, 70 + description: "Whether to deploy immediately when a matching seed is registered", 71 + default: false 72 + }, 73 + window: __MODULE__.Window 68 74 }, 69 75 required: [:seed_name, :seed_type], 70 76 example: %{
+35
apps/sower_client/lib/sower_client/orchestration/subscription/window.ex
··· 1 + defmodule SowerClient.Orchestration.Subscription.Window do 2 + use SowerClient.Schema 3 + 4 + OpenApiSpex.schema(%{ 5 + title: "SubscriptionWindow", 6 + type: :object, 7 + nullable: true, 8 + properties: %{ 9 + days: %Schema{ 10 + type: :array, 11 + items: %Schema{ 12 + type: :string, 13 + enum: ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] 14 + }, 15 + description: "Days of the week when deployments are allowed" 16 + }, 17 + time_start: %Schema{ 18 + type: :string, 19 + description: "Start of deployment window (HH:MM)", 20 + example: "09:00" 21 + }, 22 + time_end: %Schema{ 23 + type: :string, 24 + description: "End of deployment window (HH:MM)", 25 + example: "17:00" 26 + }, 27 + tz: %Schema{ 28 + type: :string, 29 + description: "IANA timezone for window evaluation", 30 + example: "America/New_York" 31 + } 32 + }, 33 + required: [:days, :time_start, :time_end, :tz] 34 + }) 35 + end