mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
239 lines
5.4 KiB
Go
239 lines
5.4 KiB
Go
package utils
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/alecthomas/chroma/v2/quick"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/sergi/go-diff/diffmatchpatch"
|
|
"golang.org/x/sys/execabs"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
// GitClone clones a git repo
|
|
func GitClone(repo, dst string) (string, error) {
|
|
if _, err := execabs.LookPath("git"); err == nil {
|
|
cmd := exec.Command("git", "clone", "--depth", "1", repo, dst)
|
|
dat, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to clone repo '%s' to %s: %v", repo, dst, err)
|
|
}
|
|
return string(dat), nil
|
|
} else { // use pure Go version of git
|
|
if _, err := git.PlainClone(dst, false, &git.CloneOptions{
|
|
URL: repo,
|
|
Depth: 1,
|
|
Progress: os.Stderr,
|
|
}); err != nil {
|
|
return "", fmt.Errorf("failed to create local copy of 'appledb' repo: %v", err)
|
|
}
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
// GitRefresh refreshes a git repo
|
|
func GitRefresh(repoPath string) (string, error) {
|
|
if _, err := execabs.LookPath("git"); err == nil {
|
|
cmd := exec.Command("git", "pull", "--rebase")
|
|
cmd.Dir = repoPath
|
|
dat, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to refresh repo '%s': %v", repoPath, err)
|
|
}
|
|
return string(dat), nil
|
|
} else { // use pure Go version of git
|
|
r, err := git.PlainOpen(repoPath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to open local copy of 'appledb' repo: %v", err)
|
|
}
|
|
w, err := r.Worktree()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get worktree of local copy of 'appledb' repo: %v", err)
|
|
}
|
|
if err = w.Pull(&git.PullOptions{
|
|
RemoteName: "origin",
|
|
// Force: true,
|
|
Progress: os.Stderr,
|
|
}); err != nil {
|
|
if !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
|
return "", fmt.Errorf("failed to update local copy of 'appledb' repo: %v", err)
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
type GitDiffConfig struct {
|
|
Tool string
|
|
Color bool
|
|
}
|
|
|
|
func GitDiff(src, dst string, conf *GitDiffConfig) (string, error) {
|
|
switch conf.Tool {
|
|
case "delta":
|
|
return createDeltaDiffPatch(src, dst, conf)
|
|
case "git":
|
|
return createGitDiffPatch(src, dst, conf)
|
|
case "go":
|
|
return createGoDiff(src, dst, conf)
|
|
default:
|
|
if _, err := exec.LookPath("delta"); err == nil {
|
|
return createDeltaDiffPatch(src, dst, conf)
|
|
} else if _, err := exec.LookPath("git"); err == nil {
|
|
return createGitDiffPatch(src, dst, conf)
|
|
}
|
|
return createGoDiff(src, dst, conf)
|
|
}
|
|
}
|
|
|
|
func createGoDiff(src, dst string, conf *GitDiffConfig) (string, error) {
|
|
dmp := diffmatchpatch.New()
|
|
|
|
diffs := dmp.DiffMain(src, dst, false)
|
|
if len(diffs) > 2 {
|
|
diffs = dmp.DiffCleanupSemanticLossless(diffs)
|
|
diffs = dmp.DiffCleanupEfficiency(diffs)
|
|
}
|
|
|
|
if len(diffs) == 1 {
|
|
if diffs[0].Type == diffmatchpatch.DiffEqual {
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
return dmp.DiffPrettyText(diffs), nil
|
|
}
|
|
|
|
func createGitDiffPatch(src, dst string, conf *GitDiffConfig) (string, error) {
|
|
tmpSrc, err := os.CreateTemp("", "src")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.Remove(tmpSrc.Name())
|
|
|
|
os.WriteFile(tmpSrc.Name(), []byte(src), 0644)
|
|
|
|
tmpDst, err := os.CreateTemp("", "dst")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.Remove(tmpDst.Name())
|
|
|
|
os.WriteFile(tmpDst.Name(), []byte(dst), 0644)
|
|
|
|
cmd := exec.Command("git", "diff", "--no-index", tmpSrc.Name(), tmpDst.Name())
|
|
|
|
dat, _ := cmd.CombinedOutput()
|
|
|
|
out := string(dat)
|
|
// strip the first 4 lines of the patch file
|
|
_, out, _ = strings.Cut(out, "\n")
|
|
_, out, _ = strings.Cut(out, "\n")
|
|
_, out, _ = strings.Cut(out, "\n")
|
|
_, out, _ = strings.Cut(out, "\n")
|
|
// strip the @@ gap lines
|
|
re := regexp.MustCompile("(?m)^@@ .*$")
|
|
out = re.ReplaceAllString(out, "")
|
|
if conf.Color {
|
|
// colorize the diff
|
|
b := new(strings.Builder)
|
|
if err := quick.Highlight(b, out, "diff", "terminal256", "nord"); err != nil {
|
|
return "", err
|
|
}
|
|
return b.String(), nil
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func createDeltaDiffPatch(src, dst string, conf *GitDiffConfig) (string, error) {
|
|
tmpSrc, err := os.CreateTemp("", "src")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.Remove(tmpSrc.Name())
|
|
|
|
os.WriteFile(tmpSrc.Name(), []byte(src), 0644)
|
|
|
|
tmpDst, err := os.CreateTemp("", "dst")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.Remove(tmpDst.Name())
|
|
|
|
os.WriteFile(tmpDst.Name(), []byte(dst), 0644)
|
|
|
|
width := 120
|
|
if term.IsTerminal(0) {
|
|
twidth, _, err := term.GetSize(0)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
width = twidth
|
|
}
|
|
|
|
cmd := exec.Command(
|
|
"delta",
|
|
"--dark",
|
|
"--diff-so-fancy",
|
|
"--side-by-side",
|
|
"--file-style", "omit",
|
|
"--hunk-header-style", "omit",
|
|
"--syntax-theme",
|
|
"Nord",
|
|
"--width", strconv.Itoa(width),
|
|
tmpSrc.Name(),
|
|
tmpDst.Name(),
|
|
)
|
|
|
|
out, _ := cmd.CombinedOutput()
|
|
// if err != nil {
|
|
// return "", fmt.Errorf("delta failed %s: %v", out, err)
|
|
// }
|
|
|
|
return string(out), nil
|
|
}
|
|
|
|
func ClangFormat(src, filename string, color bool) string {
|
|
if _, err := exec.LookPath("clang-format"); err == nil {
|
|
cmd := exec.Command(
|
|
"clang-format",
|
|
"-style={AlignConsecutiveDeclarations: true}",
|
|
"--assume-filename",
|
|
filename,
|
|
)
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return src
|
|
}
|
|
|
|
go func() {
|
|
defer stdin.Close()
|
|
io.WriteString(stdin, src)
|
|
}()
|
|
|
|
out, _ := cmd.CombinedOutput()
|
|
// if err != nil {
|
|
// return "", fmt.Errorf("clang-format failed %s: %v", out, err)
|
|
// }
|
|
|
|
src = string(out)
|
|
}
|
|
|
|
if color {
|
|
b := new(strings.Builder)
|
|
if err := quick.Highlight(b, src, "c", "terminal256", "nord"); err != nil {
|
|
return ""
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
return src
|
|
}
|