Skip to content

Commit

Permalink
auto-gen commands.go using schemas.cue as authority
Browse files Browse the repository at this point in the history
  • Loading branch information
taylormonacelli committed Nov 12, 2024
1 parent c6099a9 commit 5aba0b7
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 70 deletions.
20 changes: 14 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,31 @@ jobs:
- windows-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed
with:
go-version: '>=1.21.1'
- name: Install Tools
run: |
go install cuelang.org/go/cmd/cue@latest
go install golang.org/x/tools/cmd/goimports@latest
go install mvdan.cc/gofumpt@latest
go install github.com/segmentio/golines@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/incu6us/goimports-reviser/v3@latest
- name: Set GOVERSION environment variable (Linux/macOS)
if: runner.os != 'Windows'
run: echo "GOVERSION=$(go version)" >> $GITHUB_ENV
- name: Set GOVERSION environment variable (Windows)
if: runner.os == 'Windows'
run: echo "GOVERSION=$(go version)" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Build & Test
- name: Build and Test
run: |
go build -v
go test ./...
make build
make test
- name: Run GoReleaser
if: runner.os == 'Linux'
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf
with:
args: release --snapshot --skip=publish --clean
- name: Colored Output Test
Expand Down
34 changes: 18 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,24 @@ LDFLAGS += -X '$(PREFIX).GoVersion=$(GOVERSION)'
LDFLAGS += -X $(PREFIX).ShortGitSHA=$(SHORT_SHA)
LDFLAGS += -X $(PREFIX).FullGitSHA=$(FULL_SHA)

.DEFAULT_GOAL := iterate
.DEFAULT_GOAL := build

all: check $(BIN) install

.PHONY: iterate # lint and rebuild
iterate: check $(BIN)
all: generate check $(BIN) install

.PHONY: check # lint and vet
check: .timestamps/.check.time
check: generate .timestamps/.check.time

.timestamps/.check.time: goimports tidy fmt lint vet
@mkdir -p .timestamps
@touch $@

.PHONY: generate # run go generate
generate: core/schemas.cue core/gen/main.go
@mkdir -p cmd
go generate ./...

.PHONY: build # build
build: $(BIN)
build: generate $(BIN)

$(BIN): .timestamps/.build.time .timestamps/.tidy.time
go build -ldflags "$(LDFLAGS)" -o $@
Expand All @@ -45,52 +47,52 @@ $(BIN): .timestamps/.build.time .timestamps/.tidy.time
goreleaser: goreleaser --clean

.PHONY: goimports # goimports-reviser
goimports: .timestamps/.goimports.time
goimports: generate .timestamps/.goimports.time
.timestamps/.goimports.time: $(SRC)
goimports -w $(SRC)
goimports-reviser -output=file -set-alias -rm-unused -format $(SRC)
@mkdir -p .timestamps
@touch $@

.PHONY: tidy # go mod tidy
tidy: .timestamps/.tidy.time
tidy: generate .timestamps/.tidy.time

.timestamps/.tidy.time: go.mod go.sum
go mod tidy
@mkdir -p .timestamps
@touch $@

.PHONY: fmt # gofumt
fmt: .timestamps/.fmt.time
fmt: generate .timestamps/.fmt.time
.timestamps/.fmt.time: $(SRC)
gofumpt -extra -w $(SRC)
cue fmt --simplify --files core
@mkdir -p .timestamps
@touch $@

.PHONY: golines # golines
golines: .timestamps/.golines.time
golines: generate .timestamps/.golines.time
.timestamps/.golines.time: $(SRC)
golines -w $(SRC)
@mkdir -p .timestamps
@touch $@

.PHONY: lint # lint
lint: .timestamps/.lint.time
lint: generate .timestamps/.lint.time
.timestamps/.lint.time: $(SRC)
golangci-lint run
@mkdir -p .timestamps
@touch $@

.PHONY: vet # go vet
vet: .timestamps/.vet.time
vet: generate .timestamps/.vet.time
.timestamps/.vet.time: $(SRC)
go vet ./...
@mkdir -p .timestamps
@touch $@

.PHONY: test # go test
test:
test: generate
go test ./...
@mkdir -p .timestamps
@touch $@
Expand All @@ -103,6 +105,6 @@ install:
help:
@grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20

.PHONY: clean # clean bin
.PHONY: clean # clean bin and generated files
clean:
$(RM) -r $(BIN) .timestamps
$(RM) -r $(BIN) .timestamps cmd/commands.go
39 changes: 0 additions & 39 deletions cmd/commands.go

This file was deleted.

103 changes: 103 additions & 0 deletions core/gen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package main

import (
"bytes"
"fmt"
"go/format"
"log"
"os"
"path/filepath"
"regexp"
"strings"

"cuelang.org/go/cue/cuecontext"
)

func main() {
if len(os.Args) != 2 {
log.Fatal("Usage: gen <schema.cue>")
}

// Get the project root directory (go up two levels from schema file)
schemaPath := os.Args[1]
projectRoot := filepath.Clean(filepath.Join(filepath.Dir(schemaPath), ".."))

// Target should be in cmd/commands.go relative to project root
outputFile := filepath.Join(projectRoot, "cmd", "commands.go")

if err := generateCommands(schemaPath, outputFile); err != nil {
log.Fatal(err)
}
}

func generateCommands(schemaFile, outputFile string) error {
content, err := os.ReadFile(schemaFile)
if err != nil {
return fmt.Errorf("error reading schema file: %w", err)
}

ctx := cuecontext.New()
value := ctx.CompileBytes(content)
if err := value.Err(); err != nil {
return fmt.Errorf("error compiling schema: %w", err)
}

// Find all fields with @animal attribute and their descriptions
animals := make(map[string]string)
iter, _ := value.Fields()
for iter.Next() {
sel := iter.Selector()
docs := iter.Value().Doc()
for _, doc := range docs {
for _, c := range doc.List {
if strings.Contains(c.Text, "@animal") {
// Look for preset description in the comments
re := regexp.MustCompile(`preset: (.+)`)
for _, c2 := range doc.List {
if matches := re.FindStringSubmatch(c2.Text); len(matches) > 1 {
animals[sel.String()] = matches[1]
break
}
}
}
}
}
}

if len(animals) == 0 {
return fmt.Errorf("no animals found in schema")
}

// Generate commands.go
var buf bytes.Buffer
buf.WriteString("// Code generated by gen; DO NOT EDIT.\n\n")
buf.WriteString("package cmd\n\n")
buf.WriteString("var commands = map[string]struct {\n")
buf.WriteString("\tshort string\n")
buf.WriteString("\tlong string\n")
buf.WriteString("}{\n")

for animal, desc := range animals {
buf.WriteString(fmt.Sprintf("\t%q: {\n", animal))
buf.WriteString(fmt.Sprintf("\t\tshort: \"Generate Renovate configuration using %s preset\",\n", animal))
buf.WriteString(fmt.Sprintf("\t\tlong: \"Generate Renovate configuration using %s preset, which %s.\",\n", animal, desc))
buf.WriteString("\t},\n")
}
buf.WriteString("}\n")

formatted, err := format.Source(buf.Bytes())
if err != nil {
return fmt.Errorf("error formatting generated code: %w", err)
}

// Create output directory if it doesn't exist
if err := os.MkdirAll(filepath.Dir(outputFile), 0o755); err != nil {
return fmt.Errorf("error creating output directory: %w", err)
}

if err := os.WriteFile(outputFile, formatted, 0o644); err != nil {
return fmt.Errorf("error writing commands.go: %w", err)
}

return nil
}
Loading

0 comments on commit 5aba0b7

Please sign in to comment.