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
70 changes: 70 additions & 0 deletions .airlock/lint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
set -euo pipefail

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

# Compute changed files between base and head
BASE="${AIRLOCK_BASE_SHA:-HEAD~1}"
HEAD="${AIRLOCK_HEAD_SHA:-HEAD}"
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR "$BASE" "$HEAD" 2>/dev/null || git diff --name-only --cached)

# Filter by language
GO_FILES=$(echo "$CHANGED_FILES" | grep '\.go$' || true)
PY_FILES=$(echo "$CHANGED_FILES" | grep '\.py$' || true)
Comment on lines +10 to +14
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git diff --name-only is newline-delimited; piping through echo | grep | xargs later will break on filenames containing spaces, tabs, or newlines. Prefer a NUL-delimited pipeline (e.g., git diff -z ... with grep -z/xargs -0) so formatting/linting reliably targets the intended files.

Copilot uses AI. Check for mistakes.

ERRORS=0

# --- Go ---
if [[ -n "$GO_FILES" ]]; then
echo "=== Go: gofmt (auto-fix) ==="
echo "$GO_FILES" | xargs -I{} gofmt -w "{}" 2>/dev/null || true
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suppresses both output and failure (2>/dev/null || true), so a real formatting error (missing gofmt, unreadable file, etc.) won’t fail the job and may silently skip formatting. Consider letting gofmt failures surface (or at least track failures and set ERRORS=1) so CI can block merges on broken tooling/setup.

Suggested change
echo "$GO_FILES" | xargs -I{} gofmt -w "{}" 2>/dev/null || true
if ! echo "$GO_FILES" | xargs -I{} gofmt -w "{}"; then
echo "gofmt: failed to format one or more files"
ERRORS=1
fi

Copilot uses AI. Check for mistakes.

echo "=== Go: golangci-lint ==="
# Get unique directories containing changed Go files
GO_DIRS=$(echo "$GO_FILES" | xargs -I{} dirname "{}" | sort -u | sed 's|$|/...|')
# Run golangci-lint but only report issues in changed files
LINT_OUTPUT=$(golangci-lint run --out-format line-number $GO_DIRS 2>&1 || true)
if [[ -n "$LINT_OUTPUT" ]]; then
# Filter to only issues in changed files
FILTERED=""
while IFS= read -r file; do
MATCH=$(echo "$LINT_OUTPUT" | grep "^${file}:" || true)
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grep "^${file}:" pattern treats file as a regex fragment, so paths containing regex metacharacters (e.g., [, ], .) can mis-match and cause false positives/negatives in filtering. Use a fixed-string match (or escape the filename before constructing the regex) to ensure only issues from the exact changed file paths are included.

Suggested change
MATCH=$(echo "$LINT_OUTPUT" | grep "^${file}:" || true)
MATCH=$(echo "$LINT_OUTPUT" | awk -v f="$file" 'substr($0, 1, length(f) + 1) == f ":" { print }' || true)

Copilot uses AI. Check for mistakes.
if [[ -n "$MATCH" ]]; then
FILTERED="${FILTERED}${MATCH}"$'\n'
fi
done <<< "$GO_FILES"
if [[ -n "${FILTERED// /}" ]] && [[ "${FILTERED}" != $'\n' ]]; then
echo "$FILTERED"
echo "golangci-lint: issues found in changed files"
ERRORS=1
else
echo "golangci-lint: OK (issues only in unchanged files, skipping)"
fi
else
echo "golangci-lint: OK"
fi
fi

# --- Python ---
if [[ -n "$PY_FILES" ]]; then
echo "=== Python: ruff format (auto-fix) ==="
echo "$PY_FILES" | xargs ruff format 2>/dev/null || true

echo "=== Python: ruff check --fix ==="
echo "$PY_FILES" | xargs ruff check --fix 2>/dev/null || true
Comment on lines +52 to +55
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the Go section, these steps suppress errors and stderr, which can hide a misconfigured environment (missing ruff, invalid config, etc.) and lead to confusing “success” runs. Consider failing the script when these commands fail (or capturing failures into ERRORS) so the pipeline outcome reflects actual formatter/linter execution.

Suggested change
echo "$PY_FILES" | xargs ruff format 2>/dev/null || true
echo "=== Python: ruff check --fix ==="
echo "$PY_FILES" | xargs ruff check --fix 2>/dev/null || true
if ! echo "$PY_FILES" | xargs ruff format; then
echo "ruff format: failed"
ERRORS=1
fi
echo "=== Python: ruff check --fix ==="
if ! echo "$PY_FILES" | xargs ruff check --fix; then
echo "ruff check --fix: failed"
ERRORS=1
fi

Copilot uses AI. Check for mistakes.

echo "=== Python: ruff check (verify) ==="
if echo "$PY_FILES" | xargs ruff check 2>&1; then
echo "ruff check: OK"
else
echo "ruff check: issues found"
ERRORS=1
fi
Comment on lines +21 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n .airlock/lint.sh | sed -n '15,70p'

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 2119


🏁 Script executed:

# Verify fail-open consequence: if golangci-lint crashes, LINT_OUTPUT is empty
bash -c 'LINT_OUTPUT=$(/nonexistent/golangci-lint run 2>&1 || true); if [[ -n "$LINT_OUTPUT" ]]; then echo "HAS OUTPUT"; else echo "NO OUTPUT - SILENT PASS"; fi'

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 85


🏁 Script executed:

# Test ruff fail-through with || true: does script continue with exit 0?
bash -c 'echo "invalid" | xargs ruff check 2>/dev/null || true; echo "Exit code: $?"'

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 197


Failure suppression bypasses lint gating.

Lines 21, 27, 52, and 55 suppress failures (|| true, 2>/dev/null), allowing the script to report success even if linters/formatters fail to execute. Line 27 also uses unquoted $GO_DIRS, causing unsafe word-splitting. When golangci-lint or ruff encounters an execution error (missing binary, config failure, crash), the error output doesn't match the file-path filter pattern (line 32), gets silently dropped, and the script falsely reports "OK". This breaks the lint enforcement gate.

Suggested fix
-  echo "$GO_FILES" | xargs -I{} gofmt -w "{}" 2>/dev/null || true
+  while IFS= read -r file; do
+    [[ -n "$file" ]] || continue
+    gofmt -w "$file"
+  done <<< "$GO_FILES"

-  GO_DIRS=$(echo "$GO_FILES" | xargs -I{} dirname "{}" | sort -u | sed 's|$|/...|')
+  mapfile -t GO_DIRS < <(
+    printf '%s\n' "$GO_FILES" | xargs -I{} dirname "{}" | sort -u | sed 's|$|/...|'
+  )

-  LINT_OUTPUT=$(golangci-lint run --out-format line-number $GO_DIRS 2>&1 || true)
-  if [[ -n "$LINT_OUTPUT" ]]; then
+  set +e
+  LINT_OUTPUT=$(golangci-lint run --out-format line-number "${GO_DIRS[@]}" 2>&1)
+  LINT_EXIT=$?
+  set -e
+  if [[ $LINT_EXIT -ne 0 || -n "$LINT_OUTPUT" ]]; then
     # Filter to only issues in changed files
     FILTERED=""
@@
-    else
+    elif [[ $LINT_EXIT -eq 0 ]]; then
       echo "golangci-lint: OK (issues only in unchanged files, skipping)"
+    else
+      echo "$LINT_OUTPUT"
+      echo "golangci-lint: execution/config failure"
+      ERRORS=1
     fi
   else
     echo "golangci-lint: OK"
   fi

-  echo "$PY_FILES" | xargs ruff format 2>/dev/null || true
+  echo "$PY_FILES" | xargs ruff format

-  echo "$PY_FILES" | xargs ruff check --fix 2>/dev/null || true
+  echo "$PY_FILES" | xargs ruff check --fix
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 27-27: Double quote to prevent globbing and word splitting.

(SC2086)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.airlock/lint.sh around lines 21 - 63, The script currently swallows errors
and mis-splits paths: remove the failure suppressions and silent stderr
redirects so linters' exit codes and messages are preserved (replace uses of "||
true" and "2>/dev/null" around gofmt, golangci-lint, ruff format/check), quote
variables like "$GO_DIRS" and "$GO_FILES" to avoid word-splitting, capture both
stdout and stderr from golangci-lint into LINT_OUTPUT and if the command exits
non‑zero, propagate that by setting ERRORS=1 unless FILTERED contains legitimate
findings; likewise treat ruff format/check non‑zero exits as failures by
checking their exit status (use explicit if/else on the commands rather than
silencing) so missing binaries or crashes fail the gate instead of being hidden.

fi

if [[ -z "$GO_FILES" && -z "$PY_FILES" ]]; then
echo "No Go or Python files changed. Nothing to lint."
fi

exit $ERRORS
45 changes: 45 additions & 0 deletions .airlock/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Airlock workflow configuration
# Documentation: https://github.com/airlock-hq/airlock

name: Main Pipeline

on:
push:
branches: ['**']

jobs:
default:
name: Lint, Test & Deploy
steps:
# Rebase onto upstream to handle drift
- name: rebase
uses: airlock-hq/airlock/defaults/rebase@main

# Run linters and formatters, auto-fix issues
- name: lint
uses: airlock-hq/airlock/defaults/lint@main

# Commit auto-fix patches and lock the worktree
- name: freeze
run: airlock exec freeze

# Generate PR title and description from the diff
- name: describe
uses: airlock-hq/airlock/defaults/describe@main

# Update documentation to reflect changes
- name: document
uses: airlock-hq/airlock/defaults/document@main

# Run tests
- name: test
uses: airlock-hq/airlock/defaults/test@main

# Push changes to upstream (pauses for user approval first)
- name: push
uses: airlock-hq/airlock/defaults/push@main
require-approval: true

# Create pull/merge request
- name: create-pr
uses: airlock-hq/airlock/defaults/create-pr@main
Comment on lines +16 to +45
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
# First, verify the mutable action refs exist
echo "=== Checking for `@main` refs ==="
rg -n 'uses:\s+airlock-hq/airlock/.+@main' .airlock/workflows/main.yml

echo ""
echo "=== Full workflow file ==="
cat .airlock/workflows/main.yml

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 1728


Pin action references to immutable commits instead of @main.

All seven action references in this workflow use mutable @main branches. This allows upstream changes to alter pipeline behavior without review, weakening supply chain integrity and reproducibility. Pin each uses: entry to a specific commit SHA.

Suggested fix pattern
-        uses: airlock-hq/airlock/defaults/rebase@main
+        uses: airlock-hq/airlock/defaults/rebase@<full-commit-sha>

Apply the same pinning pattern to all uses: entries at lines 16, 20, 28, 32, 36, 40, and 45.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.airlock/workflows/main.yml around lines 16 - 45, All seven workflow steps
currently reference mutable branches (uses: airlock-hq/airlock/defaults/*@main);
replace each uses: entry for rebase, lint, describe, document, test, push, and
create-pr with the exact same repository@<commit-sha> pinned to a specific
immutable commit SHA (e.g., airlock-hq/airlock/defaults/rebase@<sha>), updating
each uses string so the workflow points to a fixed commit rather than `@main`.

58 changes: 0 additions & 58 deletions .claudeignore

This file was deleted.

76 changes: 0 additions & 76 deletions .cursor/commands/spec-kitty.accept.md

This file was deleted.

Loading
Loading