Skip to content

Commit 6bfa01a

Browse files
jdclaude
andcommitted
chore: remove Python from the repository tooling
The action's runtime is pure shell, but its dev/CI tooling still pulled in Python: a uv-run generate-doc.py for the README inputs table, and a pip-installed semgrep + yamllint in CI. Remove it all so the repo matches its own "No Python or toolchain is required" promise. - generate-doc.py is rewritten as pure bash + awk in generate-doc.sh: it parses action.yml and rewrites the inputs table between the AUTO-DOC-INPUT markers. Output is byte-identical to the previous generator (verified on both BSD awk and Ubuntu's mawk), so the autodoc drift check still passes. awk emits the finished Markdown rows directly, avoiding a TSV/`read` round-trip whose IFS-whitespace tab handling would have collapsed an empty Default column. - CI linters: drop actions/setup-python and `pip install semgrep yamllint`; run yamllint and semgrep from their pinned docker images, matching the existing actionlint step. semgrep sets an explicit entrypoint so a digest bump can't break the invocation. Renovate manages the tags + digests. - autodoc job: drop the uv setup step; it now runs the pure-bash script. No Python, uv, or pip remains in the repo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Change-Id: I82858ba068c90064336fe309a9c8996a9899dea8
1 parent 676684a commit 6bfa01a

3 files changed

Lines changed: 137 additions & 92 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,25 @@ jobs:
2020
- name: Checkout 🛎️
2121
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
2222

23-
- name: Setup Python 🔧
24-
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
25-
with:
26-
python-version: 3.14.5
27-
2823
- name: Check workflow files
2924
uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667
3025
with:
3126
args: -color
3227

33-
- name: Test 🔍
34-
run: |
35-
# nosemgrep: generic.ci.security.use-frozen-lockfile.use-frozen-lockfile-pip
36-
pip install semgrep yamllint
37-
semgrep --config=auto --error
38-
yamllint .
28+
# yamllint and semgrep run from their published images (no host Python),
29+
# matching the actionlint step above. Renovate manages the tags + digests.
30+
- name: Lint YAML 🔍
31+
uses: docker://cytopia/yamllint:1@sha256:596fb19eb71e55ba5b2fa56d8c18a615ec82adc8d3bf2d73918cb78c8f3240fb
32+
with:
33+
args: .
34+
35+
- name: Security scan 🔒
36+
uses: docker://semgrep/semgrep:1.167.0@sha256:06938c1f365d3f67b8cedd8bc117607ae64253f88a0e768e9da9408548927dd6
37+
with:
38+
# Set the entrypoint explicitly: this image has no ENTRYPOINT, and
39+
# pinning it keeps the step correct if a digest bump reintroduces one.
40+
entrypoint: semgrep
41+
args: --config=auto --error
3942

4043
autodoc:
4144
timeout-minutes: 5
@@ -44,11 +47,6 @@ jobs:
4447
- name: Checkout
4548
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
4649

47-
- name: Install uv
48-
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
49-
with:
50-
enable-cache: false
51-
5250
- name: Regenerate documentation
5351
run: ./generate-doc.sh
5452

generate-doc.py

Lines changed: 0 additions & 74 deletions
This file was deleted.

generate-doc.sh

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,128 @@
11
#!/bin/bash
22

3+
# Generate the README Inputs table from action.yml.
4+
#
5+
# Replaces tj-actions/auto-doc: parses the action's inputs and rewrites the
6+
# GitHub-flavoured Markdown table between the AUTO-DOC-INPUT markers in
7+
# README.md. Pure bash + awk so the repo needs no Python/uv toolchain.
8+
39
set -euo pipefail
410

5-
command -v uv >/dev/null 2>&1 || { echo "uv is not installed: https://docs.astral.sh/uv/" >&2; exit 1; }
11+
ROOT="$(cd "$(dirname "$0")" && pwd)"
12+
ACTION="${ROOT}/action.yml"
13+
README="${ROOT}/README.md"
14+
START="<!-- AUTO-DOC-INPUT:START - Do not remove or modify this section -->"
15+
END="<!-- AUTO-DOC-INPUT:END -->"
16+
17+
if ! grep -qF "${START}" "${README}" || ! grep -qF "${END}" "${README}"; then
18+
echo "AUTO-DOC-INPUT markers not found in README.md" >&2
19+
exit 1
20+
fi
21+
22+
# Render the README inputs table from action.yml. awk emits one finished
23+
# Markdown row per input; only the constrained GitHub Action inputs schema is
24+
# handled (2-space input names, 4-space properties, optional `|`/`>`
25+
# block-scalar descriptions).
26+
rows="$(
27+
awk '
28+
function trim(s){ sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s }
29+
# unwrap a scalar: strip matching surrounding quotes (inside which "#" is
30+
# literal), otherwise strip an inline " # comment" from a plain scalar.
31+
function scalar(s){
32+
s = trim(s)
33+
if (s ~ /^".*"$/ || s ~ /^'"'"'.*'"'"'$/) return substr(s, 2, length(s) - 2)
34+
sub(/[ \t]+#.*$/, "", s)
35+
return trim(s)
36+
}
37+
# render a (newline-joined) description into a single Markdown table cell:
38+
# wrapped prose joins with spaces; "* " lines become <br>-separated bullets.
39+
function render(raw, nl, i, line, arr, parts, np, out){
40+
np = 0; nl = split(raw, arr, "\n")
41+
for (i = 1; i <= nl; i++) {
42+
line = trim(arr[i])
43+
if (line == "") continue
44+
if (substr(line, 1, 2) == "* ") parts[++np] = "<br>\342\200\242 " trim(substr(line, 3))
45+
else if (np > 0) parts[np] = parts[np] " " line
46+
else parts[++np] = line
47+
}
48+
out = ""
49+
for (i = 1; i <= np; i++) out = out parts[i]
50+
return out
51+
}
52+
# emit the finished Markdown row; the downstream sort orders rows by the
53+
# input name that follows the identical "| `" prefix.
54+
function flush( defcell){
55+
if (cur == "") return
56+
defcell = (def == "") ? "" : "`" def "`"
57+
printf "| `%s` | string | %s | %s | %s |\n", cur, req, defcell, render(desc)
58+
}
59+
60+
BEGIN { in_inputs = 0; collecting = 0; cur = "" }
61+
{
62+
line = $0; sub(/\r$/, "", line)
63+
p = match(line, /[^ ]/); ind = (p ? p - 1 : length(line))
64+
blank = (line ~ /^[ \t]*$/)
65+
66+
if (collecting) {
67+
if (blank) { desc = desc "\n"; next }
68+
if (ind > blockind) { desc = desc (desc == "" ? "" : "\n") line; next }
69+
collecting = 0 # dedent: fall through and reprocess this line
70+
}
71+
72+
if (blank) next
73+
if (line ~ /^[ \t]*#/) next # full-line comment
74+
75+
if (ind == 0) { # top-level key
76+
flush(); cur = ""
77+
in_inputs = (line ~ /^inputs:[ \t]*$/) ? 1 : 0
78+
next
79+
}
80+
if (!in_inputs) next
81+
82+
if (ind == 2) { # new input name
83+
flush()
84+
key = line; sub(/:.*$/, "", key); cur = trim(key)
85+
req = "false"; def = ""; desc = ""
86+
next
87+
}
88+
if (ind >= 4 && cur != "") { # input property
89+
prop = trim(line)
90+
if (prop ~ /^description:/) {
91+
val = prop; sub(/^description:[ \t]*/, "", val)
92+
if (val ~ /^[|>]/) { collecting = 1; blockind = ind; desc = "" }
93+
else desc = scalar(val)
94+
} else if (prop ~ /^default:/) {
95+
val = prop; sub(/^default:[ \t]*/, "", val); def = scalar(val)
96+
} else if (prop ~ /^required:/) {
97+
val = prop; sub(/^required:[ \t]*/, "", val); val = tolower(scalar(val))
98+
req = (val == "true" || val == "yes" || val == "on") ? "true" : "false"
99+
}
100+
next
101+
}
102+
}
103+
END { flush() }
104+
' "${ACTION}" | LC_ALL=C sort
105+
)"
106+
107+
# Assemble the table; the rows already carry their Markdown formatting.
108+
table="| Input | Type | Required | Default | Description |
109+
| --- | --- | --- | --- | --- |"
110+
if [ -n "${rows}" ]; then
111+
table="${table}
112+
${rows}"
113+
fi
6114

7-
exec uv run "$(dirname "$0")/generate-doc.py"
115+
# Splice the rendered block between the markers (inclusive). The block is read
116+
# from a file rather than passed via `awk -v`, which rejects embedded newlines.
117+
tmp="$(mktemp)"
118+
blockfile="$(mktemp)"
119+
trap 'rm -f "${tmp}" "${blockfile}"' EXIT
120+
printf '%s\n\n%s\n\n%s\n' "${START}" "${table}" "${END}" > "${blockfile}"
121+
awk -v start="${START}" -v end="${END}" -v blockfile="${blockfile}" '
122+
index($0, start) { while ((getline l < blockfile) > 0) print l; close(blockfile); skip = 1; next }
123+
skip && index($0, end) { skip = 0; next }
124+
skip { next }
125+
{ print }
126+
' "${README}" > "${tmp}"
127+
mv "${tmp}" "${README}"
128+
# tmp + blockfile are cleaned by the EXIT trap above.

0 commit comments

Comments
 (0)