From 18f5ef391aa8dee74cf5a26043f222f20302ac82 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 16:14:10 +1100 Subject: [PATCH 1/7] chore: keep internal references out of docs and commit messages --- .pre-commit-config.yaml | 23 +++++++++++++++++++++++ README.md | 9 ++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..771dc11 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +# Pre-commit hooks. Install once per clone: prek install (Python pre-commit works too). +default_install_hook_types: [pre-commit, commit-msg] + +repos: + # Keeps internal references (ticket ids, secret-store paths, internal hosts and process + # names) out of user-facing docs and commit messages. The patterns are generic on purpose: + # a list of specific internal names would itself disclose them. + # Scope rule: https://docs.cachekit.io/contributing/#what-belongs-in-these-docs + - repo: local + hooks: + - id: no-internal-references + name: No internal references in docs + language: pygrep + entry: 'LAB-[0-9]+|op://|\.ts\.net|\bk3s\b|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' + files: ^(packages/[^/]+/)?README[^/]*$|^docs/ + types: [text] + stages: [pre-commit] + - id: no-internal-references-commit-msg + name: No internal references in commit message + language: pygrep + # Same patterns, skipping git's "#" comment lines, which quote the branch name. + entry: '^(?!#).*?(?:LAB-[0-9]+|op://|\.ts\.net|\bk3s\b|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-]))' + stages: [commit-msg] diff --git a/README.md b/README.md index 2e75bd4..964c138 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ is not repeated. A typical use is a secret read in a shell startup file, where every new shell would otherwise call the secret manager again: ```sh -API_TOKEN="$(ck run --ttl 12h --stale 7d -- op read op://Private/api/token)" +API_TOKEN="$(ck run --ttl 12h --stale 7d -- op item get api --fields token --reveal)" export API_TOKEN ``` @@ -278,6 +278,13 @@ Linux and macOS. The cache directory must be on a local filesystem: on NFS or SMB, concurrent first calls may each run the command, though entries are never torn. +## Contributing + +User-facing docs in this repository follow CacheKit's shared rule on what belongs in them: +[What belongs in these docs](https://docs.cachekit.io/contributing/#what-belongs-in-these-docs). +`prek install` (or `pre-commit install`) sets up hooks that reject internal references in README +files, `docs/` and commit messages. + ## License MIT. See [LICENSE](LICENSE). From 85c6373ef4932f818934b9ceef06f076f1d382b2 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 16:29:17 +1100 Subject: [PATCH 2/7] chore: narrow the cluster pattern, skip merge subjects, cover crate READMEs --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 771dc11..f645250 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,13 +11,13 @@ repos: - id: no-internal-references name: No internal references in docs language: pygrep - entry: 'LAB-[0-9]+|op://|\.ts\.net|\bk3s\b|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' - files: ^(packages/[^/]+/)?README[^/]*$|^docs/ + entry: 'LAB-[0-9]+|op://|\.ts\.net|(?i:k3s cluster)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' + files: ^((packages|crates)/[^/]+/)?README[^/]*$|^docs/ types: [text] stages: [pre-commit] - id: no-internal-references-commit-msg name: No internal references in commit message language: pygrep - # Same patterns, skipping git's "#" comment lines, which quote the branch name. - entry: '^(?!#).*?(?:LAB-[0-9]+|op://|\.ts\.net|\bk3s\b|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-]))' + # Same patterns, skipping git's "#" comment lines and "Merge" subjects, which quote branch names. + entry: '^(?!#|Merge ).*?(?:LAB-[0-9]+|op://|\.ts\.net|(?i:k3s cluster)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-]))' stages: [commit-msg] From f569b29bfa99e874c613b1cd38ebfa7a88ef58ff Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 16:59:22 +1100 Subject: [PATCH 3/7] chore: check the commit message as git records it `git commit -v` hands the commit-msg hook the staged diff below git's cut line, so a clean message failed whenever the diff matched a pattern. Both hooks now run one script: it stops at the cut line, skips lines that start with core.commentChar, and skips only a Merge subject. The patterns live in one place instead of two. --- .pre-commit-config.yaml | 13 +++--- scripts/no_internal_references.py | 68 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 scripts/no_internal_references.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f645250..c42b86b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,21 +3,18 @@ default_install_hook_types: [pre-commit, commit-msg] repos: # Keeps internal references (ticket ids, secret-store paths, internal hosts and process - # names) out of user-facing docs and commit messages. The patterns are generic on purpose: - # a list of specific internal names would itself disclose them. - # Scope rule: https://docs.cachekit.io/contributing/#what-belongs-in-these-docs + # names) out of user-facing docs and commit messages. The patterns live in the script. - repo: local hooks: - id: no-internal-references name: No internal references in docs - language: pygrep - entry: 'LAB-[0-9]+|op://|\.ts\.net|(?i:k3s cluster)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' + language: python + entry: python scripts/no_internal_references.py files: ^((packages|crates)/[^/]+/)?README[^/]*$|^docs/ types: [text] stages: [pre-commit] - id: no-internal-references-commit-msg name: No internal references in commit message - language: pygrep - # Same patterns, skipping git's "#" comment lines and "Merge" subjects, which quote branch names. - entry: '^(?!#|Merge ).*?(?:LAB-[0-9]+|op://|\.ts\.net|(?i:k3s cluster)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-]))' + language: python + entry: python scripts/no_internal_references.py --commit-msg stages: [commit-msg] diff --git a/scripts/no_internal_references.py b/scripts/no_internal_references.py new file mode 100644 index 0000000..868f3e6 --- /dev/null +++ b/scripts/no_internal_references.py @@ -0,0 +1,68 @@ +"""Reject internal references in docs and commit messages. + +The patterns are generic on purpose: a list of specific internal names would itself +disclose them. Scope rule: https://docs.cachekit.io/contributing/#what-belongs-in-these-docs +""" + +import argparse +import re +import subprocess +import sys +from collections.abc import Iterator + +PATTERN = re.compile( + r"LAB-[0-9]+|op://|\.ts\.net|(?i:k3s cluster)|dev\.cachekit\.io" + r"|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])" +) +# `git commit -v` appends the staged diff below this line, and the commit-msg hook +# sees it: git strips it only after the hook runs. +CUT_LINE = "------------------------ >8 ------------------------" + + +def comment_char() -> str: + value = subprocess.run( + ["git", "config", "core.commentChar"], capture_output=True, text=True + ).stdout.strip() + # "auto" picks a character per commit, starting from "#". + return value if value and value != "auto" else "#" + + +def message_lines(lines: list[str]) -> Iterator[tuple[int, str]]: + """The lines git keeps: up to the cut line, minus comments and a Merge subject. + + Comments and the Merge subject quote branch names, which carry ticket ids. + """ + comment = comment_char() + for lineno, line in enumerate(lines, 1): + if line.rstrip("\n") == f"{comment} {CUT_LINE}": + return + if line.startswith(comment) or (lineno == 1 and line.startswith("Merge ")): + continue + yield lineno, line + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--commit-msg", + action="store_true", + help="check a commit message as git will record it", + ) + parser.add_argument("files", nargs="+") + args = parser.parse_args() + + found = False + for path in args.files: + with open(path, encoding="utf-8", errors="replace") as f: + lines = f.readlines() + for lineno, line in ( + message_lines(lines) if args.commit_msg else enumerate(lines, 1) + ): + if PATTERN.search(line): + print(f"{path}:{lineno}:{line.rstrip()}") + found = True + return int(found) + + +if __name__ == "__main__": + sys.exit(main()) From 08fa5d5225289d914ee1d70bcc2a64001f3508b9 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 17:06:37 +1100 Subject: [PATCH 4/7] chore: one commit-message check that ignores the verbose diff and git's own merge and revert subjects --- .pre-commit-config.yaml | 27 ++++++++++-- scripts/no_internal_references.py | 68 ------------------------------- 2 files changed, 23 insertions(+), 72 deletions(-) delete mode 100644 scripts/no_internal_references.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c42b86b..8e91ddf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,18 +3,37 @@ default_install_hook_types: [pre-commit, commit-msg] repos: # Keeps internal references (ticket ids, secret-store paths, internal hosts and process - # names) out of user-facing docs and commit messages. The patterns live in the script. + # names) out of user-facing docs and commit messages. The patterns are generic on purpose: + # a list of specific internal names would itself disclose them. + # Scope rule: https://docs.cachekit.io/contributing/#what-belongs-in-these-docs - repo: local hooks: - id: no-internal-references name: No internal references in docs - language: python - entry: python scripts/no_internal_references.py + language: pygrep + entry: '\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' files: ^((packages|crates)/[^/]+/)?README[^/]*$|^docs/ types: [text] stages: [pre-commit] - id: no-internal-references-commit-msg name: No internal references in commit message + # Same patterns. git hands this hook the raw message file: "#" comment lines, the diff + # below the `git commit -v` scissors line, and its own merge and revert subjects, which + # quote branch names and earlier subjects. Only the text the author wrote is checked. language: python - entry: python scripts/no_internal_references.py --commit-msg + entry: python -c + args: + - | + import re, sys + P = re.compile(r"\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])") + GIT_SUBJECT = re.compile(r"Merge (?:branch|branches|remote-tracking branch|tag|commit) '[^']*'(?: and '[^']*')*(?: of \S+)?(?: into \S+)?|Revert \".*\"") + text = open(sys.argv[1], encoding="utf-8", errors="replace").read() + text = text.split("# ------------------------ >8 ------------------------")[0] + lines = [line for line in text.splitlines() if line.strip() and not line.startswith("#")] + if lines and GIT_SUBJECT.fullmatch(lines[0]): + lines = lines[1:] + bad = [line for line in lines if P.search(line)] + for line in bad: + print(f"{sys.argv[1]}: {line}") + sys.exit(1 if bad else 0) stages: [commit-msg] diff --git a/scripts/no_internal_references.py b/scripts/no_internal_references.py deleted file mode 100644 index 868f3e6..0000000 --- a/scripts/no_internal_references.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Reject internal references in docs and commit messages. - -The patterns are generic on purpose: a list of specific internal names would itself -disclose them. Scope rule: https://docs.cachekit.io/contributing/#what-belongs-in-these-docs -""" - -import argparse -import re -import subprocess -import sys -from collections.abc import Iterator - -PATTERN = re.compile( - r"LAB-[0-9]+|op://|\.ts\.net|(?i:k3s cluster)|dev\.cachekit\.io" - r"|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])" -) -# `git commit -v` appends the staged diff below this line, and the commit-msg hook -# sees it: git strips it only after the hook runs. -CUT_LINE = "------------------------ >8 ------------------------" - - -def comment_char() -> str: - value = subprocess.run( - ["git", "config", "core.commentChar"], capture_output=True, text=True - ).stdout.strip() - # "auto" picks a character per commit, starting from "#". - return value if value and value != "auto" else "#" - - -def message_lines(lines: list[str]) -> Iterator[tuple[int, str]]: - """The lines git keeps: up to the cut line, minus comments and a Merge subject. - - Comments and the Merge subject quote branch names, which carry ticket ids. - """ - comment = comment_char() - for lineno, line in enumerate(lines, 1): - if line.rstrip("\n") == f"{comment} {CUT_LINE}": - return - if line.startswith(comment) or (lineno == 1 and line.startswith("Merge ")): - continue - yield lineno, line - - -def main() -> int: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "--commit-msg", - action="store_true", - help="check a commit message as git will record it", - ) - parser.add_argument("files", nargs="+") - args = parser.parse_args() - - found = False - for path in args.files: - with open(path, encoding="utf-8", errors="replace") as f: - lines = f.readlines() - for lineno, line in ( - message_lines(lines) if args.commit_msg else enumerate(lines, 1) - ): - if PATTERN.search(line): - print(f"{path}:{lineno}:{line.rstrip()}") - found = True - return int(found) - - -if __name__ == "__main__": - sys.exit(main()) From bf5a79405dcbfe6d1563c3c59eff59c551bf6a73 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 17:09:14 +1100 Subject: [PATCH 5/7] chore: treat only git's own comment lines as comments in the commit-message check --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8e91ddf..900c9ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: stages: [pre-commit] - id: no-internal-references-commit-msg name: No internal references in commit message - # Same patterns. git hands this hook the raw message file: "#" comment lines, the diff + # Same patterns. git hands this hook the raw message file: "# " comment lines, the diff # below the `git commit -v` scissors line, and its own merge and revert subjects, which # quote branch names and earlier subjects. Only the text the author wrote is checked. language: python @@ -29,7 +29,7 @@ repos: GIT_SUBJECT = re.compile(r"Merge (?:branch|branches|remote-tracking branch|tag|commit) '[^']*'(?: and '[^']*')*(?: of \S+)?(?: into \S+)?|Revert \".*\"") text = open(sys.argv[1], encoding="utf-8", errors="replace").read() text = text.split("# ------------------------ >8 ------------------------")[0] - lines = [line for line in text.splitlines() if line.strip() and not line.startswith("#")] + lines = [line for line in text.splitlines() if line.strip() and not re.match(r"#(?:[ \t]|$)", line)] if lines and GIT_SUBJECT.fullmatch(lines[0]): lines = lines[1:] bad = [line for line in lines if P.search(line)] From bcc578daf09d98cab26b8f34ed7bdcf581f6eeb3 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 17:11:26 +1100 Subject: [PATCH 6/7] chore: cover every merge subject git writes, and report an unreadable message file --- .pre-commit-config.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 900c9ac..c363cb0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,8 +26,13 @@ repos: - | import re, sys P = re.compile(r"\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])") - GIT_SUBJECT = re.compile(r"Merge (?:branch|branches|remote-tracking branch|tag|commit) '[^']*'(?: and '[^']*')*(?: of \S+)?(?: into \S+)?|Revert \".*\"") - text = open(sys.argv[1], encoding="utf-8", errors="replace").read() + KIND = r"(?:branch|branches|remote-tracking branch|remote-tracking branches|tag|tags|commit|commits)" + GIT_SUBJECT = re.compile(rf"Merge {KIND} '[^']*'(?:(?:, | and ){KIND}? ?'[^']*')*(?: of \S+)?(?: into \S+)?|(?:Revert|Reapply) \".*\"") + try: + with open(sys.argv[1], encoding="utf-8", errors="replace") as f: + text = f.read() + except OSError as e: + sys.exit(f"{sys.argv[1]}: cannot read the commit message: {e}") text = text.split("# ------------------------ >8 ------------------------")[0] lines = [line for line in text.splitlines() if line.strip() and not re.match(r"#(?:[ \t]|$)", line)] if lines and GIT_SUBJECT.fullmatch(lines[0]): From e927008dd3947db5d966f32d9e58d5f571c2c3ed Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 4 Oct 2026 17:13:21 +1100 Subject: [PATCH 7/7] chore: accept semicolon-joined merge subjects and the underscore form of the cluster pattern --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c363cb0..d1f79f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: no-internal-references name: No internal references in docs language: pygrep - entry: '\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' + entry: '\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s_-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])' files: ^((packages|crates)/[^/]+/)?README[^/]*$|^docs/ types: [text] stages: [pre-commit] @@ -25,9 +25,9 @@ repos: args: - | import re, sys - P = re.compile(r"\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])") + P = re.compile(r"\b(?i:LAB)-[0-9]+|op://|\.ts\.net|(?i:\bk3s[\s_-]+(?:cluster|node|host)s?\b)|dev\.cachekit\.io|\bStage [0-9]|\bAC-[0-9]|github\.com/cachekit(?![\w-])") KIND = r"(?:branch|branches|remote-tracking branch|remote-tracking branches|tag|tags|commit|commits)" - GIT_SUBJECT = re.compile(rf"Merge {KIND} '[^']*'(?:(?:, | and ){KIND}? ?'[^']*')*(?: of \S+)?(?: into \S+)?|(?:Revert|Reapply) \".*\"") + GIT_SUBJECT = re.compile(rf"Merge {KIND} '[^']*'(?:(?:, |; | and ){KIND}? ?'[^']*')*(?: of \S+)?(?: into \S+)?|(?:Revert|Reapply) \".*\"") try: with open(sys.argv[1], encoding="utf-8", errors="replace") as f: text = f.read()