Deployment and lifecycle management for Nix
0
fork

Configure Feed

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

finish converting to ash

+368 -67
+1 -5
config/dev.exs
··· 2 2 3 3 # Configure your database 4 4 config :sower, Sower.Repo, 5 - username: "postgres", 6 - password: "postgres", 7 - hostname: "localhost", 8 - database: "sower_dev", 9 - port: 5432, 5 + url: "ecto://postgres:postgres@localhost/sower_dev", 10 6 show_sensitive_data_on_connection_error: true, 11 7 pool_size: 10 12 8
+5 -5
dev.hurl
··· 2 2 Content-Type: application/json 3 3 4 4 { 5 - "name": "adamaa", 5 + "name": "yank", 6 6 "type": "nixos", 7 - "out_path": "/nix/store/6daslzwpm9fzr6sw11irw1jcnsmspmhm-source/ssdfdsdfs" 7 + "out_path": "/nix/store/6daslzwpm9fzr6sw11irw1jcnsmspmhy-source" 8 8 } 9 9 10 - GET http://[::1]:4000/api/seeds 11 - 12 - GET http://[::1]:4000/api/seeds/latest?name=yank2&type=nixos 10 + # GET http://[::1]:4000/api/seeds 11 + # 12 + # GET http://[::1]:4000/api/seeds/latest?name=yank2&type=nixos
+51 -26
lib/sower/seed.ex
··· 3 3 data_layer: AshPostgres.DataLayer, 4 4 domain: Sower 5 5 6 + @types [:nixos, :home_manager, :nix_darwin] 7 + @derive {Jason.Encoder, only: [:id, :name, :type, :out_path]} 8 + 6 9 actions do 7 10 defaults([:read, :create, :destroy]) 8 11 9 12 create :new do 10 - accept([:name, :type]) 13 + accept([:name, :type, :out_path]) 14 + upsert?(true) 15 + upsert_identity(:seed) 16 + upsert_fields(:updated_at) 17 + end 18 + 19 + read :by_id do 20 + argument :id, :uuid do 21 + allow_nil? false 22 + end 23 + 24 + # only return one 25 + get? true 26 + 27 + filter expr(id == ^arg(:id)) 28 + end 29 + 30 + read :latest do 31 + argument :name, :string do 32 + allow_nil? false 33 + end 34 + 35 + argument :type, :atom do 36 + allow_nil? false 37 + constraints one_of: @types 38 + end 39 + 40 + # only return one 41 + get? true 42 + 43 + prepare build(filter: expr(name == ^arg(:name)), limit: 1, sort: [updated_at: :desc]) 11 44 end 12 45 end 13 46 14 47 attributes do 15 48 uuid_primary_key(:id) 49 + create_timestamp(:inserted_at) 50 + update_timestamp(:updated_at) 16 51 17 52 attribute :name, :string do 18 53 allow_nil?(false) ··· 22 57 attribute :type, :atom do 23 58 allow_nil?(false) 24 59 public?(true) 25 - constraints(one_of: [:nixos, :home_manager, :nix_darwin]) 60 + constraints(one_of: @types) 61 + end 62 + 63 + attribute :out_path, :string do 64 + allow_nil?(false) 65 + public?(true) 66 + constraints match: ~r|/nix/store/[a-z0-9]{32}-[a-z0-9]+| 26 67 end 27 68 end 28 69 29 70 code_interface do 30 - define(:new, args: [:name, :type]) 71 + define(:by_id, args: [:id]) 72 + define(:new, args: [:name, :type, :out_path]) 73 + define(:latest, args: [:name, :type]) 74 + define(:read_all, action: :read) 75 + end 76 + 77 + identities do 78 + identity(:seed, [:name, :type, :out_path]) 31 79 end 32 80 33 81 postgres do 34 82 table("seeds") 35 83 repo(Sower.Repo) 36 84 end 37 - 38 - # relationships do 39 - # belongs_to :tree, Sower.Tree 40 - # end 41 85 end 42 - 43 - # def create_or_insert_seed(attrs \\ %{}) do 44 - # %Sower.Seed.Instance{} 45 - # |> Sower.Seed.Instance.changeset(attrs) 46 - # |> Repo.insert( 47 - # on_conflict: {:replace, [:updated_at]}, 48 - # conflict_target: [:name, :type, :out_path] 49 - # ) 50 - # end 51 - # 52 - # def find_latest_seed(name, type) do 53 - # Sower.Seed.Instance 54 - # |> where([s], s.name == ^name) 55 - # |> where([s], s.type == ^type) 56 - # |> order_by([s], desc: s.updated_at) 57 - # |> first() 58 - # |> Repo.all() 59 - # |> List.first() 60 - # end
-25
lib/sower/seed/instance.ex
··· 1 - defmodule Sower.Seed.Instance do 2 - use Ecto.Schema 3 - import Ecto.Changeset 4 - 5 - # json support 6 - @derive {Jason.Encoder, only: [:id, :name, :type, :out_path]} 7 - 8 - schema "seeds" do 9 - field :name, :string 10 - field :type, :string 11 - field :out_path, :string 12 - 13 - timestamps() 14 - end 15 - 16 - def changeset(instance, attrs) do 17 - instance 18 - |> cast(attrs, [:name, :type, :out_path]) 19 - |> validate_required([:name, :type, :out_path]) 20 - |> validate_inclusion(:type, ["nixos", "home-manager", "nix-darwin"]) 21 - |> validate_format(:out_path, ~r/\/nix\/store\/[a-z0-9]{32}-[a-z0-9]+/, 22 - message: "must be a valid nix store path" 23 - ) 24 - end 25 - end
+4 -4
lib/sower_web/controllers/seed_controller.ex
··· 3 3 4 4 action_fallback SowerWeb.FallbackController 5 5 6 - def new(conn, seed_params) do 7 - with {:ok, %Sower.Seed.Instance{} = seed} <- Sower.Seed.create_or_insert_seed(seed_params) do 6 + def new(conn, %{"name" => name, "type" => type, "out_path" => out_path}) do 7 + with {:ok, %Sower.Seed{} = seed} <- Sower.Seed.new(name, type, out_path) do 8 8 conn 9 9 |> put_status(:created) 10 10 |> render(:show, seed: seed) ··· 12 12 end 13 13 14 14 def find_latest(conn, %{"name" => name, "type" => type}) do 15 - seed = Sower.Seed.find_latest_seed(name, type) 15 + seed = Sower.Seed.latest(name, type) 16 16 render(conn, :show, seed: seed) 17 17 end 18 18 19 19 def list(conn, _) do 20 - seeds = Sower.Seed.list_seeds() 20 + seeds = Sower.Seed.read_all!() 21 21 render(conn, :list, seeds: seeds) 22 22 end 23 23 end
+1 -1
lib/sower_web/live/seed_live/index.ex
··· 3 3 4 4 @impl true 5 5 def mount(_params, _session, socket) do 6 - {:ok, stream(socket, :seeds, Sower.Seed.list_seeds())} 6 + {:ok, stream(socket, :seeds, Sower.Seed.read_all!())} 7 7 end 8 8 end
+1 -1
lib/sower_web/live/seed_live/show.ex
··· 11 11 {:noreply, 12 12 socket 13 13 |> assign(:page_title, page_title(socket.assigns.live_action)) 14 - |> assign(:seed, Sower.Seed.get_seed!(id))} 14 + |> assign(:seed, Sower.Seed.by_id!(id))} 15 15 end 16 16 17 17 defp page_title(:show), do: "Show Seed"
+28
priv/repo/migrations/20240409183914_add_seed_timestamps.exs
··· 1 + defmodule Sower.Repo.Migrations.AddSeedTimestamps do 2 + @moduledoc """ 3 + Updates resources based on their most recent snapshots. 4 + 5 + This file was autogenerated with `mix ash_postgres.generate_migrations` 6 + """ 7 + 8 + use Ecto.Migration 9 + 10 + def up do 11 + alter table(:seeds) do 12 + add :inserted_at, :utc_datetime_usec, 13 + null: false, 14 + default: fragment("(now() AT TIME ZONE 'utc')") 15 + 16 + add :updated_at, :utc_datetime_usec, 17 + null: false, 18 + default: fragment("(now() AT TIME ZONE 'utc')") 19 + end 20 + end 21 + 22 + def down do 23 + alter table(:seeds) do 24 + remove :updated_at 25 + remove :inserted_at 26 + end 27 + end 28 + end
+21
priv/repo/migrations/20240409184109_add_seed_out_path.exs
··· 1 + defmodule Sower.Repo.Migrations.AddSeedOutPath do 2 + @moduledoc """ 3 + Updates resources based on their most recent snapshots. 4 + 5 + This file was autogenerated with `mix ash_postgres.generate_migrations` 6 + """ 7 + 8 + use Ecto.Migration 9 + 10 + def up do 11 + alter table(:seeds) do 12 + add :out_path, :text, null: false 13 + end 14 + end 15 + 16 + def down do 17 + alter table(:seeds) do 18 + remove :out_path 19 + end 20 + end 21 + end
+17
priv/repo/migrations/20240409190921_add_seed_uniqueness.exs
··· 1 + defmodule Sower.Repo.Migrations.AddSeedUniqueness do 2 + @moduledoc """ 3 + Updates resources based on their most recent snapshots. 4 + 5 + This file was autogenerated with `mix ash_postgres.generate_migrations` 6 + """ 7 + 8 + use Ecto.Migration 9 + 10 + def up do 11 + create unique_index(:seeds, [:name, :type, :out_path], name: "seeds_seed_index") 12 + end 13 + 14 + def down do 15 + drop_if_exists unique_index(:seeds, [:name, :type, :out_path], name: "seeds_seed_index") 16 + end 17 + end
+69
priv/resource_snapshots/repo/seeds/20240409183914.json
··· 1 + { 2 + "attributes": [ 3 + { 4 + "allow_nil?": false, 5 + "default": "fragment(\"gen_random_uuid()\")", 6 + "generated?": false, 7 + "primary_key?": true, 8 + "references": null, 9 + "size": null, 10 + "source": "id", 11 + "type": "uuid" 12 + }, 13 + { 14 + "allow_nil?": false, 15 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 16 + "generated?": false, 17 + "primary_key?": false, 18 + "references": null, 19 + "size": null, 20 + "source": "inserted_at", 21 + "type": "utc_datetime_usec" 22 + }, 23 + { 24 + "allow_nil?": false, 25 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 26 + "generated?": false, 27 + "primary_key?": false, 28 + "references": null, 29 + "size": null, 30 + "source": "updated_at", 31 + "type": "utc_datetime_usec" 32 + }, 33 + { 34 + "allow_nil?": false, 35 + "default": "nil", 36 + "generated?": false, 37 + "primary_key?": false, 38 + "references": null, 39 + "size": null, 40 + "source": "name", 41 + "type": "text" 42 + }, 43 + { 44 + "allow_nil?": false, 45 + "default": "nil", 46 + "generated?": false, 47 + "primary_key?": false, 48 + "references": null, 49 + "size": null, 50 + "source": "type", 51 + "type": "text" 52 + } 53 + ], 54 + "base_filter": null, 55 + "check_constraints": [], 56 + "custom_indexes": [], 57 + "custom_statements": [], 58 + "has_create_action": true, 59 + "hash": "426283C6FFE5283066EB85DF0E3DF2F979F52AD8884631D468B46F38A55EC1BE", 60 + "identities": [], 61 + "multitenancy": { 62 + "attribute": null, 63 + "global": null, 64 + "strategy": null 65 + }, 66 + "repo": "Elixir.Sower.Repo", 67 + "schema": null, 68 + "table": "seeds" 69 + }
+79
priv/resource_snapshots/repo/seeds/20240409184109.json
··· 1 + { 2 + "attributes": [ 3 + { 4 + "allow_nil?": false, 5 + "default": "fragment(\"gen_random_uuid()\")", 6 + "generated?": false, 7 + "primary_key?": true, 8 + "references": null, 9 + "size": null, 10 + "source": "id", 11 + "type": "uuid" 12 + }, 13 + { 14 + "allow_nil?": false, 15 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 16 + "generated?": false, 17 + "primary_key?": false, 18 + "references": null, 19 + "size": null, 20 + "source": "inserted_at", 21 + "type": "utc_datetime_usec" 22 + }, 23 + { 24 + "allow_nil?": false, 25 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 26 + "generated?": false, 27 + "primary_key?": false, 28 + "references": null, 29 + "size": null, 30 + "source": "updated_at", 31 + "type": "utc_datetime_usec" 32 + }, 33 + { 34 + "allow_nil?": false, 35 + "default": "nil", 36 + "generated?": false, 37 + "primary_key?": false, 38 + "references": null, 39 + "size": null, 40 + "source": "name", 41 + "type": "text" 42 + }, 43 + { 44 + "allow_nil?": false, 45 + "default": "nil", 46 + "generated?": false, 47 + "primary_key?": false, 48 + "references": null, 49 + "size": null, 50 + "source": "type", 51 + "type": "text" 52 + }, 53 + { 54 + "allow_nil?": false, 55 + "default": "nil", 56 + "generated?": false, 57 + "primary_key?": false, 58 + "references": null, 59 + "size": null, 60 + "source": "out_path", 61 + "type": "text" 62 + } 63 + ], 64 + "base_filter": null, 65 + "check_constraints": [], 66 + "custom_indexes": [], 67 + "custom_statements": [], 68 + "has_create_action": true, 69 + "hash": "12FC65132E858EA81C580BC5886C55B153E1CA71C8F4CC8D50A6A16D54F1F640", 70 + "identities": [], 71 + "multitenancy": { 72 + "attribute": null, 73 + "global": null, 74 + "strategy": null 75 + }, 76 + "repo": "Elixir.Sower.Repo", 77 + "schema": null, 78 + "table": "seeds" 79 + }
+91
priv/resource_snapshots/repo/seeds/20240409190921.json
··· 1 + { 2 + "attributes": [ 3 + { 4 + "allow_nil?": false, 5 + "default": "fragment(\"gen_random_uuid()\")", 6 + "generated?": false, 7 + "primary_key?": true, 8 + "references": null, 9 + "size": null, 10 + "source": "id", 11 + "type": "uuid" 12 + }, 13 + { 14 + "allow_nil?": false, 15 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 16 + "generated?": false, 17 + "primary_key?": false, 18 + "references": null, 19 + "size": null, 20 + "source": "inserted_at", 21 + "type": "utc_datetime_usec" 22 + }, 23 + { 24 + "allow_nil?": false, 25 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 26 + "generated?": false, 27 + "primary_key?": false, 28 + "references": null, 29 + "size": null, 30 + "source": "updated_at", 31 + "type": "utc_datetime_usec" 32 + }, 33 + { 34 + "allow_nil?": false, 35 + "default": "nil", 36 + "generated?": false, 37 + "primary_key?": false, 38 + "references": null, 39 + "size": null, 40 + "source": "name", 41 + "type": "text" 42 + }, 43 + { 44 + "allow_nil?": false, 45 + "default": "nil", 46 + "generated?": false, 47 + "primary_key?": false, 48 + "references": null, 49 + "size": null, 50 + "source": "type", 51 + "type": "text" 52 + }, 53 + { 54 + "allow_nil?": false, 55 + "default": "nil", 56 + "generated?": false, 57 + "primary_key?": false, 58 + "references": null, 59 + "size": null, 60 + "source": "out_path", 61 + "type": "text" 62 + } 63 + ], 64 + "base_filter": null, 65 + "check_constraints": [], 66 + "custom_indexes": [], 67 + "custom_statements": [], 68 + "has_create_action": true, 69 + "hash": "5F5922E1E04FE7AB33B759BC843EED2225392EC376F2B6EE6363212D7F584EA4", 70 + "identities": [ 71 + { 72 + "all_tenants?": false, 73 + "base_filter": null, 74 + "index_name": "seeds_seed_index", 75 + "keys": [ 76 + "name", 77 + "type", 78 + "out_path" 79 + ], 80 + "name": "seed" 81 + } 82 + ], 83 + "multitenancy": { 84 + "attribute": null, 85 + "global": null, 86 + "strategy": null 87 + }, 88 + "repo": "Elixir.Sower.Repo", 89 + "schema": null, 90 + "table": "seeds" 91 + }