From 3de554c5b2889ace653f898fb7c3ab831dfe84c3 Mon Sep 17 00:00:00 2001 From: JohnnyT Date: Wed, 23 Sep 2026 19:41:57 -0600 Subject: [PATCH] Ships the README-linked guides in the package hex.pm renders the README from the package tarball, so a relative link there resolves inside the tarball; the five guides the README links to (architecture, embedding, fixture bundles, wire format, telemetry) are extras but were not in package() files:, so each link answered 404 on the package page. They now ship. The README's ADR-0005 link and the CHANGELOG's changelog.d/README.md link were relative links to files that are not extras: HexDocs left the first as a raw href (404, and the docs build's one warning) and silently rewrote the second to the package's front page. Both now cite the file by its GitHub URL at the v0.10.1 tag, where each exists. test/packaging_test.exs gains a check that every README relative link target is in both extras and files:, so the next such link fails the suite instead of the package page. No version bump; no changelog fragment (changelog.d/README.md excludes documentation). Refs: sui-g45f --- CHANGELOG.md | 2 +- README.md | 2 +- mix.exs | 26 +++++++++++++++++++++----- test/packaging_test.exs | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 753bbbd..5f8ba1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Entries for unreleased work are not written here directly. Each issue drops a -fragment in [`changelog.d/`](changelog.d/README.md); the fragments are assembled +fragment in [`changelog.d/`](https://github.com/riddler/statifier-ui/blob/v0.10.1/changelog.d/README.md); the fragments are assembled into a version section at release. See that README for the format and for when a change warrants an entry at all. diff --git a/README.md b/README.md index 700baf6..baa99a2 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ The vocabulary that stream can carry is closed and published: string the format defines. It is the handle to hold and diff across upgrades - a consumer still ignores types it does not know, but a diff of that list is how it notices a new one. See the note on pinning the vocabulary in -[ADR-0005](docs/adr/0005-language-neutral-trace-wire-format.md). +[ADR-0005](https://github.com/riddler/statifier-ui/blob/v0.10.1/docs/adr/0005-language-neutral-trace-wire-format.md). ## Checking expressions diff --git a/mix.exs b/mix.exs index c781d9f..baf4ff5 100644 --- a/mix.exs +++ b/mix.exs @@ -36,9 +36,9 @@ defmodule StatifierUI.MixProject do defp elixirc_paths(_env), do: ["lib"] # Hexdocs configuration. These paths are read off the publisher's disk at - # `mix docs` time and need no entry in package()'s files: list - the docs - # tarball hexdocs hosts is built separately from the package tarball - # `mix deps.get` fetches. + # `mix docs` time - the docs tarball hexdocs hosts is built separately from + # the package tarball `mix deps.get` fetches. A guide the README links to + # relatively is also in package()'s files: list, for hex.pm's README page. defp docs do [ name: "StatifierUI", @@ -69,8 +69,24 @@ defmodule StatifierUI.MixProject do # `assets` is here because ADR-0009 makes it public API: the JavaScript # ships as source and the host's bundler compiles it, so a tarball without # it is a package whose documented hook cannot be imported. - # `test/packaging_test.exs` holds this list against the ADRs. - files: ~w(lib assets mix.exs README.md LICENSE CHANGELOG.md), + # The docs/ guides are here because hex.pm renders the README from this + # tarball, so a README relative link answers 404 there unless its target + # ships; each is also an extra, so the same link works on HexDocs. + # `test/packaging_test.exs` holds this list against the ADRs and the + # README's relative links. + files: ~w( + lib + assets + mix.exs + README.md + LICENSE + CHANGELOG.md + docs/architecture.md + docs/ops-embedding.md + docs/fixture-bundles.md + docs/wire-format.md + docs/telemetry.md + ), links: %{ "GitHub" => @source_url, "Changelog" => "#{@source_url}/blob/main/CHANGELOG.md" diff --git a/test/packaging_test.exs b/test/packaging_test.exs index 79cb44a..169a700 100644 --- a/test/packaging_test.exs +++ b/test/packaging_test.exs @@ -55,4 +55,41 @@ defmodule StatifierUI.PackagingTest do assert {"assets", "ADR-0009 (JavaScript ships as source)"} in @published_by_adr end end + + describe "README relative links" do + # hex.pm renders the README from the package tarball, so a relative link + # there resolves inside the tarball and answers 404 unless the target + # ships; HexDocs rewrites a relative link only when the target is an + # extra. One relative link works on GitHub, HexDocs and hex.pm alike only + # when its target is in both lists. A file that is in neither (an ADR, + # for one) is linked by absolute GitHub URL instead. + test "every relative link target is in package files: and in extras" do + config = Mix.Project.config() + files = config[:package][:files] + extras = Enum.map(config[:docs][:extras], &extra_path/1) + + targets = readme_relative_targets() + assert targets != [], "found no relative links in README.md; the scan is broken" + + for target <- targets do + assert target in extras, + "README.md links #{target} relatively, but it is not in docs() extras: #{inspect(extras)}" + + assert Enum.any?(files, &(target == &1 or String.starts_with?(target, &1 <> "/"))), + "README.md links #{target} relatively, but package() files: does not ship it: #{inspect(files)}" + end + end + end + + defp readme_relative_targets do + ~r/\]\(([^)\s]+)\)/ + |> Regex.scan(File.read!("README.md"), capture: :all_but_first) + |> List.flatten() + |> Enum.reject(&String.match?(&1, ~r{^(https?:|mailto:|#)})) + |> Enum.map(&(&1 |> String.split("#") |> hd())) + |> Enum.uniq() + end + + defp extra_path({path, _opts}), do: to_string(path) + defp extra_path(path), do: to_string(path) end