Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/actionlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# See: https://github.com/rhysd/actionlint/blob/main/docs/config.md
self-hosted-runner:
labels:
- ubuntu-latest-m
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,28 @@ jobs:

- name: Run Tests
run: ./scripts/covgate.sh

surface-lint:
timeout-minutes: 10
name: surface-lint
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

# yamllint disable rule:line-length
- name: Lint YAML
uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c # v3.1.1
with:
file_or_dir: .
config_file: .yamllint.yml

- name: Lint Shell Scripts
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
with:
scandir: ./
ignore_paths: .agents

- name: Lint GitHub Actions Workflows
uses: rhysd/actionlint@914e7df21a07ef503a81201c76d2b11c789d3fca # v1.7.12
# yamllint enable rule:line-length
3 changes: 3 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ jobs:
go-version-file: ./go.mod

- name: Initialize CodeQL
# yamllint disable rule:line-length
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4
with:
languages: go

- name: Autobuild
# yamllint disable rule:line-length
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4

- name: Perform CodeQL Analysis
# yamllint disable rule:line-length
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4
21 changes: 21 additions & 0 deletions .yamllint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# See: https://yamllint.readthedocs.io/en/stable/

extends: default

rules:
document-start:
present: false
line-length:
level: warning
allow-non-breakable-inline-mappings: true
# GitHub Actions uses `on:` as a workflow trigger key; YAML 1.1 treats `on`
# as a truthy boolean alias. Disable key checking to avoid false positives.
truthy:
check-keys: false
# SHA-pinned action comments use a single space before `#`. Reduce the
# minimum from the default 2 to 1 to match the existing convention.
comments:
min-spaces-from-content: 1

ignore: |
.agents/
61 changes: 61 additions & 0 deletions plans/completed/surface-linters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Surface Linters for gotools

## Goal

Add non-Go surface linters (YAML, shell script, GitHub Actions) to the gotools repository, matching what was added to the core repository.

## Background

The core repository added surface linters in commits `3360e12`–`6ecf0d7`. The implementation consists of:
- `.yamllint.yml` — yamllint configuration
- `.github/actionlint.yml` — actionlint configuration (minimal)
- `scripts/lint-surface.sh` — runs yamllint, shellcheck, actionlint
- `scripts/install-lint-tools.sh` — installs the three linters
- `surface-lint` CI job in `.github/workflows/ci.yml`

## Scope

Repo: `gotools` (`/home/ben/miru/workbench3/gotools`)

## Steps

### 1. Add `.yamllint.yml`

Create `.yamllint.yml` at repo root, adapted from core:
- Same rules (document-start, line-length, truthy, comments)
- Ignore `.agents/` instead of `.venv/` (gotools has `.agents/` subtree, no `.venv/`)
- No `tests/testdata/` ignore needed (gotools has no such dir)

### 2. Add `.github/actionlint.yml`

Create `.github/actionlint.yml` with minimal comment (no custom config needed).

### 3. Add `scripts/lint-surface.sh`

Copy from core verbatim — checks for yamllint, shellcheck, actionlint, then runs each.
Make executable.

### 4. Add `scripts/install-lint-tools.sh`

Copy from core verbatim — installs yamllint (via pip3/.venv), shellcheck (apt/brew), actionlint (go install).
Make executable.

### 5. Update `.github/workflows/ci.yml`

Add `surface-lint` job matching core:
- Checkout
- yamllint via `ibiqlik/action-yamllint` (SHA-pinned, same as core)
- shellcheck via `ludeeus/action-shellcheck` (SHA-pinned, same as core)
- actionlint via `rhysd/actionlint` (SHA-pinned, same as core)

## Test Steps

- `yamllint -c .yamllint.yml .` passes (run locally with yamllint installed)
- `shellcheck scripts/*.sh` passes
- `actionlint` passes on the workflows
- `scripts/lint-surface.sh` runs end-to-end without error (with tools installed)
- CI `surface-lint` job is syntactically valid (actionlint validates it)

## Validation

Preflight must report `clean` before a PR is opened. Run `./scripts/preflight.sh` and confirm no failures.
67 changes: 67 additions & 0 deletions scripts/install-lint-tools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/sh
set -e

usage() {
cat <<EOF
Usage: $(basename "$0") [options]

Install the surface linters required by scripts/lint-surface.sh:
- yamllint (YAML linter)
- shellcheck (shell script linter)
- actionlint (GitHub Actions workflow linter)

yamllint is installed into .venv if one exists, otherwise via system pip3.
shellcheck is installed via apt-get or brew.
actionlint is installed via go install.

Options:
-h, --help Show this help message and exit
EOF
}

while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
usage
exit 0
;;
*)
echo "error: unknown option: $1" >&2
usage >&2
exit 1
;;
esac
done

git_repo_root_dir=$(git rev-parse --show-toplevel)
cd "$git_repo_root_dir"

# --- yamllint ---
echo "==> Installing yamllint..."
if [ -d ".venv" ]; then
.venv/bin/pip install --quiet yamllint
else
pip3 install --quiet yamllint
fi
echo " done."

# --- shellcheck ---
echo "==> Installing shellcheck..."
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get install -y shellcheck
elif command -v brew >/dev/null 2>&1; then
brew install shellcheck
else
echo "error: no supported package manager found (apt-get, brew)." >&2
echo "Install shellcheck manually: https://github.com/koalaman/shellcheck#installing" >&2
exit 1
fi
echo " done."

# --- actionlint ---
echo "==> Installing actionlint..."
go install github.com/rhysd/actionlint/cmd/actionlint@latest
echo " done."

echo ""
echo "All surface linters installed."
12 changes: 12 additions & 0 deletions scripts/lint-surface.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh
set -e

command -v yamllint >/dev/null 2>&1 || { echo "error: yamllint not installed" >&2; exit 1; }
command -v shellcheck >/dev/null 2>&1 || { echo "error: shellcheck not installed" >&2; exit 1; }
command -v actionlint >/dev/null 2>&1 || { echo "error: actionlint not installed" >&2; exit 1; }

yamllint -c .yamllint.yml .

find . -name '*.sh' ! -path './.agents/*' -exec shellcheck {} +

actionlint
20 changes: 14 additions & 6 deletions scripts/preflight.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail

# Local validation script that mirrors CI (lint + test + coverage).
# Lint and covgate run in parallel to minimize wall-clock time.
# Local validation script that mirrors CI (lint + covgate + surface lint).
# All three run in parallel to minimize wall-clock time.

git_repo_root_dir=$(git rev-parse --show-toplevel)
cd "$git_repo_root_dir"
Expand All @@ -19,12 +19,17 @@ lint_pid=$!
./scripts/covgate.sh &
covgate_pid=$!

# Wait for both processes regardless of individual exit codes.
# set -e must not short-circuit here — both must run to completion.
./scripts/lint-surface.sh &
surface_pid=$!

# Wait for all processes regardless of individual exit codes.
# set -e must not short-circuit here — all must run to completion.
lint_rc=0
covgate_rc=0
surface_rc=0
wait "$lint_pid" || lint_rc=$?
wait "$covgate_pid" || covgate_rc=$?
wait "$surface_pid" || surface_rc=$?

echo ""

Expand All @@ -34,8 +39,11 @@ fi
if [[ $covgate_rc -ne 0 ]]; then
echo "=== Covgate FAILED ==="
fi
if [[ $lint_rc -eq 0 && $covgate_rc -eq 0 ]]; then
if [[ $surface_rc -ne 0 ]]; then
echo "=== Surface lint FAILED ==="
fi
if [[ $lint_rc -eq 0 && $covgate_rc -eq 0 && $surface_rc -eq 0 ]]; then
echo "=== All checks passed ==="
fi

exit $(( lint_rc | covgate_rc ))
exit $(( lint_rc | covgate_rc | surface_rc ))