cli/pkg/release/publishers/linuxkit_test.go
Claude 23b82482f2 refactor: rename module from github.com/host-uk/core to forge.lthn.ai/core/cli
Move module identity to our own Forgejo instance. All import paths
updated across 434 Go files, sub-module go.mod files, and go.work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 05:53:52 +00:00

938 lines
25 KiB
Go

package publishers
import (
"bytes"
"context"
"os"
"os/exec"
"path/filepath"
"testing"
"forge.lthn.ai/core/cli/pkg/io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLinuxKitPublisher_Name_Good(t *testing.T) {
t.Run("returns linuxkit", func(t *testing.T) {
p := NewLinuxKitPublisher()
assert.Equal(t, "linuxkit", p.Name())
})
}
func TestLinuxKitPublisher_ParseConfig_Good(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("uses defaults when no extended config", func(t *testing.T) {
pubCfg := PublisherConfig{Type: "linuxkit"}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, "/project/.core/linuxkit/server.yml", cfg.Config)
assert.Equal(t, []string{"iso"}, cfg.Formats)
assert.Equal(t, []string{"linux/amd64"}, cfg.Platforms)
})
t.Run("parses extended config", func(t *testing.T) {
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"config": ".core/linuxkit/custom.yml",
"formats": []any{"iso", "qcow2", "vmdk"},
"platforms": []any{"linux/amd64", "linux/arm64"},
},
}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, "/project/.core/linuxkit/custom.yml", cfg.Config)
assert.Equal(t, []string{"iso", "qcow2", "vmdk"}, cfg.Formats)
assert.Equal(t, []string{"linux/amd64", "linux/arm64"}, cfg.Platforms)
})
t.Run("handles absolute config path", func(t *testing.T) {
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"config": "/absolute/path/to/config.yml",
},
}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, "/absolute/path/to/config.yml", cfg.Config)
})
}
func TestLinuxKitPublisher_BuildLinuxKitArgs_Good(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("builds basic args for amd64", func(t *testing.T) {
args := p.buildLinuxKitArgs("/config/server.yml", "iso", "linuxkit-1.0.0-amd64", "/output", "amd64")
assert.Contains(t, args, "build")
assert.Contains(t, args, "--format")
assert.Contains(t, args, "iso")
assert.Contains(t, args, "--name")
assert.Contains(t, args, "linuxkit-1.0.0-amd64")
assert.Contains(t, args, "--dir")
assert.Contains(t, args, "/output")
assert.Contains(t, args, "/config/server.yml")
// Should not contain --arch for amd64 (default)
assert.NotContains(t, args, "--arch")
})
t.Run("builds args with arch for arm64", func(t *testing.T) {
args := p.buildLinuxKitArgs("/config/server.yml", "qcow2", "linuxkit-1.0.0-arm64", "/output", "arm64")
assert.Contains(t, args, "--arch")
assert.Contains(t, args, "arm64")
assert.Contains(t, args, "qcow2")
})
}
func TestLinuxKitPublisher_BuildBaseName_Good(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("strips v prefix", func(t *testing.T) {
name := p.buildBaseName("v1.2.3")
assert.Equal(t, "linuxkit-1.2.3", name)
})
t.Run("handles version without v prefix", func(t *testing.T) {
name := p.buildBaseName("1.2.3")
assert.Equal(t, "linuxkit-1.2.3", name)
})
}
func TestLinuxKitPublisher_GetArtifactPath_Good(t *testing.T) {
p := NewLinuxKitPublisher()
tests := []struct {
name string
outputDir string
outputName string
format string
expected string
}{
{
name: "ISO format",
outputDir: "/dist/linuxkit",
outputName: "linuxkit-1.0.0-amd64",
format: "iso",
expected: "/dist/linuxkit/linuxkit-1.0.0-amd64.iso",
},
{
name: "raw format",
outputDir: "/dist/linuxkit",
outputName: "linuxkit-1.0.0-amd64",
format: "raw",
expected: "/dist/linuxkit/linuxkit-1.0.0-amd64.raw",
},
{
name: "qcow2 format",
outputDir: "/dist/linuxkit",
outputName: "linuxkit-1.0.0-arm64",
format: "qcow2",
expected: "/dist/linuxkit/linuxkit-1.0.0-arm64.qcow2",
},
{
name: "vmdk format",
outputDir: "/dist/linuxkit",
outputName: "linuxkit-1.0.0-amd64",
format: "vmdk",
expected: "/dist/linuxkit/linuxkit-1.0.0-amd64.vmdk",
},
{
name: "gcp format",
outputDir: "/dist/linuxkit",
outputName: "linuxkit-1.0.0-amd64",
format: "gcp",
expected: "/dist/linuxkit/linuxkit-1.0.0-amd64.img.tar.gz",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
path := p.getArtifactPath(tc.outputDir, tc.outputName, tc.format)
assert.Equal(t, tc.expected, path)
})
}
}
func TestLinuxKitPublisher_GetFormatExtension_Good(t *testing.T) {
p := NewLinuxKitPublisher()
tests := []struct {
format string
expected string
}{
{"iso", ".iso"},
{"raw", ".raw"},
{"qcow2", ".qcow2"},
{"vmdk", ".vmdk"},
{"vhd", ".vhd"},
{"gcp", ".img.tar.gz"},
{"aws", ".raw"},
{"unknown", ".unknown"},
}
for _, tc := range tests {
t.Run(tc.format, func(t *testing.T) {
ext := p.getFormatExtension(tc.format)
assert.Equal(t, tc.expected, ext)
})
}
}
func TestLinuxKitPublisher_Publish_Bad(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("fails when config file not found with linuxkit installed", func(t *testing.T) {
if err := validateLinuxKitCli(); err != nil {
t.Skip("skipping test: linuxkit CLI not available")
}
release := &Release{
Version: "v1.0.0",
ProjectDir: "/nonexistent",
FS: io.Local,
}
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"config": "/nonexistent/config.yml",
},
}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err := p.Publish(context.TODO(), release, pubCfg, relCfg, false)
assert.Error(t, err)
assert.Contains(t, err.Error(), "config file not found")
})
t.Run("fails when linuxkit CLI not available", func(t *testing.T) {
if err := validateLinuxKitCli(); err == nil {
t.Skip("skipping test: linuxkit CLI is available")
}
release := &Release{
Version: "v1.0.0",
ProjectDir: "/tmp",
FS: io.Local,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err := p.Publish(context.TODO(), release, pubCfg, relCfg, false)
assert.Error(t, err)
assert.Contains(t, err.Error(), "linuxkit CLI not found")
})
t.Run("fails when repository cannot be detected and not provided", func(t *testing.T) {
if err := validateLinuxKitCli(); err != nil {
t.Skip("skipping test: linuxkit CLI not available")
}
// Create temp directory that is NOT a git repo
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
// Create a config file
configPath := filepath.Join(tmpDir, "config.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: test\n"), 0644)
require.NoError(t, err)
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"config": "config.yml",
},
}
relCfg := &mockReleaseConfig{repository: ""} // Empty repository
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
assert.Error(t, err)
assert.Contains(t, err.Error(), "could not determine repository")
})
}
func TestValidateLinuxKitCli_Good(t *testing.T) {
t.Run("returns expected error when linuxkit not installed", func(t *testing.T) {
err := validateLinuxKitCli()
if err != nil {
// LinuxKit is not installed
assert.Contains(t, err.Error(), "linuxkit CLI not found")
}
// If err is nil, linuxkit is installed - that's OK
})
}
func TestLinuxKitPublisher_Publish_WithCLI_Good(t *testing.T) {
// These tests run only when linuxkit CLI is available
if err := validateLinuxKitCli(); err != nil {
t.Skip("skipping test: linuxkit CLI not available")
}
p := NewLinuxKitPublisher()
t.Run("succeeds with dry run and valid config", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
// Create config directory and file
configDir := filepath.Join(tmpDir, ".core", "linuxkit")
err = os.MkdirAll(configDir, 0755)
require.NoError(t, err)
configPath := filepath.Join(configDir, "server.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: linuxkit/kernel:5.10\n"), 0644)
require.NoError(t, err)
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "DRY RUN: LinuxKit Build & Publish")
})
t.Run("fails with missing config file", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err = p.Publish(context.TODO(), release, pubCfg, relCfg, false)
assert.Error(t, err)
assert.Contains(t, err.Error(), "config file not found")
})
t.Run("uses relCfg repository", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
configDir := filepath.Join(tmpDir, ".core", "linuxkit")
err = os.MkdirAll(configDir, 0755)
require.NoError(t, err)
configPath := filepath.Join(configDir, "server.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: test\n"), 0644)
require.NoError(t, err)
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
relCfg := &mockReleaseConfig{repository: "custom-owner/custom-repo"}
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "custom-owner/custom-repo")
})
t.Run("detects repository when not provided", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
// Create config file
configDir := filepath.Join(tmpDir, ".core", "linuxkit")
err = os.MkdirAll(configDir, 0755)
require.NoError(t, err)
configPath := filepath.Join(configDir, "server.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: test\n"), 0644)
require.NoError(t, err)
// Initialize git repo
cmd := exec.Command("git", "init")
cmd.Dir = tmpDir
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "remote", "add", "origin", "git@github.com:detected-owner/detected-repo.git")
cmd.Dir = tmpDir
require.NoError(t, cmd.Run())
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
relCfg := &mockReleaseConfig{repository: ""} // Empty to trigger detection
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "detected-owner/detected-repo")
})
}
func TestLinuxKitPublisher_Publish_NilRelCfg_Good(t *testing.T) {
if err := validateLinuxKitCli(); err != nil {
t.Skip("skipping test: linuxkit CLI not available")
}
p := NewLinuxKitPublisher()
t.Run("handles nil relCfg by detecting repo", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
// Create config file
configDir := filepath.Join(tmpDir, ".core", "linuxkit")
err = os.MkdirAll(configDir, 0755)
require.NoError(t, err)
configPath := filepath.Join(configDir, "server.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: test\n"), 0644)
require.NoError(t, err)
// Initialize git repo
cmd := exec.Command("git", "init")
cmd.Dir = tmpDir
require.NoError(t, cmd.Run())
cmd = exec.Command("git", "remote", "add", "origin", "git@github.com:nil-owner/nil-repo.git")
cmd.Dir = tmpDir
require.NoError(t, cmd.Run())
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
err = p.Publish(context.TODO(), release, pubCfg, nil, true) // nil relCfg
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "nil-owner/nil-repo")
})
}
// mockReleaseConfig implements ReleaseConfig for testing.
type mockReleaseConfig struct {
repository string
projectName string
}
func (m *mockReleaseConfig) GetRepository() string {
return m.repository
}
func (m *mockReleaseConfig) GetProjectName() string {
return m.projectName
}
func TestLinuxKitPublisher_DryRunPublish_Good(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("outputs expected dry run information", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: "/project",
FS: io.Local,
}
cfg := LinuxKitConfig{
Config: "/project/.core/linuxkit/server.yml",
Formats: []string{"iso", "qcow2"},
Platforms: []string{"linux/amd64", "linux/arm64"},
}
err := p.dryRunPublish(release, cfg, "owner/repo")
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "DRY RUN: LinuxKit Build & Publish")
assert.Contains(t, output, "Repository: owner/repo")
assert.Contains(t, output, "Version: v1.0.0")
assert.Contains(t, output, "Config: /project/.core/linuxkit/server.yml")
assert.Contains(t, output, "Formats: iso, qcow2")
assert.Contains(t, output, "Platforms: linux/amd64, linux/arm64")
assert.Contains(t, output, "Would execute commands:")
assert.Contains(t, output, "linuxkit build")
assert.Contains(t, output, "Would upload artifacts to release:")
assert.Contains(t, output, "linuxkit-1.0.0-amd64.iso")
assert.Contains(t, output, "linuxkit-1.0.0-amd64.qcow2")
assert.Contains(t, output, "linuxkit-1.0.0-arm64.iso")
assert.Contains(t, output, "linuxkit-1.0.0-arm64.qcow2")
assert.Contains(t, output, "END DRY RUN")
})
t.Run("shows docker format usage hint", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: "/project",
FS: io.Local,
}
cfg := LinuxKitConfig{
Config: "/config.yml",
Formats: []string{"docker"},
Platforms: []string{"linux/amd64"},
}
err := p.dryRunPublish(release, cfg, "owner/repo")
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "linuxkit-1.0.0-amd64.docker.tar")
assert.Contains(t, output, "Usage: docker load <")
})
t.Run("handles single platform and format", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v2.0.0",
ProjectDir: "/project",
FS: io.Local,
}
cfg := LinuxKitConfig{
Config: "/config.yml",
Formats: []string{"iso"},
Platforms: []string{"linux/amd64"},
}
err := p.dryRunPublish(release, cfg, "owner/repo")
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "linuxkit-2.0.0-amd64.iso")
assert.NotContains(t, output, "arm64")
})
}
func TestLinuxKitPublisher_GetFormatExtension_AllFormats_Good(t *testing.T) {
p := NewLinuxKitPublisher()
tests := []struct {
format string
expected string
}{
{"iso", ".iso"},
{"iso-bios", ".iso"},
{"iso-efi", ".iso"},
{"raw", ".raw"},
{"raw-bios", ".raw"},
{"raw-efi", ".raw"},
{"qcow2", ".qcow2"},
{"qcow2-bios", ".qcow2"},
{"qcow2-efi", ".qcow2"},
{"vmdk", ".vmdk"},
{"vhd", ".vhd"},
{"gcp", ".img.tar.gz"},
{"aws", ".raw"},
{"docker", ".docker.tar"},
{"tar", ".tar"},
{"kernel+initrd", "-initrd.img"},
{"custom--format", ".custom--format"},
}
for _, tc := range tests {
t.Run(tc.format, func(t *testing.T) {
ext := p.getFormatExtension(tc.format)
assert.Equal(t, tc.expected, ext)
})
}
}
func TestLinuxKitPublisher_BuildLinuxKitArgs_AllArchitectures_Good(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("amd64 does not include arch flag", func(t *testing.T) {
args := p.buildLinuxKitArgs("/config.yml", "iso", "output--name", "/output", "amd64")
assert.Contains(t, args, "build")
assert.Contains(t, args, "--format")
assert.Contains(t, args, "iso")
assert.Contains(t, args, "--name")
assert.Contains(t, args, "output--name")
assert.Contains(t, args, "--dir")
assert.Contains(t, args, "/output")
assert.Contains(t, args, "/config.yml")
assert.NotContains(t, args, "--arch")
})
t.Run("arm64 includes arch flag", func(t *testing.T) {
args := p.buildLinuxKitArgs("/config.yml", "qcow2", "output--name", "/output", "arm64")
assert.Contains(t, args, "--arch")
assert.Contains(t, args, "arm64")
})
t.Run("other architectures include arch flag", func(t *testing.T) {
args := p.buildLinuxKitArgs("/config.yml", "raw", "output--name", "/output", "riscv64")
assert.Contains(t, args, "--arch")
assert.Contains(t, args, "riscv64")
})
}
func TestLinuxKitPublisher_ParseConfig_EdgeCases_Good(t *testing.T) {
p := NewLinuxKitPublisher()
t.Run("handles nil extended config", func(t *testing.T) {
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: nil,
}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, "/project/.core/linuxkit/server.yml", cfg.Config)
assert.Equal(t, []string{"iso"}, cfg.Formats)
assert.Equal(t, []string{"linux/amd64"}, cfg.Platforms)
})
t.Run("handles empty extended config", func(t *testing.T) {
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{},
}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, "/project/.core/linuxkit/server.yml", cfg.Config)
assert.Equal(t, []string{"iso"}, cfg.Formats)
assert.Equal(t, []string{"linux/amd64"}, cfg.Platforms)
})
t.Run("handles mixed format types in extended config", func(t *testing.T) {
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"formats": []any{"iso", 123, "qcow2"}, // includes non-string
},
}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, []string{"iso", "qcow2"}, cfg.Formats)
})
t.Run("handles mixed platform types in extended config", func(t *testing.T) {
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"platforms": []any{"linux/amd64", nil, "linux/arm64"},
},
}
cfg := p.parseConfig(pubCfg, "/project")
assert.Equal(t, []string{"linux/amd64", "linux/arm64"}, cfg.Platforms)
})
}
func TestLinuxKitPublisher_BuildBaseName_EdgeCases_Good(t *testing.T) {
p := NewLinuxKitPublisher()
tests := []struct {
name string
version string
expected string
}{
{"strips v prefix", "v1.2.3", "linuxkit-1.2.3"},
{"no v prefix", "1.2.3", "linuxkit-1.2.3"},
{"prerelease version", "v1.0.0-alpha.1", "linuxkit-1.0.0-alpha.1"},
{"build metadata", "v1.0.0+build.123", "linuxkit-1.0.0+build.123"},
{"only v", "v", "linuxkit-"},
{"empty string", "", "linuxkit-"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
name := p.buildBaseName(tc.version)
assert.Equal(t, tc.expected, name)
})
}
}
func TestLinuxKitPublisher_GetArtifactPath_AllFormats_Good(t *testing.T) {
p := NewLinuxKitPublisher()
tests := []struct {
name string
outputDir string
outputName string
format string
expected string
}{
{
name: "ISO format",
outputDir: "/dist",
outputName: "linuxkit-1.0.0-amd64",
format: "iso",
expected: "/dist/linuxkit-1.0.0-amd64.iso",
},
{
name: "ISO-BIOS format",
outputDir: "/dist",
outputName: "linuxkit-1.0.0-amd64",
format: "iso-bios",
expected: "/dist/linuxkit-1.0.0-amd64.iso",
},
{
name: "docker format",
outputDir: "/output",
outputName: "linuxkit-2.0.0-arm64",
format: "docker",
expected: "/output/linuxkit-2.0.0-arm64.docker.tar",
},
{
name: "tar format",
outputDir: "/output",
outputName: "linuxkit-1.0.0",
format: "tar",
expected: "/output/linuxkit-1.0.0.tar",
},
{
name: "kernel+initrd format",
outputDir: "/output",
outputName: "linuxkit-1.0.0",
format: "kernel+initrd",
expected: "/output/linuxkit-1.0.0-initrd.img",
},
{
name: "GCP format",
outputDir: "/output",
outputName: "linuxkit-1.0.0",
format: "gcp",
expected: "/output/linuxkit-1.0.0.img.tar.gz",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
path := p.getArtifactPath(tc.outputDir, tc.outputName, tc.format)
assert.Equal(t, tc.expected, path)
})
}
}
func TestLinuxKitPublisher_Publish_DryRun_Good(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
// Skip if linuxkit CLI is not available
if err := validateLinuxKitCli(); err != nil {
t.Skip("skipping test: linuxkit CLI not available")
}
p := NewLinuxKitPublisher()
t.Run("dry run succeeds with valid config file", func(t *testing.T) {
// Create temp directory with config file
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
configDir := filepath.Join(tmpDir, ".core", "linuxkit")
err = os.MkdirAll(configDir, 0755)
require.NoError(t, err)
configPath := filepath.Join(configDir, "server.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: linuxkit/kernel:5.10\n"), 0644)
require.NoError(t, err)
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{Type: "linuxkit"}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "DRY RUN: LinuxKit Build & Publish")
})
t.Run("dry run uses custom config path", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
customConfigPath := filepath.Join(tmpDir, "custom-config.yml")
err = os.WriteFile(customConfigPath, []byte("kernel:\n image: custom\n"), 0644)
require.NoError(t, err)
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v1.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"config": customConfigPath,
},
}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "custom-config.yml")
})
t.Run("dry run with multiple formats and platforms", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "linuxkit-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
configPath := filepath.Join(tmpDir, "config.yml")
err = os.WriteFile(configPath, []byte("kernel:\n image: test\n"), 0644)
require.NoError(t, err)
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
release := &Release{
Version: "v2.0.0",
ProjectDir: tmpDir,
FS: io.Local,
}
pubCfg := PublisherConfig{
Type: "linuxkit",
Extended: map[string]any{
"config": "config.yml",
"formats": []any{"iso", "qcow2", "vmdk"},
"platforms": []any{"linux/amd64", "linux/arm64"},
},
}
relCfg := &mockReleaseConfig{repository: "owner/repo"}
err = p.Publish(context.TODO(), release, pubCfg, relCfg, true)
_ = w.Close()
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
os.Stdout = oldStdout
require.NoError(t, err)
output := buf.String()
// Check all format/platform combinations are listed
assert.Contains(t, output, "linuxkit-2.0.0-amd64.iso")
assert.Contains(t, output, "linuxkit-2.0.0-amd64.qcow2")
assert.Contains(t, output, "linuxkit-2.0.0-amd64.vmdk")
assert.Contains(t, output, "linuxkit-2.0.0-arm64.iso")
assert.Contains(t, output, "linuxkit-2.0.0-arm64.qcow2")
assert.Contains(t, output, "linuxkit-2.0.0-arm64.vmdk")
})
}