Deployment and lifecycle management for Nix
0
fork

Configure Feed

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

server: store_path -> artifact

+72 -112
+1 -1
apps/sower/lib/sower/accounts.ex
··· 35 35 email: email 36 36 } 37 37 ) do 38 - case Repo.get_by(User, [oidc_id: oidc_id], skip_org_id: true) do 38 + case Repo.get_by(User, [oidc_id: oidc_id], skip_org_id: true) |> dbg() do 39 39 nil -> 40 40 {:ok, organization} = find_or_create_org_default(name) 41 41
-8
apps/sower/lib/sower/accounts/user.ex
··· 22 22 timestamps() 23 23 end 24 24 25 - def get_by_email(email) do 26 - Repo.get_by(User, [email: email], skip_org_id: true) 27 - end 28 - 29 - def get_by_email!(email) do 30 - Repo.get_by!(User, [email: email], skip_org_id: true) 31 - end 32 - 33 25 def get_by_id!(id) do 34 26 Repo.get!(User, id, skip_org_id: true) 35 27 end
+3 -3
apps/sower/lib/sower/orchestration.ex
··· 414 414 %SowerClient.Schemas.Orchestration.UpgradeRequest{} = upgrade 415 415 ) do 416 416 with subs when subs != [] <- get_subscription_sids(upgrade.subscription_sids), 417 - seed_store_paths <- 418 - subs |> Enum.map(&Sower.Seed.latest_store_path(&1.seed)) |> Enum.map(& &1.store_path), 417 + seed_artifacts <- 418 + subs |> Enum.map(&Sower.Seed.latest_artifact(&1.seed)) |> Enum.map(& &1.artifact), 419 419 {:ok, deploy} <- 420 420 create_deployment(%{ 421 - store_paths: seed_store_paths, 421 + artifacts: seed_artifacts, 422 422 subscriptions: subs 423 423 }) do 424 424 {:ok,
+21 -16
apps/sower/lib/sower/repo/seeds/org.ex
··· 1 1 defmodule Sower.Repo.Seeds.Org do 2 + require Logger 3 + 2 4 @enforce_keys [:name] 3 5 defstruct [:name, :email] 4 6 ··· 11 13 end 12 14 13 15 {:ok, user} = 14 - case Sower.Accounts.User.get_by_email(org_seed.email) do 15 - nil -> 16 + case Sower.Repo.all_by(Sower.Accounts.User, [email: org_seed.email], skip_org_id: true) do 17 + [] -> 16 18 {:ok, org} = Sower.Accounts.Organization.create(%{name: org_seed.name}) 17 19 Sower.Repo.put_org_id(org.org_id) 18 20 ··· 23 25 oidc_id: Ecto.UUID.generate() 24 26 }) 25 27 26 - user -> 28 + [user] -> 27 29 {:ok, user} 30 + 31 + other -> 32 + Logger.error(msg: "Too many matching users", other: other) 33 + Kernel.exit(1) 28 34 end 29 35 30 36 user ··· 60 66 |> Enum.map(fn t -> 61 67 name = ~s"test#{t}" 62 68 63 - {:ok, seed} = 64 - case Sower.Seed.get(name, "nixos") do 65 - nil -> 66 - Sower.Seed.create(%{ 67 - name: name, 68 - seed_type: "nixos", 69 - org_id: user.org_id, 70 - store_path: 71 - ~s"/nix/store/#{Cuid2Ex.create(length: 32) |> String.downcase()}-nixos-system-#{name}-24.11.20240703.9f4128e" 72 - }) 69 + case Sower.Seed.get(name, "nixos") do 70 + nil -> 71 + Sower.Seed.create(%{ 72 + name: name, 73 + seed_type: "nixos", 74 + org_id: user.org_id, 75 + artifact: 76 + ~s"/nix/store/#{Cuid2Ex.create(length: 32) |> String.downcase()}-nixos-system-#{name}-24.11.20240703.9f4128e" 77 + }) 73 78 74 - seed -> 75 - {:ok, seed} 76 - end 79 + seed -> 80 + {:ok, seed} 81 + end 77 82 end) 78 83 end 79 84 end
+8 -15
apps/sower/lib/sower/seed.ex
··· 4 4 import Ecto.Changeset 5 5 import Ecto.Query, only: [from: 2] 6 6 7 - alias Sower.{Nix, Repo, Seed} 7 + alias Sower.{Repo, Seed} 8 8 9 9 @derive {Jason.Encoder, only: [:sid, :name, :seed_type]} 10 10 ··· 18 18 19 19 field :name, :string 20 20 field :seed_type, :string 21 - field :store_path, :string 21 + field :artifact, :string 22 22 23 23 timestamps() 24 24 end ··· 30 30 |> changeset(attrs) 31 31 |> Repo.insert( 32 32 on_conflict: {:replace, [:updated_at]}, 33 - conflict_target: [:name, :seed_type, :store_path, :org_id], 33 + conflict_target: [:name, :seed_type, :artifact, :org_id], 34 34 returning: true 35 35 ) 36 36 end ··· 73 73 ) 74 74 end 75 75 76 - def latest_store_path(%__MODULE__{id: id}) do 76 + def latest_artifact(%__MODULE__{id: id}) do 77 77 Repo.one( 78 78 from s in Seed, 79 79 where: s.id == ^id, ··· 81 81 ) 82 82 end 83 83 84 - def latest_store_path_by_sid(sid) do 84 + def latest_artifact_by_sid(sid) do 85 85 Repo.one( 86 86 from s in Seed, 87 87 where: s.sid == ^sid, ··· 91 91 92 92 defp changeset(seed, attrs) do 93 93 seed 94 - |> cast(attrs, [:name, :seed_type, :org_id, :store_path]) 94 + |> cast(attrs, [:name, :seed_type, :org_id, :artifact]) 95 95 |> validate_inclusion(:seed_type, @seed_types) 96 - |> validate_required([:name, :seed_type, :org_id, :store_path]) 97 - |> unique_constraint([:name, :seed_type, :org_id, :store_path], error_key: :unique_seed) 98 - end 99 - 100 - defp updated_at_now(seed) do 101 - seed 102 - |> change() 103 - |> put_change(:updated_at, NaiveDateTime.local_now()) 104 - |> Repo.update() 96 + |> validate_required([:name, :seed_type, :org_id, :artifact]) 97 + |> unique_constraint([:name, :seed_type, :org_id, :artifact], error_key: :unique_seed) 105 98 end 106 99 end
+2 -2
apps/sower/lib/sower_web/agent_channel.ex
··· 87 87 # def handle_in("agent:current_generation", payload, socket) do 88 88 # payload = Nix.Profile.Generation.cast!(payload) 89 89 # 90 - # store_path = Sower.Nix.submit_store_path!(payload.path) 90 + # artifact = Sower.Nix.submit_artifact!(payload.path) 91 91 # 92 92 # Sower.Orchestration.create_deployment(%{ 93 93 # deployed_at: payload.created, 94 - # store_paths: [store_path] 94 + # artifacts: [artifact] 95 95 # }) 96 96 # 97 97 # Phoenix.PubSub.broadcast(Sower.PubSub, "agent:view:#{socket.assigns.agent.sid}", payload)
+2 -2
apps/sower/lib/sower_web/controllers/api/seed_controller.ex
··· 33 33 body_params: %Schemas.Seed{ 34 34 name: name, 35 35 seed_type: seed_type, 36 - store_path: store_path 36 + artifact: artifact 37 37 } 38 38 } = conn, 39 39 _params ··· 42 42 43 43 if can(conn.assigns.access_token) 44 44 |> create?(%Sower.Seed{org_id: conn.assigns.access_token.org_id}) do 45 - case Sower.Seed.create(%{name: name, seed_type: seed_type, store_path: store_path}) do 45 + case Sower.Seed.create(%{name: name, seed_type: seed_type, artifact: artifact}) do 46 46 {:ok, %Sower.Seed{} = seed} -> 47 47 conn 48 48 |> put_status(:created)
+2 -2
apps/sower/lib/sower_web/controllers/api/seed_json.ex
··· 13 13 seed 14 14 end 15 15 16 - def show(%{store_path: store_path}) do 17 - store_path 16 + def show(%{artifact: artifact}) do 17 + artifact 18 18 end 19 19 20 20 def not_found(_) do
+3 -1
apps/sower/lib/sower_web/controllers/auth_controller.ex
··· 11 11 |> SowerWeb.UserAuth.log_in_user(user) 12 12 13 13 {:error, reason} -> 14 + Logger.error(msg: "Failed to authenticate", reason: reason) 15 + 14 16 conn 15 - |> put_flash(:error, reason) 17 + |> put_flash(:error, "Failed to authenticate") 16 18 |> redirect(to: "/") 17 19 end 18 20 end
+1 -3
apps/sower/lib/sower_web/live/agent_live/show.html.heex
··· 15 15 <:item title="Local sid">{@agent.local_sid}</:item> 16 16 <:item title="Current Generation"> 17 17 <%= if Map.has_key?(@current_generation, :path) do %> 18 - <.link patch={~p"/nix/store_paths/#{Nix.narhash_from_path(@current_generation.path)}"}> 19 - {@current_generation.path} 20 - </.link> 18 + {@current_generation.path} 21 19 <% end %> 22 20 </:item> 23 21 <:item title="subscriptions">
+1 -1
apps/sower/lib/sower_web/live/seed_live/show.ex
··· 8 8 9 9 @impl true 10 10 def handle_params(%{"sid" => sid}, _, socket) do 11 - seed = Sower.Seed.get_sid!(sid) |> Sower.Repo.preload(:store_paths) 11 + seed = Sower.Seed.get_sid!(sid) 12 12 13 13 {:noreply, 14 14 socket
+2 -10
apps/sower/lib/sower_web/live/seed_live/show.html.heex
··· 4 4 </.header> 5 5 6 6 <.list> 7 - <:item title="store_paths"> 8 - <.table 9 - id="store_paths" 10 - rows={@seed.store_paths} 11 - row_click={fn store_path -> JS.navigate(~p"/nix/store_paths/#{store_path}") end} 12 - > 13 - <:col :let={store_path}>{store_path.path}</:col> 14 - <:col :let={store_path}>{store_path.updated_at}</:col> 15 - </.table> 16 - </:item> 7 + <:item title="seed type">{@seed.seed_type}</:item> 8 + <:item title="artifact">{@seed.artifact}</:item> 17 9 <:item title="created">{@seed.inserted_at}</:item> 18 10 <:item title="updated">{@seed.updated_at}</:item> 19 11 </.list>
+2 -2
apps/sower/priv/repo/migrations/20240803030149_create_seeds.exs
··· 8 8 9 9 add :name, :string, null: false 10 10 add :seed_type, :string, null: false 11 - add :store_path, :string, null: false 11 + add :artifact, :string, null: false 12 12 13 13 timestamps() 14 14 end 15 15 16 16 create unique_index(:seeds, [:id, :org_id]) 17 - create unique_index(:seeds, [:name, :seed_type, :store_path, :org_id]) 17 + create unique_index(:seeds, [:name, :seed_type, :artifact, :org_id]) 18 18 create unique_index(:seeds, [:sid]) 19 19 end 20 20 end
+2 -2
apps/sower/priv/repo/migrations/20240831023317_create_users_auth_tables.exs
··· 28 28 create index(:users_tokens, [:user_id]) 29 29 30 30 create unique_index(:users, :sid) 31 - create unique_index(:users, :email) 32 - create unique_index(:users, :oidc_id) 31 + create unique_index(:users, [:email, :org_id]) 32 + create unique_index(:users, [:oidc_id, :org_id]) 33 33 create unique_index(:users_tokens, :sid) 34 34 create unique_index(:users_tokens, [:context, :token]) 35 35 end
-21
apps/sower/test/support/fixtures/nix_fixtures.ex
··· 18 18 19 19 cache 20 20 end 21 - 22 - @doc """ 23 - Generate a store_path. 24 - """ 25 - def store_path_fixture(attrs \\ %{}) do 26 - {:ok, store_path} = 27 - attrs 28 - |> Enum.into(%{ 29 - path: random_store_path() 30 - }) 31 - |> Sower.Nix.create_store_path() 32 - 33 - store_path 34 - end 35 - 36 - def random_store_path(name \\ "apath-0.1") do 37 - digest = 38 - for _ <- 1..32, into: "", do: <<Enum.random(~c"0123456789abcdefghijklmnopqrstuvwxyz")>> 39 - 40 - "/nix/store/#{digest}-#{name}" 41 - end 42 21 end
+4 -5
apps/sower/test/support/fixtures/seed_fixtures.ex
··· 6 6 7 7 def unique_seed_name, do: "seed#{System.unique_integer()}" 8 8 9 - def random_store_path do 9 + def random_nix_artifact do 10 10 "/nix/store/#{:crypto.strong_rand_bytes(32) |> Base.encode16() |> String.slice(0..31) |> String.downcase()}-something" 11 11 end 12 12 ··· 14 14 Enum.into(attrs, %{ 15 15 name: unique_seed_name(), 16 16 seed_type: "nixos", 17 - store_path: random_store_path() 17 + artifact: random_nix_artifact() 18 18 }) 19 19 end 20 20 ··· 27 27 seed 28 28 end 29 29 30 - def store_path_fixture(attrs \\ %{}) do 30 + def artifact_fixture(attrs \\ %{}) do 31 31 attrs 32 - |> Enum.into(%{path: random_store_path()}) 33 - |> Sower.Nix.submit_store_path!() 32 + |> Enum.into(%{path: random_nix_artifact()}) 34 33 end 35 34 end
+3 -3
apps/sower_client/lib/schemas/seed.ex
··· 22 22 description: "Type of the seed", 23 23 enum: @seed_types 24 24 }, 25 - store_path: %Schema{ 25 + artifact: %Schema{ 26 26 type: :string, 27 27 description: "Store path of the seed" 28 28 } 29 29 }, 30 - required: ~w(name seed_type store_path)a, 30 + required: ~w(name seed_type artifact)a, 31 31 example: %{ 32 32 "id" => "example4ser3adju75ddusbr", 33 33 "name" => "myhost", 34 34 "seed_type" => "nixos", 35 - "store_path" => "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-nixos" 35 + "artifact" => "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-nixos" 36 36 } 37 37 }) 38 38
+1 -1
bin/seed-ci
··· 125 125 $configs | each { |c| 126 126 # simplify for output 127 127 $c.targets | filter { |t| $t.build != null } | each { |t| 128 - { seed_type: $c.seed_type, name: $t.name, store_path: $t.build.outputs.out.0 } 128 + { seed_type: $c.seed_type, name: $t.name, artifact: $t.build.outputs.out.0 } 129 129 } 130 130 } | flatten 131 131 }
+4 -4
client-go/client.gen.go
··· 1 1 // Package client provides primitives to interact with the openapi HTTP API. 2 2 // 3 - // Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.4.1 DO NOT EDIT. 3 + // Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.5.0 DO NOT EDIT. 4 4 package client 5 5 6 6 import ( ··· 42 42 43 43 // Seed A seed is an installable unit 44 44 type Seed struct { 45 + // Artifact Store path of the seed 46 + Artifact string `json:"artifact"` 47 + 45 48 // Name Name of the seed 46 49 Name string `json:"name"` 47 50 ··· 50 53 51 54 // Sid sid of the seed set by the server 52 55 Sid *string `json:"sid,omitempty"` 53 - 54 - // StorePath Store path of the seed 55 - StorePath string `json:"store_path"` 56 56 } 57 57 58 58 // SeedSeedType Type of the seed
+1 -1
mix.lock
··· 47 47 "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, 48 48 "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, 49 49 "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.0", "2791fac0e2776b640192308cc90c0dbcf67843ad51387ed4ecae2038263d708d", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b3a1fa036d7eb2f956774eda7a7638cf5123f8f2175aca6d6420a7f95e598e1c"}, 50 - "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.1", "dfb8b5d60bb581eeeff0152d3dbe5d4f10f66d80cac8b6d9ad731454de2e16a3", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c77e3a7af23f10de1eab0b9aa0c8b6ad6bee395517a69dbc8f0ac4bb625b64b9"}, 50 + "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.2", "af6f090e3dc7d5ff41de10aa1039e0543e8151f99afa44097a832bcb139790d8", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "54b2e4a31b8689a1604b3a2e0b1d54bb89e9476022c9ebbe585e9dd800674965"}, 51 51 "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, 52 52 "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, 53 53 "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"},
+9 -9
openapi.json
··· 42 42 "Seed": { 43 43 "description": "A seed is an installable unit", 44 44 "example": { 45 + "artifact": "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-nixos", 45 46 "id": "example4ser3adju75ddusbr", 46 47 "name": "myhost", 47 - "seed_type": "nixos", 48 - "store_path": "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-nixos" 48 + "seed_type": "nixos" 49 49 }, 50 50 "properties": { 51 + "artifact": { 52 + "description": "Store path of the seed", 53 + "type": "string", 54 + "x-struct": null, 55 + "x-validate": null 56 + }, 51 57 "name": { 52 58 "description": "Name of the seed", 53 59 "type": "string", ··· 72 78 "type": "string", 73 79 "x-struct": null, 74 80 "x-validate": null 75 - }, 76 - "store_path": { 77 - "description": "Store path of the seed", 78 - "type": "string", 79 - "x-struct": null, 80 - "x-validate": null 81 81 } 82 82 }, 83 83 "required": [ 84 84 "name", 85 85 "seed_type", 86 - "store_path" 86 + "artifact" 87 87 ], 88 88 "title": "Seed", 89 89 "type": "object",