Compare commits

..

37 Commits
v0.1.0 ... main

Author SHA1 Message Date
b2fc47ddf4 remove package level var
set gignore client in the context and use it to access the client from subcommands.
2025-04-14 11:15:34 +01:00
f5f89d8515 upd docstring 2025-04-14 09:03:32 +01:00
7a964a031c return err from createTemplate and check it 2025-04-13 17:41:18 +01:00
00641cdc85 move getEnv into main.go 2025-04-13 17:03:57 +01:00
478a172588 upd usage in readme 2025-04-13 14:49:18 +01:00
6adea84322 no need to pass client around 2025-04-13 14:35:43 +01:00
2fa49d0dd0 upd CHANGELOG, README 2025-04-13 14:23:57 +01:00
23ec3b85c1 migrate to cobra 2025-04-13 14:23:48 +01:00
github-actions[bot]
2c1d2ed99c chore: auto-update Go modules 2025-04-07 00:05:05 +00:00
29270a2c14 fix loglevel example in readme
Some checks failed
CI / Lint (push) Has been cancelled
Auto-Update Go Modules / update-go-modules (push) Has been cancelled
2025-04-05 22:24:49 +01:00
ee86db76a2 fix loglevel example in readme 2025-04-05 22:19:37 +01:00
6aac14b9ed -loglevel now string flag
upd README, CHANGELOG
2025-04-05 22:18:28 +01:00
38b0611e4e update error/logging messages
Some checks failed
CI / Lint (push) Has been cancelled
2025-03-31 22:05:22 +01:00
6b41418c00 split long desc across lines
Some checks failed
Auto-Update Go Modules / update-go-modules (push) Has been cancelled
2025-03-17 22:52:12 +00:00
github-actions[bot]
4800b29707 chore: auto-update Go modules 2025-03-17 00:05:32 +00:00
8a9539ea60 Taskfile skip the generate task if templates already exist
Some checks failed
Auto-Update Go Modules / update-go-modules (push) Has been cancelled
make note of --force flag.
2025-03-16 02:14:25 +00:00
626e40b653 add 0.2.0 and 0.3.0 to CHANGELOG
Some checks failed
CI / Lint (push) Has been cancelled
2025-03-14 21:58:57 +00:00
fd9c7194c1 CLI may now accept multiple template names 2025-03-14 21:53:06 +00:00
388a204299 open file in append mode 2025-03-14 21:49:55 +00:00
a3c2d2cfbf test against empty string
Some checks are pending
CI / Lint (push) Waiting to run
2025-03-13 18:22:52 +00:00
7ada4e9328 remove the done channel
Some checks are pending
CI / Lint (push) Waiting to run
add prune task to Taskfile
2025-03-12 21:03:52 +00:00
eaf091dc1a upd links
Some checks are pending
CI / Lint (push) Waiting to run
2025-03-12 15:48:43 +00:00
99871a9040 fix links in special thanks 2025-03-12 15:46:56 +00:00
1ca4304806 add Install with Go tools
remove release task from Taskfile
2025-03-12 15:15:42 +00:00
59521e1cd0 rename gen.go to main.go
no changes to file contents
2025-03-12 15:15:08 +00:00
fcb23a3c01 create templates concurrently 2025-03-12 13:30:17 +00:00
c2b7dfcb18 rename registry functions 2025-03-12 12:54:13 +00:00
f0b64e3a75 add golangci-lint config + action
Some checks failed
CI / Lint (push) Has been cancelled
2025-03-10 15:21:54 +00:00
32714f55d2 rename GignoreClient to Client
add docstrings
2025-03-10 15:21:27 +00:00
8e53c4fbeb add platform badges 2025-03-10 11:38:31 +00:00
6fc81a4c96 add bin/ to .gitignore 2025-03-10 11:19:37 +00:00
820ed2055b return err from listTemplates 2025-03-10 11:19:30 +00:00
eba8dd0113 log gen message at info level 2025-03-10 11:19:03 +00:00
f262277aca rename TemplateRegistry factory method 2025-03-10 11:18:42 +00:00
c11b94ec0d add upd go modules action 2025-03-10 11:08:37 +00:00
7539d8ca76 upd special thanks 2025-03-10 00:50:05 +00:00
bd8dc7c594 add loglevel flag to README. 2025-03-09 23:25:33 +00:00
20 changed files with 493 additions and 153 deletions

29
.github/workflows/golang-ci.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: CI
on:
push:
branches: [ "main" ]
paths:
- '**.go'
pull_request:
branches: [ "main" ]
paths:
- '**.go'
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Install golangci-lint
run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
- name: Run golangci-lint
run: golangci-lint run ./...

30
.github/workflows/update-go-modules.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Auto-Update Go Modules
on:
schedule:
- cron: "0 0 * * 1" # Runs every Monday at midnight
jobs:
update-go-modules:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable
- name: Update Dependencies
run: |
go get -u ./...
go mod tidy
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add go.mod go.sum
git commit -m "chore: auto-update Go modules"
git push

1
.gitignore vendored
View File

@ -9,6 +9,7 @@
*.dll *.dll
*.so *.so
*.dylib *.dylib
bin/
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.test

54
.golangci.yml Normal file
View File

@ -0,0 +1,54 @@
run:
# timeout for analysis, e.g. 30s, 3m, default is 1m
timeout: 3m
# exclude test files
tests: true
linters:
# Set to true runs only fast linters.
# Good option for 'lint on save', pre-commit hook or CI.
fast: true
disable-all: true
enable:
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- gofumpt
- misspell
- unparam
- gosec
- asciicheck
- errname
- gci
- godot
- goimports
- revive
linters-settings:
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
- pattern: 'a[b:len(a)]'
replacement: 'a[b:]'
misspell:
locale: UK
errcheck:
check-type-assertions: true
issues:
max-same-issues: 0
max-issues-per-linter: 0
exclude-use-default: false
exclude:
# gosec: Duplicated errcheck checks
- G104
# gosec: integer overflow conversion int -> uint32
- G115

View File

@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# [0.5.0] - 2025-04-13
### Changed
- CLI component migrated to Cobra. This introduces the following changes:
- `list` is now a subcommand.
- `create` has been added as a subcommand, use it to create a new .gitignore file.
- Env var `GIGNORE_TEMPLATE_DIR` changed to `GIGNORE_TEMPLATE_ROOT`
- Env var `GIGNORE_LOGLEVEL` may now be used to set the logging level.
# [0.4.0] - 2025-04-05
### Changed
- `-loglevel` flag is now of type string. It accepts any one of trace, debug, info, warn, error, fatal or panic.
- It defaults to warn.
# [0.3.0] - 2025-14-03
### Added
- CLI may now accept multiple template names, example `gignore go python`. One will be appended after the other.
### Changed
- Filewriter now opens file in append mode.
# [0.2.0] - 2025-10-03
### Fixed
- Template .gitignore are now written concurrently.
# [0.1.0] - 2025-09-03 # [0.1.0] - 2025-09-03
### Added ### Added

View File

@ -1,7 +1,19 @@
# Gignore - Generate .gitinore files ![Windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white)
![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)
![macOS](https://img.shields.io/badge/mac%20os-000000?style=for-the-badge&logo=macos&logoColor=F0F0F0)
# Gignore - Generate .gitignore files
## Install ## Install
With Go tools:
```bash
go generate ./...
go install ./cmd/gignore
```
With [Task][task]: With [Task][task]:
```bash ```bash
@ -11,43 +23,75 @@ task install
## Usage ## Usage
```bash ```bash
Usage of gignore: Usage:
gignore [flags] <template> gignore [flags]
gignore [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
create Create a new .gitignore file
help Help about any command
list List all .gitignore files in the root template repository
Flags: Flags:
-dir string -h, --help help for gignore
directory containing .gitignore templates (default "gitignoreio") -l, --loglevel string Log level (trace, debug, info, warn, error, fatal, panic) (default "warn")
-list -r, --root string Root directory to search for .gitignore files (default "gitignoreio")
list available templates
-ls
list available templates (shorthand)
Example: Use "gignore [command] --help" for more information about a command.
gignore go ```
For example:
```bash
gignore create go
``` ```
## Custom Templates ## Custom Templates
It's possible to add your own custom templates, simply create a directory in `internal/registry/templates`. You'll need to rebuild the project before you can load the new templates. It's possible to add your own custom templates, simply create a directory in `internal/registry/templates`. You'll need to [reinstall](https://github.com/onyx-and-iris/gignore?tab=readme-ov-file#install) the project before you can load the new templates.
Then pass the dir name as a flag, for example: Then pass the dir name as a flag, for example:
```bash ```bash
gignore -dir=custom go gignore -root=custom create go
``` ```
You may set an environment variable `GIGNORE_TEMPLATE_DIR` to avoid passing the `-dir` flag each time. You may set an environment variable `GIGNORE_TEMPLATE_ROOT` to avoid passing the `-root` flag each time.
If a template is requested but not found in the custom directory then the gitignoreio registry will act as a fallback. If a template is requested but not found in the custom directory then the gitignoreio registry will act as a fallback.
## Logging
The `-loglevel` flag allows you to control the verbosity of the application's logging output.
Acceptable values for this flag are:
- `trace`
- `debug`
- `info`
- `warn`
- `error`
- `fatal`
- `panic`
For example, to set the log level to `debug`, you can use:
```bash
gignore -loglevel=debug create python
```
The default log level is `warn` if the flag is not specified.
## Special Thanks ## Special Thanks
[gitignore.io][gitignoreio] For providing such a useful .gitignore service [gitignore.io][gitignoreio] For providing such a useful .gitignore service
[gigo][gigo] For writing the Go client library for gitignore.io [cuonglm][cuonglm] For writing the [gogi][gogi] client library for gitignore.io
[task]: https://taskfile.dev/ [task]: https://taskfile.dev/
[gitignoreio]: https://www.toptal.com/developers/gitignore [gitignoreio]: https://www.toptal.com/developers/gitignore
[gigo]: https://github.com/mh-cbon/gigo [cuonglm]: https://github.com/cuonglm
[gogi]: https://github.com/cuonglm/gogi
[ignore]: https://github.com/neptship/ignore [ignore]: https://github.com/neptship/ignore

View File

@ -23,13 +23,6 @@ tasks:
- task: build-windows - task: build-windows
- task: build-linux - task: build-linux
release:
desc: Generate the gitignore.io templates and then build the gignore project for Windows and Linux
deps: [generate]
cmds:
- task: build-windows
- task: build-linux
vet: vet:
desc: Vet the code desc: Vet the code
deps: [fmt] deps: [fmt]
@ -42,19 +35,26 @@ tasks:
- go fmt ./... - go fmt ./...
generate: generate:
desc: Generate the gitignore.io templates desc: |
Generate the gitignore.io templates.
This task will be skipped if the templates already exist.
You may use the `--force` flag to regenerate the templates.
cmds: cmds:
- go generate . - go generate ./...
status:
- ls internal/registry/templates/gitignoreio/*.gitignore >/dev/null
build-windows: build-windows:
desc: Build the gignore project for Windows desc: Build the gignore project for Windows
cmds: cmds:
- GOOS=windows GOARCH=amd64 go build -o {{.WINDOWS}} -ldflags="-X main.Version={{.GIT_COMMIT}}" ./cmd/{{.PROGRAM}} - GOOS=windows GOARCH=amd64 go build -o {{.WINDOWS}} -ldflags="-X main.Version={{.GIT_COMMIT}}" ./cmd/{{.PROGRAM}}
internal: true
build-linux: build-linux:
desc: Build the gignore project for Linux desc: Build the gignore project for Linux
cmds: cmds:
- GOOS=linux GOARCH=amd64 go build -o {{.LINUX}} -ldflags="-X main.Version={{.GIT_COMMIT}}" ./cmd/{{.PROGRAM}} - GOOS=linux GOARCH=amd64 go build -o {{.LINUX}} -ldflags="-X main.Version={{.GIT_COMMIT}}" ./cmd/{{.PROGRAM}}
internal: true
test: test:
desc: Run tests desc: Run tests
@ -71,3 +71,8 @@ tasks:
desc: Clean the build artifacts desc: Clean the build artifacts
cmds: cmds:
- '{{.SHELL}} rm -r {{.BIN_DIR}}' - '{{.SHELL}} rm -r {{.BIN_DIR}}'
prune:
desc: Empty the gitignoreio template registry
cmds:
- '{{.SHELL}} rm internal/registry/templates/gitignoreio/*.gitignore'

View File

@ -1,16 +1,17 @@
// Package main generates gitignore.io templates using the gogi library.
package main package main
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"strings" "strings"
"github.com/cuonglm/gogi" "github.com/cuonglm/gogi"
log "github.com/sirupsen/logrus"
) )
func main() { func main() {
fmt.Println("Generating gitignore.io templates...") log.Info("Generating gitignore.io templates...")
gogiClient, _ := gogi.NewHTTPClient() gogiClient, _ := gogi.NewHTTPClient()
@ -19,10 +20,22 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
errChan := make(chan error)
for _, template := range templates { for _, template := range templates {
go func() {
err := createTemplate(template) err := createTemplate(template)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create template %s: %v\n", template.Name, err) errChan <- fmt.Errorf("Failed to create template %s: %v", template.Name, err)
return
}
errChan <- nil
}()
}
for range templates {
if err := <-errChan; err != nil {
log.Error(err)
} }
} }
} }

22
cmd/gignore/context.go Normal file
View File

@ -0,0 +1,22 @@
// Package main provides the entry point for the gignore CLI tool,
// including commands like listing available .gitignore templates.
package main
import (
"context"
"github.com/onyx-and-iris/gignore"
log "github.com/sirupsen/logrus"
)
type contextKey string
const clientKey contextKey = "client"
func getClientFromContext(ctx context.Context) *gignore.Client {
client, ok := ctx.Value(clientKey).(*gignore.Client)
if !ok {
log.Fatal("Client not found in context")
}
return client
}

50
cmd/gignore/create.go Normal file
View File

@ -0,0 +1,50 @@
// Package main provides the entry point for the gignore CLI tool,
// including commands like listing available .gitignore templates.
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
)
// createCmd is the command to create a new .gitignore file.
var createCmd = &cobra.Command{
Use: "create",
Short: "Create a new .gitignore file",
Long: `Create a new .gitignore file in the current directory.
At least one template must be specified.
Multiple templates can be specified, and they will be combined into a single .gitignore file.
Example:
gignore create python
gignore create python go`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
cmd.Help()
return
}
for _, arg := range args {
err := createTemplate(cmd.Context(), arg)
cobra.CheckErr(err)
}
},
}
// init initialises the create command and adds it to the root command.
func init() {
rootCmd.AddCommand(createCmd)
}
// createTemplate creates a new .gitignore file using the specified template.
func createTemplate(ctx context.Context, template string) error {
client := getClientFromContext(ctx)
err := client.Create(template)
if err != nil {
return err
}
fmt.Printf("√ created %s .gitignore file\n", template)
return nil
}

49
cmd/gignore/list.go Normal file
View File

@ -0,0 +1,49 @@
// Package main provides the entry point for the gignore CLI tool,
// including commands like listing available .gitignore templates.
package main
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
)
// listCmd is the command to list all .gitignore templates.
var listCmd = &cobra.Command{
Use: "list",
Short: "List all .gitignore files in the root template repository",
Long: `List all .gitignore files in the root template repository.
This command will search the root template repository for .gitignore files and print their paths to the console.
The root template repository can be specified using the --root flag.
You can use this command to quickly find all available .gitignore templates.
Example:
gignore --root=<path> list`,
Run: func(cmd *cobra.Command, _ []string) {
err := listTemplates(cmd.Context())
cobra.CheckErr(err)
},
}
// init initialises the list command and adds it to the root command.
func init() {
rootCmd.AddCommand(listCmd)
}
// listTemplates retrieves and prints all .gitignore templates available from the gignore client.
func listTemplates(ctx context.Context) error {
client := getClientFromContext(ctx)
templates, err := client.List()
if err != nil {
return err
}
var output strings.Builder
for _, template := range templates {
output.WriteString(template + "\n")
}
fmt.Print(output.String())
return nil
}

View File

@ -1,79 +1,65 @@
// Package main provides the entry point for the gignore CLI tool,
// including commands like listing available .gitignore templates.
package main package main
import ( import (
"flag" "context"
"fmt" "os"
"slices"
"github.com/onyx-and-iris/gignore" "github.com/onyx-and-iris/gignore"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
) )
// rootCmd is the root command for the gignore CLI tool.
var rootCmd = &cobra.Command{
Use: "gignore",
Short: "A command line tool to manage .gitignore files",
Long: `gignore is a command line tool that helps you manage your .gitignore files.
You can use it to list available templates and create new .gitignore files.
It supports various programming languages.
Example:
gignore list
gignore create python`,
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
// Initialise the logger
loglevel, err := log.ParseLevel(cmd.Flag("loglevel").Value.String())
cobra.CheckErr(err)
log.SetLevel(loglevel)
// Initialise the gignore client
client := gignore.New(
gignore.WithTemplateDirectory(cmd.Flag("root").Value.String()),
)
// Set the client in the context
// This allows us to access the client in the command handlers
ctx := context.WithValue(context.Background(), clientKey, client)
cmd.SetContext(ctx)
},
Run: func(cmd *cobra.Command, _ []string) {
cmd.Help()
},
}
// init initialises the root command and adds global flags.
func init() {
getEnv := func(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
rootCmd.PersistentFlags().
StringP("root", "r", getEnv("GIGNORE_TEMPLATE_ROOT", gignore.DefaultTemplateDirectory), "Root directory to search for .gitignore files")
rootCmd.PersistentFlags().
StringP("loglevel", "l", getEnv("GIGNORE_LOGLEVEL", "warn"), "Log level (trace, debug, info, warn, error, fatal, panic)")
}
func main() { func main() {
flag.Usage = func() { if err := rootCmd.Execute(); err != nil {
w := flag.CommandLine.Output() log.Fatal(err)
fmt.Fprint(w, "Usage of gignore:\n")
fmt.Fprintf(w, " gignore [flags] <template>\n")
fmt.Fprint(w, "\n")
fmt.Fprint(w, "Flags:\n")
flag.PrintDefaults()
fmt.Fprint(w, "\n")
fmt.Fprintf(w, "Example:\n")
fmt.Fprint(w, " gignore go\n")
}
var (
list bool
templateDir string
loglevel int
)
flag.BoolVar(&list, "list", false, "list available templates")
flag.BoolVar(&list, "ls", false, "list available templates (shorthand)")
flag.StringVar(
&templateDir,
"dir",
getEnv("GIGNORE_TEMPLATE_DIR", "gitignoreio"),
"directory containing .gitignore templates",
)
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "log level")
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "log level (shorthand)")
flag.Parse()
if slices.Contains(log.AllLevels, log.Level(loglevel)) {
log.SetLevel(log.Level(loglevel))
}
client := gignore.New(gignore.WithTemplateDirectory(templateDir))
if list {
listTemplates(client)
return
}
args := flag.Args()
if len(args) != 1 {
flag.Usage()
return
}
err := client.Create(args[0])
if err != nil {
log.Fatalf("failed to create .gitignore file: %v", err)
}
fmt.Printf("√ created %s .gitignore file\n", args[0])
}
func listTemplates(client *gignore.GignoreClient) {
templates, err := client.List()
if err != nil {
log.Fatalf("failed to list templates: %v", err)
}
for _, template := range templates {
fmt.Println(template)
} }
} }

View File

@ -1,11 +0,0 @@
package main
import "os"
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if len(value) == 0 {
return defaultValue
}
return value
}

View File

@ -1,16 +1,16 @@
// Package gignore provides a way to manage .gitignore files and templates.
package gignore package gignore
import ( import (
"fmt" "fmt"
"strings"
"github.com/onyx-and-iris/gignore/internal/registry"
) )
type templateNotFoundError struct { type templateNotFoundError struct {
template string template string
registry *registry.TemplateRegistry templatesSearched []string
} }
func (e *templateNotFoundError) Error() string { func (e *templateNotFoundError) Error() string {
return fmt.Sprintf("template '%s' not found in %s registry", e.template, e.registry.Directory) return fmt.Sprintf("template '%s' not found in %s registry", e.template, strings.Join(e.templatesSearched, ", "))
} }

View File

@ -3,65 +3,73 @@ package gignore
import ( import (
"io" "io"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/gignore/internal/filewriter" "github.com/onyx-and-iris/gignore/internal/filewriter"
"github.com/onyx-and-iris/gignore/internal/registry" "github.com/onyx-and-iris/gignore/internal/registry"
log "github.com/sirupsen/logrus"
) )
//go:generate go run cmd/gen/gen.go //go:generate go run cmd/gen/main.go
type GignoreClient struct { // DefaultTemplateDirectory is the default directory for .gitignore templates.
const DefaultTemplateDirectory = "gitignoreio"
// Client is a client for managing .gitignore templates.
type Client struct {
registry *registry.TemplateRegistry registry *registry.TemplateRegistry
writer io.Writer writer io.Writer
} }
func New(options ...Option) *GignoreClient { // New creates a new Client with the provided options.
gc := &GignoreClient{ func New(options ...Option) *Client {
registry.NewTemplateRegistry(), c := &Client{
filewriter.New()} registry.New(),
filewriter.New(),
}
for _, option := range options { for _, option := range options {
option(gc) option(c)
} }
return gc return c
} }
func (g *GignoreClient) List() ([]string, error) { // List returns a list of available .gitignore templates.
return g.registry.ListTemplates() func (c *Client) List() ([]string, error) {
return c.registry.List()
} }
func (g *GignoreClient) Create(template string) error { // Create generates a .gitignore file from the specified template.
ok, err := g.registry.Contains(template) func (c *Client) Create(template string) error {
ok, err := c.registry.Contains(template)
if err != nil { if err != nil {
return err return err
} }
if !ok { if !ok {
templateNotFoundErr := &templateNotFoundError{template, g.registry} templateNotFoundErr := &templateNotFoundError{template, []string{c.registry.Directory}}
if g.registry.Directory == "gitignoreio" { if c.registry.Directory == DefaultTemplateDirectory {
return templateNotFoundErr return templateNotFoundErr
} }
log.Errorf("%s. Checking default registry...", templateNotFoundErr) c.registry.Directory = DefaultTemplateDirectory
ok, err = c.registry.Contains(template)
g.registry.Directory = "gitignoreio"
ok, err = g.registry.Contains(template)
if err != nil { if err != nil {
return err return err
} }
if !ok { if !ok {
templateNotFoundErr.templatesSearched = append(templateNotFoundErr.templatesSearched, c.registry.Directory)
return templateNotFoundErr return templateNotFoundErr
} }
log.Infof("template '%s' found in default gitignoreio registry", template) log.Debugf("template '%s' found in gitignoreio registry", template)
} else {
log.Debugf("template '%s' found in %s registry", template, c.registry.Directory)
} }
content, err := g.registry.GetTemplate(template) content, err := c.registry.Get(template)
if err != nil { if err != nil {
return err return err
} }
_, err = g.writer.Write(content) _, err = c.writer.Write(content)
if err != nil { if err != nil {
return err return err
} }

7
go.mod
View File

@ -5,6 +5,11 @@ go 1.24.0
require ( require (
github.com/cuonglm/gogi v1.0.1 github.com/cuonglm/gogi v1.0.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
) )
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
golang.org/x/sys v0.32.0 // indirect
)

14
go.sum
View File

@ -1,17 +1,27 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cuonglm/gogi v1.0.1 h1:Jotx6uAfFK6YHFOOek37R9y3Ae9qp/nUt/3mYGCl+44= github.com/cuonglm/gogi v1.0.1 h1:Jotx6uAfFK6YHFOOek37R9y3Ae9qp/nUt/3mYGCl+44=
github.com/cuonglm/gogi v1.0.1/go.mod h1:ZLU5wl3d+FSSkiYYDpmPJI2dWdAGj8q28rFjpeWv1g4= github.com/cuonglm/gogi v1.0.1/go.mod h1:ZLU5wl3d+FSSkiYYDpmPJI2dWdAGj8q28rFjpeWv1g4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,3 +1,4 @@
// Package filewriter provides functionality to write content to a .gitignore file.
package filewriter package filewriter
import ( import (
@ -6,11 +7,14 @@ import (
"os" "os"
) )
// FileWriter provides functionality to write content to a .gitignore file.
type FileWriter struct { type FileWriter struct {
targetFileName string
} }
// New creates a new FileWriter with the default target file name.
func New() *FileWriter { func New() *FileWriter {
return &FileWriter{} return &FileWriter{".gitignore"}
} }
func (fw *FileWriter) writeContent(content []byte, dst io.Writer) (int64, error) { func (fw *FileWriter) writeContent(content []byte, dst io.Writer) (int64, error) {
@ -25,14 +29,14 @@ func (fw *FileWriter) writeContent(content []byte, dst io.Writer) (int64, error)
} }
func (fw *FileWriter) Write(content []byte) (int, error) { func (fw *FileWriter) Write(content []byte) (int, error) {
f, err := os.Create(".gitignore") f, err := os.OpenFile(fw.targetFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil { if err != nil {
return 0, err return 0, err
} }
defer f.Close() defer f.Close()
const header = `# Auto-generated .gitignore by gignore: github.com/onyx-and-iris/gignore` const header = "# Auto-generated .gitignore by gignore: github.com/onyx-and-iris/gignore\n"
const footer = `# End of gignore: github.com/onyx-and-iris/gignore` const footer = "\n# End of gignore: github.com/onyx-and-iris/gignore\n"
var sz int64 var sz int64
n, err := fw.writeContent([]byte(header), f) n, err := fw.writeContent([]byte(header), f)

View File

@ -1,3 +1,4 @@
// Package registry provides functionality to manage and retrieve gitignore templates.
package registry package registry
import ( import (
@ -10,12 +11,14 @@ import (
//go:embed templates //go:embed templates
var templates embed.FS var templates embed.FS
// TemplateRegistry provides methods to manage and retrieve gitignore templates.
type TemplateRegistry struct { type TemplateRegistry struct {
templates fs.FS templates fs.FS
Directory string Directory string
} }
func NewTemplateRegistry() *TemplateRegistry { // New creates a new instance of TemplateRegistry.
func New() *TemplateRegistry {
return &TemplateRegistry{ return &TemplateRegistry{
templates: templates, templates: templates,
} }
@ -25,6 +28,7 @@ func (t *TemplateRegistry) filePath(name string) string {
return fmt.Sprintf("templates/%s/%s.gitignore", t.Directory, name) return fmt.Sprintf("templates/%s/%s.gitignore", t.Directory, name)
} }
// Contains checks if a template with the given name exists in the registry.
func (t *TemplateRegistry) Contains(name string) (bool, error) { func (t *TemplateRegistry) Contains(name string) (bool, error) {
_, err := fs.Stat(t.templates, t.filePath(name)) _, err := fs.Stat(t.templates, t.filePath(name))
if err != nil { if err != nil {
@ -37,7 +41,8 @@ func (t *TemplateRegistry) Contains(name string) (bool, error) {
return true, nil return true, nil
} }
func (t *TemplateRegistry) GetTemplate(name string) ([]byte, error) { // Get retrieves the content of the gitignore template with the given name.
func (t *TemplateRegistry) Get(name string) ([]byte, error) {
data, err := fs.ReadFile(t.templates, t.filePath(name)) data, err := fs.ReadFile(t.templates, t.filePath(name))
if err != nil { if err != nil {
return nil, err return nil, err
@ -45,7 +50,8 @@ func (t *TemplateRegistry) GetTemplate(name string) ([]byte, error) {
return data, nil return data, nil
} }
func (t *TemplateRegistry) ListTemplates() ([]string, error) { // List lists all the gitignore templates in the registry.
func (t *TemplateRegistry) List() ([]string, error) {
var paths []string var paths []string
err := fs.WalkDir( err := fs.WalkDir(

View File

@ -1,9 +1,11 @@
package gignore package gignore
type Option func(*GignoreClient) // Option is a function that configures a GignoreClient.
type Option func(*Client)
// WithTemplateDirectory sets the template directory for the GignoreClient.
func WithTemplateDirectory(directory string) Option { func WithTemplateDirectory(directory string) Option {
return func(g *GignoreClient) { return func(c *Client) {
g.registry.Directory = directory c.registry.Directory = directory
} }
} }