From b59e6e35ff46ed41215114ea5e953d7c68ec36dd Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 15:53:44 +0000 Subject: [PATCH] feat(release): publish raw platform artifacts --- pkg/release/release.go | 116 ++++++++++++++++++++++++++++++++---- pkg/release/release_test.go | 22 +++++++ 2 files changed, 125 insertions(+), 13 deletions(-) diff --git a/pkg/release/release.go b/pkg/release/release.go index 5385dfc..0dabc23 100644 --- a/pkg/release/release.go +++ b/pkg/release/release.go @@ -5,6 +5,8 @@ package release import ( "context" + "sort" + "strings" "dappco.re/go/core" "dappco.re/go/core/build/internal/ax" @@ -120,35 +122,123 @@ func findArtifacts(filesystem io.Medium, distDir string) ([]build.Artifact, erro return nil, coreerr.E("release.findArtifacts", "dist/ directory not found", nil) } - var artifacts []build.Artifact - entries, err := filesystem.List(distDir) if err != nil { return nil, coreerr.E("release.findArtifacts", "failed to read dist/", err) } + var artifacts []build.Artifact for _, entry := range entries { if entry.IsDir() { continue } - name := entry.Name() - path := ax.Join(distDir, name) - - // Include archives and checksums - if core.HasSuffix(name, ".tar.gz") || - core.HasSuffix(name, ".tar.xz") || - core.HasSuffix(name, ".zip") || - core.HasSuffix(name, ".txt") || - core.HasSuffix(name, ".asc") || - core.HasSuffix(name, ".sig") { - artifacts = append(artifacts, build.Artifact{Path: path}) + if artifact, ok := releaseArtifactFromName(ax.Join(distDir, entry.Name()), entry.Name()); ok { + artifacts = append(artifacts, artifact) } } + if len(artifacts) > 0 { + return artifacts, nil + } + + platformArtifacts, err := findPlatformArtifacts(filesystem, distDir) + if err != nil { + return nil, err + } + + return platformArtifacts, nil +} + +func findPlatformArtifacts(filesystem io.Medium, distDir string) ([]build.Artifact, error) { + entries, err := filesystem.List(distDir) + if err != nil { + return nil, coreerr.E("release.findArtifacts", "failed to read dist/", err) + } + + var artifacts []build.Artifact + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + osValue, archValue, ok := parsePlatformDir(entry.Name()) + if !ok { + continue + } + + platformDir := ax.Join(distDir, entry.Name()) + files, err := filesystem.List(platformDir) + if err != nil { + continue + } + + for _, file := range files { + if file.IsDir() { + continue + } + + name := file.Name() + if !shouldPublishRawArtifact(name) { + continue + } + + artifacts = append(artifacts, build.Artifact{ + Path: ax.Join(platformDir, name), + OS: osValue, + Arch: archValue, + }) + } + } + + sort.Slice(artifacts, func(i, j int) bool { + return artifacts[i].Path < artifacts[j].Path + }) + return artifacts, nil } +func releaseArtifactFromName(path, name string) (build.Artifact, bool) { + if shouldPublishArchive(name) || shouldPublishChecksum(name) || shouldPublishSignature(name) { + return build.Artifact{Path: path}, true + } + return build.Artifact{}, false +} + +func shouldPublishArchive(name string) bool { + return core.HasSuffix(name, ".tar.gz") || + core.HasSuffix(name, ".tar.xz") || + core.HasSuffix(name, ".zip") +} + +func shouldPublishChecksum(name string) bool { + return core.HasSuffix(name, ".txt") +} + +func shouldPublishSignature(name string) bool { + return core.HasSuffix(name, ".asc") || + core.HasSuffix(name, ".sig") +} + +func shouldPublishRawArtifact(name string) bool { + if name == "" || strings.HasPrefix(name, ".") { + return false + } + if name == "artifact_meta.json" || name == "CHECKSUMS.txt" || name == "CHECKSUMS.txt.asc" { + return false + } + return true +} + +func parsePlatformDir(name string) (string, string, bool) { + osValue, archValue, ok := strings.Cut(name, "_") + if !ok || osValue == "" || archValue == "" { + return "", "", false + } + + return osValue, archValue, true +} + // Run executes the full release process: determine version, build artifacts, // generate changelog, and publish to configured targets. // For separated concerns, prefer `core build` then `core ci` (Publish). diff --git a/pkg/release/release_test.go b/pkg/release/release_test.go index 16f9afc..689e0a8 100644 --- a/pkg/release/release_test.go +++ b/pkg/release/release_test.go @@ -151,6 +151,28 @@ func TestRelease_FindArtifacts_Good(t *testing.T) { assert.Len(t, artifacts, 1) }) + t.Run("falls back to raw platform artifacts when no archives exist", func(t *testing.T) { + dir := t.TempDir() + distDir := ax.Join(dir, "dist") + require.NoError(t, ax.MkdirAll(ax.Join(distDir, "linux_amd64"), 0755)) + require.NoError(t, ax.MkdirAll(ax.Join(distDir, "windows_amd64"), 0755)) + + require.NoError(t, ax.WriteFile(ax.Join(distDir, "linux_amd64", "myapp"), []byte("binary"), 0755)) + require.NoError(t, ax.WriteFile(ax.Join(distDir, "windows_amd64", "myapp.exe"), []byte("binary"), 0755)) + require.NoError(t, ax.WriteFile(ax.Join(distDir, "linux_amd64", "artifact_meta.json"), []byte("{}"), 0644)) + + artifacts, err := findArtifacts(io.Local, distDir) + require.NoError(t, err) + + require.Len(t, artifacts, 2) + assert.Equal(t, ax.Join(distDir, "linux_amd64", "myapp"), artifacts[0].Path) + assert.Equal(t, "linux", artifacts[0].OS) + assert.Equal(t, "amd64", artifacts[0].Arch) + assert.Equal(t, ax.Join(distDir, "windows_amd64", "myapp.exe"), artifacts[1].Path) + assert.Equal(t, "windows", artifacts[1].OS) + assert.Equal(t, "amd64", artifacts[1].Arch) + }) + t.Run("returns empty slice for empty dist directory", func(t *testing.T) { dir := t.TempDir() distDir := ax.Join(dir, "dist")