Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ jobs:
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
if git diff --name-only "$BASE_SHA" HEAD | grep -q '\.proto$'; then
if git diff --name-only "$BASE_SHA" HEAD | grep -qE '\.proto$|^Makefile$|^buf\.|^protocol/codegen/|^protocol/go/internal/|^sdk/codegen/'; then
echo "proto=true" >> "$GITHUB_OUTPUT"
else
echo "proto=false" >> "$GITHUB_OUTPUT"
Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# make
# To run all lint checks: `LINT_OPTIONS= make lint`

.PHONY: all build clean connect-wrapper-generate docker-build fix fmt go-lint license lint proto-generate proto-lint sdk/sdk test tidy toolcheck
.PHONY: all build clean connect-wrapper-generate docker-build fix fmt go-lint license lint proto-generate proto-helper-generate proto-lint sdk/sdk test tidy toolcheck

MODS=protocol/go lib/ocrypto lib/fixtures lib/flattening lib/identifier sdk service examples
HAND_MODS=lib/ocrypto lib/fixtures lib/flattening lib/identifier sdk service examples
Expand Down Expand Up @@ -74,7 +74,7 @@ govulncheck:

proto-generate: toolcheck
# remove all generated directories under protocol/go
find protocol/go -mindepth 1 -maxdepth 1 -type d -exec rm -rf {} +
find protocol/go -mindepth 1 -maxdepth 1 -type d ! -name internal -exec rm -rf {} +
rm -rf docs/grpc docs/openapi
Comment thread
marythought marked this conversation as resolved.
buf generate service
buf generate service --template buf.gen.grpc.docs.yaml
Expand All @@ -84,11 +84,15 @@ proto-generate: toolcheck
buf generate buf.build/grpc-ecosystem/grpc-gateway -o tmp-gen --template buf.gen.grpc.docs.yaml
buf generate buf.build/grpc-ecosystem/grpc-gateway -o tmp-gen --template buf.gen.openapi.docs.yaml

cd protocol/codegen && go run .
go run ./sdk/codegen

connect-wrapper-generate:
go run ./sdk/codegen

proto-helper-generate:
cd protocol/codegen && go run .

policy-sql-gen:
@which sqlc > /dev/null || { echo "sqlc not found, please install it: https://docs.sqlc.dev/en/stable/overview/install.html"; exit 1; }
sqlc generate -f service/policy/db/sqlc.yaml
Expand Down
3 changes: 3 additions & 0 deletions protocol/codegen/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/opentdf/platform/protocol/codegen

go 1.25.5
5 changes: 5 additions & 0 deletions protocol/codegen/go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Isolate this module from the root workspace so `go run .` resolves locally
// without adding protocol/codegen to the root go.work.
go 1.25.5

use .
157 changes: 157 additions & 0 deletions protocol/codegen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Command codegen copies helper source files from protocol/go/internal/ into their
Comment thread
marythought marked this conversation as resolved.
// corresponding proto package directories with a "Code generated" header prepended.
// Helper source files import proto types explicitly for IDE support; the copier strips
// the self-referencing import and type qualifiers so the output compiles in-package.
//
// See https://github.com/opentdf/platform/pull/3232 for background on the source-file codegen approach.
package main

import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
)

// helperMapping defines a source directory (relative to protocol/go/internal/) and its
// target directory (relative to protocol/go/) where files will be copied.
type helperMapping struct {
// Source is the subdirectory under internal/ containing the source files.
Source string
// Target is the subdirectory under protocol/go/ where files are copied.
Target string
// ProtoImportPath is the full Go import path of the target proto package.
ProtoImportPath string
// ProtoImportAlias is the import alias used in the source files for the proto package.
ProtoImportAlias string
}

var mappings = []helperMapping{
{
Source: "authorization/v2",
Target: "authorization/v2",
ProtoImportPath: "github.com/opentdf/platform/protocol/go/authorization/v2",
ProtoImportAlias: "authorizationv2",
},
}

const generatedHeader = "// Code generated by protocol/codegen. DO NOT EDIT.\n\n"

func main() {
baseDir, err := getBaseDir()
if err != nil {
log.Fatal(err)
}

helpersDir := filepath.Join(baseDir, "internal")
for _, m := range mappings {
srcDir := filepath.Join(helpersDir, m.Source)
dstDir := filepath.Join(baseDir, m.Target)
if err := copyHelpers(srcDir, dstDir, m); err != nil {
log.Fatalf("copying helpers from %s to %s: %v", srcDir, dstDir, err)
}
}
}

// generatedFile holds a transformed helper ready to write.
type generatedFile struct {
dst string
content []byte
src string
}

func copyHelpers(srcDir, dstDir string, m helperMapping) error {
entries, err := os.ReadDir(srcDir)
if err != nil {
return fmt.Errorf("reading source directory: %w", err)
}

// Read and transform all source files before touching the target directory.
var files []generatedFile
for _, entry := range entries {
name := entry.Name()
if entry.IsDir() || !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
continue
}

src := filepath.Join(srcDir, name)
content, err := os.ReadFile(src)
if err != nil {
return fmt.Errorf("reading %s: %w", src, err)
}

transformed := rewriteImports(string(content), m)
outName := strings.TrimSuffix(name, ".go") + ".gen.go"

files = append(files, generatedFile{
dst: filepath.Join(dstDir, outName),
content: []byte(generatedHeader + transformed),
src: src,
})
}

// Only remove stale .gen.go files once all reads succeeded.
if err := removeGenFiles(dstDir); err != nil {
return fmt.Errorf("cleaning target directory: %w", err)
}

for _, f := range files {
if err := os.WriteFile(f.dst, f.content, 0o644); err != nil {
return fmt.Errorf("writing %s: %w", f.dst, err)
}
fmt.Printf(" %s -> %s\n", f.src, f.dst)
}
return nil
}

func removeGenFiles(dir string) error {
entries, err := os.ReadDir(dir)
if err != nil {
return fmt.Errorf("reading directory: %w", err)
}
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".gen.go") {
if err := os.Remove(filepath.Join(dir, entry.Name())); err != nil {
return fmt.Errorf("removing %s: %w", entry.Name(), err)
}
}
}
return nil
}

// rewriteImports removes the self-referencing proto import and strips the alias qualifier
// from type references so the file compiles inside the proto package.
//
// The qualifier regex also matches inside string literals and comments. This is acceptable
// because we control the source files and don't use the alias in non-code contexts.
func rewriteImports(content string, m helperMapping) string {
// Remove the import line: `authorizationv2 "github.com/.../authorization/v2"`
importLineRe := regexp.MustCompile(
`(?m)^\s*` + regexp.QuoteMeta(m.ProtoImportAlias) + `\s+"` + regexp.QuoteMeta(m.ProtoImportPath) + `"\s*\n`,
)
content = importLineRe.ReplaceAllString(content, "")

// Strip the alias qualifier from type references: `authorizationv2.Foo` -> `Foo`
qualifierRe := regexp.MustCompile(regexp.QuoteMeta(m.ProtoImportAlias) + `\.`)
content = qualifierRe.ReplaceAllString(content, "")

// Clean up empty import blocks left behind when the proto import was the only one.
emptyImportRe := regexp.MustCompile(`\nimport \(\n\)\n`)
content = emptyImportRe.ReplaceAllString(content, "")

return content
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// getBaseDir returns the protocol/go/ directory by navigating from this file's location.
// From protocol/codegen/main.go, go up two levels to protocol/, then into go/.
func getBaseDir() (string, error) {
_, filename, _, ok := runtime.Caller(0)
if !ok {
return "", errors.New("could not determine current file location")
}
return filepath.Join(filepath.Dir(filepath.Dir(filename)), "go"), nil
}
Loading
Loading