From 23b1c522a559533da0de6dff251b20ef122d7b5e Mon Sep 17 00:00:00 2001 From: hunterdsp Date: Thu, 6 Aug 2026 20:43:33 -0400 Subject: [PATCH 1/2] chore: relock with the current uv, which normalises dependency markers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every `uv run` rewrote uv.lock in place, because the committed file was produced by an older uv that omitted the resolution markers this one writes out. Nothing about the resolved versions changed — only how the markers are spelled. The cost was not cosmetic: `make lint` runs mdformat through `uv run`, so the hook mutated a file while pre-commit was watching, which reads as "files were modified by this hook". Every commit failed and rolled back its own fixes until the lockfile was rewritten by hand. Co-Authored-By: Claude Opus 5 (1M context) --- uv.lock | 144 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/uv.lock b/uv.lock index d8f27e4..d511e11 100644 --- a/uv.lock +++ b/uv.lock @@ -38,9 +38,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "exceptiongroup" }, - { name = "idna" }, - { name = "typing-extensions" }, + { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, + { name = "idna", marker = "python_full_version < '3.10'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ @@ -55,9 +55,9 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ @@ -153,7 +153,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ @@ -168,7 +168,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } wheels = [ @@ -207,7 +207,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -255,8 +255,8 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "certifi" }, - { name = "h11" }, + { name = "certifi", marker = "python_full_version < '3.10'" }, + { name = "h11", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/18/e51f5729684acaaab4d1cfca0777ac0f86440c65d2814992442ed5ef02e1/httpcore2-2.0.0.tar.gz", hash = "sha256:403692e0a0e8ea6de90993cdea815b2454d2ff5426e61d2a244846c12506af76", size = 63864, upload-time = "2026-05-12T17:59:22.073Z" } wheels = [ @@ -271,8 +271,8 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "h11" }, - { name = "truststore" }, + { name = "h11", marker = "python_full_version >= '3.10'" }, + { name = "truststore", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/39/a8/20ed1ed79cbc2ecdf5301c0968ab7c85547212e2a7bd126ddd2d986e206e/httpcore2-2.9.1.tar.gz", hash = "sha256:4d8acbf8b306f48c9d6046591fd5ba4037d1b1b1000d140fc2c3eab1e9a0c0e2", size = 67089, upload-time = "2026-07-24T09:21:03.867Z" } wheels = [ @@ -287,10 +287,10 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" } }, - { name = "certifi" }, - { name = "httpcore2", version = "2.0.0", source = { registry = "https://pypi.org/simple" } }, - { name = "idna" }, + { name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "certifi", marker = "python_full_version < '3.10'" }, + { name = "httpcore2", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "idna", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/70/95/868b3df4aa6f9cecb42aaf82a55a5a2c8ef40df76d709b812e1553d6a928/httpx2-2.0.0.tar.gz", hash = "sha256:f354249d2a9edce26e08fd2ad2276e98317b5763f673bb0d90f3ce9382cc2aed", size = 79506, upload-time = "2026-05-12T17:59:23.174Z" } wheels = [ @@ -305,11 +305,11 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "anyio", version = "4.14.2", source = { registry = "https://pypi.org/simple" } }, - { name = "httpcore2", version = "2.9.1", source = { registry = "https://pypi.org/simple" } }, - { name = "idna" }, - { name = "truststore" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "anyio", version = "4.14.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "httpcore2", version = "2.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "truststore", marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/21/14/38128fbafd7e0ed41d874df6c9a653d47c2d111cfe59e2b4ac95161b4abd/httpx2-2.9.1.tar.gz", hash = "sha256:1932a768737e3666291582833da748cc4e563c337cf96706fccc04fa6e58764a", size = 95458, upload-time = "2026-07-24T09:21:04.972Z" } wheels = [ @@ -354,7 +354,7 @@ name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markupsafe" }, + { name = "markupsafe", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ @@ -363,7 +363,7 @@ wheels = [ [[package]] name = "just-bashit" -version = "0.3.2" +version = "0.4.0" source = { editable = "." } [package.dev-dependencies] @@ -410,7 +410,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "mdurl" }, + { name = "mdurl", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ @@ -425,7 +425,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "mdurl" }, + { name = "mdurl", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ @@ -533,8 +533,8 @@ name = "mdformat" version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" } }, - { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3f/05/32b5e14b192b0a8a309f32232c580aefedd9d06017cb8fe8fce34bec654c/mdformat-1.0.0.tar.gz", hash = "sha256:4954045fcae797c29f86d4ad879e43bb151fa55dbaf74ac6eaeacf1d45bb3928", size = 56953, upload-time = "2025-10-16T12:05:03.695Z" } wheels = [ @@ -546,9 +546,9 @@ name = "mdformat-frontmatter" version = "2.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdformat" }, - { name = "mdit-py-plugins" }, - { name = "ruamel-yaml" }, + { name = "mdformat", marker = "python_full_version >= '3.10'" }, + { name = "mdit-py-plugins", marker = "python_full_version >= '3.10'" }, + { name = "ruamel-yaml", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/90/ae8a3d20066295367d9048ad480bcde3afe694eb592fec9c35bbdb7d68eb/mdformat_frontmatter-2.1.2.tar.gz", hash = "sha256:9c9c3159c38407f47e567202749c74ce7680017516cd158c82400f015c6a01d1", size = 9425, upload-time = "2026-05-23T05:14:26.324Z" } wheels = [ @@ -560,10 +560,10 @@ name = "mdformat-gfm" version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" } }, - { name = "mdformat" }, - { name = "mdit-py-plugins" }, - { name = "wcwidth" }, + { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mdformat", marker = "python_full_version >= '3.10'" }, + { name = "mdit-py-plugins", marker = "python_full_version >= '3.10'" }, + { name = "wcwidth", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/6f/a626ebb142a290474401b67e2d61e73ce096bf7798ee22dfe6270f924b3f/mdformat_gfm-1.0.0.tar.gz", hash = "sha256:d1d49a409a6acb774ce7635c72d69178df7dce1dc8cdd10e19f78e8e57b72623", size = 10112, upload-time = "2025-10-16T09:12:22.402Z" } wheels = [ @@ -575,8 +575,8 @@ name = "mdformat-gfm-alerts" version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdformat" }, - { name = "mdit-py-plugins" }, + { name = "mdformat", marker = "python_full_version >= '3.10'" }, + { name = "mdit-py-plugins", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bf/17/98382efef9d4a95fef60f1b5bf9e79a5f62f6698c747d2c99baacdf4cff8/mdformat_gfm_alerts-2.1.0.tar.gz", hash = "sha256:27673500976f08eb9e53cfa359f2a528c1be12052d3f8358996b9c5b1cc0c5c2", size = 9624, upload-time = "2026-07-26T06:46:31.343Z" } wheels = [ @@ -588,10 +588,10 @@ name = "mdformat-mkdocs" version = "5.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdformat" }, - { name = "mdformat-gfm" }, - { name = "mdit-py-plugins" }, - { name = "more-itertools" }, + { name = "mdformat", marker = "python_full_version >= '3.10'" }, + { name = "mdformat-gfm", marker = "python_full_version >= '3.10'" }, + { name = "mdit-py-plugins", marker = "python_full_version >= '3.10'" }, + { name = "more-itertools", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2e/8c/ca9c13017fcb224e9a0c17c214279eb7273318d1890cd0adc80a3c30e443/mdformat_mkdocs-5.3.0.tar.gz", hash = "sha256:9ae35940cfc1d350c41dda717963c90c669937fbbe3be32412a2b975e4bf891d", size = 33319, upload-time = "2026-08-02T18:38:07.954Z" } wheels = [ @@ -603,7 +603,7 @@ name = "mdit-py-plugins" version = "0.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" } }, + { name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" } wheels = [ @@ -669,11 +669,11 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "cfgv", version = "3.4.0", source = { registry = "https://pypi.org/simple" } }, - { name = "identify", version = "2.6.15", source = { registry = "https://pypi.org/simple" } }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, + { name = "cfgv", version = "3.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "identify", version = "2.6.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "nodeenv", marker = "python_full_version < '3.10'" }, + { name = "pyyaml", marker = "python_full_version < '3.10'" }, + { name = "virtualenv", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } wheels = [ @@ -688,11 +688,11 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "cfgv", version = "3.5.0", source = { registry = "https://pypi.org/simple" } }, - { name = "identify", version = "2.6.19", source = { registry = "https://pypi.org/simple" } }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, + { name = "cfgv", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "identify", version = "2.6.19", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "nodeenv", marker = "python_full_version >= '3.10'" }, + { name = "pyyaml", marker = "python_full_version >= '3.10'" }, + { name = "virtualenv", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } wheels = [ @@ -707,7 +707,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "wcwidth" }, + { name = "wcwidth", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ @@ -722,7 +722,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "wcwidth" }, + { name = "wcwidth", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz", hash = "sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", size = 435492, upload-time = "2026-07-26T20:56:14.758Z" } wheels = [ @@ -883,9 +883,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv", version = "1.2.1", source = { registry = "https://pypi.org/simple" } }, - { name = "typing-inspection" }, + { name = "pydantic", marker = "python_full_version < '3.10'" }, + { name = "python-dotenv", version = "1.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "typing-inspection", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } wheels = [ @@ -900,9 +900,9 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv", version = "1.2.2", source = { registry = "https://pypi.org/simple" } }, - { name = "typing-inspection" }, + { name = "pydantic", marker = "python_full_version >= '3.10'" }, + { name = "python-dotenv", version = "1.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "typing-inspection", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } wheels = [ @@ -923,8 +923,8 @@ name = "pymdown-extensions" version = "11.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, + { name = "markdown", marker = "python_full_version >= '3.10'" }, + { name = "pyyaml", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/21/a9/5f0c535ba3b08fe09270c16808e053a968868242ecbd5676d4e3a488bf28/pymdown_extensions-11.0.1.tar.gz", hash = "sha256:dd2905ae6fc5b75582fafb139a1266ffc754705efa902aa50067fa7ff4f94ec0", size = 857113, upload-time = "2026-07-02T17:59:22.955Z" } wheels = [ @@ -1214,7 +1214,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "bracex", version = "2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "bracex", version = "2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421, upload-time = "2025-06-22T19:14:02.49Z" } wheels = [ @@ -1229,7 +1229,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "bracex", version = "3.0.1", source = { registry = "https://pypi.org/simple" } }, + { name = "bracex", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/16/25/1da725838132221e33568973da484ff43813662ccc06ebf7f6e3abddfcd5/wcmatch-11.0.tar.gz", hash = "sha256:55d95c2447789712774b198ceec72939e88b5618f1f8f0a9b605bf7740b63b96", size = 141360, upload-time = "2026-07-10T05:50:24.183Z" } wheels = [ @@ -1250,14 +1250,14 @@ name = "zensical" version = "0.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.4.2", source = { registry = "https://pypi.org/simple" } }, - { name = "deepmerge" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "pygments" }, - { name = "pymdown-extensions" }, - { name = "pyyaml" }, - { name = "tomli" }, + { name = "click", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "deepmerge", marker = "python_full_version >= '3.10'" }, + { name = "jinja2", marker = "python_full_version >= '3.10'" }, + { name = "markdown", marker = "python_full_version >= '3.10'" }, + { name = "pygments", marker = "python_full_version >= '3.10'" }, + { name = "pymdown-extensions", marker = "python_full_version >= '3.10'" }, + { name = "pyyaml", marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/53/f3657dc0ed7666b29cededfeb424b28b8cf1f6ca75f7066af76fca8c1bcf/zensical-0.0.52.tar.gz", hash = "sha256:b11b79dd1bb7da4c1a5293cbc5a2f4394d980bf2bf1c4c326062bc5ddcf2a2e8", size = 3991761, upload-time = "2026-07-30T10:22:51.45Z" } wheels = [ From c618d0cd75c4e07bab947c48b5c71af05bb6a2cb Mon Sep 17 00:00:00 2001 From: hunterdsp Date: Thu, 6 Aug 2026 20:43:53 -0400 Subject: [PATCH 2/2] docs: document make-run, fix paths stale since 0.2.0, gate both MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make-run shipped in 0.4.0 as the headline feature with no page, no nav entry, and no mention in the README — so the one tool whose purpose is ending hand-transcribed commands could only be found by reading its source. The new page covers all five functions, the rule that an undefined variable is an error and never an empty string, and why the queries go through a sentinel makefile rather than --eval. Every documented source path had also been wrong since 0.2.0. Scripts moved to src/just_bashit/ for Python packaging and the docs kept saying src/, so `. just-bashit/src/datetime.sh` failed for anyone who copied it — including out of an unpacked release tarball, which carries the current layout. The README additionally described a package layout that no longer existed and never mentioned installation or the entry points. The gate is what keeps this from recurring. Two invariants, one per way it went unnoticed: every file in src/just_bashit/ is named somewhere in the docs, and every page under docs/ is reachable from zensical.toml's nav, since an unlinked page builds cleanly and looks exactly like one that was never written. docs/changelog.md is excluded as a mention source — it records every script ever added, so counting it would pass everything the moment it was released. Dispatched from pre-commit rather than DOCS_CHECK_PRE_CMDS because CI runs make test, make coverage and make lint, not make docs-check; a gate hung off docs-check would be local-only. always_run rather than types: [markdown] because what breaks it is adding a script, and that commit touches no markdown at all. Both failure modes were verified red before the fixes went in. Co-Authored-By: Claude Opus 5 (1M context) --- .pre-commit-config.yaml | 10 ++ CHANGELOG.md | 31 +++++++ Makefile | 13 +++ README.md | 39 +++++++- docs/getting-started.md | 6 +- docs/index.md | 3 +- docs/inspect.md | 2 +- docs/install-deps.md | 2 +- docs/just-runit.md | 4 +- docs/libraries/datetime.md | 4 +- docs/libraries/environment.md | 8 +- docs/libraries/file.md | 8 +- docs/libraries/format.md | 6 +- docs/libraries/logging.md | 6 +- docs/libraries/make-run.md | 166 ++++++++++++++++++++++++++++++++++ docs/libraries/match.md | 6 +- docs/libraries/network.md | 4 +- docs/libraries/path.md | 6 +- docs/libraries/pkg.md | 6 +- docs/libraries/toml.md | 4 +- docs/templates.md | 10 +- scripts/docs_coverage.sh | 85 +++++++++++++++++ zensical.toml | 1 + 23 files changed, 385 insertions(+), 45 deletions(-) create mode 100644 docs/libraries/make-run.md create mode 100644 scripts/docs_coverage.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40952cc..416dc1c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,3 +31,13 @@ repos: language: system pass_filenames: false types: [markdown] + + # always_run, not types: [markdown] — what breaks this gate is adding a + # script under src/just_bashit/, and that commit touches no markdown at + # all. A markdown-scoped hook would have stayed silent for make-run. + - id: docs-coverage + name: docs-coverage + entry: make -s docs-coverage + language: system + pass_filenames: false + always_run: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bc3aed..87f8670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ ## [Unreleased] +### Added + +- **`make-run` is documented.** It shipped in 0.4.0 as the headline feature + and appeared in no page, no nav entry, and no line of the README — so the + one tool whose whole purpose is ending hand-transcribed commands could + only be found by reading its source. New page under Libraries covering + `mk-var`, `mk-vars`, `mk-run`, `mk-has` and `mk-origin`, the + undefined-is-not-empty rule, and why the queries avoid `--eval`. + +- **`docs-coverage` gate.** Two invariants, one for each way the above went + unnoticed: every file shipped in `src/just_bashit/` must be named + somewhere in the docs, and every page under `docs/` must be reachable + from `zensical.toml`'s nav. `docs/changelog.md` is excluded as a mention + source — it records every script ever added, so counting it would pass + everything the moment it was released. Dispatched from pre-commit with + `always_run`, so it runs inside `make lint` (which CI runs) and fires on + commits touching no markdown, which is what adding a script looks like. + +### Fixed + +- **The docs pointed at `src/`, where nothing has lived since 0.2.0.** + Scripts moved to `src/just_bashit/` for Python packaging and the docs kept + the old path, so every `. just-bashit/src/datetime.sh` in the README, the + getting-started guide, and all ten library pages failed for anyone who + copied one — including from an unpacked release tarball, which carries the + current layout. + +- **The README described a package layout that no longer existed**, and never + mentioned installation, the `jb` / `jbx` / `jb-inspect` entry points, or + `make-run`. + ## [0.4.0] - 2026-08-06 ### Added diff --git a/Makefile b/Makefile index b869fd4..bfeb6ac 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,19 @@ else \ fi endef +# ── docs coverage ───────────────────────────────────────────────────────────── +# A repo-local target, so it needs naming here or help-check reports a rule +# that help does not list. +# +# Dispatched from .pre-commit-config.yaml rather than DOCS_CHECK_PRE_CMDS: CI +# runs `make test`, `make coverage` and `make lint` — not `make docs-check` — +# so a docs gate hung off docs-check would be local-only, which is how a rule +# nobody enforces sits on main indefinitely. +LOCAL_TARGETS += docs-coverage + +docs-coverage: ## Verify every shipped script is documented and in the nav + @bash scripts/docs_coverage.sh + # ── all ─────────────────────────────────────────────────────────────────────── # Lint first: shellcheck and shfmt are seconds, the bats suite is minutes. # diff --git a/README.md b/README.md index df2e2e5..6837881 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,31 @@ Evolving set of [shfmt](https://github.com/mvdan/sh#shfmt)-conformant, [bats](ht **[Documentation](https://just-buildit.github.io/just-bashit/)** +## Install + +```bash +uv tool install just-bashit # or: pip install just-bashit +``` + +That puts three commands on `PATH`: `jb` (top-level CLI), `jbx` (ephemeral +runner — fetch a script, call a function, discard), and `jb-inspect`. + +Nothing needs installing to *use* a script. `jbx` fetches on demand: + +```bash +jbx install-deps # install this repo's packages +jbx make-run mk-var -C ~/doppler DOCS_BUILD_CMD +``` + ## Getting Started -A release package contains shell libraries along with a script and two function templates for developing your own tools. +A release package contains shell libraries along with script and function +templates for developing your own tools. ``` just-bashit +--README.md - +--src/ + +--src/just_bashit/ | +-- datetime.sh | +-- environment.sh : : @@ -36,7 +53,23 @@ just-bashit Some libraries depend on others so it's best to use the whole package and source whatever you need, for example: ```bash -. just-bashit/src/datetime.sh # contains iso-8601-basic() +. just-bashit/src/just_bashit/datetime.sh # contains iso-8601-basic() iso-8601-basic -d '10:32 AM EDT Jan 5 1982' 19820105T143200Z ``` + +## Ask a repo what its commands are + +`make-run` resolves a repository's targets and command variables from that +repository's own `Makefile`, so nothing downstream has to carry a second copy +of a command that will drift from it: + +```bash +jbx make-run mk-var -C ~/doppler DOCS_BUILD_CMD +# uv run --group docs zensical build --clean --strict + +jbx make-run mk-run -C ~/doppler docs # run it, whatever it is +``` + +An **undefined** variable is an error, never an empty string — see +[the docs](https://just-buildit.github.io/just-bashit/libraries/make-run/). diff --git a/docs/getting-started.md b/docs/getting-started.md index 6515121..3f35d7d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -35,8 +35,8 @@ All libraries **must be sourced**, not executed. Each file enforces this at load time and will exit with an error if you try to run it directly. ```bash -. just-bashit/src/logging.sh # correct -bash just-bashit/src/logging.sh # error: "This file must be sourced." +. just-bashit/src/just_bashit/logging.sh # correct +bash just-bashit/src/just_bashit/logging.sh # error: "This file must be sourced." ``` Some libraries depend on others (`logging.sh` sources `format.sh`, @@ -49,7 +49,7 @@ chain resolves automatically from relative paths. Every function accepts `-h`: ```bash -. just-bashit/src/datetime.sh +. just-bashit/src/just_bashit/datetime.sh iso-8601-basic -h ``` diff --git a/docs/index.md b/docs/index.md index 0e63d90..99ec7b9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -29,7 +29,7 @@ Download the latest release, extract it, and source whatever you need: ```bash tar xf just-bashit.tar.gz -. just-bashit/src/datetime.sh +. just-bashit/src/just_bashit/datetime.sh iso-8601-basic # 20260522T143200Z ``` @@ -61,6 +61,7 @@ package and source individual files from it. | [file](libraries/file.md) | `add-line` `remove-line` `add-contents` | | [format](libraries/format.md) | `trim-from` `color-echo` | | [logging](libraries/logging.md) | `log` `log-wait` | +| [make-run](libraries/make-run.md) | `mk-var` `mk-vars` `mk-run` `mk-has` `mk-origin` | | [match](libraries/match.md) | `is-number` | | [network](libraries/network.md) | `test-internet-access` | | [path](libraries/path.md) | `get-scriptpath` `set-scriptpath` | diff --git a/docs/inspect.md b/docs/inspect.md index d33aabc..6929dee 100644 --- a/docs/inspect.md +++ b/docs/inspect.md @@ -263,5 +263,5 @@ jbx inspect -v 2>&1 | head jbx inspect | diff jb.versions - # Pin script to a specific commit for reproducible CI output -jbx gh:just-buildit/just-bashit/src/inspect.sh@abc1234 -w +jbx gh:just-buildit/just-bashit/src/just_bashit/inspect.sh@abc1234 -w ``` diff --git a/docs/install-deps.md b/docs/install-deps.md index 25ce1c5..36dd04a 100644 --- a/docs/install-deps.md +++ b/docs/install-deps.md @@ -282,7 +282,7 @@ jbx install-deps --template deps.toml # write to file ### Pin to a specific script commit for reproducible CI ```bash -jbx gh:just-buildit/just-bashit/src/install-deps.sh@abc1234 +jbx gh:just-buildit/just-bashit/src/just_bashit/install-deps.sh@abc1234 ``` ______________________________________________________________________ diff --git a/docs/just-runit.md b/docs/just-runit.md index 5dc2d8b..c0f4395 100644 --- a/docs/just-runit.md +++ b/docs/just-runit.md @@ -155,7 +155,7 @@ used. # See which URL a cache file came from and when it was fetched cat ~/.cache/just-runit/.meta # ts=1779583354 -# url=https://raw.githubusercontent.com/just-buildit/just-bashit/main/src/install-deps.sh +# url=https://raw.githubusercontent.com/just-buildit/just-bashit/main/src/just_bashit/install-deps.sh ``` **Force a fresh fetch (keep the entry, overwrite it)** @@ -173,7 +173,7 @@ jbx -n https://example.com/tool.sh run **Purge a single entry** ```bash -jb cache clear https://raw.githubusercontent.com/just-buildit/just-bashit/main/src/install-deps.sh +jb cache clear https://raw.githubusercontent.com/just-buildit/just-bashit/main/src/just_bashit/install-deps.sh ``` Or manually — find the hash with `-v` then remove the pair: diff --git a/docs/libraries/datetime.md b/docs/libraries/datetime.md index adf963b..d737c04 100644 --- a/docs/libraries/datetime.md +++ b/docs/libraries/datetime.md @@ -1,13 +1,13 @@ # datetime -Source: `src/datetime.sh` +Source: `src/just_bashit/datetime.sh` ## iso-8601-basic Generate a path- and filename-friendly ISO 8601 UTC timestamp. ```bash -. just-bashit/src/datetime.sh +. just-bashit/src/just_bashit/datetime.sh iso-8601-basic # 20260522T143200Z ``` diff --git a/docs/libraries/environment.md b/docs/libraries/environment.md index d971645..c3a631e 100644 --- a/docs/libraries/environment.md +++ b/docs/libraries/environment.md @@ -1,6 +1,6 @@ # environment -Source: `src/environment.sh` +Source: `src/just_bashit/environment.sh` Idempotent management of `~/.bashrc` entries and command existence checks. @@ -11,7 +11,7 @@ ______________________________________________________________________ Write a line to `~/.bashrc` only if it is not already present. ```bash -. just-bashit/src/environment.sh +. just-bashit/src/just_bashit/environment.sh # Write a verbatim line set-bashrc 'alias ll="ls -la"' @@ -45,7 +45,7 @@ ______________________________________________________________________ Remove a line from `~/.bashrc` if present. No-op if not found. ```bash -. just-bashit/src/environment.sh +. just-bashit/src/just_bashit/environment.sh # Remove a verbatim line unset-bashrc 'alias ll="ls -la"' @@ -79,7 +79,7 @@ ______________________________________________________________________ Return 0 if a command is available in `PATH`, 1 otherwise. ```bash -. just-bashit/src/environment.sh +. just-bashit/src/just_bashit/environment.sh check-command-exists curl && echo "curl is available" check-command-exists nonexistent || echo "not found" diff --git a/docs/libraries/file.md b/docs/libraries/file.md index 8c04c0a..97c4ce4 100644 --- a/docs/libraries/file.md +++ b/docs/libraries/file.md @@ -1,6 +1,6 @@ # file -Source: `src/file.sh` +Source: `src/just_bashit/file.sh` Idempotent line-level file content management. @@ -12,7 +12,7 @@ Write a line to a file only if it is not already present. Optionally write a blank line. ```bash -. just-bashit/src/file.sh +. just-bashit/src/just_bashit/file.sh # Append a line (idempotent — safe to call repeatedly) add-line "export PATH=$PATH:/opt/myapp/bin" /etc/environment @@ -48,7 +48,7 @@ ______________________________________________________________________ Remove a line from a file if present. No-op if not found. ```bash -. just-bashit/src/file.sh +. just-bashit/src/just_bashit/file.sh remove-line "export PATH=$PATH:/opt/myapp/bin" /etc/environment @@ -82,7 +82,7 @@ ______________________________________________________________________ Copy every line from one file into another, skipping duplicates. ```bash -. just-bashit/src/file.sh +. just-bashit/src/just_bashit/file.sh # Merge new.conf into existing.conf without duplicating lines add-contents new.conf existing.conf diff --git a/docs/libraries/format.md b/docs/libraries/format.md index c38ae4d..79caae3 100644 --- a/docs/libraries/format.md +++ b/docs/libraries/format.md @@ -1,6 +1,6 @@ # format -Source: `src/format.sh` +Source: `src/just_bashit/format.sh` String trimming and colorized output. @@ -12,7 +12,7 @@ Trim a string at a marker character, from either direction, with optional greedy and keep-marker modes. ```bash -. just-bashit/src/format.sh +. just-bashit/src/just_bashit/format.sh trim-from 'report.2026.tar.gz' # report.2026.tar trim-from -r 'report.2026.tar.gz' # 2026.tar.gz @@ -53,7 +53,7 @@ ______________________________________________________________________ Print colorized text to stdout using ANSI escape sequences. ```bash -. just-bashit/src/format.sh +. just-bashit/src/just_bashit/format.sh color-echo -bc green "Build succeeded" color-echo -bc red "Build failed" diff --git a/docs/libraries/logging.md b/docs/libraries/logging.md index 30b2911..2ed00db 100644 --- a/docs/libraries/logging.md +++ b/docs/libraries/logging.md @@ -1,6 +1,6 @@ # logging -Source: `src/logging.sh` +Source: `src/just_bashit/logging.sh` Structured, colorized logging with timestamps. Depends on `format.sh`, `match.sh`, and `datetime.sh` — all resolved automatically from relative paths. @@ -12,7 +12,7 @@ ______________________________________________________________________ Log a message to stdout with a timestamp and type label. ```bash -. just-bashit/src/logging.sh +. just-bashit/src/just_bashit/logging.sh log "Starting deployment" # [20260522T143200Z::INFO]::Starting deployment @@ -74,7 +74,7 @@ ______________________________________________________________________ Sleep for a given duration with input validation. ```bash -. just-bashit/src/logging.sh +. just-bashit/src/just_bashit/logging.sh log-wait 5 # sleep 5 seconds log-wait 0.5 # sleep 500ms diff --git a/docs/libraries/make-run.md b/docs/libraries/make-run.md new file mode 100644 index 0000000..2ffb92c --- /dev/null +++ b/docs/libraries/make-run.md @@ -0,0 +1,166 @@ +# make-run + +Source: `src/just_bashit/make-run.sh` + +Ask a repository what its commands actually are, instead of transcribing +them. A repo bases its `Makefile` on +[`standard.mk`](https://just-buildit.github.io/standard.mk), which defines +the sanctioned defaults with `?=`; the repo then overrides what it must. +`make-run` reads the result of that layering, so there is no second copy to +vendor and nothing to keep in sync. + +The problem it removes is drift. A skill doc said `make docs` runs +`uv run zensical build --clean`, the sanctioned default was +`uv run --group dev zensical build --clean --strict`, and doppler — which +overrides `ZENSICAL` — actually ran `uv run --group docs ...`. Three answers, +two wrong, and nothing to make them disagree out loud. + +```bash +jbx make-run mk-var -C ~/doppler DOCS_BUILD_CMD +# uv run --group docs zensical build --clean --strict +``` + +______________________________________________________________________ + +## Functions + +| Function | Answers | +| ------------------------------ | -------------------------------------------- | +| `mk-var [-C DIR] NAME` | What is `NAME` set to, fully expanded? | +| `mk-vars [-C DIR] [REGEX]` | What is every file-defined variable set to? | +| `mk-run [-C DIR] TARGET [...]` | Run `TARGET` with the repo's own recipe | +| `mk-has [-C DIR] TARGET` | Does the repo define `TARGET`? (predicate) | +| `mk-origin [-C DIR] NAME` | Where did make get `NAME` — file, default, … | + +Every function takes `-C DIR` (default: the current directory) and `-h`. +Nothing is built and no recipe is run, except by `mk-run`, which is the one +function that is supposed to. + +______________________________________________________________________ + +## Usage + +`make-run.sh` is a library: it must be sourced, and it calls no other +just-bashit library. Either run it ephemerally through +[`jbx`](../just-runit.md), which sources the script and invokes the named +function: + +```bash +jbx make-run mk-var -C ~/doppler DOCS_BUILD_CMD +jbx make-run mk-has -C ~/doppler docs && echo "doppler builds docs" +jbx just-bashit:make-run -l # list the functions it defines +``` + +or source it into a shell that needs several of the functions: + +```bash +. just-bashit/src/just_bashit/make-run.sh + +mk-run -C ~/doppler docs +``` + +______________________________________________________________________ + +## The load-bearing rule: undefined is not empty + +`mk-var` **fails** when a variable was never defined, and **succeeds +printing an empty line** when it was defined as empty: + +```bash +mk-var -C ~/doppler DOCS_CHECK_CMD # legitimately empty → exit 0, blank line +mk-var -C ~/doppler DOSC_BUILD_CMD # typo, undefined → exit 1, message +# make-run: DOSC_BUILD_CMD is not defined in /home/you/doppler +``` + +Collapsing those two states is how a wrong command reads as a blank one — a +caller publishes an empty string where a real command belongs and nothing +looks broken. `mk-var` asks make's own `$(origin)` first, which is the only +reliable way to tell the two apart. `mk-origin` exposes that answer directly: + +```bash +mk-origin -C ~/doppler DOCS_BUILD_CMD # file +mk-origin -C ~/doppler CC # default +mk-origin -C ~/doppler NOPE # undefined +``` + +The same rule applies one level down. An unparseable makefile fails loudly +rather than handing back an empty database: `make -pRrq` exits 1 in question +mode, which is normal and ignored, and 2 when it cannot read the makefiles +at all, which is reported. + +______________________________________________________________________ + +## Examples + +### Read one command + +```bash +mk-var -C ~/doppler DOCS_BUILD_CMD +# uv run --group docs zensical build --clean --strict +``` + +### Read a whole family of them + +`mk-vars` takes no built-in list of "interesting" variables — such a list +would be exactly the hand-maintained copy this library exists to abolish. +Filter by name with an ERE instead, and note that all values resolve in a +single `make` invocation: + +```bash +mk-vars -C ~/doppler '_CMD$' +# DOCS_BUILD_CMD=uv run --group docs zensical build --clean --strict +# TEST_CMD=ctest --test-dir build --output-on-failure +``` + +### Run a target without knowing its recipe + +```bash +mk-run -C ~/doppler docs +``` + +`mk-run docs` runs whatever *that* repo means by `docs`, so a caller never +needs to know the command and therefore can never drift from it. The target +is verified to exist first, so a rename fails with a useful message instead +of make's `No rule to make target`. Arguments after the target pass straight +through to make: + +```bash +mk-run -C ~/doppler test -j8 +``` + +### Branch on whether a repo supports something + +```bash +if mk-has -C "${repo}" docs-check; then + mk-run -C "${repo}" docs-check +fi +``` + +`mk-has` reads make's target database rather than dry-running, so a target +with a missing prerequisite still answers honestly instead of erroring. + +______________________________________________________________________ + +## Requirements + +- **GNU make 3.81 or later.** The library expands its queries through a + throwaway sentinel makefile passed alongside the repo's own via `-f`, + rather than `--eval`, which arrived in 3.82 — macOS still ships 3.81 as + `/usr/bin/make`, so an `--eval` implementation passes every Linux runner + and fails only on the platform most likely to be running an old make. +- **A makefile in `DIR`.** `GNUmakefile`, `makefile`, or `Makefile`, in + make's own search order. Anything else exits 1 with + `make-run: no makefile in DIR`. +- `standard.mk` is not required. Any makefile can be queried; the defaults + layering is simply what makes the answers worth asking for. + +______________________________________________________________________ + +## What it does not do + +Nothing is built by `mk-var`, `mk-vars`, `mk-has`, or `mk-origin`. The +sentinel target is `.PHONY` with a no-op recipe, so make neither reports it +up to date nor runs any real recipe — the expansion happens at recipe run +time, which is what lets `$(info ...)` observe fully-layered values. +Evaluating the same expression while the makefiles are still being read +yields nothing, because the variables do not exist yet. diff --git a/docs/libraries/match.md b/docs/libraries/match.md index f422f98..02006a9 100644 --- a/docs/libraries/match.md +++ b/docs/libraries/match.md @@ -1,6 +1,6 @@ # match -Source: `src/match.sh` +Source: `src/just_bashit/match.sh` Regex pattern constants and numeric validation. @@ -18,7 +18,7 @@ Exported variables for use in `[[ =~ ]]` expressions: | `IS_ONLY_SIGNED_NUMBER` | `IS_SIGNED_NUMBER` anchored to full string | ```bash -. just-bashit/src/match.sh +. just-bashit/src/just_bashit/match.sh [[ "3.14" =~ $IS_ONLY_NUMBER ]] && echo "numeric" [[ "-7" =~ $IS_ONLY_SIGNED_NUMBER ]] && echo "signed numeric" @@ -31,7 +31,7 @@ ______________________________________________________________________ Return 0 (PASS) if a string is a valid number, 1 (FAIL) otherwise. ```bash -. just-bashit/src/match.sh +. just-bashit/src/just_bashit/match.sh is-number 42 # PASS is-number 3.14 # PASS diff --git a/docs/libraries/network.md b/docs/libraries/network.md index a4bce27..5a6d5d6 100644 --- a/docs/libraries/network.md +++ b/docs/libraries/network.md @@ -1,6 +1,6 @@ # network -Source: `src/network.sh` +Source: `src/just_bashit/network.sh` Network connectivity testing. Depends on `format.sh` and `environment.sh`. @@ -13,7 +13,7 @@ Test internet connectivity by probing URLs with `curl`, `wget`, and/or `ping` timeout. ```bash -. just-bashit/src/network.sh +. just-bashit/src/just_bashit/network.sh # Silent check — returns 0/1 test-internet-access && echo "online" || echo "offline" diff --git a/docs/libraries/path.md b/docs/libraries/path.md index 1e84e04..1e48322 100644 --- a/docs/libraries/path.md +++ b/docs/libraries/path.md @@ -1,6 +1,6 @@ # path -Source: `src/path.sh` +Source: `src/just_bashit/path.sh` Resolve the directory containing the calling script, regardless of how it was invoked (executed or sourced, symlinked or direct). @@ -13,7 +13,7 @@ Print the absolute, symlink-resolved path to the directory containing the calling script. ```bash -. just-bashit/src/path.sh +. just-bashit/src/just_bashit/path.sh # In your own script: SCRIPTDIR=$(get-scriptpath) @@ -45,7 +45,7 @@ Set the `SCRIPTPATH` environment variable to the calling script's directory. Must be called via `eval` so the export propagates to the calling environment. ```bash -. just-bashit/src/path.sh +. just-bashit/src/just_bashit/path.sh eval $(set-scriptpath) echo "${SCRIPTPATH}" # /absolute/path/to/your/script/dir diff --git a/docs/libraries/pkg.md b/docs/libraries/pkg.md index ffc7723..fbec6f9 100644 --- a/docs/libraries/pkg.md +++ b/docs/libraries/pkg.md @@ -1,6 +1,6 @@ # pkg -Source: `src/pkg.sh` +Source: `src/just_bashit/pkg.sh` Package manager detection and installed-version querying. @@ -12,7 +12,7 @@ Print the name of the active package manager for the running OS. Returns non-zero and prints to stderr if the OS is unrecognised. ```bash -. just-bashit/src/pkg.sh +. just-bashit/src/just_bashit/pkg.sh pm=$(get-pkg-mgr) # e.g. "pacman" on Arch, "apt" on Debian echo "Using ${pm}" @@ -52,7 +52,7 @@ Print the installed version of a package using the specified package manager. Prints nothing (not an error) if the package is not installed. ```bash -. just-bashit/src/pkg.sh +. just-bashit/src/just_bashit/pkg.sh get-pkg-version apt curl # e.g. "8.5.0-2" get-pkg-version pacman bash # e.g. "5.2.37-1" diff --git a/docs/libraries/toml.md b/docs/libraries/toml.md index 198ed50..5abe93a 100644 --- a/docs/libraries/toml.md +++ b/docs/libraries/toml.md @@ -1,6 +1,6 @@ # toml -Source: `src/toml.sh` +Source: `src/just_bashit/toml.sh` Pure-bash parser for the TOML subset used by just-bashit dependency files: `[group.pm]` sections with `packages = [...]` and `cmd = [...]` arrays. @@ -14,7 +14,7 @@ Extract each double-quoted string value from a TOML fragment, one per line. Skips empty quoted strings. ```bash -. just-bashit/src/toml.sh +. just-bashit/src/just_bashit/toml.sh toml_strings '"curl", "wget"' # prints: curl\nwget toml_strings '"a", "", "b"' # prints: a\nb (empty string skipped) diff --git a/docs/templates.md b/docs/templates.md index 4cfbb58..73f6f88 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -1,7 +1,7 @@ # Templates -Source: `src/function-template.sh`, `src/script-template`, -`src/bashrc-template.sh`, `src/profile-template.sh` +Source: `src/just_bashit/function-template.sh`, `src/just_bashit/script-template`, +`src/just_bashit/bashrc-template.sh`, `src/just_bashit/profile-template.sh` Copy-paste starting points for new bash functions and scripts, plus the opinionated shell configuration [`setup-system`](setup-system.md) installs. @@ -16,7 +16,7 @@ getopts-based option parsing, a heredoc help string, variable initialization before and after `getopts`, and a nested helper function. ```bash -. just-bashit/src/function-template.sh +. just-bashit/src/just_bashit/function-template.sh full-on-template -h # show usage full-on-template -p myvalue arg1 arg2 @@ -32,7 +32,7 @@ A stripped-down function template for simple functions that don't need the full getopts machinery. ```bash -. just-bashit/src/function-template.sh +. just-bashit/src/just_bashit/function-template.sh minimalist-template -h minimalist-template arg1 @@ -53,7 +53,7 @@ An executable script template (not a library) demonstrating: ```bash # Copy and rename -cp just-bashit/src/script-template my-script +cp just-bashit/src/just_bashit/script-template my-script chmod +x my-script ./my-script -h ``` diff --git a/scripts/docs_coverage.sh b/scripts/docs_coverage.sh new file mode 100644 index 0000000..10b54fe --- /dev/null +++ b/scripts/docs_coverage.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# ############################################################################ +# docs_coverage.sh — repo gate, not a shipped library. # +# ############################################################################ +# Two invariants, because make-run shipped in 0.4.0 breaking both and nothing # +# said so: it was documented nowhere, and a page written for it would still # +# have been invisible without a nav entry. # +# # +# 1. Every file shipped in src/just_bashit/ is named somewhere in the # +# user-facing docs. # +# 2. Every page under docs/ is reachable from zensical.toml's nav. # +# # +# docs/changelog.md is excluded as a mention source. It records every script # +# ever added, so counting it would pass every script the moment it was # +# released — a gate that is green by construction. # +# # +# Failures accumulate: both checks always run and every offender is listed, # +# so the set gets fixed once instead of being rediscovered one push at a # +# time. # +# ############################################################################ + +set -uo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/.." || exit 1 + +rc=0 + +# --------------------------------------------------------------------------- +# 1. Shipped scripts are documented. +# +# Anything a user sources or executes counts. The Python launcher shims and +# the packaged template toml do not: they are packaging plumbing, not +# something anyone reads about. +# +# find rather than `git ls-files`, so a page that exists but is not yet staged +# still counts — a gate that fails until you `git add` reads as noise. Under +# pre-commit this is moot anyway, since it stashes unstaged work first and the +# gate then sees exactly the tree being committed. +# --------------------------------------------------------------------------- +undocumented=() +while IFS= read -r path; do + name="${path##*/}" + case "${name}" in + *.py | *.toml) continue ;; + esac + if ! grep -rqF -- "${name}" README.md docs \ + --exclude=changelog.md 2>/dev/null; then + undocumented+=("${name}") + fi +done < <(find src/just_bashit -type f) + +if ((${#undocumented[@]} > 0)); then + echo "ERROR: shipped scripts documented nowhere under docs/:" + printf ' %s\n' "${undocumented[@]}" + echo " Add a page (docs/libraries/NAME.md for a sourced library," + echo " docs/NAME.md for a command) and list it in zensical.toml nav." + rc=1 +fi + +# --------------------------------------------------------------------------- +# 2. Pages are reachable. +# +# A page absent from nav builds fine and is even served, so nothing fails — +# it is simply unlinked, which from the outside looks the same as never +# having been written. Nav paths are relative to docs/, so the prefix is +# stripped before looking each one up. +# --------------------------------------------------------------------------- +unlisted=() +while IFS= read -r page; do + if ! grep -qF -- "\"${page#docs/}\"" zensical.toml; then + unlisted+=("${page}") + fi +done < <(find docs -name '*.md') + +if ((${#unlisted[@]} > 0)); then + echo "ERROR: docs pages missing from zensical.toml nav:" + printf ' %s\n' "${unlisted[@]}" + rc=1 +fi + +if ((rc == 0)); then + echo "docs-coverage: every shipped script documented, every page in nav" +fi + +exit "${rc}" diff --git a/zensical.toml b/zensical.toml index c9230a7..f3395cc 100644 --- a/zensical.toml +++ b/zensical.toml @@ -26,6 +26,7 @@ nav = [ {"file" = "libraries/file.md"}, {"format" = "libraries/format.md"}, {"logging" = "libraries/logging.md"}, + {"make-run" = "libraries/make-run.md"}, {"match" = "libraries/match.md"}, {"network" = "libraries/network.md"}, {"path" = "libraries/path.md"},