improvement: custom contributors list

This commit is contained in:
Zach Daniel 2023-10-01 13:41:23 -04:00
parent d43618384f
commit 33467d034f
13 changed files with 308 additions and 13 deletions

View file

@ -26,12 +26,13 @@ config :appsignal, :config, revision: "test-4"
config :ash_hq,
ash_apis: [
AshHq.Accounts,
AshHq.Ashley,
AshHq.Blog,
AshHq.Discord,
AshHq.Docs,
AshHq.Accounts,
AshHq.MailingList,
AshHq.Discord
AshHq.Github,
AshHq.MailingList
]
config :ash_hq, AshHq.Repo,

View file

@ -41,7 +41,8 @@ defmodule AshHq.Application do
# Start the Endpoint (http/https)
AshHqWeb.Endpoint,
{AshHq.Docs.Library.Agent, nil},
{Cluster.Supervisor, [topologies, [name: AshHq.ClusterSupervisor]]}
{Cluster.Supervisor, [topologies, [name: AshHq.ClusterSupervisor]]},
AshHq.Github.Monitor
# Start a worker by calling: AshHq.Worker.start_link(arg)
# {AshHq.Worker, arg}
] ++ importer ++ discord_bot

View file

@ -53,6 +53,7 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
{AshHq.Docs.Extensions.Search.Preparations.DeselectSearchable, []}
)
|> Ash.Resource.Builder.add_attribute(:searchable, AshHq.Docs.Search.Types.TsVector,
generated?: true,
private?: true
)
end

View file

@ -0,0 +1,36 @@
defmodule AshHq.Github.Contributor do
@moduledoc "A contributor to any package deployed on Ash HQ."
use Ash.Resource,
data_layer: AshPostgres.DataLayer
actions do
defaults [:create, :read, :update, :destroy]
read :in_order do
prepare build(sort: [order: :asc])
end
end
attributes do
integer_primary_key :id do
generated? false
writable? true
end
attribute :login, :string, allow_nil?: false
attribute :avatar_url, :string, allow_nil?: false
attribute :html_url, :string, allow_nil?: false
attribute :order, :integer, allow_nil?: false
end
postgres do
table "contributors"
repo AshHq.Repo
end
code_interface do
define_for AshHq.Github
define :in_order
end
end

View file

@ -0,0 +1,8 @@
defmodule AshHq.Github do
@moduledoc "Api for interacting with data synchronized from github."
use Ash.Api
resources do
resource AshHq.Github.Contributor
end
end

View file

@ -0,0 +1,63 @@
defmodule AshHq.Github.Monitor do
@moduledoc "Polls github for new contributors every 6 hours"
use GenServer
require Logger
def start_link(state, opts \\ []) do
GenServer.start_link(__MODULE__, state, opts)
end
def init(_) do
{:ok, %{}, {:continue, :poll}}
end
def handle_continue(:poll, state) do
{:noreply, poll(state)}
end
def handle_info(:poll, state) do
{:noreply, poll(state)}
end
defp poll(state) do
AshHq.Docs.Library
|> AshHq.Docs.read!()
|> Stream.flat_map(fn library ->
:timer.sleep(1000)
"https://api.github.com/repos/#{library.repo_org}/#{library.name}/contributors"
|> Req.get!(headers: [{"Authorization", "token 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"}])
|> case do
%{status: 200, body: body} ->
Stream.map(body, &Map.take(&1, ["id", "avatar_url", "html_url", "login"]))
resp ->
Logger.error("Invalid response from GH: #{inspect(resp)}")
[]
end
end)
|> Stream.with_index()
|> Stream.map(fn {contributor, index} ->
Map.put(contributor, "order", index)
end)
|> Stream.uniq_by(&Map.get(&1, "id"))
|> AshHq.Github.bulk_create(AshHq.Github.Contributor, :create,
upsert?: true,
upsert_fields: [:order, :login, :avatar_url, :html_url],
return_errors?: true,
stop_on_error?: true
)
state
rescue
e ->
Logger.error("Error while getting contributors: #{inspect(e)}")
state
catch
e ->
Logger.error("Error while getting contributors: #{inspect(e)}")
state
after
Process.send_after(self(), :poll, :timer.hours(6))
end
end

View file

@ -14,6 +14,8 @@ defmodule AshHqWeb.Pages.Home do
data(signed_up, :boolean, default: false)
data(email_form, :any)
data(theme, :atom, default: :default)
data(contributors, :list, default: [])
data(contributor_count, :integer, default: 0)
def render(assigns) do
~F"""
@ -345,13 +347,17 @@ defmodule AshHqWeb.Pages.Home do
<div class="flex flex-col text-center items-center mt-24">
<p class="mt-4 text-3xl sm:text-4xl text-base-dark-900 font-extrabold tracking-tight dark:text-base-light-50 mb-16">
It wouldn't be possible without our amazing community!
It wouldn't be possible without our amazing community.<br>
<CalloutText text={"#{@contributor_count} contributors"} /> and counting!
</p>
<a href="https://github.com/ash-project/ash/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ash-project/ash">
</a>
<div class="grid mx-auto gap-3 grid-cols-8 sm:grid-cols-10 md:grid-cols-114">
{#for %{login: login, avatar_url: avatar_url, html_url: html_url} <- @contributors}
<a href={html_url} class="flex flex-col items-center justify-center">
<img class="h-12 w-12 rounded-full" src={avatar_url} alt={login}>
</a>
{/for}
</div>
<a
href="docs/guides/ash/latest/how_to/contribute"
class="flex justify-center items-center w-full md:w-auto h-10 px-4 rounded-lg bg-primary-light-500 dark:bg-primary-dark-500 font-semibold dark:text-white dark:hover:bg-primary-dark-700 hover:bg-primary-light-700 mt-6"
@ -519,10 +525,14 @@ defmodule AshHqWeb.Pages.Home do
end
def mount(socket) do
contributors = AshHq.Github.Contributor.in_order!()
{:ok,
assign(
socket,
signed_up: false,
contributor_count: Enum.count(contributors),
contributors: contributors,
email_form:
AshPhoenix.Form.for_create(AshHq.MailingList.Email, :create,
api: AshHq.MailingList,

View file

@ -46,12 +46,14 @@ defmodule AshHq.MixProject do
{:ash_graphql, github: "ash-project/ash_graphql"},
{:ash_json_api, github: "ash-project/ash_json_api"},
{:ash_appsignal, "~> 0.1.0"},
{:appsignal_phoenix, "~> 2.0"},
{:gun, "== 2.0.1", env: :prod, hex: "remedy_gun", override: true},
{:ash_authentication, "~> 3.10"},
{:ash_authentication_phoenix, "~> 1.6"},
{:ash_blog, github: "ash-project/ash_blog"},
{:ash_csv, github: "ash-project/ash_csv"},
# HTTP calls
{:req, "~> 0.4.3"},
# Appsignal
{:appsignal_phoenix, "~> 2.0"},
# Api Docs
{:open_api_spex, "~> 3.16"},
# Discord
@ -97,6 +99,7 @@ defmodule AshHq.MixProject do
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
# locked for compatibility
{:gun, "== 2.0.1", env: :prod, hex: "remedy_gun", override: true},
{:phoenix_live_view, "0.18.16"},
{:finch, "~> 0.10"},
{:floki, ">= 0.30.0"},

View file

@ -4,7 +4,7 @@
"appsignal": {:hex, :appsignal, "2.7.9", "efc11601a848f153752778356bc86f9af03e925f15a961f714cd702a83cee434", [:make, :mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:decorator, "~> 1.2.3 or ~> 1.3", [hex: :decorator, repo: "hexpm", optional: false]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, ">= 1.3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "84fff1dcba3d3fcf542c528ffb0f4eba891da0f68125fcf57e9b30d1a1a4a6e9"},
"appsignal_phoenix": {:hex, :appsignal_phoenix, "2.3.4", "c83a8e15a51456db7d722a21bfe9a45e23618b550219caa8fb6d4853f61b5734", [:mix], [{:appsignal, ">= 2.7.6 and < 3.0.0", [hex: :appsignal, repo: "hexpm", optional: false]}, {:appsignal_plug, ">= 2.0.15 and < 3.0.0", [hex: :appsignal_plug, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.11 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.9", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1ca040fbfa653bdda25735031d8b89c81ebb50c475bc35d5ac0e13236e5f6600"},
"appsignal_plug": {:hex, :appsignal_plug, "2.0.15", "758a8a78944878e8461bbc77ca86219121a56f4299c6d79940ab083cf9afea00", [:mix], [{:appsignal, ">= 2.7.6 and < 3.0.0", [hex: :appsignal, repo: "hexpm", optional: false]}, {:plug, ">= 1.1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1c6059049e2081e808aaef04e2b9917e06277f61a35a0e103db860d08cbc41f1"},
"ash": {:git, "https://github.com/ash-project/ash.git", "acb9a0ee539896be4a844465933b904ed67c5034", []},
"ash": {:git, "https://github.com/ash-project/ash.git", "559b2f81af9c7a456ffa9280bd46cb841071025c", []},
"ash_admin": {:git, "https://github.com/ash-project/ash_admin.git", "3002af9ec69dc475582ef5f445064e4594bf45ac", []},
"ash_appsignal": {:hex, :ash_appsignal, "0.1.2", "a6eb1927a13c11006aad0d9ffaa011143344dd04c9b07ab94f459498b8ddc6d4", [:mix], [{:appsignal, "~> 2.0", [hex: :appsignal, repo: "hexpm", optional: false]}, {:ash, ">= 2.14.14", [hex: :ash, repo: "hexpm", optional: false]}], "hexpm", "dae3158337d2a36b76f04519ebe6d08ef5296823831993cef6069eeb879c5b94"},
"ash_authentication": {:hex, :ash_authentication, "3.11.15", "7834446cdd13bb471bded630aa0e0e4fb8795ffffe0c294dc22448d3778ff035", [:mix], [{:ash, ">= 2.5.11 and < 3.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:assent, "~> 0.2", [hex: :assent, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:finch, "~> 0.16.0", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.5", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}, {:spark, ">= 1.1.39 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "931926d8dd3fc5ac54a354d69aad6aed30438977d7971bf3fdfae4c563557d21"},
@ -14,7 +14,7 @@
"ash_graphql": {:git, "https://github.com/ash-project/ash_graphql.git", "cf57747d79f848608252ab167d74bb59aaef2345", []},
"ash_json_api": {:git, "https://github.com/ash-project/ash_json_api.git", "31ece4fad9920c7e45c600d38ac82218296c4612", []},
"ash_phoenix": {:git, "https://github.com/ash-project/ash_phoenix.git", "35e4d2931e1664383c9a085a90f846e58986c8c8", []},
"ash_postgres": {:git, "https://github.com/ash-project/ash_postgres.git", "e8a4b41758abb375cb97080115b74e3a69b1b145", []},
"ash_postgres": {:git, "https://github.com/ash-project/ash_postgres.git", "472b17ce64f774834d7d9028880f3653d0f1a05b", []},
"assent": {:hex, :assent, "0.2.7", "aa68f68e577077c091ce722bff8fe1ae56b95b274bb8107f7a5406cc15a65da7", [:mix], [{:certifi, ">= 0.0.0", [hex: :certifi, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: true]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:ssl_verify_fun, ">= 0.0.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: true]}], "hexpm", "08106af439de4f9de114c0334de4c848de7cfbe53a5a52d342a784c4f6bc86f3"},
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
@ -111,6 +111,7 @@
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"premailex": {:hex, :premailex, "0.3.16", "25c0c9c969f0025bbfdb06834f8f0fbd46e5ec50f5c252e6492165802ffbd2a6", [:mix], [{:certifi, ">= 0.0.0", [hex: :certifi, repo: "hexpm", optional: true]}, {:floki, "~> 0.19", [hex: :floki, repo: "hexpm", optional: false]}, {:meeseeks, "~> 0.11", [hex: :meeseeks, repo: "hexpm", optional: true]}, {:ssl_verify_fun, ">= 0.0.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: true]}], "hexpm", "c6b042f89ca63025dfbe3ef54fdbbe9d5f043b7c33d8e58f43a41d13a9475111"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"req": {:hex, :req, "0.4.3", "bb4cd1661a234b9c779b984dd137761f7ff705f45d0008ba40c8f420a4307b43", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "9bc88c84f101cfe884260d3413b72aaad6d94ccedccc1f2bcef8e94bd68c5536"},
"salsa20": {:hex, :salsa20, "1.0.4", "404cbea1fa8e68a41bcc834c0a2571ac175580fec01cc38cc70c0fb9ffc87e9b", [:mix], [], "hexpm", "745ddcd8cfa563ddb0fd61e7ce48d5146279a2cf7834e1da8441b369fdc58ac6"},
"slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"},
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},

View file

@ -0,0 +1,22 @@
defmodule AshHq.Repo.Migrations.MigrateResources56 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
create table(:contributors, primary_key: false) do
add(:id, :bigint, null: false, primary_key: true)
add(:login, :text, null: false)
add(:avatar_url, :text, null: false)
add(:html_url, :text, null: false)
end
end
def down do
drop(table(:contributors))
end
end

View file

@ -0,0 +1,21 @@
defmodule AshHq.Repo.Migrations.MigrateResources57 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
alter table(:contributors) do
add(:order, :bigint, null: false)
end
end
def down do
alter table(:contributors) do
remove(:order)
end
end
end

View file

@ -0,0 +1,59 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "id",
"references": null,
"generated?": false,
"allow_nil?": false,
"primary_key?": true
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "login",
"references": null,
"generated?": false,
"allow_nil?": false,
"primary_key?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "avatar_url",
"references": null,
"generated?": false,
"allow_nil?": false,
"primary_key?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "html_url",
"references": null,
"generated?": false,
"allow_nil?": false,
"primary_key?": false
}
],
"table": "contributors",
"hash": "C10DEE81B6ACD502EB81B3C5C88A90704B66F1D597271949A7288FDD9903F1BB",
"repo": "Elixir.AshHq.Repo",
"schema": null,
"check_constraints": [],
"identities": [],
"custom_indexes": [],
"multitenancy": {
"global": null,
"strategy": null,
"attribute": null
},
"base_filter": null,
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,69 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "id",
"references": null,
"allow_nil?": false,
"primary_key?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "login",
"references": null,
"allow_nil?": false,
"primary_key?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "avatar_url",
"references": null,
"allow_nil?": false,
"primary_key?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "html_url",
"references": null,
"allow_nil?": false,
"primary_key?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "order",
"references": null,
"allow_nil?": false,
"primary_key?": false,
"generated?": false
}
],
"table": "contributors",
"hash": "86B46C10D847FCFB7D7EC20435C89A3101652A1736A6530421FB49CE1BF9020C",
"repo": "Elixir.AshHq.Repo",
"identities": [],
"schema": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"custom_statements": [],
"base_filter": null,
"check_constraints": [],
"has_create_action": true
}