Compare commits
No commits in common. "main" and "v0.0.1" have entirely different histories.
10 changed files with 27 additions and 206 deletions
|
|
@ -1,12 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 4
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.{md,yml,yaml,json,txt}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
run:
|
|
||||||
timeout: 5m
|
|
||||||
go: "1.26"
|
|
||||||
|
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- govet
|
|
||||||
- errcheck
|
|
||||||
- staticcheck
|
|
||||||
- unused
|
|
||||||
- gosimple
|
|
||||||
- ineffassign
|
|
||||||
- typecheck
|
|
||||||
- gocritic
|
|
||||||
- gofmt
|
|
||||||
disable:
|
|
||||||
- exhaustive
|
|
||||||
- wrapcheck
|
|
||||||
|
|
||||||
issues:
|
|
||||||
exclude-use-default: false
|
|
||||||
max-same-issues: 0
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
# Contributing
|
|
||||||
|
|
||||||
Thank you for your interest in contributing!
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
- **Go Version**: 1.26 or higher is required.
|
|
||||||
- **Tools**: `golangci-lint` and `task` (Taskfile.dev) are recommended.
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
1. **Testing**: Ensure all tests pass before submitting changes.
|
|
||||||
```bash
|
|
||||||
go test ./...
|
|
||||||
```
|
|
||||||
2. **Code Style**: All code must follow standard Go formatting.
|
|
||||||
```bash
|
|
||||||
gofmt -w .
|
|
||||||
go vet ./...
|
|
||||||
```
|
|
||||||
3. **Linting**: We use `golangci-lint` to maintain code quality.
|
|
||||||
```bash
|
|
||||||
golangci-lint run ./...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commit Message Format
|
|
||||||
We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification:
|
|
||||||
- `feat`: A new feature
|
|
||||||
- `fix`: A bug fix
|
|
||||||
- `docs`: Documentation changes
|
|
||||||
- `refactor`: A code change that neither fixes a bug nor adds a feature
|
|
||||||
- `chore`: Changes to the build process or auxiliary tools and libraries
|
|
||||||
|
|
||||||
Example: `feat: add new endpoint for health check`
|
|
||||||
|
|
||||||
## License
|
|
||||||
By contributing to this project, you agree that your contributions will be licensed under the **European Union Public Licence (EUPL-1.2)**.
|
|
||||||
11
README.md
11
README.md
|
|
@ -1,11 +0,0 @@
|
||||||
[](https://pkg.go.dev/forge.lthn.ai/core/go-git)
|
|
||||||
[](LICENSE.md)
|
|
||||||
[](go.mod)
|
|
||||||
|
|
||||||
# go-git
|
|
||||||
|
|
||||||
Go module: `forge.lthn.ai/core/go-git`
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[EUPL-1.2](LICENSE.md)
|
|
||||||
46
Taskfile.yml
46
Taskfile.yml
|
|
@ -1,46 +0,0 @@
|
||||||
version: '3'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
test:
|
|
||||||
desc: Run all tests
|
|
||||||
cmds:
|
|
||||||
- go test ./...
|
|
||||||
|
|
||||||
lint:
|
|
||||||
desc: Run golangci-lint
|
|
||||||
cmds:
|
|
||||||
- golangci-lint run ./...
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
desc: Format all Go files
|
|
||||||
cmds:
|
|
||||||
- gofmt -w .
|
|
||||||
|
|
||||||
vet:
|
|
||||||
desc: Run go vet
|
|
||||||
cmds:
|
|
||||||
- go vet ./...
|
|
||||||
|
|
||||||
build:
|
|
||||||
desc: Build all Go packages
|
|
||||||
cmds:
|
|
||||||
- go build ./...
|
|
||||||
|
|
||||||
cov:
|
|
||||||
desc: Run tests with coverage and open HTML report
|
|
||||||
cmds:
|
|
||||||
- go test -coverprofile=coverage.out ./...
|
|
||||||
- go tool cover -html=coverage.out
|
|
||||||
|
|
||||||
tidy:
|
|
||||||
desc: Tidy go.mod
|
|
||||||
cmds:
|
|
||||||
- go mod tidy
|
|
||||||
|
|
||||||
check:
|
|
||||||
desc: Run fmt, vet, lint, and test in sequence
|
|
||||||
cmds:
|
|
||||||
- task: fmt
|
|
||||||
- task: vet
|
|
||||||
- task: lint
|
|
||||||
- task: test
|
|
||||||
5
git.go
5
git.go
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -106,12 +105,12 @@ func getStatus(ctx context.Context, path, name string) RepoStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Staged (index has changes)
|
// Staged (index has changes)
|
||||||
if slices.Contains([]byte{'A', 'D', 'R', 'M'}, x) {
|
if x == 'A' || x == 'D' || x == 'R' || x == 'M' {
|
||||||
status.Staged++
|
status.Staged++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modified in working tree
|
// Modified in working tree
|
||||||
if slices.Contains([]byte{'M', 'D'}, y) {
|
if y == 'M' || y == 'D' {
|
||||||
status.Modified++
|
status.Modified++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -3,7 +3,7 @@ module forge.lthn.ai/core/go-git
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
forge.lthn.ai/core/go v0.1.0
|
forge.lthn.ai/core/go v0.0.1
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -1,5 +1,5 @@
|
||||||
forge.lthn.ai/core/go v0.1.0 h1:Ow/1NTajrrNPO0zgkskEyEGdx4SKpiNqTaqM0txNOYI=
|
forge.lthn.ai/core/go v0.0.1 h1:6DFABiGUccu3iQz2avpYbh0X24xccIsve6TSipziKT4=
|
||||||
forge.lthn.ai/core/go v0.1.0/go.mod h1:lwi0tccAlg5j3k6CfoNJEueBc5l9mUeSBX/x6uY8ZbQ=
|
forge.lthn.ai/core/go v0.0.1/go.mod h1:vr4W9GMcyKbOJWmo22zQ9KmzLbdr2s17Q6LkVjpOeFU=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
|
|
||||||
49
service.go
49
service.go
|
|
@ -2,8 +2,6 @@ package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"iter"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"forge.lthn.ai/core/go/pkg/framework"
|
"forge.lthn.ai/core/go/pkg/framework"
|
||||||
)
|
)
|
||||||
|
|
@ -105,43 +103,24 @@ func (s *Service) handleTask(c *framework.Core, t framework.Task) (any, bool, er
|
||||||
// Status returns last status result.
|
// Status returns last status result.
|
||||||
func (s *Service) Status() []RepoStatus { return s.lastStatus }
|
func (s *Service) Status() []RepoStatus { return s.lastStatus }
|
||||||
|
|
||||||
// All returns an iterator over all last known statuses.
|
|
||||||
func (s *Service) All() iter.Seq[RepoStatus] {
|
|
||||||
return slices.Values(s.lastStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dirty returns an iterator over repos with uncommitted changes.
|
|
||||||
func (s *Service) Dirty() iter.Seq[RepoStatus] {
|
|
||||||
return func(yield func(RepoStatus) bool) {
|
|
||||||
for _, st := range s.lastStatus {
|
|
||||||
if st.Error == nil && st.IsDirty() {
|
|
||||||
if !yield(st) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ahead returns an iterator over repos with unpushed commits.
|
|
||||||
func (s *Service) Ahead() iter.Seq[RepoStatus] {
|
|
||||||
return func(yield func(RepoStatus) bool) {
|
|
||||||
for _, st := range s.lastStatus {
|
|
||||||
if st.Error == nil && st.HasUnpushed() {
|
|
||||||
if !yield(st) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirtyRepos returns repos with uncommitted changes.
|
// DirtyRepos returns repos with uncommitted changes.
|
||||||
func (s *Service) DirtyRepos() []RepoStatus {
|
func (s *Service) DirtyRepos() []RepoStatus {
|
||||||
return slices.Collect(s.Dirty())
|
var dirty []RepoStatus
|
||||||
|
for _, st := range s.lastStatus {
|
||||||
|
if st.Error == nil && st.IsDirty() {
|
||||||
|
dirty = append(dirty, st)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
// AheadRepos returns repos with unpushed commits.
|
// AheadRepos returns repos with unpushed commits.
|
||||||
func (s *Service) AheadRepos() []RepoStatus {
|
func (s *Service) AheadRepos() []RepoStatus {
|
||||||
return slices.Collect(s.Ahead())
|
var ahead []RepoStatus
|
||||||
|
for _, st := range s.lastStatus {
|
||||||
|
if st.Error == nil && st.HasUnpushed() {
|
||||||
|
ahead = append(ahead, st)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ahead
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -24,13 +23,10 @@ func TestService_DirtyRepos_Good(t *testing.T) {
|
||||||
dirty := s.DirtyRepos()
|
dirty := s.DirtyRepos()
|
||||||
assert.Len(t, dirty, 3)
|
assert.Len(t, dirty, 3)
|
||||||
|
|
||||||
names := slices.Collect(func(yield func(string) bool) {
|
names := make([]string, len(dirty))
|
||||||
for _, d := range dirty {
|
for i, d := range dirty {
|
||||||
if !yield(d.Name) {
|
names[i] = d.Name
|
||||||
return
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
assert.Contains(t, names, "dirty-modified")
|
assert.Contains(t, names, "dirty-modified")
|
||||||
assert.Contains(t, names, "dirty-untracked")
|
assert.Contains(t, names, "dirty-untracked")
|
||||||
assert.Contains(t, names, "dirty-staged")
|
assert.Contains(t, names, "dirty-staged")
|
||||||
|
|
@ -68,13 +64,10 @@ func TestService_AheadRepos_Good(t *testing.T) {
|
||||||
ahead := s.AheadRepos()
|
ahead := s.AheadRepos()
|
||||||
assert.Len(t, ahead, 2)
|
assert.Len(t, ahead, 2)
|
||||||
|
|
||||||
names := slices.Collect(func(yield func(string) bool) {
|
names := make([]string, len(ahead))
|
||||||
for _, a := range ahead {
|
for i, a := range ahead {
|
||||||
if !yield(a.Name) {
|
names[i] = a.Name
|
||||||
return
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
assert.Contains(t, names, "ahead-by-one")
|
assert.Contains(t, names, "ahead-by-one")
|
||||||
assert.Contains(t, names, "ahead-by-five")
|
assert.Contains(t, names, "ahead-by-five")
|
||||||
}
|
}
|
||||||
|
|
@ -97,30 +90,6 @@ func TestService_AheadRepos_Good_EmptyStatus(t *testing.T) {
|
||||||
assert.Empty(t, ahead)
|
assert.Empty(t, ahead)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_Iterators_Good(t *testing.T) {
|
|
||||||
s := &Service{
|
|
||||||
lastStatus: []RepoStatus{
|
|
||||||
{Name: "clean"},
|
|
||||||
{Name: "dirty", Modified: 1},
|
|
||||||
{Name: "ahead", Ahead: 2},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test All()
|
|
||||||
all := slices.Collect(s.All())
|
|
||||||
assert.Len(t, all, 3)
|
|
||||||
|
|
||||||
// Test Dirty()
|
|
||||||
dirty := slices.Collect(s.Dirty())
|
|
||||||
assert.Len(t, dirty, 1)
|
|
||||||
assert.Equal(t, "dirty", dirty[0].Name)
|
|
||||||
|
|
||||||
// Test Ahead()
|
|
||||||
ahead := slices.Collect(s.Ahead())
|
|
||||||
assert.Len(t, ahead, 1)
|
|
||||||
assert.Equal(t, "ahead", ahead[0].Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestService_Status_Good(t *testing.T) {
|
func TestService_Status_Good(t *testing.T) {
|
||||||
expected := []RepoStatus{
|
expected := []RepoStatus{
|
||||||
{Name: "repo1", Branch: "main"},
|
{Name: "repo1", Branch: "main"},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue