feat(cli): add BuildSourceArchive for plugin publish tar.zst
This commit is contained in:
parent
680cbe0160
commit
31e7b72b49
41
cmd/ninja/internal/archive/archive.go
Normal file
41
cmd/ninja/internal/archive/archive.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildSourceArchive runs `git archive --format=tar HEAD` in repoDir and
|
||||||
|
// compresses the result with zstd. Returns the compressed bytes.
|
||||||
|
//
|
||||||
|
// Only tracked files at HEAD are included. .gitignored files that were
|
||||||
|
// never tracked are excluded. Tracked-then-gitignored files are still
|
||||||
|
// included — callers may warn separately.
|
||||||
|
func BuildSourceArchive(repoDir string) ([]byte, error) {
|
||||||
|
cmd := exec.Command("git", "archive", "--format=tar", "HEAD")
|
||||||
|
cmd.Dir = repoDir
|
||||||
|
var tarOut, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &tarOut
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("git archive: %v: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressed bytes.Buffer
|
||||||
|
enc, err := zstd.NewWriter(&compressed, zstd.WithEncoderLevel(zstd.SpeedDefault))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(enc, &tarOut); err != nil {
|
||||||
|
_ = enc.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := enc.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return compressed.Bytes(), nil
|
||||||
|
}
|
||||||
89
cmd/ninja/internal/archive/archive_test.go
Normal file
89
cmd/ninja/internal/archive/archive_test.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildSourceArchive_RoundTrip(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
run := func(name string, args ...string) {
|
||||||
|
t.Helper()
|
||||||
|
cmd := exec.Command(name, args...)
|
||||||
|
cmd.Dir = dir
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"GIT_AUTHOR_NAME=t",
|
||||||
|
"GIT_AUTHOR_EMAIL=t@t",
|
||||||
|
"GIT_COMMITTER_NAME=t",
|
||||||
|
"GIT_COMMITTER_EMAIL=t@t",
|
||||||
|
)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s %v: %v\n%s", name, args, err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run("git", "init", "-q")
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "plugin.mod"),
|
||||||
|
[]byte("[plugin]\nname=\"x\"\nscope=\"@s\"\nversion=\"0.1.0\"\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "ignored.log"), []byte("nope"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("ignored.log\n"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
run("git", "add", "plugin.mod", ".gitignore")
|
||||||
|
run("git", "commit", "-qm", "init")
|
||||||
|
|
||||||
|
zstdBytes, err := BuildSourceArchive(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildSourceArchive: %v", err)
|
||||||
|
}
|
||||||
|
if len(zstdBytes) == 0 {
|
||||||
|
t.Fatal("empty archive")
|
||||||
|
}
|
||||||
|
|
||||||
|
dec, err := zstd.NewReader(bytes.NewReader(zstdBytes))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer dec.Close()
|
||||||
|
tr := tar.NewReader(dec)
|
||||||
|
got := map[string]string{}
|
||||||
|
for {
|
||||||
|
hdr, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
buf, err := io.ReadAll(tr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got[hdr.Name] = string(buf)
|
||||||
|
}
|
||||||
|
if _, ok := got["plugin.mod"]; !ok {
|
||||||
|
t.Errorf("expected plugin.mod in archive, got %v", keys(got))
|
||||||
|
}
|
||||||
|
if _, ok := got["ignored.log"]; ok {
|
||||||
|
t.Errorf("ignored.log should not be in archive (gitignored + untracked)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func keys(m map[string]string) []string {
|
||||||
|
out := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
out = append(out, k)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user