Skip to content

Commit d21dac4

Browse files
committed
feat: add ccg version command and --namespace global flag
- version subcommand with ldflags injection (version/commit/date) - Makefile for build with automatic git tag injection - --namespace PersistentFlag on root cmd (build/update/search) - release workflow updated with version ldflags - docs: CLI reference, README updated
1 parent 1b5d42d commit d21dac4

12 files changed

Lines changed: 201 additions & 2 deletions

File tree

.github/workflows/release.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ jobs:
5656
GOARCH: ${{ matrix.goarch }}
5757
CGO_ENABLED: 1
5858
run: |
59-
go build -tags "fts5" -ldflags "-s -w" -o ${{ matrix.artifact }} ./cmd/ccg/
59+
VERSION=${GITHUB_REF#refs/tags/}
60+
COMMIT=${GITHUB_SHA::7}
61+
DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
62+
go build -tags "fts5" -ldflags "-s -w -X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}" -o ${{ matrix.artifact }} ./cmd/ccg/
6063
6164
- name: Install UPX (Linux)
6265
if: runner.os == 'Linux'

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
2+
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
3+
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
4+
PKG = github.com/imtaebin/code-context-graph/cmd/ccg
5+
LDFLAGS = -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)
6+
7+
.PHONY: build install test clean
8+
9+
build:
10+
CGO_ENABLED=1 go build -tags "fts5" -ldflags '$(LDFLAGS)' -o ccg ./cmd/ccg/
11+
12+
install:
13+
CGO_ENABLED=1 go install -tags "fts5" -ldflags '$(LDFLAGS)' ./cmd/ccg/
14+
15+
test:
16+
CGO_ENABLED=1 go test -tags "fts5" ./...
17+
18+
clean:
19+
rm -f ccg

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ go install github.com/tae2089/code-context-graph/cmd/ccg@latest
3333

3434
```bash
3535
CGO_ENABLED=1 go build -tags "fts5" -o ccg ./cmd/ccg/
36+
37+
# Or use Makefile (injects version from git tag automatically)
38+
make build
3639
```
3740

3841
## Quick Start
@@ -50,6 +53,13 @@ ccg search "결제" # finds functions with @intent/@domainRule about payme
5053

5154
# Graph statistics
5255
ccg status
56+
57+
# Version info
58+
ccg version
59+
60+
# Namespace isolation (MSA)
61+
ccg build ./backend --namespace backend
62+
ccg search --namespace backend "auth"
5363
```
5464

5565
## MCP Server

cmd/ccg/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ var (
5353
_ mcpserver.IncrementalSyncer = (*incremental.Syncer)(nil)
5454
)
5555

56+
var (
57+
version = "dev"
58+
commit = "unknown"
59+
date = "unknown"
60+
)
61+
5662
// main wires CLI dependencies and executes the root command.
5763
// @intent 애플리케이션 시작 시 DB, 워커, MCP 실행 의존성을 구성해 CLI를 실행한다.
5864
// @sideEffect 시그널 핸들러를 등록하고 명령 실행 중 필요한 리소스를 초기화한다.
@@ -61,6 +67,11 @@ func main() {
6167

6268
deps := &cli.Deps{
6369
Logger: logger,
70+
Version: cli.VersionInfo{
71+
Version: version,
72+
Commit: commit,
73+
Date: date,
74+
},
6475
}
6576

6677
deps.InitFunc = func(driver, dsn string) error {
@@ -158,7 +169,7 @@ func buildWalkers(logger *slog.Logger) map[string]*treesitter.Walker {
158169
{treesitter.RustSpec, []string{".rs"}},
159170
{treesitter.KotlinSpec, []string{".kt", ".kts"}},
160171
{treesitter.PHPSpec, []string{".php"}},
161-
{treesitter.LuaSpec, []string{".lua"}},
172+
{treesitter.LuaSpec, []string{".lua", ".luau"}},
162173
}
163174

164175
walkers := make(map[string]*treesitter.Walker)

guide/cli-reference.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# CLI Reference
22

3+
## Global Flags
4+
5+
| Flag | Description |
6+
|------|-------------|
7+
| `--namespace <name>` | Namespace for data isolation (e.g. `--namespace backend`) |
8+
| `--db-driver <driver>` | Database driver: `sqlite`, `postgres` (default `sqlite`) |
9+
| `--db-dsn <dsn>` | Database connection string (default `ccg.db`) |
10+
| `--log-level <level>` | Log level: `debug`, `info`, `warn`, `error` (default `info`) |
11+
| `--log-json` | Output logs in JSON format |
12+
| `--config <path>` | Config file path (default: `.ccg.yaml` in `./` then `~/.config/ccg/`) |
13+
14+
### Namespace
15+
16+
MSA 환경에서 서비스별 코드 그래프를 하나의 DB에 격리 저장할 수 있습니다.
17+
18+
```bash
19+
# 서비스별 빌드
20+
ccg build ./backend --namespace backend
21+
ccg build ./frontend --namespace frontend
22+
23+
# 특정 namespace 내에서만 검색
24+
ccg search --namespace backend "auth"
25+
26+
# 증분 업데이트도 namespace 적용
27+
ccg update ./backend --namespace backend
28+
```
29+
330
## Commands
431

532
| Command | Description |
@@ -23,6 +50,7 @@
2350
| `ccg hooks install --lint-strict` | Install hook that blocks commit on issues |
2451
| `ccg lint [--out dir]` | 8-category docs lint |
2552
| `ccg lint --strict` | Exit 1 on issues (for CI/pre-commit) |
53+
| `ccg version` | Print build version, commit, date |
2654

2755
### Serve
2856

internal/cli/build.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/spf13/cobra"
88
"github.com/tae2089/trace"
99

10+
"github.com/imtaebin/code-context-graph/internal/ctxns"
1011
"github.com/imtaebin/code-context-graph/internal/service"
1112
)
1213

@@ -45,6 +46,9 @@ func newBuildCmd(deps *Deps) *cobra.Command {
4546
}
4647

4748
ctx := context.Background()
49+
if ns, _ := cmd.Flags().GetString("namespace"); ns != "" {
50+
ctx = ctxns.WithNamespace(ctx, ns)
51+
}
4852
stats, err := svc.Build(ctx, opts)
4953
if err != nil {
5054
return trace.Wrap(err, "build project")

internal/cli/build_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,32 @@ func Beta() {}
191191
_ = ctx
192192
}
193193

194+
func TestBuildCommand_Namespace_StoresWithNamespace(t *testing.T) {
195+
deps, stdout, stderr, db := setupBuildTest(t)
196+
197+
dir := t.TempDir()
198+
writeGoFile(t, dir, "hello.go", `package hello
199+
func Hello() {}
200+
`)
201+
202+
err := executeCmd(deps, stdout, stderr, "build", "--namespace", "backend", dir)
203+
if err != nil {
204+
t.Fatalf("unexpected error: %v", err)
205+
}
206+
207+
var nodes []model.Node
208+
db.Where("namespace = ?", "backend").Find(&nodes)
209+
if len(nodes) == 0 {
210+
t.Fatal("expected nodes with namespace 'backend'")
211+
}
212+
213+
for _, n := range nodes {
214+
if n.Namespace != "backend" {
215+
t.Errorf("expected namespace 'backend', got %q", n.Namespace)
216+
}
217+
}
218+
}
219+
194220
func TestBuildCommand_NoRecursive_SkipsSubdirs(t *testing.T) {
195221
deps, stdout, stderr, db := setupBuildTest(t)
196222

internal/cli/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Deps struct {
3333
ServeFunc func(cfg ServeConfig) error
3434
InitFunc func(dbDriver, dsn string) error
3535
CleanupFunc func()
36+
Version VersionInfo
3637
}
3738

3839
// NewRootCmd creates the root cobra command with all subcommands attached.
@@ -108,6 +109,7 @@ func NewRootCmd(deps *Deps) *cobra.Command {
108109
// Global database configuration flags
109110
rootCmd.PersistentFlags().String("db-driver", "sqlite", "Database driver (sqlite, postgres)")
110111
rootCmd.PersistentFlags().String("db-dsn", "ccg.db", "Database connection string")
112+
rootCmd.PersistentFlags().String("namespace", "", "Namespace for data isolation (e.g. --namespace backend)")
111113

112114
// Bind flags to viper
113115
_ = viper.BindPFlag("db.driver", rootCmd.PersistentFlags().Lookup("db-driver"))
@@ -132,6 +134,7 @@ func NewRootCmd(deps *Deps) *cobra.Command {
132134
newIndexCmd(deps),
133135
newLintCmd(deps),
134136
newRagIndexCmd(deps),
137+
newVersionCmd(deps),
135138
)
136139

137140
return rootCmd

internal/cli/search.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
"github.com/spf13/cobra"
99
"github.com/tae2089/trace"
10+
11+
"github.com/imtaebin/code-context-graph/internal/ctxns"
1012
)
1113

1214
// newSearchCmd creates the full-text search command.
@@ -24,6 +26,9 @@ func newSearchCmd(deps *Deps) *cobra.Command {
2426
RunE: func(cmd *cobra.Command, args []string) error {
2527
query := args[0]
2628
ctx := context.Background()
29+
if ns, _ := cmd.Flags().GetString("namespace"); ns != "" {
30+
ctx = ctxns.WithNamespace(ctx, ns)
31+
}
2732

2833
fetchLimit := limit
2934
if pathPrefix != "" {

internal/cli/update.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/tae2089/trace"
1313

1414
"github.com/imtaebin/code-context-graph/internal/analysis/incremental"
15+
"github.com/imtaebin/code-context-graph/internal/ctxns"
1516
"github.com/imtaebin/code-context-graph/internal/pathutil"
1617
)
1718

@@ -71,6 +72,9 @@ func newUpdateCmd(deps *Deps) *cobra.Command {
7172
}
7273

7374
ctx := context.Background()
75+
if ns, _ := cmd.Flags().GetString("namespace"); ns != "" {
76+
ctx = ctxns.WithNamespace(ctx, ns)
77+
}
7478
stats, err := deps.Syncer.Sync(ctx, files)
7579
if err != nil {
7680
return trace.Wrap(err, "incremental sync")

0 commit comments

Comments
 (0)