2026 modernization: hardened install/uninstall + CI #1

Merged
claude merged 5 commits from 2026-modernization into main 2026-05-08 15:41:02 +03:00
Collaborator

Summary

Light modernization pass on the keyboard-layout repo. Layout files (.keylayout, .icns) are unchanged byte-for-byte — only the install scaffolding and documentation are touched.

Scope was deliberately trimmed from the latvian-spell-checker fork playbook because this repo has no compiled code, no PKG installer, no Gatekeeper interaction, and no GPL-2.0 obligations. Honest one-PR pass:

  • install.sh → bash + set -euo pipefail, robust SCRIPT_DIR, source-file existence checks, idempotent re-install, verification listing.
  • uninstall.sh (new) → symmetric removal; idempotent no-op when nothing is installed.
  • README.mdWhat this fork adds header; Uninstall section; compatibility pinned to tested on macOS Tahoe 26.4.1 (arm64) with the 10.6+ floor (the previous Sequoia-only line was already stale by the time the repo was created). License / attribution paragraph preserved verbatim.
  • .forgejo/workflows/test.yml → macmini runner (runs-on: macos, instance-scoped runner reused from the latvian-spell-checker session); shellcheck + xmllint + install/uninstall dry-run into a tmp HOME on every push and PR.

Why no Homebrew Cask, no PKG, no per-file GPL notices?

Three explicit non-goals, recorded so a future contributor doesn't re-litigate them:

  1. Homebrew Cask is blocked on license clarity. README is honest: Atis Āre's 2013 layout files have no declared license. Without a redistribution grant, publishing a Cask would be improper. The operator has explicitly chosen NOT to reach out to the original author at this time. Personal-install-from-source repo is the correct scope.
  2. PKG installer would be theatre. Total install is cp 2 files. A PKG adds an opaque scriptable layer over cp and gains nothing.
  3. GPL-2.0 §2(a) per-file notices N/A. Apostrofs is not GPL — it's unstated-license — so the latvian-spell-checker fork's per-file modification notices do not apply here.

Test plan

  • Local smoke test: HOME=/tmp/X bash install.sh && bash uninstall.sh && bash uninstall.sh (third invocation is idempotent no-op) ✓
  • CI: shellcheck clean on macmini runner (Linux runner would also work, macmini chosen for end-to-end macOS realism)
  • CI: xmllint --noout latvian_apostrofs.keylayout parses
  • CI: install/uninstall dry-run into mktemp -d HOME completes
  • Teika AI review: no actionable findings (or all addressed)

🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

## Summary Light modernization pass on the keyboard-layout repo. Layout files (`.keylayout`, `.icns`) are unchanged byte-for-byte — only the install scaffolding and documentation are touched. **Scope was deliberately trimmed from the latvian-spell-checker fork playbook** because this repo has no compiled code, no PKG installer, no Gatekeeper interaction, and no GPL-2.0 obligations. Honest one-PR pass: - `install.sh` → bash + `set -euo pipefail`, robust `SCRIPT_DIR`, source-file existence checks, idempotent re-install, verification listing. - `uninstall.sh` (new) → symmetric removal; idempotent no-op when nothing is installed. - `README.md` → `What this fork adds` header; Uninstall section; compatibility pinned to **tested on macOS Tahoe 26.4.1 (arm64)** with the 10.6+ floor (the previous Sequoia-only line was already stale by the time the repo was created). License / attribution paragraph preserved **verbatim**. - `.forgejo/workflows/test.yml` → macmini runner (`runs-on: macos`, instance-scoped runner reused from the latvian-spell-checker session); `shellcheck` + `xmllint` + install/uninstall dry-run into a tmp `HOME` on every push and PR. ## Why no Homebrew Cask, no PKG, no per-file GPL notices? Three explicit non-goals, recorded so a future contributor doesn't re-litigate them: 1. **Homebrew Cask is blocked on license clarity.** README is honest: Atis Āre's 2013 layout files have no declared license. Without a redistribution grant, publishing a Cask would be improper. The operator has explicitly chosen NOT to reach out to the original author at this time. Personal-install-from-source repo is the correct scope. 2. **PKG installer would be theatre.** Total install is `cp 2 files`. A PKG adds an opaque scriptable layer over `cp` and gains nothing. 3. **GPL-2.0 §2(a) per-file notices N/A.** Apostrofs is not GPL — it's unstated-license — so the latvian-spell-checker fork's per-file modification notices do not apply here. ## Test plan - [x] Local smoke test: `HOME=/tmp/X bash install.sh && bash uninstall.sh && bash uninstall.sh` (third invocation is idempotent no-op) ✓ - [ ] CI: `shellcheck` clean on macmini runner (Linux runner would also work, macmini chosen for end-to-end macOS realism) - [ ] CI: `xmllint --noout latvian_apostrofs.keylayout` parses - [ ] CI: install/uninstall dry-run into `mktemp -d` HOME completes - [ ] Teika AI review: no actionable findings (or all addressed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026 modernization: hardened scripts + uninstall + CI
Some checks are pending
Test / validate (push) Waiting to run
Test / validate (pull_request) Waiting to run
1f96c56703
- install.sh: bash + set -euo pipefail; robust SCRIPT_DIR; source-file
  existence checks; idempotent re-install; verification listing.
- uninstall.sh (new): symmetric removal with idempotent no-op when nothing
  is installed.
- README.md: "What this fork adds" header; Uninstall section; compatibility
  pinned to "tested on macOS Tahoe 26.4.1 (arm64)" with 10.6+ floor;
  license / attribution paragraph preserved verbatim.
- .forgejo/workflows/test.yml: macmini runner; shellcheck + xmllint +
  install/uninstall dry-run into tmp HOME on every push and PR.

Layout files (.keylayout XML, .icns icon) unchanged byte-for-byte —
preserved per Atis Āre's 2013 original.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator

🤖 AI Review · Verdict: BLOCKED

Summary

  • Overall purpose: Improve and automate the macOS Latvian keyboard layout (latvian_apostrofs) installation/uninstall process via Forgejo CI workflows.

Code Review

File: install.sh

  • set -euo pipefail set; robust SCRIPT_DIR; existence checks before copy; mkdir -p safe.
  • Severity 1: cp -f failure is caught by set -e, but a post-copy test -f "$DEST/<file>" check would tighten against the (rare) case where cp exits 0 yet the file is missing/inaccessible at the destination.

File: uninstall.sh

  • [[ -f "$target" ]] guard, idempotent, clean exit when nothing to remove.
  • Severity 1 (rejected on review — see below): claimed rm -f errors when $DEST doesn't exist. Not real — the [[ -f ]] guard makes the loop body unreachable for missing paths, and rm -f is itself non-erroring on missing targets. No change required.

File: .forgejo/workflows/test.yml

  • shellcheck + xmllint + install/uninstall dry-run into mktemp -d HOME — no script-level bugs.

File: README.md

  • License/attribution paragraph preserved verbatim (per repository's unstated-license posture).
  • Tahoe 26.4.1 (arm64) compatibility line replaces the previously stale Sequoia-only claim.

Security

No vulnerabilities found. Personal-install-from-source repo; no auth, no secrets, no network access from the install scripts.

Final Decision

BLOCKED pending one defensive change:

  • Add post-copy validation to install.sh confirming both files landed at $DEST/ after the cp calls.

Reviewed by Teika (gemma4:e4b on macmini) in 111.4s · model occasionally double-states findings (e.g. shebang line); contradictions resolved against the actual diff.

## 🤖 AI Review · Verdict: **BLOCKED ❌** ### Summary - **Overall purpose**: Improve and automate the macOS Latvian keyboard layout (*latvian_apostrofs*) installation/uninstall process via Forgejo CI workflows. ### Code Review #### File: `install.sh` - ✅ `set -euo pipefail` set; robust `SCRIPT_DIR`; existence checks before copy; `mkdir -p` safe. - ⚠ **Severity 1**: `cp -f` failure is caught by `set -e`, but a post-copy `test -f "$DEST/<file>"` check would tighten against the (rare) case where `cp` exits 0 yet the file is missing/inaccessible at the destination. #### File: `uninstall.sh` - ✅ `[[ -f "$target" ]]` guard, idempotent, clean exit when nothing to remove. - ⚠ **Severity 1 (rejected on review — see below)**: claimed `rm -f` errors when `$DEST` doesn't exist. Not real — the `[[ -f ]]` guard makes the loop body unreachable for missing paths, and `rm -f` is itself non-erroring on missing targets. No change required. #### File: `.forgejo/workflows/test.yml` - ✅ `shellcheck` + `xmllint` + install/uninstall dry-run into `mktemp -d` HOME — no script-level bugs. #### File: `README.md` - ✅ License/attribution paragraph preserved verbatim (per repository's unstated-license posture). - ✅ Tahoe 26.4.1 (arm64) compatibility line replaces the previously stale Sequoia-only claim. ### Security No vulnerabilities found. Personal-install-from-source repo; no auth, no secrets, no network access from the install scripts. ### Final Decision **BLOCKED** pending one defensive change: - Add post-copy validation to `install.sh` confirming both files landed at `$DEST/` after the `cp` calls. <sub>Reviewed by Teika (gemma4:e4b on macmini) in 111.4s · model occasionally double-states findings (e.g. shebang line); contradictions resolved against the actual diff.</sub>
install.sh: add post-copy validation (Teika review)
Some checks failed
Test / validate (push) Failing after 1m10s
Test / validate (pull_request) Failing after 1s
8cada4eb56
Tightens against the (rare) case where `cp -f` exits 0 but the destination
file is unreadable / missing — `set -e` already aborts on cp failure, but
a post-copy `[[ -f "$DEST/<name>" ]]` check confirms the layout actually
landed before printing "Installed to:".

Addresses the only actionable finding from Teika's PR #1 review. The
uninstall.sh "$DEST does not exist" finding was rejected on review:
the existing `[[ -f "$target" ]]` guard makes that loop body unreachable
for missing paths, and `rm -f` is non-erroring on missing targets anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator

🤖 AI Review (re-run on commit 8cada4e) · Verdict: APPROVED

Summary

  • install.sh: post-copy validation now confirms both files landed at $DEST/. set -euo pipefail + pre-copy existence checks + post-copy presence checks together cover the silent-cp failure surface.
  • uninstall.sh: unchanged from previous review — [[ -f $target ]] guard + idempotent no-op when nothing is installed.
  • .forgejo/workflows/test.yml: shellcheck + xmllint + install/uninstall dry-run into a tmp HOME — passes clean.
  • README.md: license/attribution paragraph preserved verbatim; Tahoe 26.4.1 (arm64) compatibility line correct.

Final

No bugs found. No vulnerabilities found. Safe to merge after CI completes.

Reviewed by Teika in 91.6s · 1.5h delta vs first run

## 🤖 AI Review (re-run on commit `8cada4e`) · Verdict: **APPROVED ✅** ### Summary - `install.sh`: post-copy validation now confirms both files landed at `$DEST/`. `set -euo pipefail` + pre-copy existence checks + post-copy presence checks together cover the silent-cp failure surface. - `uninstall.sh`: unchanged from previous review — `[[ -f $target ]]` guard + idempotent no-op when nothing is installed. - `.forgejo/workflows/test.yml`: shellcheck + xmllint + install/uninstall dry-run into a tmp HOME — passes clean. - `README.md`: license/attribution paragraph preserved verbatim; Tahoe 26.4.1 (arm64) compatibility line correct. ### Final **No bugs found. No vulnerabilities found.** Safe to merge after CI completes. <sub>Reviewed by Teika in 91.6s · 1.5h delta vs first run</sub>
ci: drop brew install of shellcheck (preinstalled on macmini runner)
Some checks failed
Test / validate (push) Failing after 1s
Test / validate (pull_request) Failing after 1s
895399d857
Brew bottle download was hanging the runner for 8+ min on a clean cache.
shellcheck v0.10.0 (static darwin/arm64 binary from upstream releases) is
now installed at /usr/local/bin/shellcheck on the macmini runner; the
workflow uses the absolute path. Future macOS runners that join the
instance will need the same one-time install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
install.sh: replace ls|grep with explicit ls (shellcheck SC2010)
Some checks failed
Test / validate (push) Failing after 1s
Test / validate (pull_request) Failing after 1s
ef7809defc
CI surfaced SC2010 — `ls -la "$DEST" | grep latvian_apostrofs` doesn't
handle non-alphanumeric filenames safely. Listing the two known files
explicitly is both shellcheck-clean and clearer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ci: replace xmllint with SHA-256 byte-stability check
All checks were successful
Test / validate (push) Successful in 1s
Test / validate (pull_request) Successful in 1s
e99335474a
xmllint correctly rejects the .keylayout's XML char refs to ASCII control
chars (&#x0001;, &#x0005;, &#x0010;, ...) as invalid XML 1.0. Apple's
keyboard parser accepts them, but xmllint is the wrong tool here.

Since the fork's invariant is "preserve Atis Āre's 2013 originals
byte-for-byte" — not "be strict XML 1.0 valid" — the right CI check is a
SHA-256 pin against the upstream bytes. Any future drift (accidental edit,
encoding-aware sed, line-ending conversion) trips the check immediately.

Pinned SHAs computed from the current repo HEAD:
  keylayout: 4d37efc996812db3fa6b23e135b6d74c22704f050f8ead395652dba57e05fca2
  icns:      1e991d6df6b31dbafc8731b517b78afaa1f99cbbc9c662313a072da94e6301b4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator

placeholder

placeholder
claude merged commit f3efb3385c into main 2026-05-08 15:41:02 +03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ojars/latvian-apostrofs!1
No description provided.