Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 8 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v1.5.0)"
required: true

permissions:
contents: write
Expand All @@ -20,14 +15,20 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.inputs.tag || github.ref }}
ref: ${{ github.ref }}
persist-credentials: false
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Verify release source
run: |
go mod tidy
git diff --exit-code -- go.mod go.sum
go test ./...
- uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
28 changes: 15 additions & 13 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ version: 2

project_name: ganbatte

before:
hooks:
- go mod tidy
- go test ./...

builds:
- binary: gnb
env:
Expand All @@ -22,24 +17,31 @@ builds:
- -s -w

archives:
- format: tar.gz
- formats: [tar.gz]
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
format: zip
formats: [zip]

brews:
homebrew_casks:
- repository:
owner: bssm-oss
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
binaries:
- gnb
homepage: https://github.com/bssm-oss/ganbatte
description: "Workflow/shortcut management CLI for lazy developers"
license: MIT
install: |
bin.install "gnb"
test: |
system "#{bin}/gnb", "--help"
directory: Casks
generate_completions_from_executable:
executable: "bin/gnb"
args:
- completion
shell_parameter_format: cobra
shells:
- bash
- zsh
- fish

checksum:
name_template: checksums.txt
Expand Down
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,53 @@

All notable changes to this project will be documented in this file.

## [Unreleased]

### Added
- `gnb list --scope global|project` for inspecting one config scope at a time.
- Project config discovery now walks parent directories, so `.ganbatte.*` works from nested repo paths.

### Fixed
- `gnb run` and `gnb show` now use merged global/project config, matching `gnb list` behavior.
- `gnb run` now asks for confirmation when a project item overrides a global item unless `--yes` is used.
- Release workflow now passes the Homebrew tap token using the environment variable expected by GoReleaser.
- Release workflow now validates manual dispatch tags and runs tests before publishing tokens are injected.
- CI lint issues reported by golangci-lint v1.64.8.

## [1.5.3] - 2026-04-19

### Added
- `gnb doctor` detects aliases that collide with system commands and reports actionable shell-integration guidance.

### Fixed
- Shell Integration diagnostics now produce clearer output and avoid stale p10k guidance.

## [1.5.2] - 2026-04-19

### Fixed
- `gnb shell-init` writes generated shell functions to stdout when executed through the root command.

## [1.5.1] - 2026-04-19

### Fixed
- `gnb shell-init` output stream handling for shell eval usage.

## [1.5.0] - 2026-04-19

### Fixed
- Config loading now warns and consistently picks one active file when multiple format files coexist.
- `doctor --fix` p10k handling avoids false positives and cleans up leftover blank lines.

### Added
- `doctor --fix` can repair p10k instant prompt ordering for shell-init lines.

## [1.4.2] - 2026-04-18

### Fixed
- Alias tag filtering behavior.
- `gnb export --aliases-only` output.
- Homebrew tap repository path in release configuration.

## [1.4.1] - 2026-04-18

### Added
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@
## Install

```bash
# Homebrew (macOS / Linux)
brew install bssm-oss/tap/ganbatte
# Homebrew Cask (macOS)
brew install --cask bssm-oss/tap/ganbatte

# go install
# Go install (macOS / Linux)
go install github.com/justn-hyeok/ganbatte@latest

# 로컬 빌드
Expand Down
143 changes: 143 additions & 0 deletions cmd/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"runtime"
"testing"

"github.com/justn-hyeok/ganbatte/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -22,6 +23,7 @@ func executeCmd(args ...string) (string, error) {
_ = runCmd.Flags().Set("dry-run", "false")
_ = runCmd.Flags().Set("yes", "false")
_ = listCmd.Flags().Set("tag", "")
_ = listCmd.Flags().Set("scope", "")
_ = suggestCmd.Flags().Set("apply", "false")
_ = suggestCmd.Flags().Set("min-frequency", "5")
_ = suggestCmd.Flags().Set("min-sequence", "3")
Expand Down Expand Up @@ -265,6 +267,89 @@ run = "pnpm build"
assert.NotContains(t, out, "deploy")
}

func TestListScopeFilter(t *testing.T) {
home := setupTestHome(t)
projectDir := t.TempDir()
t.Chdir(projectDir)

configDir := filepath.Join(home, ".config", "ganbatte")
require.NoError(t, os.MkdirAll(configDir, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(configDir, "config.toml"), []byte(`
version = "0.1.0"
[alias.global]
cmd = "echo global"
`), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(projectDir, ".ganbatte.toml"), []byte(`
version = "0.1.0"
[alias.project]
cmd = "echo project"
`), 0o644))

out, err := executeCmd("list", "--scope", "global")
require.NoError(t, err)
assert.Contains(t, out, "global")
assert.NotContains(t, out, "project")

out, err = executeCmd("list", "--scope", "project")
require.NoError(t, err)
assert.Contains(t, out, "project")
assert.NotContains(t, out, "global")
}

func TestListScopeFilterConflictLabels(t *testing.T) {
home := setupTestHome(t)
projectDir := t.TempDir()
t.Chdir(projectDir)

configDir := filepath.Join(home, ".config", "ganbatte")
require.NoError(t, os.MkdirAll(configDir, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(configDir, "config.toml"), []byte(`
version = "0.1.0"
[alias.shared]
cmd = "echo global"
`), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(projectDir, ".ganbatte.toml"), []byte(`
version = "0.1.0"
[alias.shared]
cmd = "echo project"
`), 0o644))

out, err := executeCmd("list", "--scope", "global")
require.NoError(t, err)
assert.Contains(t, out, "shared: echo global [global]")
assert.NotContains(t, out, "[project]")

out, err = executeCmd("list", "--scope", "project")
require.NoError(t, err)
assert.Contains(t, out, "shared: echo project [project]")
}

func TestProjectOverrides(t *testing.T) {
scoped := &config.ScopedConfig{
Global: &config.Config{
Aliases: map[string]config.Alias{"shared": {Cmd: "echo global"}},
Workflows: map[string]config.Workflow{"deploy": {Description: "global"}},
},
Project: &config.Config{
Aliases: map[string]config.Alias{"shared": {Cmd: "echo project"}},
Workflows: map[string]config.Workflow{"deploy": {Description: "project"}},
},
}

assert.True(t, projectOverrides(scoped, "shared", "alias"))
assert.True(t, projectOverrides(scoped, "deploy", "workflow"))
assert.False(t, projectOverrides(scoped, "missing", "alias"))
}

func TestListInvalidScope(t *testing.T) {
setupTestHome(t)
_, _ = executeCmd("init", "--format", "toml")

_, err := executeCmd("list", "--scope", "local")
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid scope")
}

func TestShowWorkflow(t *testing.T) {
home := setupTestHome(t)

Expand Down Expand Up @@ -319,6 +404,64 @@ confirm = true
assert.Contains(t, out, "git push -f origin main")
}

func TestRunWorkflowYesSkipsConfirm(t *testing.T) {
home := setupTestHome(t)

configDir := filepath.Join(home, ".config", "ganbatte")
require.NoError(t, os.MkdirAll(configDir, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(configDir, "config.toml"), []byte(`
version = "0.1.0"
[workflow.deploy]
description = "Deploy"
[[workflow.deploy.steps]]
run = "printf deploy"
confirm = true
`), 0o644))

out, err := executeCmd("run", "deploy", "--yes")
require.NoError(t, err)
assert.Contains(t, out, "Running workflow: Deploy")
assert.Contains(t, out, "Step 1/1: printf deploy")
assert.NotContains(t, out, "Run 'printf deploy'?")
}

func TestRunProjectAliasFromParentDir(t *testing.T) {
setupTestHome(t)
projectRoot := t.TempDir()
nestedDir := filepath.Join(projectRoot, "sub", "dir")
require.NoError(t, os.MkdirAll(nestedDir, 0o755))
require.NoError(t, os.MkdirAll(filepath.Join(projectRoot, ".git"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(projectRoot, ".ganbatte.toml"), []byte(`
version = "0.1.0"
[alias.project]
cmd = "printf project"
`), 0o644))
t.Chdir(nestedDir)

out, err := executeCmd("run", "project")
require.NoError(t, err)
assert.Contains(t, out, "Running: printf project")
}

func TestShowProjectAliasFromParentDir(t *testing.T) {
setupTestHome(t)
projectRoot := t.TempDir()
nestedDir := filepath.Join(projectRoot, "sub", "dir")
require.NoError(t, os.MkdirAll(nestedDir, 0o755))
require.NoError(t, os.MkdirAll(filepath.Join(projectRoot, ".git"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(projectRoot, ".ganbatte.toml"), []byte(`
version = "0.1.0"
[alias.project]
cmd = "echo project"
`), 0o644))
t.Chdir(nestedDir)

out, err := executeCmd("show", "project")
require.NoError(t, err)
assert.Contains(t, out, "Alias: project")
assert.Contains(t, out, "Command: echo project")
}

func TestAddGlobalFlag(t *testing.T) {
setupTestHome(t)
_, _ = executeCmd("init", "--format", "toml")
Expand Down
2 changes: 1 addition & 1 deletion cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ func checkP10kOrdering(cmd *cobra.Command, home string, fix bool) bool {
}

result := applyP10kFix(lines, gnbLine, p10kLine)
if err := os.WriteFile(zshrc, []byte(strings.Join(result, "\n")), 0644); err != nil {
if err := os.WriteFile(zshrc, []byte(strings.Join(result, "\n")), 0o644); err != nil {
cmd.Printf("[ERROR] Could not write %s: %v\n", zshrc, err)
return true
}
Expand Down
Loading
Loading