Skip to content

Commit daa10c9

Browse files
committed
feat: workspace and dashboard precommit tooling
Root mix.exs runs each sub-app task via elixir -S mix with --erl ansi_enabled so colored output survives the captured-pipe subprocess. durable_dashboard: add precommit (Elixir + frontend formatting, compile --warnings-as-errors, tsc typecheck, test) plus cli/0 preferred_envs and assets.format / assets.typecheck aliases; adopt shared metadata and ex_doc to match durable. Order precommit so the frontend cmd steps run before the Elixir compile-then-test block; an interleaved mix cmd breaks lazy jason loading during the test run.
1 parent b1fbeb8 commit daa10c9

3 files changed

Lines changed: 105 additions & 9 deletions

File tree

durable_dashboard/mix.exs

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
11
defmodule DurableDashboard.MixProject do
22
use Mix.Project
33

4+
# Shared monorepo metadata. Canonical values live in ../shared.exs; Hex
5+
# tarballs can't reference files outside the package root, so we copy that
6+
# file next to this mix.exs (the copy is what ships) and read it back.
7+
# Edit ../shared.exs, never the git-ignored copy beside this file.
8+
shared_src = Path.join(__DIR__, "../shared.exs")
9+
shared_dst = Path.join(__DIR__, "shared.exs")
10+
11+
if File.exists?(shared_src) and
12+
(not File.exists?(shared_dst) or File.read!(shared_dst) != File.read!(shared_src)) do
13+
File.cp!(shared_src, shared_dst)
14+
end
15+
16+
{shared, _bindings} = Code.eval_file(shared_dst)
17+
18+
# Versioned independently of durable.
419
@version "0.0.0-alpha"
5-
@source_url "https://github.com/wavezync/durable"
20+
21+
# Hex requirement for durable when this package is published. In the monorepo
22+
# durable is a path dependency (see durable_dep/0); the published package must
23+
# depend on a released Hex version instead. durable and durable_dashboard
24+
# version independently, so bump this to match the durable release you target.
25+
@durable_version "~> 0.0.0-alpha"
26+
27+
@elixir_requirement Keyword.fetch!(shared, :elixir)
28+
@source_url Keyword.fetch!(shared, :source_url)
29+
@homepage_url Keyword.fetch!(shared, :homepage_url)
30+
@maintainers Keyword.fetch!(shared, :maintainers)
31+
@licenses Keyword.fetch!(shared, :licenses)
632

733
def project do
834
[
935
app: :durable_dashboard,
1036
version: @version,
11-
elixir: "~> 1.15",
37+
elixir: @elixir_requirement,
1238
elixirc_paths: elixirc_paths(Mix.env()),
1339
start_permanent: Mix.env() == :prod,
1440
aliases: aliases(),
1541
deps: deps(),
1642
name: "DurableDashboard",
1743
description: "Web dashboard for Durable workflow engine",
1844
source_url: @source_url,
45+
homepage_url: @homepage_url,
46+
docs: docs(),
1947
package: package()
2048
]
2149
end
2250

51+
def cli do
52+
[
53+
preferred_envs: [precommit: :test]
54+
]
55+
end
56+
2357
def application do
2458
[
2559
extra_applications: [:logger]
@@ -31,29 +65,74 @@ defmodule DurableDashboard.MixProject do
3165

3266
defp deps do
3367
[
34-
{:durable, path: "../durable"},
68+
{:durable, durable_dep()},
3569
{:phoenix_live_view, "~> 1.1"},
3670
{:phoenix, "~> 1.8"},
3771
{:jason, "~> 1.4"},
38-
{:lazy_html, ">= 0.1.0", only: :test}
72+
{:lazy_html, ">= 0.1.0", only: :test},
73+
{:ex_doc, "~> 0.34", only: :dev, runtime: false}
3974
]
4075
end
4176

77+
# Path dependency for local monorepo development; the released Hex version when
78+
# building or publishing the package. Hex refuses to publish a package with a
79+
# path dependency, so the hex.build / hex.publish tasks (or DURABLE_PUBLISH=1)
80+
# switch to the version requirement.
81+
defp durable_dep do
82+
if publishing?(), do: @durable_version, else: [path: "../durable"]
83+
end
84+
85+
defp publishing? do
86+
System.get_env("DURABLE_PUBLISH") in ~w(1 true) or publish_task?(System.argv())
87+
end
88+
89+
defp publish_task?([task | _]), do: task in ~w(hex.build hex.publish)
90+
defp publish_task?(_), do: false
91+
4292
defp aliases do
4393
[
4494
setup: ["deps.get", "assets.setup"],
4595
"assets.setup": ["cmd --cd assets pnpm install"],
4696
"assets.build": ["cmd --cd assets pnpm build"],
97+
# Formats + autofixes the React/TS island via Biome (biome check --write).
98+
"assets.format": ["cmd --cd assets pnpm lint:fix"],
99+
"assets.typecheck": ["cmd --cd assets pnpm typecheck"],
47100
"hex.build": ["assets.build", "hex.build"],
48-
"hex.publish": ["assets.build", "hex.publish"]
101+
"hex.publish": ["assets.build", "hex.publish"],
102+
# All formatting + checks, Elixir and frontend. `format` + `assets.format`
103+
# cover formatting (Elixir + TS); `assets.typecheck` is the JS-side static
104+
# check (this package has no credo dependency). Runs in :test env via cli/0.
105+
#
106+
# Ordering matters: the `assets.*` steps shell out via `mix cmd --cd assets`,
107+
# which shifts the VM's cwd. A `cmd` step interleaved between `compile` and
108+
# `test` breaks lazy loading of :jason for the test run, so we run all the
109+
# frontend (cmd) steps first and keep the Elixir `format → compile → test`
110+
# block contiguous and last.
111+
precommit: [
112+
"assets.format",
113+
"assets.typecheck",
114+
"format",
115+
"compile --warnings-as-errors",
116+
"test"
117+
]
118+
]
119+
end
120+
121+
defp docs do
122+
[
123+
main: "readme",
124+
source_url: @source_url,
125+
source_ref: "v#{@version}",
126+
extras: ["README.md"]
49127
]
50128
end
51129

52130
defp package do
53131
[
54-
licenses: ["MIT"],
55-
links: %{"GitHub" => @source_url},
56-
files: ~w(lib priv .formatter.exs mix.exs)
132+
maintainers: @maintainers,
133+
licenses: @licenses,
134+
links: %{"GitHub" => @source_url, "Homepage" => @homepage_url},
135+
files: ~w(lib priv .formatter.exs mix.exs README.md LICENSE shared.exs)
57136
]
58137
end
59138
end

durable_dashboard/mix.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
"crontab": {:hex, :crontab, "1.2.0", "503611820257939d5d0fd272eb2b454f48a470435a809479ddc2c40bb515495c", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "ebd7ef4d831e1b20fa4700f0de0284a04cac4347e813337978e25b4cc5cc2207"},
44
"db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"},
55
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
6+
"earmark_parser": {:hex, :earmark_parser, "1.4.45", "cba8369ab2a1342e419bc2760eec731b17be828941dcf494045d44766227e1d5", [:mix], [], "hexpm", "d3ec045bf122965db20c0bdb420e19ee1415843135327124918473feb4b328e8"},
67
"ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"},
78
"ecto_sql": {:hex, :ecto_sql, "3.13.5", "2f8282b2ad97bf0f0d3217ea0a6fff320ead9e2f8770f810141189d182dc304e", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aa36751f4e6a2b56ae79efb0e088042e010ff4935fc8684e74c23b1f49e25fdc"},
89
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
10+
"ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"},
911
"fine": {:hex, :fine, "0.1.6", "4bf7151493443c454aac9f2fa2f34f5fefd0346a83fb5586a016c4a135c63247", [:mix], [], "hexpm", "5638eb4495488e885ebec167fa57973e5c35e1a50c344eb7666c90ec1c4e3b12"},
1012
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
1113
"lazy_html": {:hex, :lazy_html, "0.1.11", "136c8e9cd616b4f4e9c1562daa683880891120b759606dc4c3b6b18058ba5d79", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "3b1be592929c31eca1a21673d25696e5c14cddfe922d9d1a3e3b48be4163883b"},
14+
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
15+
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
16+
"makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"},
1217
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
1318
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
19+
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
1420
"phoenix": {:hex, :phoenix, "1.8.5", "919db335247e6d4891764dc3063415b0d2457641c5f9b3751b5df03d8e20bbcf", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, 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.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "83b2bb125127e02e9f475c8e3e92736325b5b01b0b9b05407bcb4083b7a32485"},
1521
"phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"},
1622
"phoenix_live_view": {:hex, :phoenix_live_view, "1.1.27", "9afcab28b0c82afdc51044e661bcd5b8de53d242593d34c964a37710b40a42af", [: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", "415735d0b2c612c9104108b35654e977626a0cb346711e1e4f1ed16e3c827ede"},

mix.exs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,22 @@ defmodule DurableWorkspace.MixProject do
2222
]
2323
end
2424

25+
# Build an alias that runs `mix <command>` in each sub-app as its own OS
26+
# process. We invoke `elixir -S mix` (rather than `mix` directly) so we can
27+
# pass `--erl "-elixir ansi_enabled <bool>"`: each child's output is captured
28+
# via `into:` (a pipe, not a TTY), so the child would otherwise disable ANSI
29+
# and we'd lose colored test/credo/compiler output. Forwarding the root's
30+
# `IO.ANSI.enabled?/0` keeps colors when the workspace runs in a terminal and
31+
# correctly drops them when redirected (e.g. CI). A non-zero exit from any
32+
# sub-app makes the whole workspace task fail.
2533
defp cmd(command) do
34+
ansi = IO.ANSI.enabled?()
35+
base = ["--erl", "-elixir ansi_enabled #{ansi}", "-S", "mix", command]
36+
2637
for app <- @apps do
2738
fn args ->
2839
{_, code} =
29-
System.cmd("mix", [command | args],
40+
System.cmd("elixir", base ++ args,
3041
into: IO.binstream(:stdio, :line),
3142
cd: app
3243
)

0 commit comments

Comments
 (0)