Deployment and lifecycle management for Nix
0
fork

Configure Feed

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

server: improve dev setup and build out some more cross resource UIs

sorry for the lazy commit

+114 -75
+18 -17
apps/sower/lib/sower/config.ex
··· 107 107 "minimum" => 80, 108 108 "maximum" => 65535 109 109 }, 110 - "organization" => %{ 111 - "type" => "object", 112 - "properties" => %{ 113 - "mode" => %{ 114 - "type" => "string", 115 - "enum" => ["single", "multi"], 116 - "default" => "single", 117 - "description" => 118 - "Whether to run in single or multiple organization mode. Will create all new resources in a default organization if set to single." 119 - }, 120 - "name" => %{ 121 - "type" => "string", 122 - "default" => "default organization", 123 - "description" => "Name of the default organization in single org mode" 124 - } 125 - } 126 - }, 110 + # this isn't actually supported yet 111 + # "organization" => %{ 112 + # "type" => "object", 113 + # "properties" => %{ 114 + # "mode" => %{ 115 + # "type" => "string", 116 + # "enum" => ["single", "multi"], 117 + # "default" => "single", 118 + # "description" => 119 + # "Whether to run in single or multiple organization mode. Will create all new resources in a default organization if set to single." 120 + # }, 121 + # "name" => %{ 122 + # "type" => "string", 123 + # "default" => "default organization", 124 + # "description" => "Name of the default organization in single org mode" 125 + # } 126 + # } 127 + # }, 127 128 "public_url" => %{ 128 129 "type" => "string", 129 130 "format" => "uri"
+9
apps/sower/lib/sower/orchestration.ex
··· 171 171 alias Sower.Orchestration.Subscription 172 172 173 173 @doc """ 174 + Find deployments for an agent 175 + """ 176 + def deployments_for_agent(%Agent{} = agent) do 177 + agent.subscriptions 178 + |> Enum.map(& &1.deployments) 179 + |> dbg() 180 + end 181 + 182 + @doc """ 174 183 Returns the list of subscriptions. 175 184 176 185 ## Examples
+30 -2
apps/sower/lib/sower/repo/seeds/preseed.ex
··· 18 18 end 19 19 20 20 def for_dev(email) do 21 - name = email |> String.split("@") |> List.first() 21 + Application.load(:sower) 22 22 23 - simple_org_and_key(%Org{name: name, email: email}, Path.absname(".dev-api-token")) 23 + token_file = Path.absname(".dev-api-token") 24 + 25 + Ecto.Migrator.with_repo(Sower.Repo, fn _repo -> 26 + case Sower.Repo.all_by(Sower.Accounts.User, [email: email], skip_org_id: true) do 27 + [] -> 28 + Logger.error(msg: "User for email not found. Did you log in first?") 29 + Kernel.exit(1) 30 + 31 + [user] -> 32 + case Sower.Accounts.Organization.list() do 33 + [_org] -> 34 + access_token = 35 + Org.access_token(user, "dev token", %{ 36 + "expires_at" => Date.add(Date.utc_today(), 30) 37 + }) 38 + 39 + File.write!(token_file, access_token.token) 40 + Logger.info("Wrote #{token_file}") 41 + 42 + orgs -> 43 + Logger.error( 44 + msg: "Can't handle no organizations or more than one organization.", 45 + organizations: orgs 46 + ) 47 + 48 + Kernel.exit(1) 49 + end 50 + end 51 + end) 24 52 end 25 53 26 54 defp simple_org_and_key(%Org{} = org, token_file) do
+2 -1
apps/sower/lib/sower/seed.ex
··· 66 66 end 67 67 68 68 def list() do 69 - Repo.all(Seed) 69 + query = from s in Seed, order_by: [desc: s.updated_at] 70 + Repo.all(query) 70 71 end 71 72 72 73 def latest(name, seed_type) do
+4 -2
apps/sower/lib/sower_web/live/agent_live/show.ex
··· 16 16 17 17 @impl true 18 18 def handle_params(%{"sid" => sid}, _, socket) do 19 + orchestration = 20 + Orchestration.get_agent_sid!(sid) |> Sower.Repo.preload(subscriptions: [:deployments]) 21 + 19 22 socket = 20 23 socket 21 24 |> assign(:page_title, page_title(socket.assigns.live_action)) 22 - |> assign(:agent, Orchestration.get_agent_sid!(sid) |> Sower.Repo.preload(:subscriptions)) 25 + |> assign( :agent, orchestration) 23 26 |> add_online_status() 24 27 |> assign(:current_generation, %{}) 25 28 ··· 39 42 %Nix.Profile.Generation{} = generation, 40 43 socket 41 44 ) do 42 - dbg(generation) 43 45 {:noreply, assign(socket, :current_generation, generation)} 44 46 end 45 47
+11
apps/sower/lib/sower_web/live/agent_live/show.html.heex
··· 28 28 <:col :let={subscription}>{subscription.updated_at}</:col> 29 29 </.table> 30 30 </:item> 31 + <:item title="deployments"> 32 + <.table 33 + id="subscriptions" 34 + rows={@agent.subscriptions} 35 + row_click={fn subscription -> JS.navigate(~p"/subscriptions/#{subscription.sid}") end} 36 + > 37 + <:col :let={subscription}>{subscription.seed_name}</:col> 38 + <:col :let={subscription}>{subscription.seed_type}</:col> 39 + <:col :let={subscription}>{subscription.updated_at}</:col> 40 + </.table> 41 + </:item> 31 42 </.list> 32 43 33 44 <.back navigate={~p"/agents"}>Back to agents</.back>
+1 -19
apps/sower/lib/sower_web/live/deployment_live/index.ex
··· 19 19 > 20 20 <:col :let={{_id, deployment}} label="sid">{deployment.sid}</:col> 21 21 <:col :let={{_id, deployment}} label="result">{deployment.result}</:col> 22 - <:col :let={{_id, deployment}} label="initiated">{deployment.inserted_at}</:col> 23 - <:col :let={{_id, deployment}} label="done">{deployment.deployed_at}</:col> 22 + <:col :let={{_id, deployment}} label="completed">{deployment.deployed_at}</:col> 24 23 <:action :let={{_id, deployment}}> 25 24 <div class="sr-only"> 26 25 <.link navigate={~p"/deployments/#{deployment}"}>Show</.link> 27 26 </div> 28 - <.link navigate={~p"/deployments/#{deployment}/edit"}>Edit</.link> 29 - </:action> 30 - <:action :let={{id, deployment}}> 31 - <.link 32 - phx-click={JS.push("delete", value: %{id: deployment.id}) |> hide("##{id}")} 33 - data-confirm="Are you sure?" 34 - > 35 - Delete 36 - </.link> 37 27 </:action> 38 28 </.table> 39 29 </Layouts.app> ··· 46 36 socket 47 37 |> assign(:page_title, "Listing Deployments") 48 38 |> stream(:deployments, Orchestration.list_deployments())} 49 - end 50 - 51 - @impl true 52 - def handle_event("delete", %{"id" => id}, socket) do 53 - deployment = Orchestration.get_deployment!(id) 54 - {:ok, _} = Orchestration.delete_deployment(deployment) 55 - 56 - {:noreply, stream_delete(socket, :deployments, deployment)} 57 39 end 58 40 end
+1 -1
apps/sower/lib/sower_web/live/subscription_live/show.ex
··· 15 15 |> assign(:page_title, page_title(socket.assigns.live_action)) 16 16 |> assign( 17 17 :subscription, 18 - Orchestration.get_subscription_sid!(sid) |> Sower.Repo.preload([:agent]) 18 + Orchestration.get_subscription_sid!(sid) |> Sower.Repo.preload([:agent, :deployments]) 19 19 )} 20 20 end 21 21
+9
apps/sower/lib/sower_web/live/subscription_live/show.html.heex
··· 17 17 </:item> 18 18 <:item title="Seed Name">{@subscription.seed_name}</:item> 19 19 <:item title="Seed Type">{@subscription.seed_type}</:item> 20 + <:item title="Deployments"> 21 + <.table 22 + id="deployments" 23 + rows={@subscription.deployments} 24 + row_click={fn deployment -> JS.navigate(~p"/deployments/#{deployment.sid}") end} 25 + > 26 + <:col :let={deployment}>{deployment.sid}</:col> 27 + </.table> 28 + </:item> 20 29 </.list> 21 30 22 31 <.back navigate={~p"/subscriptions"}>Back to subscriptions</.back>
-2
apps/sower/lib/sower_web/router.ex
··· 48 48 live "/agents/:sid/show/edit", AgentLive.Show, :edit 49 49 50 50 live "/deployments", DeploymentLive.Index, :index 51 - live "/deployments/new", DeploymentLive.Form, :new 52 51 live "/deployments/:sid", DeploymentLive.Show, :show 53 - live "/deployments/:sid/edit", DeploymentLive.Form, :edit 54 52 55 53 get "/forges/:sid/login", Forge.OauthController, :login 56 54 get "/forges/oauth/callback", Forge.OauthController, :callback
+8 -6
apps/sower/priv/repo/seeds.exs
··· 10 10 # We recommend using the bang functions (`insert!`, `update!` 11 11 # and so on) as they will fail if something goes wrong. 12 12 13 - alias Sower.Repo.Seeds.Org 14 - 15 - user = Org.new_org_and_user(%Org{name: "default organization", email: "default@sower.dev"}) 16 - 17 - user |> Org.access_token() 18 - user |> Org.fake_seeds() 13 + # if you want fake environment 14 + # alias Sower.Repo.Seeds.Org 15 + # 16 + # user = Org.new_org_and_user(%Org{name: "default organization", email: "default@sower.dev"}) 17 + # 18 + # user |> Org.access_token() 19 + # 20 + # user |> Org.fake_seeds()
+6 -2
config/config.exs
··· 8 8 import Config 9 9 10 10 config :sower, ecto_repos: [Sower.Repo] 11 - config :sower, Sower.Repo, migration_primary_key: [type: :identity] 11 + 12 + config :sower, Sower.Repo, 13 + migration_primary_key: [type: :identity], 14 + # store with usec mostly to avoid having to truncate utc_now() 15 + migration_timestamps: [type: :utc_datetime_usec] 12 16 13 17 # Configures the endpoint 14 18 config :sower, SowerWeb.Endpoint, ··· 58 62 59 63 config :sower, :generators, 60 64 migration: true, 61 - timestamp_type: :utc_datetime 65 + timestamp_type: :utc_datetime_usec 62 66 63 67 # Import environment specific config. This must remain at the bottom 64 68 # of this file so it overrides the configuration defined above.
-1
config/dev.exs
··· 3 3 config :sower, 4 4 dev_routes: true 5 5 6 - # Configure your database 7 6 config :sower, Sower.Repo, 8 7 show_sensitive_data_on_connection_error: true, 9 8 pool_size: 10
-17
config/prod.exs
··· 17 17 format: "$metadata[$level] $message\n", 18 18 metadata: [:request_id, :mfa] 19 19 20 - config :libcluster, 21 - topologies: [ 22 - consul: [ 23 - strategy: Cluster.Strategy.Consul, 24 - config: [ 25 - base_url: "http://localhost:8500", 26 - 27 - # The Consul endpoints used to fetch service nodes. 28 - list_using: [ 29 - Cluster.Strategy.Consul.Agent, 30 - {Cluster.Strategy.Consul.Health, [passing: true]} 31 - ], 32 - service: [name: "sower"] 33 - ] 34 - ] 35 - ] 36 - 37 20 # Runtime production configuration, including reading 38 21 # of environment variables, is done on config/runtime.exs.
+15 -5
justfile
··· 21 21 check-nix: 22 22 nix build .#checks.x86_64-linux.default --print-build-logs 23 23 24 - dev: && start 25 - mix deps.get 26 - mix deps.compile 27 - mix ecto.setup 28 - mix assets.build 24 + clean: 25 + mix ecto.drop 26 + git clean -dnx --exclude .dev\* --exclude .jj --exclude .secret.envrc --exclude dev-\* 27 + # git clean won't purge the deps directories 28 + rm -rf deps 29 29 30 30 dev-add-user email: 31 31 mix run apps/sower/priv/repo/seeds-user.exs {{ email }} --no-start ··· 57 57 openapi-generate: openapi-output 58 58 go generate ./client-go 59 59 60 + reset: clean setup 61 + 60 62 set-version: && openapi-generate 61 63 @echo "Current version: $(cat VERSION)" 62 64 @read -p "New version? " new_version; [ -n "$new_version" ] && echo -n $new_version > VERSION 65 + 66 + setup: 67 + mix deps.get 68 + mix deps.compile 69 + mix ecto.setup 70 + mix assets.build 71 + # just dev-add-user <email> 72 + # just dev-seed-from-local 63 73 64 74 release: set-version 65 75 git add VERSION openapi.json