cli/pkg/build/checksum.go
Snider 26b47ee073
Migrate pkg/build to io.Medium abstraction (#287)
* chore(io): Migrate pkg/build to Medium abstraction

- Updated io.Medium interface with Open() and Create() methods to support streaming.
- Migrated pkg/build, pkg/build/builders, and pkg/build/signing to use io.Medium.
- Added FS field to build.Config and updated build.Builder interface.
- Refactored checksum and archive logic to use io.Medium streaming.
- Updated pkg/release and pkg/build/buildcmd to use io.Local.
- Updated unit tests to match new signatures.

* chore(io): Migrate pkg/build to Medium abstraction (fix CI)

- Fixed formatting in pkg/build/builders/wails.go.
- Fixed TestLoadConfig_Testdata and TestDiscover_Testdata to use absolute paths with io.Local to ensure compatibility with GitHub CI.
- Verified that all build and release tests pass.

* chore(io): Migrate pkg/build to Medium abstraction (fix CI paths)

- Ensured that outputDir and configPath are absolute in runProjectBuild.
- Fixed TestLoadConfig_Testdata and TestDiscover_Testdata to use absolute paths correctly.
- Verified that all build and release tests pass locally.

* chore(io): Migrate pkg/build to Medium abstraction (final fix)

- Improved io.Local to handle relative paths relative to CWD when rooted at "/".
- This makes io.Local a drop-in replacement for the 'os' package for most use cases.
- Ensured absolute paths are used in build logic and tests where appropriate.
- Fixed formatting and cleaned up debug prints.

* chore(io): address code review and fix CI

- Fix MockFile.Read to return io.EOF
- Use filepath.Match in TaskfileBuilder for precise globbing
- Stream xz data in createTarXzArchive to avoid in-memory string conversion
- Fix TestPath_RootFilesystem in local medium tests
- Fix formatting in pkg/build/buildcmd/cmd_project.go

* chore(io): resolve merge conflicts and final migration of pkg/build

- Resolved merge conflicts in pkg/io/io.go, pkg/io/local/client.go, and pkg/release/release.go.
- Reconciled io.Medium interface with upstream changes (unifying to fs.File for Open).
- Integrated upstream validatePath logic into the local medium.
- Completed migration of pkg/build and related packages to io.Medium.
- Addressed previous code review feedback on MockMedium and TaskfileBuilder.

* chore(io): resolve merge conflicts and finalize migration

- Resolved merge conflicts with dev branch.
- Unified io.Medium interface (Open returns fs.File, Create returns io.WriteCloser).
- Integrated upstream validatePath logic.
- Ensured all tests pass across pkg/io, pkg/build, and pkg/release.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:59:10 +00:00

97 lines
2.6 KiB
Go

// Package build provides project type detection and cross-compilation for the Core build system.
package build
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"path/filepath"
io_interface "github.com/host-uk/core/pkg/io"
"sort"
"strings"
)
// Checksum computes SHA256 for an artifact and returns the artifact with the Checksum field filled.
func Checksum(fs io_interface.Medium, artifact Artifact) (Artifact, error) {
if artifact.Path == "" {
return Artifact{}, fmt.Errorf("build.Checksum: artifact path is empty")
}
// Open the file
file, err := fs.Open(artifact.Path)
if err != nil {
return Artifact{}, fmt.Errorf("build.Checksum: failed to open file: %w", err)
}
defer func() { _ = file.Close() }()
// Compute SHA256 hash
hasher := sha256.New()
if _, err := io.Copy(hasher, file); err != nil {
return Artifact{}, fmt.Errorf("build.Checksum: failed to hash file: %w", err)
}
checksum := hex.EncodeToString(hasher.Sum(nil))
return Artifact{
Path: artifact.Path,
OS: artifact.OS,
Arch: artifact.Arch,
Checksum: checksum,
}, nil
}
// ChecksumAll computes checksums for all artifacts.
// Returns a slice of artifacts with their Checksum fields filled.
func ChecksumAll(fs io_interface.Medium, artifacts []Artifact) ([]Artifact, error) {
if len(artifacts) == 0 {
return nil, nil
}
var checksummed []Artifact
for _, artifact := range artifacts {
cs, err := Checksum(fs, artifact)
if err != nil {
return checksummed, fmt.Errorf("build.ChecksumAll: failed to checksum %s: %w", artifact.Path, err)
}
checksummed = append(checksummed, cs)
}
return checksummed, nil
}
// WriteChecksumFile writes a CHECKSUMS.txt file with the format:
//
// sha256hash filename1
// sha256hash filename2
//
// The artifacts should have their Checksum fields filled (call ChecksumAll first).
// Filenames are relative to the output directory (just the basename).
func WriteChecksumFile(fs io_interface.Medium, artifacts []Artifact, path string) error {
if len(artifacts) == 0 {
return nil
}
// Build the content
var lines []string
for _, artifact := range artifacts {
if artifact.Checksum == "" {
return fmt.Errorf("build.WriteChecksumFile: artifact %s has no checksum", artifact.Path)
}
filename := filepath.Base(artifact.Path)
lines = append(lines, fmt.Sprintf("%s %s", artifact.Checksum, filename))
}
// Sort lines for consistent output
sort.Strings(lines)
content := strings.Join(lines, "\n") + "\n"
// Write the file using the medium (which handles directory creation in Write)
if err := fs.Write(path, content); err != nil {
return fmt.Errorf("build.WriteChecksumFile: failed to write file: %w", err)
}
return nil
}