Skip to content

Commit c699cf5

Browse files
authored
feat(initial release) (#2)
2 parents d3a5fc6 + b6620e3 commit c699cf5

347 files changed

Lines changed: 27801 additions & 1575 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Local Development Setup
2+
3+
How to set up, run, and work with this project locally. Non-obvious dependencies, environment config, common setup issues.
4+
5+
- This machine has a stale `GOROOT=/Users/alex/apps/go` env var that breaks the homebrew Go toolchain. Run go commands as `env -u GOROOT go ...` (build, test, vet, gofmt).
6+
- When iterating on check/rule logic, delete `.codeguard/cache.json` before self-scanning (`make codeguard-ci`). The scan cache keys on file hash + config hash but NOT the codeguard binary version, so rule-logic changes replay stale per-file findings — cross-file analyses (duplicate-code, import graphs) can appear to report zero findings when they're actually being skipped.
7+
- Cross-file checks (clone detection, dependency graphs) only observe every file via `VisitTargetFiles` (cache-bypassing); `ScanTargetFiles` skips evaluators on cache hits and silently produces empty cross-file state on a second scan.

.codeguard/codeguard.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
name: codeguard-repo-ci
2+
exclude:
3+
- .claude/**
4+
- .codeguard/cache.json
5+
- .codeguard/cache.slop-history.json
6+
- .gomodcache/**
7+
- tests/**/.codeguard/cache.json
8+
waivers:
9+
- rule: quality.max-file-lines
10+
path: internal/codeguard/rules/catalog_quality.go
11+
reason: rule catalog is intentionally dense and should still be scanned by other checks
12+
- rule: quality.max-file-lines
13+
path: tests/checks/features_test.go
14+
reason: consolidated feature coverage is intentionally broad and should still be scanned by other checks
215
targets:
316
- name: repository
4-
path: .
17+
path: ..
518
language: go
619
entrypoints:
720
- cmd/codeguard
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: homebrew-validation.yml
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- develop
7+
- master
8+
- main
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
formula-validation:
15+
runs-on: ${{ matrix.os }}
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
os:
20+
- ubuntu-latest
21+
- macos-latest
22+
23+
steps:
24+
- name: Check out code
25+
uses: actions/checkout@v6
26+
27+
- name: Set up Homebrew
28+
uses: Homebrew/actions/setup-homebrew@main
29+
30+
- name: Clone Homebrew tap
31+
shell: bash
32+
env:
33+
TAP_REPOSITORY: devr-tools/homebrew-tap
34+
run: |
35+
set -euo pipefail
36+
git clone --depth 1 "https://github.com/${TAP_REPOSITORY}.git" "$RUNNER_TEMP/homebrew-tap"
37+
38+
- name: Patch tap formula for current checkout
39+
shell: bash
40+
run: |
41+
set -euo pipefail
42+
43+
version="$(awk -F'"' '/^const Number = / { print $2; exit }' internal/version/version.go)"
44+
archive_path="$RUNNER_TEMP/codeguard-src.tar.gz"
45+
tap_dir="$RUNNER_TEMP/homebrew-tap"
46+
formula_path="$RUNNER_TEMP/homebrew-tap/Formula/codeguard.rb"
47+
git archive --format=tar.gz --output "$archive_path" HEAD
48+
sha256="$(shasum -a 256 "$archive_path" | awk '{print $1}')"
49+
50+
if [ -z "$version" ]; then
51+
echo "failed to extract codeguard version from internal/version/version.go"
52+
exit 1
53+
fi
54+
55+
cat > "$formula_path" <<EOF
56+
class Codeguard < Formula
57+
desc "Repository policy and AI code review CLI for CI"
58+
homepage "https://github.com/devr-tools/codeguard"
59+
url "file://$archive_path"
60+
sha256 "$sha256"
61+
version "$version"
62+
license "Apache-2.0"
63+
depends_on "go" => :build
64+
65+
def install
66+
ldflags = "-s -w -X github.com/devr-tools/codeguard/internal/version.Number=v#{version}"
67+
system "go", "build", *std_go_args(output: bin/"codeguard", ldflags: ldflags), "./cmd/codeguard"
68+
end
69+
70+
test do
71+
assert_match version.to_s, shell_output("#{bin}/codeguard version")
72+
end
73+
end
74+
EOF
75+
76+
git -C "$tap_dir" config user.name "github-actions[bot]"
77+
git -C "$tap_dir" config user.email "41898282+github-actions[bot]@users.noreply.github.com"
78+
git -C "$tap_dir" add Formula/codeguard.rb
79+
git -C "$tap_dir" commit -m "test: patch codeguard formula for validation"
80+
81+
- name: Tap current formula checkout
82+
shell: bash
83+
run: |
84+
set -euo pipefail
85+
brew tap devr-tools/tap "$RUNNER_TEMP/homebrew-tap"
86+
87+
- name: Build formula from source
88+
shell: bash
89+
run: |
90+
set -euo pipefail
91+
HOMEBREW_NO_AUTO_UPDATE=1 brew install --build-from-source devr-tools/tap/codeguard
92+
93+
- name: Run formula test
94+
shell: bash
95+
run: |
96+
set -euo pipefail
97+
brew test devr-tools/tap/codeguard

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ jobs:
158158
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
159159

160160
- name: Upload release bundle
161-
uses: actions/upload-artifact@v4
161+
uses: actions/upload-artifact@v7
162162
with:
163163
name: dist-${{ steps.release.outputs.tag }}
164164
path: dist/*

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ go.work.sum
3535
# Editor/IDE
3636
.idea/
3737
.vscode/
38+
**/.codeguard/cache.slop-history.json

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
`codeguard` is a standalone Go service and CLI for repository checks across code quality, design boundaries, security, CI/CD hygiene, AI prompt governance, and repo-specific policy rules.
77

8-
It now supports repository exclusions, baselines, waivers, changed-lines diff scans, SARIF output, GitHub annotations, custom rule packs, policy profiles, scan caching, doctor checks, rule discovery from the CLI, native TypeScript/Python quality, design, and security heuristics, and language-specific command checks.
8+
It now supports repository exclusions, baselines, waivers, changed-lines diff scans, SARIF output, GitHub annotations, custom rule packs, natural-language custom rules through an optional AI runtime, policy profiles, scan caching, doctor checks, rule discovery from the CLI, native TypeScript/Python quality, design, and security heuristics, and language-specific command checks.
9+
10+
AI-generated-code quality coverage includes an AI-failure-mode rule pack, `slop_score` artifacts, provenance-aware review policy hooks, local idiom drift checks, optional provider-backed hybrid triage and semantic review passes, natural-language custom rules through an optional AI runtime, and a verified-fix flow that only returns patches after isolated patch validation plus test reruns succeed.
911

1012
The public Go SDK lives at `github.com/devr-tools/codeguard/pkg/codeguard`.
1113

@@ -23,6 +25,11 @@ Or build from source:
2325
make build
2426
```
2527

28+
Other install paths:
29+
30+
- GitHub Releases: tagged archives for direct download
31+
- Homebrew: `brew install devr-tools/tap/codeguard`
32+
2633
Or run in Docker:
2734

2835
```bash
@@ -39,7 +46,7 @@ make release-check
3946
make deploy
4047
```
4148

42-
The GitHub release flow follows the same branch and release-please model as `cleanr`, using `.github/workflows/cd.yml`, `.github/workflows/release.yml`, `.github/release-please-config.json`, and `.release-please-manifest.json`.
49+
The GitHub release flow follows the same branch and release-please model as `cleanr`, using `.github/workflows/cd.yml`, `.github/workflows/release.yml`, `.github/workflows/homebrew-validation.yml`, `.github/release-please-config.json`, and `.release-please-manifest.json`.
4350

4451
For SDK consumers:
4552

@@ -96,7 +103,12 @@ func main() {
96103
## Docs
97104

98105
- [Getting started](/Users/alex/Documents/GitHub/codeguard/docs/getting-started.md:1)
106+
- [AI-generated code quality](/Users/alex/Documents/GitHub/codeguard/docs/ai-quality.md:1)
107+
- [Agent-native features](/Users/alex/Documents/GitHub/codeguard/docs/agent-native.md:1)
99108
- [Integrations](/Users/alex/Documents/GitHub/codeguard/docs/integrations.md:1)
109+
- [Hook-pack examples](/Users/alex/Documents/GitHub/codeguard/examples/hooks/README.md:1)
100110
- [SDK guide](/Users/alex/Documents/GitHub/codeguard/docs/sdk.md:1)
111+
- [Release automation](/Users/alex/Documents/GitHub/codeguard/docs/release-automation.md:1)
112+
- [Homebrew packaging](/Users/alex/Documents/GitHub/codeguard/docs/homebrew.md:1)
101113
- [Checks reference](/Users/alex/Documents/GitHub/codeguard/docs/checks.md:1)
102114
- [Architecture](/Users/alex/Documents/GitHub/codeguard/docs/architecture.md:1)

action.yml

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
name: codeguard
2-
description: Run CodeGuard repository policy checks
1+
name: Devr Codeguard
2+
description: Run Devr CodeGuard repository policy checks for AI generated and human code
3+
branding:
4+
icon: shield
5+
color: gray-dark
6+
37

48
inputs:
59
config:
6-
description: Path to the CodeGuard config file
10+
description: Path to the Devr CodeGuard config file
711
required: false
812
default: codeguard.yaml
913
profile:
@@ -22,8 +26,16 @@ inputs:
2226
description: Output format
2327
required: false
2428
default: github
29+
comment-fix-mode:
30+
description: PR comment mode. Use sticky to create or update a fix-oriented PR comment.
31+
required: false
32+
default: "off"
33+
comment-tag:
34+
description: Sticky marker used to update an existing CodeGuard PR comment.
35+
required: false
36+
default: codeguard-action-comment
2537
version:
26-
description: CodeGuard version to install
38+
description: Devr CodeGuard version to install
2739
required: false
2840
default: latest
2941

@@ -35,24 +47,69 @@ runs:
3547
with:
3648
go-version: "1.23"
3749

38-
- name: Install CodeGuard
50+
- name: Install Devr CodeGuard
3951
shell: bash
4052
run: go install github.com/devr-tools/codeguard/cmd/codeguard@${{ inputs.version }}
4153

42-
- name: Run CodeGuard
54+
- name: Run Devr CodeGuard
55+
id: scan
4356
shell: bash
4457
run: |
58+
set +e
4559
if [ -n "${{ inputs.profile }}" ]; then
4660
codeguard scan \
4761
-config "${{ inputs.config }}" \
4862
-mode "${{ inputs.mode }}" \
4963
-base-ref "${{ inputs.base-ref }}" \
5064
-format "${{ inputs.format }}" \
5165
-profile "${{ inputs.profile }}"
66+
status=$?
5267
else
5368
codeguard scan \
5469
-config "${{ inputs.config }}" \
5570
-mode "${{ inputs.mode }}" \
5671
-base-ref "${{ inputs.base-ref }}" \
5772
-format "${{ inputs.format }}"
73+
status=$?
74+
fi
75+
echo "exit_code=$status" >> "$GITHUB_OUTPUT"
76+
exit 0
77+
78+
- name: Post Devr CodeGuard fix comment
79+
if: ${{ inputs.comment-fix-mode != 'off' && (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') }}
80+
shell: bash
81+
working-directory: ${{ github.action_path }}
82+
env:
83+
GITHUB_TOKEN: ${{ github.token }}
84+
run: |
85+
report_file="$(mktemp)"
86+
set +e
87+
if [ -n "${{ inputs.profile }}" ]; then
88+
codeguard scan \
89+
-config "${{ inputs.config }}" \
90+
-mode "${{ inputs.mode }}" \
91+
-base-ref "${{ inputs.base-ref }}" \
92+
-format github-comment \
93+
-profile "${{ inputs.profile }}" > "$report_file"
94+
else
95+
codeguard scan \
96+
-config "${{ inputs.config }}" \
97+
-mode "${{ inputs.mode }}" \
98+
-base-ref "${{ inputs.base-ref }}" \
99+
-format github-comment > "$report_file"
100+
fi
101+
set -e
102+
if [ ! -s "$report_file" ]; then
103+
echo "CodeGuard comment report was empty; skipping comment publish."
104+
exit 0
58105
fi
106+
go run ./cmd/codeguard-action-comment \
107+
-body-file "$report_file" \
108+
-repository "${{ github.repository }}" \
109+
-marker "${{ inputs.comment-tag }}" \
110+
-mode "${{ inputs.comment-fix-mode }}"
111+
112+
- name: Fail workflow on Devr CodeGuard findings
113+
if: ${{ steps.scan.outputs.exit_code != '0' }}
114+
shell: bash
115+
run: exit "${{ steps.scan.outputs.exit_code }}"
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
12+
"github.com/devr-tools/codeguard/internal/githubaction"
13+
)
14+
15+
func main() {
16+
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
17+
}
18+
19+
func run(args []string, stdout io.Writer, stderr io.Writer) int {
20+
fs := flag.NewFlagSet("codeguard-action-comment", flag.ContinueOnError)
21+
fs.SetOutput(stderr)
22+
bodyFile := fs.String("body-file", "", "path to markdown body file")
23+
eventPath := fs.String("event-path", os.Getenv("GITHUB_EVENT_PATH"), "path to the GitHub event payload")
24+
repository := fs.String("repository", os.Getenv("GITHUB_REPOSITORY"), "repository in owner/name format")
25+
token := fs.String("token", os.Getenv("GITHUB_TOKEN"), "GitHub token")
26+
apiURL := fs.String("api-url", os.Getenv("GITHUB_API_URL"), "GitHub API base URL")
27+
marker := fs.String("marker", "codeguard-action-comment", "sticky comment marker")
28+
mode := fs.String("mode", "sticky", "comment mode: sticky or new")
29+
if err := fs.Parse(args); err != nil {
30+
return 1
31+
}
32+
33+
if strings.TrimSpace(*bodyFile) == "" {
34+
_, _ = fmt.Fprintln(stderr, "body-file is required")
35+
return 1
36+
}
37+
if strings.TrimSpace(*repository) == "" {
38+
_, _ = fmt.Fprintln(stderr, "repository is required")
39+
return 1
40+
}
41+
if strings.TrimSpace(*token) == "" {
42+
_, _ = fmt.Fprintln(stderr, "token is required")
43+
return 1
44+
}
45+
46+
body, err := os.ReadFile(filepath.Clean(*bodyFile))
47+
if err != nil {
48+
_, _ = fmt.Fprintf(stderr, "read body file: %v\n", err)
49+
return 1
50+
}
51+
prNumber, err := githubaction.ResolvePullRequestNumber(*eventPath)
52+
if err != nil {
53+
_, _ = fmt.Fprintf(stderr, "resolve pull request number: %v\n", err)
54+
return 1
55+
}
56+
57+
commentBody := githubaction.WrapCommentBody(string(body), *marker)
58+
client := githubaction.CommentPublisher{
59+
BaseURL: githubaction.NormalizeAPIURL(*apiURL),
60+
Token: strings.TrimSpace(*token),
61+
Client: http.DefaultClient,
62+
}
63+
64+
if err := client.Publish(*repository, prNumber, commentBody, strings.TrimSpace(*mode)); err != nil {
65+
_, _ = fmt.Fprintf(stderr, "publish comment: %v\n", err)
66+
return 1
67+
}
68+
69+
_, _ = fmt.Fprintf(stdout, "published CodeGuard PR comment on #%d\n", prNumber)
70+
return 0
71+
}

docs/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
# CodeGuard Docs
22

33
- [Getting started](getting-started.md)
4+
- [AI-generated code quality](ai-quality.md)
5+
- [Agent-native features](agent-native.md)
46
- [Integrations](integrations.md)
7+
- [Hook-pack examples](../examples/hooks/README.md)
58
- [SDK guide](sdk.md)
9+
- [Release automation](release-automation.md)
10+
- [Homebrew packaging](homebrew.md)
611
- [Architecture](architecture.md)
712
- [Checks](checks.md)

0 commit comments

Comments
 (0)