Skip to content

Commit

Permalink
feat: support output vendor archive in cli
Browse files Browse the repository at this point in the history
  • Loading branch information
kulti committed Nov 25, 2024
1 parent 6a112cd commit 082653e
Show file tree
Hide file tree
Showing 224 changed files with 18,474 additions and 15 deletions.
74 changes: 61 additions & 13 deletions internal/archive/zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,27 @@ import (

var errZipInvalidFilePath = errors.New("invalid file path in zip")

func ZipDir(path string) ([]byte, error) {
func ZipDirToBytes(path string) ([]byte, error) {
buf := new(bytes.Buffer)
zw := zip.NewWriter(buf)
err := zipDir(path, buf)
return buf.Bytes(), err
}

func ZipDirToFile(path, zipFilePath string) error {
file, err := os.Create(zipFilePath)
if err != nil {
return fmt.Errorf("creates archive file: %w", err)
}
defer file.Close()
return zipDir(path, file)
}

func zipDir(path string, w io.Writer) error {
zw := zip.NewWriter(w)
defer zw.Close()

fsys := os.DirFS(path)
err := fs.WalkDir(fsys, ".", func(path string, entry fs.DirEntry, err error) error {
return fs.WalkDir(fsys, ".", func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
Expand All @@ -43,15 +58,6 @@ func ZipDir(path string) ([]byte, error) {
}
return nil
})
if err != nil {
return nil, fmt.Errorf("walk dir: %w", err)
}

if err := zw.Close(); err != nil {
return nil, fmt.Errorf("close zip: %w", err)
}

return buf.Bytes(), nil
}

func UnzipBytesToDir(data []byte, path string) error {
Expand All @@ -62,6 +68,48 @@ func UnzipBytesToDir(data []byte, path string) error {
return unzipToDir(archive, path)
}

func UnzipFileToDir(zipFile, path string) error {
archive, err := zip.OpenReader(zipFile)
if err != nil {
return fmt.Errorf("create zip reader: %w", err)
}
defer archive.Close()
return unzipToDir(&archive.Reader, path)
}

func UnzipFileToFilesMap(zipFile string) (map[string][]byte, error) {
archive, err := zip.OpenReader(zipFile)
if err != nil {
return nil, fmt.Errorf("create zip reader: %w", err)
}
defer archive.Close()
return unzipToFilesMap(&archive.Reader)
}

func unzipToFilesMap(archive *zip.Reader) (map[string][]byte, error) {
files := make(map[string][]byte, len(archive.File))
for _, f := range archive.File {
if f.FileInfo().IsDir() {
continue
}

fileInArchive, err := f.Open()
if err != nil {
return nil, fmt.Errorf("open archive file: %w", err)
}

file, err := io.ReadAll(fileInArchive)
if err != nil {
return nil, fmt.Errorf("read archive file: %w", err)
}

fileInArchive.Close()
files[f.Name] = file
}

return files, nil
}

func unzipToDir(archive *zip.Reader, path string) error {
for _, f := range archive.File {
if f.FileInfo().IsDir() {
Expand All @@ -87,7 +135,7 @@ func unzipToDir(archive *zip.Reader, path string) error {
return fmt.Errorf("open archive file: %w", err)
}

//#nosec G110 -- this server for internal usage only.
//#nosec G110 -- this server for internal usage only and tests.
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
return fmt.Errorf("copy: %w", err)
}
Expand Down
12 changes: 12 additions & 0 deletions internal/rockamalg/rockamalg.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"sync"
"text/template"

"github.com/enapter/rockamalg/internal/archive"
"github.com/enapter/rockamalg/internal/rockamalg/analyzer"
)

Expand All @@ -29,6 +30,7 @@ type AmalgParams struct {
Rockspec string
Lua string
Output string
Vendor string
Isolate bool
DisableDebug bool
AllowDevDeps bool
Expand Down Expand Up @@ -128,6 +130,12 @@ func (a *amalg) Do(ctx context.Context) error {
if err := a.wrapWithMsg(a.installDependencies, "Installing dependencies")(ctx); err != nil {
return fmt.Errorf("install dependencies: %w", err)
}

if a.p.Vendor != "" {
if err := a.wrapWithMsg(a.buildVendorArchive, "Building vendor archive")(ctx); err != nil {
return fmt.Errorf("build vendor archive: %w", err)
}
}
}

if err := a.wrapWithMsg(a.calculateRequires, "Calculating requires")(ctx); err != nil {
Expand Down Expand Up @@ -240,6 +248,10 @@ func (a *amalg) installDependencies(ctx context.Context) error {
return nil
}

func (a *amalg) buildVendorArchive(_ context.Context) error {
return archive.ZipDirToFile(a.tree, a.p.Vendor)
}

func (a *amalg) calculateRequires(ctx context.Context) error {
var err error
if a.p.Isolate {
Expand Down
8 changes: 8 additions & 0 deletions internal/rockamalgcli/cmd_amalg.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type cmdAmalg struct {
deps string
rockspec string
output string
vendor string
lua string
isolate bool
disableDebug bool
Expand Down Expand Up @@ -54,6 +55,12 @@ See the tutorial https://developers.enapter.com/docs/tutorial/lua-complex/introd
Destination: &cmd.output,
Required: true,
},
&cli.StringFlag{
Name: "vendor",
Aliases: []string{"v"},
Usage: "Vendor zip archive file name",
Destination: &cmd.vendor,
},
&cli.BoolFlag{
Name: "isolate",
Aliases: []string{"i"},
Expand Down Expand Up @@ -94,6 +101,7 @@ See the tutorial https://developers.enapter.com/docs/tutorial/lua-complex/introd
Rockspec: cmd.rockspec,
Lua: cmd.lua,
Output: cmd.output,
Vendor: cmd.vendor,
Writer: cliCtx.App.Writer,
Isolate: cmd.isolate,
DisableDebug: cmd.disableDebug,
Expand Down
1 change: 1 addition & 0 deletions internal/rockamalgcli/testdata/helps/rockamalg amalg
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ OPTIONS:
--deps value, -d value Use dependencies file
--rockspec value, -r value Use rockspec file for dependencies
--output value, -o value Output Lua file name
--vendor value, -v value Vendor zip archive file name
--isolate, -i Enable isolate mode (default: false)
--disable-debug Disable debug mode (default: false)
--allow-dev-dependencies Allow to use dev dependencies (default: false)
Expand Down
88 changes: 87 additions & 1 deletion tests/integration/amalg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ package integration_test

import (
"bytes"
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"path/filepath"
"reflect"
"regexp"
"strings"
"testing"

"github.com/stretchr/testify/require"

"github.com/enapter/rockamalg/internal/archive"
)

type rockstype int
Expand Down Expand Up @@ -106,6 +111,7 @@ func testAmalg(t *testing.T, testdataDir string, rt rockstype) {
stdoutBytes := execDockerCommand(t, testOpts.amalgArgs...)
checkExpectedWithBytes(t, testOpts.expectedStdout, stdoutBytes)
checkExpectedWithFile(t, testOpts.expectedLua, testOpts.outLuaFileName)
checkExpectedDirWithZippedFile(t, testOpts.expectedVendor, testOpts.vendorFileName)

stdoutBytes = execDockerCommand(t, testOpts.luaExecArgs...)
checkExpectedWithBytes(t, testOpts.expectedLuaExec, stdoutBytes)
Expand Down Expand Up @@ -150,10 +156,12 @@ func generateAmalgTests(files []fs.DirEntry) []amalgtest {

type testOpts struct {
outLuaFileName string
vendorFileName string
amalgArgs []string
luaExecArgs []string
expectedStdout string
expectedLua string
expectedVendor string
expectedLuaExec string
luaPath string
depsFileName string
Expand Down Expand Up @@ -183,6 +191,12 @@ func buildTestOpts(
t.Cleanup(func() { os.Remove(outLuaFile.Name()) })
require.NoError(t, outLuaFile.Close())

vendorFile, err := os.CreateTemp(testdataPath, "vendor_*.zip")
require.NoError(t, err)

t.Cleanup(func() { os.Remove(vendorFile.Name()) })
require.NoError(t, vendorFile.Close())

luaNameBytes, err := os.ReadFile(filepath.Join(testdataPath, "lua"))
require.NoError(t, err)

Expand All @@ -200,7 +214,8 @@ func buildTestOpts(
"-v", filepath.Join(curdir, "testdata/rocks")+":/opt/rocks",
)
}
amalgArgs = append(amalgArgs, "enapter/rockamalg", "amalg", "-o", outLuaFile.Name())
amalgArgs = append(amalgArgs, "enapter/rockamalg", "amalg",
"-output", outLuaFile.Name(), "-vendor", vendorFile.Name())

depsFileName := filepath.Join(testdataPath, "deps")
if isExist(t, depsFileName) {
Expand Down Expand Up @@ -240,9 +255,11 @@ func buildTestOpts(
amalgArgs: amalgArgs,
luaExecArgs: luaExecArgs,
outLuaFileName: outLuaFile.Name(),
vendorFileName: vendorFile.Name(),
expectedStdout: filepath.Join(testdataPath, o.stdout),
expectedLua: exepctedLuaFileName,
expectedLuaExec: filepath.Join(testdataPath, "out.lua.exec"),
expectedVendor: filepath.Join(testdataPath, "vendor"),
luaPath: filepath.Join(testdataPath, luaName),
depsFileName: depsFileName,
rockspecFileName: rockspecFileName,
Expand Down Expand Up @@ -277,6 +294,31 @@ func execDockerCommand(t *testing.T, args ...string) []byte {
return stdoutBuf.Bytes()
}

func checkExpectedDirWithZippedFile(t *testing.T, expectedDirName, actualZipFileName string) {
t.Helper()

if update {
require.NoError(t, os.RemoveAll(expectedDirName))
if !fileIsEmpty(t, actualZipFileName) {
require.NoError(t, archive.UnzipFileToDir(actualZipFileName, expectedDirName))
}
} else {
if fileIsEmpty(t, actualZipFileName) {
require.False(t, isExist(t, expectedDirName), "empty vendor.zip, but expected")
return
}

expected := dirToFilesMap(t, expectedDirName)
actual, err := archive.UnzipFileToFilesMap(actualZipFileName)
require.NoError(t, err)

require.Equal(t, expected, actual)
if !reflect.DeepEqual(expected, actual) {
require.Fail(t, "vendor differs — run test with update to see differences")
}
}
}

func checkExpectedWithFile(t *testing.T, exepctedFileName, actualFileName string) {
t.Helper()

Expand All @@ -299,6 +341,35 @@ func checkExpectedWithBytes(t *testing.T, exepctedFileName string, actualData []
}
}

func dirToFilesMap(t *testing.T, path string) map[string][]byte {
t.Helper()

files := make(map[string][]byte)
fsys := os.DirFS(path)
err := fs.WalkDir(fsys, ".", func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
if entry.IsDir() {
return nil
}

file, err := fsys.Open(path)
if err != nil {
return fmt.Errorf("open file %q: %w", path, err)
}

data, err := io.ReadAll(file)
if err != nil {
return fmt.Errorf("read file %q: %w", path, err)
}
files[path] = data
return nil
})
require.NoError(t, err)
return files
}

func isExist(t *testing.T, path string) bool {
t.Helper()

Expand All @@ -313,3 +384,18 @@ func isExist(t *testing.T, path string) bool {

return false
}

func fileIsEmpty(t *testing.T, path string) bool {
t.Helper()

fi, err := os.Stat(path)
if err == nil {
return fi.Size() == 0
}

if !os.IsNotExist(err) {
t.Fatalf("stat finished with error: %v", err)
}

return false
}
2 changes: 1 addition & 1 deletion tests/integration/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func isDirectory(t *testing.T, path string) bool {
func zipDir(t *testing.T, path string) []byte {
t.Helper()

data, err := archive.ZipDir(path)
data, err := archive.ZipDirToBytes(path)
require.NoError(t, err)

return data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Setting up configuration... Done
Generating rockspec... Done
Installing dependencies... Done
Building vendor archive... Done
Calculating requires... Done
Amalgamating... Done
Cleaning up result... Done
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Setting up configuration... Done
Generating rockspec... Done
Installing dependencies... Done
Building vendor archive... Done
Calculating requires... Done
Amalgamating... Done
Cleaning up result... Done
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Setting up configuration... Done
Generating rockspec... Done
Installing dependencies... Done
Building vendor archive... Done
Calculating requires... Done
Amalgamating... Done
Cleaning up result... Done
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Setting up configuration... Done
Generating rockspec... Done
Installing dependencies... Done
Building vendor archive... Done
Calculating requires... Done
Amalgamating... Done
Cleaning up result... Done
Loading

0 comments on commit 082653e

Please sign in to comment.