Repository: chainguard-dev/apko
Pull request #1613 (2025-04-10 18:13:40)

Drop pkg/apk/tarball
This is used in exactly one place within apko now that the complicated parts have been moved to melange, so we no longer expose the implementation and just move it into the package where it's used.


Patch:
diff --git a/pkg/apk/tarball/testdata/bar/bar.txt b/internal/cli/testdata/bar/bar.txt
similarity index 100%
rename from pkg/apk/tarball/testdata/bar/bar.txt
rename to internal/cli/testdata/bar/bar.txt
diff --git a/pkg/apk/tarball/testdata/foo/foo.txt b/internal/cli/testdata/foo/foo.txt
similarity index 100%
rename from pkg/apk/tarball/testdata/foo/foo.txt
rename to internal/cli/testdata/foo/foo.txt
diff --git a/pkg/apk/README.md b/pkg/apk/README.md
index 0c25206..03c62fc 100644
--- a/pkg/apk/README.md
+++ b/pkg/apk/README.md
@@ -9,7 +9,6 @@ Also includes supporting utilities for working with filesystems, including:
 read-write, chmod/chown, devices, and symlinks capabilities
 * an implementation of that FS in memory, i.e. a memfs
 * an implementation of that FS on top of a directory, which uses the memfs for features the underlying disk does not support
-* tarball features
 
 Documentation is available at [https://pkg.go.dev/chainguard.dev/apko/pkg/apk](https://pkg.go.dev/chainguard.dev/apko/pkg/apk).
 
@@ -59,11 +58,6 @@ It also provides two implementations of that interface:
 * `memfs` is an in-memory implementation of `FullFS`. It is fully functional, but remember that it uses memory, so loading very large files into it will hit limits.
 * `rwosfs` is an on-disk implementation of `FullFS`. It is fully functional, including capabilities that may not exist on the underlying filesystem, like symlinks, devices, chown/chmod and case-sensitivity. The metadata for every file on disk also is in-memory, enabling those additional capabilities. Contents are not stored in memory.
 
-### Tarball
-
-`chainguard.dev/apko/pkg/apk/tarball` provides a utility to write an [fs.FS](https://pkg.go.dev/io/fs#FS) to a tarball. It is implemented on a `tarball.Context`, which lets
-you provide overrides for timestamps, UID/GID, and other features.
-
 ### apk
 
 `chainguard.dev/apko/pkg/apk/apk` is the heart of this library. It provides a native go
diff --git a/pkg/build/build_implementation.go b/pkg/build/build_implementation.go
index a134006..80c85fc 100644
--- a/pkg/build/build_implementation.go
+++ b/pkg/build/build_implementation.go
@@ -34,7 +34,6 @@ import (
 	"github.com/chainguard-dev/clog"
 
 	"chainguard.dev/apko/pkg/apk/apk"
-	"chainguard.dev/apko/pkg/apk/tarball"
 	"chainguard.dev/apko/pkg/lock"
 	"chainguard.dev/apko/pkg/options"
 )
@@ -110,7 +109,7 @@ func (bc *Context) BuildTarball(ctx context.Context) (string, hash.Hash, hash.Ha
 
 	diffid := sha256.New()
 
-	if err := tarball.WriteTar(ctx, io.MultiWriter(diffid, gzw), bc.fs); err != nil {
+	if err := writeTar(ctx, io.MultiWriter(diffid, gzw), bc.fs); err != nil {
 		return "", nil, nil, 0, fmt.Errorf("failed to generate tarball for image: %w", err)
 	}
 	if err := gzw.Close(); err != nil {
diff --git a/pkg/apk/tarball/write.go b/pkg/build/tarball.go
similarity index 85%
rename from pkg/apk/tarball/write.go
rename to pkg/build/tarball.go
index 7a1f461..4d72195 100644
--- a/pkg/apk/tarball/write.go
+++ b/pkg/build/tarball.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package tarball
+package build
 
 import (
 	"archive/tar"
@@ -31,7 +31,26 @@ import (
 
 const xattrTarPAXRecordsPrefix = "SCHILY.xattr."
 
-func writeTar(ctx context.Context, tw *tar.Writer, fsys apkfs.FullFS, users, groups map[int]string) error { //nolint:gocyclo
+// writeTar writes a tarball to the provided io.Writer from the provided fs.FS.
+// The etc/passwd and etc/group file provide username and group name mappings for the tar.
+func writeTar(ctx context.Context, dst io.Writer, fsys apkfs.FullFS) error { //nolint:gocyclo
+	ctx, span := otel.Tracer("go-apk").Start(ctx, "WriteTar")
+	defer span.End()
+
+	tw := tar.NewWriter(dst)
+
+	// get the uname and gname maps
+	usersFile, _ := passwd.ReadUserFile(fsys, "etc/passwd")
+	groupsFile, _ := passwd.ReadGroupFile(fsys, "etc/group")
+	users := map[int]string{}
+	groups := map[int]string{}
+	for _, u := range usersFile.Entries {
+		users[int(u.UID)] = u.UserName
+	}
+	for _, g := range groupsFile.Entries {
+		groups[int(g.GID)] = g.GroupName
+	}
+
 	buf := make([]byte, 1<<20)
 
 	if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
@@ -122,7 +141,6 @@ func writeTar(ctx context.Context, tw *tar.Writer, fsys apkfs.FullFS, users, gro
 			if err != nil {
 				return err
 			}
-
 			defer data.Close()
 
 			if _, err := io.CopyBuffer(tw, data, buf); err != nil {
@@ -132,34 +150,11 @@ func writeTar(ctx context.Context, tw *tar.Writer, fsys apkfs.FullFS, users, gro
 
 		return nil
 	}); err != nil {
-		return err
+		return fmt.Errorf("writing tar archive: %w", err)
 	}
 
-	return nil
-}
-
-// WriteTar writes a tarball to the provided io.Writer from the provided fs.FS.
-// The etc/passwd and etc/group file provide username and group name mappings for the tar.
-func WriteTar(ctx context.Context, dst io.Writer, src apkfs.FullFS) error {
-	ctx, span := otel.Tracer("go-apk").Start(ctx, "WriteTar")
-	defer span.End()
-
-	tw := tar.NewWriter(dst)
-	defer tw.Close()
-
-	// get the uname and gname maps
-	usersFile, _ := passwd.ReadUserFile(src, "etc/passwd")
-	groupsFile, _ := passwd.ReadGroupFile(src, "etc/group")
-	users := map[int]string{}
-	groups := map[int]string{}
-	for _, u := range usersFile.Entries {
-		users[int(u.UID)] = u.UserName
-	}
-	for _, g := range groupsFile.Entries {
-		groups[int(g.GID)] = g.GroupName
-	}
-	if err := writeTar(ctx, tw, src, users, groups); err != nil {
-		return fmt.Errorf("writing TAR archive failed: %w", err)
+	if err := tw.Close(); err != nil {
+		return fmt.Errorf("closing tar writer: %w", err)
 	}
 
 	return nil


Test patch:
diff --git a/pkg/apk/tarball/write_test.go b/pkg/build/tarball_test.go
similarity index 89%
rename from pkg/apk/tarball/write_test.go
rename to pkg/build/tarball_test.go
index b1bfbfc..ac97901 100644
--- a/pkg/apk/tarball/write_test.go
+++ b/pkg/build/tarball_test.go
@@ -1,4 +1,4 @@
-package tarball
+package build
 
 import (
 	"archive/tar"
@@ -28,11 +28,8 @@ func TestWriteTar(t *testing.T) {
 	require.NoError(t, err, "error setting xattr on %s", dir)
 	err = m.SetXattr(file, "user.file", []byte("bar"))
 	require.NoError(t, err, "error setting xattr on %s", file)
-	tw := tar.NewWriter(&buf)
-	err = writeTar(context.TODO(), tw, m, nil, nil)
+	err = writeTar(context.Background(), &buf, m)
 	require.NoError(t, err, "error writing tar")
-	err = tw.Close()
-	require.NoError(t, err, "error closing tar writer")
 
 	// now should be able to read the tar and check the xattrs
 	tr := tar.NewReader(&buf)