Skip to content

Commit 5873147

Browse files
committed
🎨✅ Address review comments
Signed-off-by: Evaline Ju <69598118+evaline-ju@users.noreply.github.com>
1 parent 8c7e773 commit 5873147

4 files changed

Lines changed: 103 additions & 14 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ jobs:
101101
run: go build -v ./...
102102

103103
# The authbridge-lite image is this same authbridge-proxy binary built
104-
# with exclude_plugin_* tags (only jwt-validation + token-exchange).
104+
# with the trimmed plugin set derived by authbridge/scripts/lite-tags.
105105
# Build AND test that tag set on every PR — build.yaml only exercises
106106
# it on tag/main pushes, and this guards against lite-only regressions.
107-
- name: Build + test lite variant (exclude_plugin_* tags)
107+
- name: Build + test lite variant
108108
if: matrix.binary == 'authbridge-proxy'
109109
run: |
110110
TAGS=$(go -C ../../scripts/lite-tags run .)

authbridge/cmd/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,6 @@ inbound mechanism, and the preset fills only that one's address.
7676
- **Size-constrained, no protocol-aware events needed**: use the
7777
`authbridge-lite` image — the `authbridge-proxy` binary built with
7878
`exclude_plugin_*` tags from `authbridge/scripts/lite-tags` (trimmed
79-
plugin set). Same listener layout, but abctl will only see denial
80-
events and basic auth-level invocations for the plugins the trimmed
81-
set drops.
79+
plugin set). Same listener layout, but without parsers/OPA — abctl
80+
will only see denial events and basic auth-level invocations, not
81+
full A2A/MCP/Inference protocol context.

authbridge/scripts/lite-tags/main.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
// New default-on plugins are excluded from lite automatically. To keep one
77
// in lite, add its build-tag suffix to liteKeep.
88
//
9-
// Usage:
10-
//
11-
// go -C authbridge/scripts/lite-tags run . # default path, CWD-independent
12-
// go -C authbridge/scripts/lite-tags run . <dir> # override the plugins dir
9+
// Usage: every call site uses `go -C <path>/scripts/lite-tags run .`, which
10+
// chdirs to this module before exec so the default plugins path resolves
11+
// correctly. Callers can pass an override path as the first argument.
1312
package main
1413

1514
import (
@@ -31,13 +30,18 @@ var liteKeep = map[string]bool{
3130
"staticinject": true,
3231
}
3332

34-
// defaultPluginsDir is relative to this script's module directory, so
35-
// `go -C authbridge/scripts/lite-tags run .` finds it regardless of the
36-
// caller's CWD. Callers with an unusual layout can pass a plugins path
37-
// as the first argument.
33+
// defaultPluginsDir is resolved relative to the process working directory
34+
// (filepath.Glob and os.ReadFile know nothing about module layout). It
35+
// works because every call site uses `go -C <this-module> run .`, which
36+
// chdirs before exec.
3837
const defaultPluginsDir = "../../cmd/authbridge-proxy"
3938

40-
var buildTagPattern = regexp.MustCompile(`^//go:build !exclude_plugin_(\S+)$`)
39+
// buildTagPattern matches `!exclude_plugin_<name>` inside a //go:build
40+
// directive. The pattern does NOT anchor to end-of-line so compound
41+
// directives like `//go:build !exclude_plugin_opa && !nocgo` still yield
42+
// the exclude tag — otherwise the plugin would silently stay in the lite
43+
// build.
44+
var buildTagPattern = regexp.MustCompile(`^//go:build\s+!exclude_plugin_(\w+)`)
4145

4246
func main() {
4347
dir := defaultPluginsDir
@@ -63,6 +67,11 @@ func discover(dir string) ([]string, error) {
6367

6468
var tags []string
6569
for _, path := range matches {
70+
// Skip test files: `plugins_*_test.go` matches the same glob but
71+
// isn't a plugin build directive.
72+
if strings.HasSuffix(path, "_test.go") {
73+
continue
74+
}
6675
name, err := extractExcludeSuffix(path)
6776
if err != nil {
6877
return nil, err
@@ -72,6 +81,11 @@ func discover(dir string) ([]string, error) {
7281
}
7382
tags = append(tags, "exclude_plugin_"+name)
7483
}
84+
// Fail closed if no tags were derived: an empty CSV would produce
85+
// `go build -tags ""` and silently ship a full binary as "lite".
86+
if len(tags) == 0 {
87+
return nil, fmt.Errorf("no exclude tags derived from %s: every default-on plugin is in liteKeep, or the build-tag convention changed", dir)
88+
}
7589
sort.Strings(tags)
7690
return tags, nil
7791
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
// TestDiscover_ExpectedTags fails when the shipped lite tag set changes —
11+
// a plugin added/removed or liteKeep edited. Deliberate changes update
12+
// the want string; unintended changes are caught here.
13+
func TestDiscover_ExpectedTags(t *testing.T) {
14+
tags, err := discover(defaultPluginsDir)
15+
if err != nil {
16+
t.Fatalf("discover: %v", err)
17+
}
18+
got := strings.Join(tags, ",")
19+
want := "exclude_plugin_a2aparser,exclude_plugin_ibac,exclude_plugin_inferenceparser,exclude_plugin_mcpparser,exclude_plugin_opa,exclude_plugin_sparc,exclude_plugin_tokenbroker,exclude_plugin_toolprune"
20+
if got != want {
21+
t.Errorf("output changed\n got: %s\nwant: %s", got, want)
22+
}
23+
}
24+
25+
// TestDiscover_FailsClosedOnEmptyTags — an empty result would flow to
26+
// `go build -tags ""` and ship a full binary as "lite".
27+
func TestDiscover_FailsClosedOnEmptyTags(t *testing.T) {
28+
dir := t.TempDir()
29+
writePlugin(t, dir, "plugins_jwtvalidation.go", "!exclude_plugin_jwtvalidation")
30+
31+
if _, err := discover(dir); err == nil {
32+
t.Fatal("want error when every plugin is in liteKeep, got nil")
33+
}
34+
}
35+
36+
// TestDiscover_CompoundBuildDirective — before dropping the $ anchor,
37+
// `!exclude_plugin_X && !Y` silently failed to match and the plugin
38+
// stayed in the lite build.
39+
func TestDiscover_CompoundBuildDirective(t *testing.T) {
40+
dir := t.TempDir()
41+
writePlugin(t, dir, "plugins_examplecompound.go", "!exclude_plugin_examplecompound && !nocgo")
42+
writePlugin(t, dir, "plugins_jwtvalidation.go", "!exclude_plugin_jwtvalidation") // keep-listed, prevents fail-closed
43+
44+
tags, err := discover(dir)
45+
if err != nil {
46+
t.Fatalf("discover: %v", err)
47+
}
48+
if got := strings.Join(tags, ","); got != "exclude_plugin_examplecompound" {
49+
t.Errorf("got %q, want exclude_plugin_examplecompound", got)
50+
}
51+
}
52+
53+
// TestDiscover_SkipsTestFiles — the plugins_*.go glob would otherwise
54+
// scan a future plugins_foo_test.go as a plugin definition.
55+
func TestDiscover_SkipsTestFiles(t *testing.T) {
56+
dir := t.TempDir()
57+
writePlugin(t, dir, "plugins_ghost_test.go", "!exclude_plugin_ghost")
58+
writePlugin(t, dir, "plugins_a2aparser.go", "!exclude_plugin_a2aparser")
59+
60+
tags, err := discover(dir)
61+
if err != nil {
62+
t.Fatalf("discover: %v", err)
63+
}
64+
if got := strings.Join(tags, ","); got != "exclude_plugin_a2aparser" {
65+
t.Errorf("got %q, want exclude_plugin_a2aparser (test file must be skipped)", got)
66+
}
67+
}
68+
69+
func writePlugin(t *testing.T, dir, name, buildConstraint string) {
70+
t.Helper()
71+
content := "//go:build " + buildConstraint + "\n\npackage main\n"
72+
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
73+
t.Fatalf("write %s: %v", name, err)
74+
}
75+
}

0 commit comments

Comments
 (0)