Skip to content
Open
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
157 changes: 157 additions & 0 deletions cmd/changelog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package cmd

import (
"errors"
"fmt"
"os"
"strings"

"github.com/Snider/Borg/pkg/changelog"
"github.com/Snider/Borg/pkg/datanode"
"github.com/Snider/Borg/pkg/tim"
"github.com/Snider/Borg/pkg/trix"
"github.com/Snider/Borg/pkg/vcs"
"github.com/spf13/cobra"
)

var changelogCmd = NewChangelogCmd()

func NewChangelogCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "changelog [old] [new]",
Short: "Generate a changelog between two archives",
Args: func(cmd *cobra.Command, args []string) error {
source, _ := cmd.Flags().GetString("source")
if source != "" {
if len(args) != 1 {
return errors.New("accepts one archive when --source is set")
}
} else {
if len(args) != 2 {
return errors.New("accepts two archives when --source is not set")
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
format, _ := cmd.Flags().GetString("format")
password, _ := cmd.Flags().GetString("password")
source, _ := cmd.Flags().GetString("source")

var oldNode, newNode *datanode.DataNode
var err error

if source != "" {
oldNode, err = getDataNode(args[0], password)
if err != nil {
return fmt.Errorf("failed to read old archive: %w", err)
}
newNode, err = getDataNodeFromSource(source)
if err != nil {
return fmt.Errorf("failed to read source: %w", err)
}
} else {
oldNode, err = getDataNode(args[0], password)
if err != nil {
return fmt.Errorf("failed to read old archive: %w", err)
}
newNode, err = getDataNode(args[1], password)
if err != nil {
return fmt.Errorf("failed to read new archive: %w", err)
}
}

report, err := changelog.GenerateReport(oldNode, newNode)
if err != nil {
return fmt.Errorf("failed to generate changelog: %w", err)
}

var output string
switch format {
case "markdown":
output, err = changelog.FormatAsMarkdown(report)
case "json":
output, err = changelog.FormatAsJSON(report)
default:
output, err = changelog.FormatAsText(report)
}

if err != nil {
return fmt.Errorf("failed to format changelog: %w", err)
}

fmt.Fprint(os.Stdout, output)

return nil
},
}

cmd.Flags().String("format", "text", "Output format (text, markdown, json)")
cmd.Flags().String("password", "", "Password for encrypted archives")
cmd.Flags().String("source", "", "Remote source to compare against (e.g., github:org/repo)")

return cmd
}

func getDataNode(filePath, password string) (*datanode.DataNode, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}

// Handle .stim files
if strings.HasSuffix(filePath, ".stim") || (len(data) >= 4 && string(data[:4]) == "STIM") {
if password == "" {
return nil, fmt.Errorf("password required for .stim files")
}
m, err := tim.FromSigil(data, password)
if err != nil {
return nil, err
}
tarball, err := m.ToTar()
if err != nil {
return nil, err
}
return datanode.FromTar(tarball)
}

// Handle .trix files
if strings.HasSuffix(filePath, ".trix") {
dn, err := trix.FromTrix(data, password)
if err != nil {
return nil, err
}
return dn, nil
}

// Assume it's a tarball
return datanode.FromTar(data)
}

func getDataNodeFromSource(source string) (*datanode.DataNode, error) {
parts := strings.SplitN(source, ":", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid source format: %s", source)
}

sourceType := parts[0]
sourcePath := parts[1]

switch sourceType {
case "github":
url := "https://" + sourceType + ".com/" + sourcePath
gitCloner := vcs.NewGitCloner()
return gitCloner.CloneGitRepository(url, os.Stdout)
default:
return nil, fmt.Errorf("unsupported source type: %s", sourceType)
}
}


func GetChangelogCmd() *cobra.Command {
return changelogCmd
}

func init() {
RootCmd.AddCommand(GetChangelogCmd())
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.25.0
require (
github.com/Snider/Enchantrix v0.0.2
github.com/fatih/color v1.18.0
github.com/go-git/go-git/v5 v5.16.3
github.com/go-git/go-git/v5 v5.16.4
github.com/google/go-github/v39 v39.2.0
github.com/klauspost/compress v1.18.2
github.com/mattn/go-isatty v0.0.20
Expand All @@ -25,6 +25,7 @@ require (
github.com/bep/debounce v1.2.1 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
Expand All @@ -49,11 +50,13 @@ require (
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.49.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/tkrajina/go-reflector v0.5.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
Expand All @@ -65,4 +68,5 @@ require (
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
Expand Down
Loading
Loading