ci(wp11): shellcheck + fish -n + bootstrap self-test

Neuer GitHub-Actions-Workflow .github/workflows/ci.yml: shellcheck fuer alle POSIX/bash-Skripte und git-Hooks, 'fish -n' fuer alle fish-Skripte, sowie scripts/selftest.sh (Bootstrap-Dry-Run sh+fish gegen isoliertes Fake-HOME mit Assertions). copilot-bootstrap.sh propagiert jetzt ebenfalls *:Zone.Identifier (Paritaet zur fish-Variante). shellcheck-Direktiven fuer bewusste Muster (SC2016 literaler Alias, SC1090 dynamische Config, SC2043 erweiterbare Hook-Schleife). Lokal verifiziert: shellcheck exit 0, selftest beide Varianten PASS.
This commit is contained in:
Conrad Schulz 2026-06-10 10:48:27 +02:00
parent 9e14a5fe90
commit deca872ab2
3 changed files with 115 additions and 3 deletions

39
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,39 @@
name: CI
on:
push:
branches: [master, main]
pull_request:
permissions:
contents: read
jobs:
shell:
name: Lint & self-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: ShellCheck (POSIX/bash scripts + git hooks)
run: |
shellcheck --version
shellcheck scripts/*.sh \
git-templates/hooks/pre-commit \
git-templates/hooks/post-merge \
git-templates/hooks/post-commit
- name: Install fish
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq fish
- name: Fish syntax check
run: |
for f in scripts/*.fish; do
echo "fish -n $f"
fish -n "$f"
done
- name: Self-test (bootstrap dry-run, sh + fish)
run: sh scripts/selftest.sh

View file

@ -134,11 +134,12 @@ _append_gitignore() {
fi fi
} }
_append_gitignore "data/**/*" "# Persistente Service-Daten (nie committen!)" _append_gitignore "data/**/*" "# Persistente Service-Daten (nie committen!)"
_append_gitignore "!data/.gitkeep" "" _append_gitignore "!data/.gitkeep" ""
_append_gitignore "*:Zone.Identifier" "# Windows Mark-of-the-Web / NTFS ADS cruft"
if [ "$GITIGNORE_UPDATED" -eq 1 ]; then if [ "$GITIGNORE_UPDATED" -eq 1 ]; then
echo " ✓ .gitignore aktualisiert (data/ excluded)" echo " ✓ .gitignore aktualisiert (data/ + Windows-ADS excluded)"
else else
echo " ─ .gitignore already has required entries, skipping" echo " ─ .gitignore already has required entries, skipping"
fi fi

72
scripts/selftest.sh Executable file
View file

@ -0,0 +1,72 @@
#!/usr/bin/env sh
# selftest.sh Funktionspruefung fuer copilot-bootstrap.{sh,fish}.
#
# Baut ein isoliertes Fake-Template-Verzeichnis und ein Ziel-Repo in einem
# temporaeren HOME und prueft, dass Bootstrap die erwartete Struktur erzeugt.
# Veraendert weder das echte HOME noch ein echtes Repo. Lokal und in CI nutzbar:
# sh scripts/selftest.sh
set -eu
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
build_template() {
tpl="$1"
mkdir -p "$tpl/.github" "$tpl/.vscode" "$tpl/hooks" "$tpl/docs" "$tpl/history/summary"
echo "framework" > "$tpl/.github/copilot-instructions.md"
echo '{}' > "$tpl/.vscode/settings.json"
echo '{}' > "$tpl/.vscode/extensions.json"
printf '#!/bin/sh\nexit 0\n' > "$tpl/hooks/pre-commit"
printf '#!/bin/sh\nexit 0\n' > "$tpl/hooks/post-commit"
for d in USER ADMIN MAINTAINER; do
echo "$d" > "$tpl/docs/$d.md"
done
echo ctx > "$tpl/history/summary/PROJECT_CONTEXT.md"
}
assert_file() {
if [ ! -f "$1" ]; then
echo "FAIL: erwartete Datei fehlt: $1" >&2
exit 1
fi
}
run_case() {
label="$1"
runner="$2" # sh | fish
script="$3"
home="$WORK/home_$label"
target="$WORK/target_$label"
mkdir -p "$home/.git-templates" "$target"
build_template "$home/.git-templates"
git -C "$target" init -q
HOME="$home" "$runner" "$REPO_DIR/scripts/$script" "$target" >/dev/null
assert_file "$target/.github/copilot-instructions.md"
assert_file "$target/.vscode/settings.json"
assert_file "$target/.vscode/extensions.json"
assert_file "$target/docs/USER.md"
assert_file "$target/docs/ADMIN.md"
assert_file "$target/docs/MAINTAINER.md"
assert_file "$target/history/summary/PROJECT_CONTEXT.md"
assert_file "$target/.git/hooks/pre-commit"
assert_file "$target/.git/hooks/post-commit"
assert_file "$target/data/.gitkeep"
if ! grep -q ':Zone.Identifier' "$target/.gitignore"; then
echo "FAIL: $label .gitignore enthaelt kein Zone.Identifier-Muster" >&2
exit 1
fi
echo "PASS: $label ($script)"
}
run_case sh_bootstrap sh copilot-bootstrap.sh
if command -v fish >/dev/null 2>&1; then
run_case fish_bootstrap fish copilot-bootstrap.fish
else
echo "SKIP: fish nicht installiert fish-Bootstrap uebersprungen"
fi
echo "selftest OK"