88 lines
3.1 KiB
Go
88 lines
3.1 KiB
Go
package delivery
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/artifact"
|
|
internalbuild "gitea.maximumdirect.net/eric/ReleaseKit/internal/build"
|
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/gitea"
|
|
)
|
|
|
|
func declaredBuildAssets(result internalbuild.Result) ([]gitea.ExpectedAsset, error) {
|
|
if len(result.Targets) == 0 {
|
|
if len(result.Artifacts) != 0 {
|
|
return nil, fmt.Errorf("source-only build result contains artifacts")
|
|
}
|
|
return nil, nil
|
|
}
|
|
if len(result.Artifacts) != len(result.Targets) {
|
|
return nil, fmt.Errorf("build result must contain one artifact for each selected target")
|
|
}
|
|
declarations := artifact.ExpectedBuild(result.BinaryName, result.Version, result.Targets)
|
|
wantByTarget := make(map[artifact.Target]string, len(declarations))
|
|
for _, declaration := range declarations {
|
|
target := declaration.Target
|
|
if !artifact.ValidComponent(target.OS) || !artifact.ValidComponent(target.Arch) {
|
|
return nil, fmt.Errorf("build result contains an invalid target")
|
|
}
|
|
if _, exists := wantByTarget[target]; exists {
|
|
return nil, fmt.Errorf("build result repeats target %q", target.OS+"/"+target.Arch)
|
|
}
|
|
wantByTarget[target] = declaration.Filename
|
|
}
|
|
assets := make([]gitea.ExpectedAsset, 0, len(result.Artifacts)+1)
|
|
seenTargets := make(map[artifact.Target]bool, len(result.Artifacts))
|
|
for _, artifact := range result.Artifacts {
|
|
wantName, ok := wantByTarget[artifact.Target]
|
|
if !ok || seenTargets[artifact.Target] || artifact.Filename != wantName {
|
|
return nil, fmt.Errorf("build artifact %q does not match a selected target and its declared name", artifact.Filename)
|
|
}
|
|
seenTargets[artifact.Target] = true
|
|
if filepath.Base(artifact.Path) != artifact.Filename {
|
|
return nil, fmt.Errorf("build artifact path %q does not end with filename %q", artifact.Path, artifact.Filename)
|
|
}
|
|
info, err := os.Stat(artifact.Path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("inspect build artifact %q: %w", artifact.Filename, err)
|
|
}
|
|
if !info.Mode().IsRegular() {
|
|
return nil, fmt.Errorf("build artifact %q is not a regular file", artifact.Filename)
|
|
}
|
|
file, err := os.Open(artifact.Path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open build artifact %q: %w", artifact.Filename, err)
|
|
}
|
|
hash := sha256.New()
|
|
_, copyErr := io.Copy(hash, file)
|
|
closeErr := file.Close()
|
|
if copyErr != nil {
|
|
return nil, fmt.Errorf("hash build artifact %q: %w", artifact.Filename, copyErr)
|
|
}
|
|
if closeErr != nil {
|
|
return nil, fmt.Errorf("close build artifact %q: %w", artifact.Filename, closeErr)
|
|
}
|
|
if artifact.SHA256 != hex.EncodeToString(hash.Sum(nil)) {
|
|
return nil, fmt.Errorf("build artifact %q does not match its declared SHA-256 digest", artifact.Filename)
|
|
}
|
|
assets = append(assets, gitea.ExpectedAsset{
|
|
Filename: artifact.Filename,
|
|
SHA256: artifact.SHA256,
|
|
Size: info.Size(),
|
|
Path: artifact.Path,
|
|
})
|
|
}
|
|
manifest, err := gitea.BuildManifest(assets)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build checksum manifest: %w", err)
|
|
}
|
|
if manifest.Filename != "" {
|
|
assets = append(assets, manifest)
|
|
}
|
|
return assets, nil
|
|
}
|