updated readme

51f56a7 · gmorell · 2026-05-14 04:28

17 files +303 -24

Files changed

added .dockerignore
+46 −0
@@ -0,0 +1,46 @@
1 +# This file excludes paths from the Docker build context.
2 +#
3 +# By default, Docker's build context includes all files (and folders) in the
4 +# current directory. Even if a file isn't copied into the container it is still sent to
5 +# the Docker daemon.
6 +#
7 +# There are multiple reasons to exclude files from the build context:
8 +#
9 +# 1. Prevent nested folders from being copied into the container (ex: exclude
10 +# /assets/node_modules when copying /assets)
11 +# 2. Reduce the size of the build context and improve build time (ex. /build, /deps, /doc)
12 +# 3. Avoid sending files containing sensitive information
13 +#
14 +# More information on using .dockerignore is available here:
15 +# https://docs.docker.com/engine/reference/builder/#dockerignore-file
16 +
17 +.dockerignore
18 +
19 +# Ignore git, but keep git HEAD and refs to access current commit hash if needed:
20 +#
21 +# $ cat .git/HEAD | awk '{print ".git/"$2}' | xargs cat
22 +# d0b8727759e1e0e7aa3d41707d12376e373d5ecc
23 +.git
24 +!.git/HEAD
25 +!.git/refs
26 +
27 +# Common development/test artifacts
28 +/cover/
29 +/doc/
30 +/test/
31 +/tmp/
32 +.elixir_ls
33 +
34 +# Mix artifacts
35 +/_build/
36 +/deps/
37 +*.ez
38 +
39 +# Generated on crash by the VM
40 +erl_crash.dump
41 +
42 +# Static artifacts - These should be fetched and built inside the Docker image
43 +# https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Release.html#module-docker
44 +/assets/node_modules/
45 +/priv/static/assets/
46 +/priv/static/cache_manifest.json
added .gitlab-ci.yml
+11 −0
@@ -0,0 +1,11 @@
1 +build-master:
2 + retry: 1
3 + stage: build
4 + image:
5 + name: gcr.io/kaniko-project/executor:v1.23.2-debug
6 + entrypoint: [""]
7 + only:
8 + - master
9 + script:
10 + - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
11 + - /kaniko/executor --force --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE --registry-mirror us0.regi.kube.gmp.io
added Dockerfile
+132 −0
@@ -0,0 +1,132 @@
1 +# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian
2 +# instead of Alpine to avoid DNS resolution issues in production.
3 +#
4 +# https://hub.docker.com/r/hexpm/elixir/tags?name=ubuntu
5 +# https://hub.docker.com/_/ubuntu/tags
6 +#
7 +# This file is based on these images:
8 +#
9 +# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
10 +# - https://hub.docker.com/_/debian/tags?name=trixie-20260505-slim - for the release image
11 +# - https://pkgs.org/ - resource for finding needed packages
12 +# - Ex: docker.io/hexpm/elixir:1.19.5-erlang-28.4.1-debian-trixie-20260505-slim
13 +#
14 +ARG ELIXIR_VERSION=1.19.5
15 +ARG OTP_VERSION=28.4.1
16 +ARG DEBIAN_VERSION=trixie-20260505-slim
17 +
18 +ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
19 +ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}"
20 +
21 +FROM ${BUILDER_IMAGE} AS builder
22 +
23 +# install build dependencies.
24 +# * build-essential, git — Elixir / mix build chain
25 +# * pkg-config, libssl-dev — gix transitively links against system openssl
26 +# * curl, ca-certificates — needed to bootstrap rustup
27 +RUN apt-get update \
28 + && apt-get install -y --no-install-recommends \
29 + build-essential git pkg-config libssl-dev curl ca-certificates \
30 + && rm -rf /var/lib/apt/lists/*
31 +
32 +# Rust via rustup, pinned. apt's rust on trixie is 1.85, but recent
33 +# transitive gix deps (icu_*, home) require 1.86+; rustup keeps the
34 +# compiler decoupled from the OS rev. Pinned to a known-good rev so
35 +# builds are reproducible.
36 +ENV RUSTUP_HOME=/usr/local/rustup \
37 + CARGO_HOME=/usr/local/cargo \
38 + PATH=/usr/local/cargo/bin:${PATH} \
39 + RUST_VERSION=1.88.0
40 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
41 + | sh -s -- -y --profile minimal --default-toolchain ${RUST_VERSION} \
42 + && rustc --version && cargo --version
43 +
44 +# prepare build dir
45 +WORKDIR /app
46 +
47 +# install hex + rebar
48 +RUN mix local.hex --force \
49 + && mix local.rebar --force
50 +
51 +# set build ENV
52 +ENV MIX_ENV="prod"
53 +
54 +# install mix dependencies
55 +COPY mix.exs mix.lock ./
56 +RUN mix deps.get --only $MIX_ENV
57 +RUN mkdir config
58 +
59 +# copy compile-time config files before we compile dependencies
60 +# to ensure any relevant config change will trigger the dependencies
61 +# to be re-compiled.
62 +COPY config/config.exs config/${MIX_ENV}.exs config/
63 +RUN mix deps.compile
64 +
65 +RUN mix assets.setup
66 +
67 +COPY priv priv
68 +
69 +COPY lib lib
70 +
71 +# Rust source for the gitgud_gix NIF — Rustler invokes `cargo` from
72 +# this directory during `mix compile`, so it must be present.
73 +COPY native native
74 +
75 +# Compile the release
76 +RUN mix compile
77 +
78 +COPY assets assets
79 +
80 +# compile assets
81 +RUN mix assets.deploy
82 +
83 +# Changes to config/runtime.exs don't require recompiling the code
84 +COPY config/runtime.exs config/
85 +
86 +COPY rel rel
87 +RUN mix release
88 +
89 +# start a new build stage so that the final image will only contain
90 +# the compiled release and other runtime necessities
91 +FROM ${RUNNER_IMAGE} AS final
92 +
93 +# Runtime packages.
94 +# * libstdc++6, openssl, libncurses6, locales, ca-certificates — Erlang runtime
95 +# * git — the Cli backend shells out to git for ops the NIF hasn't covered
96 +# yet (commit_tree, update_ref, plumbing for profile-README seeding,
97 +# post-receive hook helpers). Also used by the embedded SSH daemon to
98 +# spawn upload-pack / receive-pack per session.
99 +# * tini — proper PID 1 / zombie reaping, recommended for any container
100 +# image running long-lived child processes (we fork git per session).
101 +RUN apt-get update \
102 + && apt-get install -y --no-install-recommends \
103 + libstdc++6 openssl libncurses6 locales ca-certificates \
104 + git tini \
105 + && rm -rf /var/lib/apt/lists/*
106 +
107 +# Set the locale
108 +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
109 + && locale-gen
110 +
111 +ENV LANG=en_US.UTF-8
112 +ENV LANGUAGE=en_US:en
113 +ENV LC_ALL=en_US.UTF-8
114 +
115 +WORKDIR "/app"
116 +RUN chown nobody /app
117 +
118 +# set runner ENV
119 +ENV MIX_ENV="prod"
120 +
121 +# Only copy the final release from the build stage
122 +COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/git_gud ./
123 +
124 +USER nobody
125 +
126 +# tini as PID 1 — reaps the git child processes we fork from the embedded
127 +# SSH daemon + plumbing helpers. Without this, zombies accumulate across
128 +# pushes / pulls in long-lived containers. The package installs the
129 +# binary at /usr/bin/tini on debian; symlinks for safety.
130 +ENTRYPOINT ["/usr/bin/tini", "--"]
131 +
132 +CMD ["/app/bin/server"]
modified PROGRESS.md
+2 −2
@@ -77,8 +77,8 @@ Operators can gate who can sign up. Pattern ported from diogramos, extended from
77 77
78 78 ### Config flag
79 79
80 - **`config :git_gud, :registration_mode, :open | :invite_only | :closed`** (default `:open`).
81 - `Accounts.registration_mode/0` accepts both atom and string values (so env-var-driven `:runtime.exs` config works the same) and falls back to `:open` for unknown values.
80 +- **`config :git_gud, :registration_mode, :open | :invite_only | :closed`** (default `:invite_only` — safer for fresh deploys; the test env overrides to `:open` so the existing open-registration assertions keep passing).
81 +- `Accounts.registration_mode/0` accepts both atom and string values (so env-var-driven `:runtime.exs` config works the same) and falls back to `:invite_only` for unknown values.
82 82 - `Accounts.register_user/1` short-circuits with `{:error, :registration_closed}` when the mode is `:closed` — defense in depth so a direct API call doesn't bypass the LV gate.
83 83
84 84 ### Schema + context
modified README.md
+19 −6
@@ -19,10 +19,12 @@ external tools (Forgejo runner, Garage, zot) rather than reinvent them.
19 19 - Server-side post-receive hook fans out to commit-backfill workers
20 20 - **Git LFS** (Batch API) over pluggable object storage
21 21 - **Repo browsing**
22 - Tree, blob, commit, log views with deep linking
23 - **Line-level diff renderer** with Lumis-highlighted hunks on PR show, commit show, and compare; parsed diffs cached per `(base, head)`
22 + - Tree, blob, commit, log views with deep linking and a **shared repo header** on every subpage (title + Issues/Labels/Forks/Compare/Packages/Actions/Settings strip + Fork button)
23 + - **Clone-URL copy widget** on the repo show page — three-state expanding pill (HTTP/SSH toggle → copy button → inline URL text), one-click clipboard with execCommand fallback
24 + - **Line-level diff renderer** with Lumis-highlighted hunks on PR show, commit show, and compare; parsed diffs cached per `(base, head)`. **Lazy per-file expansion** — large diffs keep hunks out of the DOM until you open the file
24 25 - **Syntax highlighting** via [Lumis](https://lumis.sh) (tree-sitter, Rust NIF) — cached per-blob
25 26 - **Full GFM markdown** with cross-references (`#42`, `#x2a` hex, `owner/repo#42`), GitHub-style alerts, footnotes, task lists, fenced-code syntax highlighting — cached per-render
27 + - **Language stats**: 0.3em GitHub-style bar on the repo page driven by a per-commit Oban job; click for `/stats/languages` (full table) and `/stats/committers` (commits + churn per author)
26 28 - Per-user editor theme (curated subset of Lumis themes — Dracula, Solarized, Monokai, Tokyo Night, Catppuccin, etc) — separate from the daisyUI page theme
27 29 - **Collaboration**
28 30 - Issues + comments with Write/Preview tabs, label picker, query-string autofill
@@ -31,6 +33,16 @@ external tools (Forgejo runner, Garage, zot) rather than reinvent them.
31 33 - Forks + fork-sync (fast-forward CAS) + compare-any-two-refs view
32 34 - Branch protection, deploy keys, webhooks, wikis
33 35 - **Labels** with HTML color picker + 16-swatch palette + live preview; **org-level labels** inherited by every repo unless the repo opts out (and re-opts in via a hidden-labels expander)
36 +- **Profiles**
37 + - **User + org profile pages** at `/u/:handle` and `/orgs/:handle`. Display name, bio, url, location, company, public email, timezone, socials. Two-column layout with bio on the left, pinned repos + README on the right.
38 + - **Theme-aware identicon avatars** — deterministic 5×5 mirrored grid, fills reference daisyUI CSS variables so swapping themes recolors every avatar live. No upload pipeline yet; the identicon is the avatar.
39 + - **Pinned repos** — drag-to-reorder grid, up to 7 per user / per org, includes repos from orgs you're a member of. Removed-from-org? Your personal pins of that org's repos clean themselves up.
40 + - **Auto-created same-name profile README** — registering `alice` provisions `alice/alice` with a seeded "Hi, I'm @alice 👋 …" `README.md` on the default branch. Edit it like any other repo; the rendered output shows on the profile.
41 +- **Account + org governance**
42 + - **Registration modes**: `:open` / `:invite_only` (default) / `:closed`, configurable per-instance
43 + - **Invites** — single-use tokens with create / list / revoke per user; UI at `/users/settings/invites`
44 + - **Org visibility**: `public` / `internal` / `private`. Repos can't be more open than their org; tightening an org cascades to over-public repos in one transaction. Public orgs are accessible to anonymous viewers; private orgs require membership.
45 + - **Org members management** at `/orgs/:handle/settings/members` — add by handle or email, change roles, remove, with a last-admin guard so an org can never end up admin-less
34 46 - **Auth & MFA**
35 47 - Magic-link login + email/password
36 48 - **TOTP** (any RFC 6238 authenticator) with QR enrollment + 10 single-use recovery codes
@@ -42,7 +54,7 @@ external tools (Forgejo runner, Garage, zot) rather than reinvent them.
42 54 - Inbound: Follow, Offer (federated PR), Update, Reject, Create(Note) comments, Like/Dislike as PR reviews
43 55 - Outbound `Offer` from the PR new page; outbound Like/Dislike on local reviews of federated PRs
44 56 - Per-instance moderation: allowlist mode (default), per-instance + per-actor blocks, interaction policies (`public` / `followers` / `approved`)
45 - **Actionable reports queue** in admin: inline previews, delete content, suspend remote actors, forward to source instance via AP `Flag`
57 + - **Actionable reports queue** in admin: inline previews, **replace-with-mod-message** (reversible — admins + the original author can `<details>`-toggle the original), suspend remote actors, forward to source instance via AP `Flag`. Re-reporting moderated content is gated at the API layer.
46 58 - **`mix gitgud.federation.suggest_blocks`** — peer block lists become a *review queue*, not enforcement (cascades are deliberately declined)
47 59 - **CI**
48 60 - Speaks the **Forgejo Actions runner protocol** (Twirp) — point any modern `forgejo-runner` at it; protocol path autodetected (`ping.v1`, `runner.v1`, with/without `_apis/`)
@@ -130,6 +142,7 @@ These all live in `config/*.exs`. Key ones:
130 142 | `GitGud.Storage.Disk` `root` | Where Disk writes blobs | `priv/storage` |
131 143 | `GitGud.Ssh.Server` `enabled` | Embedded SSH daemon | `false` (dev: `true`) |
132 144 | `GitGud.Ssh.Server` `port` | SSH daemon port | `2222` |
145 +| `:registration_mode` | `:open` / `:invite_only` / `:closed` | `:invite_only` |
133 146 | `GitGud.Federation` `federation_mode` | `:allowlist` or `:open` | `:allowlist` |
134 147 | `GitGud.Cache` | Nebulex cache sizing | 50k entries / 200MB |
135 148 | `GitGud.Accounts.TwoFactor` `required` / `grace_period_days` | Instance-wide MFA enforcement + grace window | `false` / `14` |
@@ -218,16 +231,16 @@ external one works today and is well-tested by the Forgejo community.
218 231
219 232 See [ROADMAP.md](./ROADMAP.md). Highlights:
220 233
221 - gix NIF wiring for log / commit / tree / merge_tree (Cli fallback works)
234 +- gix NIF wiring for log / commit / tree / diff_stats / merge_base / merge_tree (Cli fallback works; slice 56 fills in the hot paths)
222 235 - In-browser PR conflict resolution
223 - Lazy per-file diff expansion for very large diffs
236 +- Avatar file uploads (the theme-aware identicon is the avatar today)
224 237 - Native runner (low priority while the external one works)
225 238 - Code search (Postgres FTS handles issues/PRs; code search is its own beast)
226 239 - RFC 9421 (HTTP Messages) signatures when peers upgrade from draft-cavage-12
227 240 - Reputation aging for new federated hosts
228 241
229 242 See [PROGRESS.md](./PROGRESS.md) for the slice-by-slice history of how
230 the forge was built, and what each shipped — currently through slice 42.
243 +the forge was built, and what each shipped — currently through slice 55.
231 244
232 245 ## Project conventions
233 246
modified config/config.exs
+3 −2

Click to load diff…

modified config/test.exs
+5 −0

Click to load diff…

modified lib/git_gud/accounts.ex
+5 −5

Click to load diff…

added lib/git_gud/release.ex
+30 −0

Click to load diff…

modified lib/git_gud_web/controllers/page_html/home.html.heex
+29 −2

Click to load diff…

modified mix.exs
+3 −1

Click to load diff…

modified mix.lock
+0 −1

Click to load diff…

added rel/overlays/bin/migrate
+5 −0

Click to load diff…

added rel/overlays/bin/migrate.bat
+1 −0

Click to load diff…

added rel/overlays/bin/server
+5 −0

Click to load diff…

added rel/overlays/bin/server.bat
+2 −0

Click to load diff…

modified test/git_gud/invites_test.exs
+5 −5

Click to load diff…

Parents: 6709221