mirror of
https://github.com/onyx-and-iris/exclude.git
synced 2026-03-30 23:19:09 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1312fb781 | |||
|
|
aa6c6a0fd3 | ||
| b956f58f5e | |||
| bfa3314ad3 | |||
| 92e84ca582 | |||
| 9173cbe01b | |||
| 3d33857432 | |||
| 147f474986 | |||
| dabb14f65c | |||
| 196a7dac01 | |||
| 8059255d52 | |||
| 8f6a5d4472 | |||
|
|
04bd90bf80 | ||
| 736310a67f | |||
| 8a8524a029 |
22
.gitignore
vendored
22
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
# Auto-generated .gitignore by gignore: github.com/onyx-and-iris/gignore
|
||||
# Generated by ignr: github.com/onyx-and-iris/ignr
|
||||
|
||||
### Go ###
|
||||
## Go ##
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
@ -15,13 +15,27 @@ bin/
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
# Code coverage profiles and other test artifacts
|
||||
*.out
|
||||
coverage.*
|
||||
*.coverprofile
|
||||
profile.cov
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# End of gignore: github.com/onyx-and-iris/gignore
|
||||
# env file
|
||||
.env
|
||||
.envrc
|
||||
|
||||
# Editor/IDE
|
||||
# .idea/
|
||||
# .vscode/
|
||||
|
||||
# End of ignr
|
||||
|
||||
testdata/
|
||||
@ -13,8 +13,7 @@ builds:
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- darwin
|
||||
|
||||
archives:
|
||||
- formats: ['tar.gz']
|
||||
@ -37,6 +36,7 @@ changelog:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^chore:'
|
||||
|
||||
release:
|
||||
footer: >-
|
||||
|
||||
5
.pre-commit-config.yaml
Normal file
5
.pre-commit-config.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
repos:
|
||||
- repo: https://github.com/tekwizely/pre-commit-golang
|
||||
rev: v1.0.0-rc.4
|
||||
hooks:
|
||||
- id: go-mod-tidy
|
||||
62
README.md
Normal file
62
README.md
Normal file
@ -0,0 +1,62 @@
|
||||

|
||||

|
||||

|
||||
|
||||
# exclude
|
||||
|
||||
Exclude files from local git tracking
|
||||
|
||||
## Install
|
||||
|
||||
```console
|
||||
go install github.com/onyx-and-iris/exclude@latest
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
*flags*
|
||||
|
||||
- --path/-p: Path the exclude file resides in (default is .git/info/)
|
||||
|
||||
*environment variables*
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export EXCLUDE_PATH="./cmd/testdata/"
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Add
|
||||
|
||||
usage: *exclude add [pattern-1] [pattern-2]...*
|
||||
|
||||
```console
|
||||
exclude add "*.log", "temp/"
|
||||
```
|
||||
|
||||
### Del
|
||||
|
||||
usage: *exclude del [pattern]*
|
||||
|
||||
```console
|
||||
exclude del "*.log"
|
||||
```
|
||||
|
||||
### List
|
||||
|
||||
```console
|
||||
exclude list
|
||||
```
|
||||
|
||||
### Reset
|
||||
|
||||
```console
|
||||
exclude reset
|
||||
```
|
||||
|
||||
## Special Thanks
|
||||
|
||||
- [spf13](https://github.com/spf13) for the [cobra](https://github.com/spf13/cobra) and [viper](https://github.com/spf13/viper) packages.
|
||||
- The developers at [charmbracelet](https://github.com/charmbracelet) for the [fang](https://github.com/charmbracelet/fang) package.
|
||||
61
Taskfile.yml
Normal file
61
Taskfile.yml
Normal file
@ -0,0 +1,61 @@
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
PROGRAM: exclude
|
||||
SHELL: '{{if eq .OS "Windows_NT"}}powershell{{end}}'
|
||||
BIN_DIR: bin
|
||||
VERSION:
|
||||
sh: 'git describe --tags $(git rev-list --tags --max-count=1)'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
desc: Build the exclude project
|
||||
cmds:
|
||||
- task: build
|
||||
|
||||
build:
|
||||
desc: Build the exclude project
|
||||
deps: [vet]
|
||||
cmds:
|
||||
- task: build-windows
|
||||
- task: build-linux
|
||||
- task: build-macos
|
||||
|
||||
vet:
|
||||
desc: Vet the code
|
||||
deps: [fmt]
|
||||
cmds:
|
||||
- go vet ./...
|
||||
|
||||
fmt:
|
||||
desc: Fmt the code
|
||||
cmds:
|
||||
- go fmt ./...
|
||||
|
||||
build-windows:
|
||||
desc: Build the exclude project for Windows
|
||||
cmds:
|
||||
- GOOS=windows GOARCH=amd64 go build -o {{.BIN_DIR}}/{{.PROGRAM}}_windows_amd64.exe -ldflags="-X main.version={{.VERSION}}"
|
||||
internal: true
|
||||
|
||||
build-linux:
|
||||
desc: Build the exclude project for Linux
|
||||
cmds:
|
||||
- GOOS=linux GOARCH=amd64 go build -o {{.BIN_DIR}}/{{.PROGRAM}}_linux_amd64 -ldflags="-X main.version={{.VERSION}}"
|
||||
internal: true
|
||||
|
||||
build-macos:
|
||||
desc: Build the exclude project for macOS
|
||||
cmds:
|
||||
- GOOS=darwin GOARCH=amd64 go build -o {{.BIN_DIR}}/{{.PROGRAM}}_darwin_amd64 -ldflags="-X main.version={{.VERSION}}"
|
||||
internal: true
|
||||
|
||||
test:
|
||||
desc: Run tests
|
||||
cmds:
|
||||
- go test ./...
|
||||
|
||||
clean:
|
||||
desc: Clean the build artifacts
|
||||
cmds:
|
||||
- '{{.SHELL}} rm -r {{.BIN_DIR}}'
|
||||
31
cmd/add.go
31
cmd/add.go
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -15,27 +16,37 @@ var addCmd = &cobra.Command{
|
||||
This is useful for excluding files or directories from version control without modifying the .gitignore file.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
f, ok := FileFromContext(cmd.Context())
|
||||
ctx, ok := ContextObjectFromContext(cmd.Context())
|
||||
if !ok {
|
||||
return fmt.Errorf("no exclude file found in context")
|
||||
}
|
||||
return runAddCommand(f, args)
|
||||
return runAddCommand(ctx.Out, ctx.File, args)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(addCmd)
|
||||
RootCmd.AddCommand(addCmd)
|
||||
}
|
||||
|
||||
func runAddCommand(f io.Writer, args []string) error {
|
||||
for _, pattern := range args {
|
||||
if _, err := fmt.Fprintln(f, pattern); err != nil {
|
||||
return fmt.Errorf("error writing to exclude file: %w", err)
|
||||
// runAddCommand adds the specified patterns to the exclude file, ensuring no duplicates
|
||||
// It handles both file and in-memory buffer cases for testing
|
||||
func runAddCommand(out io.Writer, f io.ReadWriter, args []string) error {
|
||||
existingPatterns, err := readExistingPatterns(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading existing patterns: %v", err)
|
||||
}
|
||||
}
|
||||
fmt.Println("Patterns added to .git/info/exclude file:")
|
||||
|
||||
for _, pattern := range args {
|
||||
fmt.Println(" -", pattern)
|
||||
if slices.Contains(existingPatterns, pattern) {
|
||||
fmt.Fprintf(out, "Pattern '%s' already exists in the exclude file. Skipping.\n", pattern)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := fmt.Fprintln(f, pattern)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to exclude file: %v", err)
|
||||
}
|
||||
fmt.Fprintf(out, "Added pattern '%s' to the exclude file.\n", pattern)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
50
cmd/add_test.go
Normal file
50
cmd/add_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunAddCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
existing string
|
||||
args []string
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "Add new patterns",
|
||||
existing: "",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOutput: "Added pattern '*.log' to the exclude file.\nAdded pattern 'temp/' to the exclude file.\n",
|
||||
},
|
||||
{
|
||||
name: "Add duplicate patterns",
|
||||
existing: "*.log\ntemp/\n",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOutput: "Pattern '*.log' already exists in the exclude file. Skipping.\nPattern 'temp/' already exists in the exclude file. Skipping.\n",
|
||||
},
|
||||
{
|
||||
name: "Add mix of new and duplicate patterns",
|
||||
existing: "*.log\n",
|
||||
args: []string{"*.log", "temp/"},
|
||||
expectedOutput: "Pattern '*.log' already exists in the exclude file. Skipping.\nAdded pattern 'temp/' to the exclude file.\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
f := bytes.NewBufferString(tt.existing)
|
||||
|
||||
err := runAddCommand(&buf, f, tt.args)
|
||||
if err != nil {
|
||||
t.Fatalf("runAddCommand returned an error: %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != tt.expectedOutput {
|
||||
t.Errorf("Expected output:\n%s\nGot:\n%s", tt.expectedOutput, buf.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2,20 +2,27 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
const fileContextKey contextKey = "excludeFile"
|
||||
const contextObjectKey = contextKey("contextObject")
|
||||
|
||||
// ContextWithFile returns a new context with the given file set.
|
||||
func ContextWithFile(ctx context.Context, file *os.File) context.Context {
|
||||
return context.WithValue(ctx, fileContextKey, file)
|
||||
type contextObject struct {
|
||||
File *os.File
|
||||
Out io.Writer
|
||||
}
|
||||
|
||||
// FileFromContext retrieves the file from the context, if it exists.
|
||||
func FileFromContext(ctx context.Context) (*os.File, bool) {
|
||||
file, ok := ctx.Value(fileContextKey).(*os.File)
|
||||
return file, ok
|
||||
func createContext(file *os.File, out io.Writer) context.Context {
|
||||
return context.WithValue(context.Background(), contextObjectKey, &contextObject{
|
||||
File: file,
|
||||
Out: out,
|
||||
})
|
||||
}
|
||||
|
||||
func ContextObjectFromContext(ctx context.Context) (*contextObject, bool) {
|
||||
obj, ok := ctx.Value(contextObjectKey).(*contextObject)
|
||||
return obj, ok
|
||||
}
|
||||
|
||||
86
cmd/del.go
Normal file
86
cmd/del.go
Normal file
@ -0,0 +1,86 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// delCmd represents the del command
|
||||
var delCmd = &cobra.Command{
|
||||
Use: "del",
|
||||
Short: "Delete a pattern from the exclude file",
|
||||
Long: `The del command removes a specified pattern from the .git/info/exclude file.
|
||||
This is useful for un-excluding files or directories that were previously excluded.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx, ok := ContextObjectFromContext(cmd.Context())
|
||||
if !ok {
|
||||
return fmt.Errorf("no exclude file found in context")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("no pattern provided to delete")
|
||||
}
|
||||
pattern := args[0]
|
||||
return runDelCommand(ctx.Out, ctx.File, pattern)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(delCmd)
|
||||
}
|
||||
|
||||
// runDelCommand deletes the specified pattern from the exclude file and writes the updated content back
|
||||
// It handles both file and in-memory buffer cases for testing
|
||||
func runDelCommand(out io.Writer, f any, pattern string) error {
|
||||
r, ok := f.(io.Reader)
|
||||
if !ok {
|
||||
return fmt.Errorf("provided file does not support Reader")
|
||||
}
|
||||
existingPatterns, err := readExistingPatterns(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading existing patterns: %v", err)
|
||||
}
|
||||
|
||||
if !slices.Contains(existingPatterns, pattern) {
|
||||
fmt.Fprintf(out, "Pattern '%s' not found in the exclude file. Nothing to delete.\n", pattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
var updatedPatterns []string
|
||||
for _, p := range existingPatterns {
|
||||
if p != pattern {
|
||||
updatedPatterns = append(updatedPatterns, p)
|
||||
}
|
||||
}
|
||||
|
||||
var w io.Writer
|
||||
if t, ok := f.(truncater); ok {
|
||||
if err := t.Truncate(0); err != nil {
|
||||
return fmt.Errorf("error truncating exclude file: %w", err)
|
||||
}
|
||||
if s, ok := f.(io.Seeker); ok {
|
||||
if _, err := s.Seek(0, 0); err != nil {
|
||||
return fmt.Errorf("error seeking to the beginning of exclude file: %w", err)
|
||||
}
|
||||
}
|
||||
w, _ = f.(io.Writer)
|
||||
} else if buf, ok := f.(interface{ Reset() }); ok {
|
||||
buf.Reset()
|
||||
w, _ = f.(io.Writer)
|
||||
} else {
|
||||
return fmt.Errorf("provided file does not support writing")
|
||||
}
|
||||
|
||||
if err := writeDefaultExcludeContent(w); err != nil {
|
||||
return fmt.Errorf("error writing default exclude content: %w", err)
|
||||
}
|
||||
for _, p := range updatedPatterns {
|
||||
if _, err := fmt.Fprintln(w, p); err != nil {
|
||||
return fmt.Errorf("error writing updated patterns to exclude file: %v", err)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "Deleted pattern '%s' from the exclude file.\n", pattern)
|
||||
return nil
|
||||
}
|
||||
49
cmd/del_test.go
Normal file
49
cmd/del_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunDelCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initialContent string
|
||||
patternToDelete string
|
||||
expectedOutput string
|
||||
expectedContent string
|
||||
}{
|
||||
{
|
||||
name: "Delete existing pattern",
|
||||
initialContent: defaultExcludeFileContent + "node_modules\n.DS_Store\n",
|
||||
patternToDelete: "node_modules",
|
||||
expectedOutput: defaultExcludeFileContent + ".DS_Store\n" + "Deleted pattern 'node_modules' from the exclude file.\n",
|
||||
},
|
||||
{
|
||||
name: "Delete non-existing pattern",
|
||||
initialContent: defaultExcludeFileContent + "node_modules\n.DS_Store\n",
|
||||
patternToDelete: "dist",
|
||||
expectedOutput: "Pattern 'dist' not found in the exclude file. Nothing to delete.\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(tt.initialContent)
|
||||
|
||||
err := runDelCommand(&buf, &buf, tt.patternToDelete)
|
||||
if err != nil {
|
||||
t.Fatalf("runDelCommand returned an error: %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != tt.expectedOutput {
|
||||
t.Errorf(
|
||||
"Expected output and content:\n%s\nGot:\n%s",
|
||||
tt.expectedOutput,
|
||||
buf.String(),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
17
cmd/list.go
17
cmd/list.go
@ -18,31 +18,36 @@ that are not empty and do not start with a comment (#).
|
||||
This is useful for reviewing which files or directories are currently excluded from version control.`,
|
||||
Args: cobra.NoArgs, // No arguments expected
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
f, ok := FileFromContext(cmd.Context())
|
||||
ctx, ok := ContextObjectFromContext(cmd.Context())
|
||||
if !ok {
|
||||
return fmt.Errorf("no exclude file found in context")
|
||||
}
|
||||
return runListCommand(f, args)
|
||||
return runListCommand(ctx.Out, ctx.File)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(listCmd)
|
||||
RootCmd.AddCommand(listCmd)
|
||||
}
|
||||
|
||||
// runListCommand is the function that will be executed when the list command is called
|
||||
func runListCommand(f io.Reader, _ []string) error {
|
||||
// Read from the exclude file line by line
|
||||
func runListCommand(out io.Writer, f io.Reader) error {
|
||||
var count int
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line != "" && !strings.HasPrefix(line, "#") {
|
||||
fmt.Println(line) // Print each non-empty, non-comment line
|
||||
fmt.Fprintln(out, line)
|
||||
count++
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("error reading exclude file: %w", err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
fmt.Fprintln(out, "No patterns found in the exclude file.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
46
cmd/list_test.go
Normal file
46
cmd/list_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunListCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Empty file",
|
||||
input: defaultExcludeFileContent,
|
||||
expected: "No patterns found in the exclude file.\n",
|
||||
},
|
||||
{
|
||||
name: "Exclude file with patterns",
|
||||
input: defaultExcludeFileContent + "node_modules/\nbuild/\n# This is a comment\n",
|
||||
expected: "node_modules/\nbuild/\n",
|
||||
},
|
||||
{
|
||||
name: "Exclude file with only comments and empty lines",
|
||||
input: defaultExcludeFileContent + "# Comment 1\n# Comment 2\n\n",
|
||||
expected: "No patterns found in the exclude file.\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reader := bytes.NewBufferString(tt.input)
|
||||
var output bytes.Buffer
|
||||
|
||||
err := runListCommand(&output, reader)
|
||||
if err != nil {
|
||||
t.Fatalf("runListCommand returned an error: %v", err)
|
||||
}
|
||||
|
||||
if output.String() != tt.expected {
|
||||
t.Errorf("Expected output:\n%q\nGot:\n%q", tt.expected, output.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
45
cmd/reset.go
45
cmd/reset.go
@ -3,7 +3,7 @@ package cmd
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -15,36 +15,47 @@ var resetCmd = &cobra.Command{
|
||||
Long: `The reset command clears all patterns from the .git/info/exclude file.
|
||||
This is useful for starting fresh or removing all exclusions at once.`,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
f, ok := FileFromContext(cmd.Context())
|
||||
ctx, ok := ContextObjectFromContext(cmd.Context())
|
||||
if !ok {
|
||||
return fmt.Errorf("no exclude file found in context")
|
||||
}
|
||||
return runResetCommand(f)
|
||||
return resetAndWriteExcludeFile(ctx.Out, ctx.File)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(resetCmd)
|
||||
RootCmd.AddCommand(resetCmd)
|
||||
}
|
||||
|
||||
//go:embed template/exclude
|
||||
var defaultExcludeFile string
|
||||
// Truncate and seek to beginning
|
||||
type truncater interface{ Truncate(size int64) error }
|
||||
|
||||
// runResetCommand clears the exclude file
|
||||
func runResetCommand(f *os.File) error {
|
||||
// Clear the exclude file by truncating it
|
||||
if err := f.Truncate(0); err != nil {
|
||||
// resetAndWriteExcludeFile truncates and resets the file, then writes the default content
|
||||
func resetAndWriteExcludeFile(out io.Writer, f any) error {
|
||||
// Try to assert to io.ReadWriteSeeker for file operations
|
||||
rws, ok := f.(io.ReadWriteSeeker)
|
||||
if !ok {
|
||||
// If not a file, try as io.Writer (for test buffers)
|
||||
if w, ok := f.(io.Writer); ok {
|
||||
return writeDefaultExcludeContent(w)
|
||||
}
|
||||
return fmt.Errorf("provided file does not support ReadWriteSeeker or Writer")
|
||||
}
|
||||
|
||||
t, ok := f.(truncater)
|
||||
if !ok {
|
||||
return fmt.Errorf("provided file does not support Truncate")
|
||||
}
|
||||
if err := t.Truncate(0); err != nil {
|
||||
return fmt.Errorf("error truncating exclude file: %w", err)
|
||||
}
|
||||
// Reset the file pointer to the beginning
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
if _, err := rws.Seek(0, 0); err != nil {
|
||||
return fmt.Errorf("error seeking to the beginning of exclude file: %w", err)
|
||||
}
|
||||
|
||||
// Write the default exclude patterns to the file
|
||||
if _, err := f.WriteString(defaultExcludeFile); err != nil {
|
||||
return fmt.Errorf("error writing default exclude file: %w", err)
|
||||
err := writeDefaultExcludeContent(rws)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing default exclude content: %w", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Exclude file reset successfully.\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
28
cmd/reset_test.go
Normal file
28
cmd/reset_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunResetCommand(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if err := resetAndWriteExcludeFile(&buf, &buf); err != nil {
|
||||
t.Fatalf("resetAndWriteExcludeFile failed: %v", err)
|
||||
}
|
||||
|
||||
resetContent, err := io.ReadAll(&buf)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read from temp file: %v", err)
|
||||
}
|
||||
|
||||
if string(resetContent) != defaultExcludeFileContent {
|
||||
t.Errorf(
|
||||
"unexpected content after reset:\nGot:\n%s\nExpected:\n%s",
|
||||
string(resetContent),
|
||||
defaultExcludeFileContent,
|
||||
)
|
||||
}
|
||||
}
|
||||
74
cmd/root.go
74
cmd/root.go
@ -1,56 +1,74 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
const defaultExcludeFileContent = `# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
||||
|
||||
`
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "exclude",
|
||||
Short: "A command line tool to manage .git/info/exclude files",
|
||||
Long: `Exclude is a command line tool designed to help you manage your .git/info/exclude files.
|
||||
It allows you to add, list, and remove patterns from the exclude file easily.
|
||||
It allows you to add, list, and delete patterns from the exclude file easily.
|
||||
This tool is particularly useful for developers who want to keep their repository clean
|
||||
by excluding certain files or directories from version control.`,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
// This function runs before any command is executed.
|
||||
// You can use it to set up global configurations or checks.
|
||||
// For example, you might want to check if the .git directory exists
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if strings.HasPrefix(cmd.CommandPath(), "exclude completion") ||
|
||||
strings.HasPrefix(cmd.CommandPath(), "exclude help") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(".git"); os.IsNotExist(err) {
|
||||
cmd.Println("Error: This command must be run in a Git repository.")
|
||||
os.Exit(1)
|
||||
return fmt.Errorf("this command must be run in a Git repository")
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(".git/info/exclude", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
f, err := os.OpenFile(filepath.Join(viper.GetString("path"), "exclude"), os.O_RDWR|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
cmd.Println("Error opening .git/info/exclude file:", err)
|
||||
os.Exit(1)
|
||||
return fmt.Errorf("error opening exclude file: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), fileContextKey, f)
|
||||
ctx := createContext(f, cmd.OutOrStdout())
|
||||
cmd.SetContext(ctx)
|
||||
return nil
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
if f, ok := FileFromContext(cmd.Context()); ok {
|
||||
defer f.Close()
|
||||
}
|
||||
},
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
// Run: func(cmd *cobra.Command, args []string) { },
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if strings.HasPrefix(cmd.CommandPath(), "exclude completion") ||
|
||||
strings.HasPrefix(cmd.CommandPath(), "exclude help") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
if ctx, ok := ContextObjectFromContext(cmd.Context()); ok {
|
||||
defer ctx.File.Close()
|
||||
} else {
|
||||
return fmt.Errorf("unable to retrieve context after command execution")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().
|
||||
StringP("path", "p", ".git/info/", "Path the exclude file resides in (default is .git/info/)")
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
viper.SetEnvPrefix("EXCLUDE")
|
||||
viper.AutomaticEnv()
|
||||
if err := viper.BindPFlag("path", RootCmd.PersistentFlags().Lookup("path")); err != nil {
|
||||
panic(fmt.Errorf("unable to bind flags: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
||||
32
cmd/util.go
Normal file
32
cmd/util.go
Normal file
@ -0,0 +1,32 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// readExistingPatterns reads the existing patterns from the exclude file, ignoring comments and empty lines
|
||||
func readExistingPatterns(f io.Reader) ([]string, error) {
|
||||
var patterns []string
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line != "" && !strings.HasPrefix(line, "#") {
|
||||
patterns = append(patterns, line)
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error scanning exclude file: %v", err)
|
||||
}
|
||||
return patterns, nil
|
||||
}
|
||||
|
||||
// writeDefaultExcludeContent writes the default exclude content to the writer
|
||||
func writeDefaultExcludeContent(w io.Writer) error {
|
||||
if _, err := w.Write([]byte(defaultExcludeFileContent)); err != nil {
|
||||
return fmt.Errorf("error writing default exclude file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
45
go.mod
45
go.mod
@ -1,10 +1,45 @@
|
||||
module github.com/onyx-and-iris/exclude
|
||||
|
||||
go 1.24.3
|
||||
|
||||
require github.com/spf13/cobra v1.9.1
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/charmbracelet/fang v1.0.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/viper v1.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
charm.land/lipgloss/v2 v2.0.2 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260316091819-b93f6a3b8502 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260329003944-7eda8903d971 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/mango v0.2.0 // indirect
|
||||
github.com/muesli/mango-cobra v1.3.0 // indirect
|
||||
github.com/muesli/mango-pflag v0.2.0 // indirect
|
||||
github.com/muesli/roff v0.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
)
|
||||
|
||||
98
go.sum
98
go.sum
@ -1,10 +1,100 @@
|
||||
charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
|
||||
charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
|
||||
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||
github.com/charmbracelet/fang v1.0.0 h1:jESBY40agJOlLYnnv9jE0mLqDGTxEk0hkOnx7YGyRlQ=
|
||||
github.com/charmbracelet/fang v1.0.0/go.mod h1:P5/DNb9DddQ0Z0dbc0P3ol4/ix5Po7Ofr2KMBfAqoCo=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260316091819-b93f6a3b8502 h1:hzWNs3UQRSUTS6YCbLaQnwqKBFXT5Yh1OOw6+26apqg=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260316091819-b93f6a3b8502/go.mod h1:mkUCcxn9w9j89JJp3pOza5tmDQZPgIB75UfmQlFYvas=
|
||||
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260329003944-7eda8903d971 h1:0yC3HXybXjsyg+l2qiTSLXSCwF85fQZOZvmyAw8elLI=
|
||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260329003944-7eda8903d971/go.mod h1:nsExn0DGyX0lh9LwLHTn2Gg+hafdzfSXnC+QmEJTZFY=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ=
|
||||
github.com/muesli/mango v0.2.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
|
||||
github.com/muesli/mango-cobra v1.3.0 h1:vQy5GvPg3ndOSpduxutqFoINhWk3vD5K2dXo5E8pqec=
|
||||
github.com/muesli/mango-cobra v1.3.0/go.mod h1:Cj1ZrBu3806Qw7UjxnAUgE+7tllUBj1NCLQDwwGx19E=
|
||||
github.com/muesli/mango-pflag v0.2.0 h1:QViokgKDZQCzKhYe1zH8D+UlPJzBSGoP9yx0hBG0t5k=
|
||||
github.com/muesli/mango-pflag v0.2.0/go.mod h1:X9LT1p/pbGA1wjvEbtwnixujKErkP0jVmrxwrw3fL0Y=
|
||||
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
||||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
28
main.go
28
main.go
@ -1,7 +1,31 @@
|
||||
package main
|
||||
|
||||
import "github.com/onyx-and-iris/exclude/cmd"
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/fang"
|
||||
"github.com/onyx-and-iris/exclude/cmd"
|
||||
)
|
||||
|
||||
var version string // Version of the CLI, set during build time
|
||||
|
||||
func versionFromBuild() string {
|
||||
if version != "" {
|
||||
return version
|
||||
}
|
||||
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return "(unable to read version)"
|
||||
}
|
||||
return strings.Split(info.Main.Version, "-")[0]
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
if err := fang.Execute(context.Background(), cmd.RootCmd, fang.WithVersion(versionFromBuild())); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user