(Potentially Breaking Change) Git-Based Hak Chunk Ordering
This commit is contained in:
+114
-6
@@ -11,7 +11,9 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.westgate.pw/ShadowsOverWestgate/sow-tools/internal/erf"
|
||||
"gitea.westgate.pw/ShadowsOverWestgate/sow-tools/internal/gff"
|
||||
@@ -47,9 +49,10 @@ type BuildManifestHAK struct {
|
||||
}
|
||||
|
||||
type assetResource struct {
|
||||
Rel string
|
||||
Resource erf.Resource
|
||||
Size int64
|
||||
Rel string
|
||||
Resource erf.Resource
|
||||
Size int64
|
||||
ChangedAt time.Time
|
||||
}
|
||||
|
||||
type hakChunk struct {
|
||||
@@ -447,6 +450,15 @@ func fieldStringValueForScripts(value gff.Value) (string, bool) {
|
||||
|
||||
func collectAssetResources(p *project.Project) ([]assetResource, error) {
|
||||
var hakResources []assetResource
|
||||
assetChangedAt, err := collectGitAssetChangeTimes(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
assetsRelPath, err := filepath.Rel(p.Root, p.AssetsDir())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve assets dir relative path: %w", err)
|
||||
}
|
||||
assetsRelPath = filepath.ToSlash(assetsRelPath)
|
||||
|
||||
for _, rel := range p.Inventory.AssetFiles {
|
||||
abs := filepath.Join(p.AssetsDir(), filepath.FromSlash(rel))
|
||||
@@ -454,10 +466,20 @@ func collectAssetResources(p *project.Project) ([]assetResource, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := os.Stat(abs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stat %s: %w", abs, err)
|
||||
}
|
||||
repoRel := filepath.ToSlash(filepath.Join(assetsRelPath, filepath.FromSlash(rel)))
|
||||
changedAt := assetChangedAt[repoRel]
|
||||
if changedAt.IsZero() {
|
||||
changedAt = info.ModTime()
|
||||
}
|
||||
hakResources = append(hakResources, assetResource{
|
||||
Rel: rel,
|
||||
Resource: resource,
|
||||
Size: erf.ArchiveSize([]erf.Resource{resource}),
|
||||
Rel: rel,
|
||||
Resource: resource,
|
||||
Size: erf.ArchiveSize([]erf.Resource{resource}),
|
||||
ChangedAt: changedAt,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -467,6 +489,73 @@ func collectAssetResources(p *project.Project) ([]assetResource, error) {
|
||||
return hakResources, nil
|
||||
}
|
||||
|
||||
func collectGitAssetChangeTimes(p *project.Project) (map[string]time.Time, error) {
|
||||
result := make(map[string]time.Time, len(p.Inventory.AssetFiles))
|
||||
if len(p.Inventory.AssetFiles) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
assetsRelPath, err := filepath.Rel(p.Root, p.AssetsDir())
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("resolve assets dir relative path: %w", err)
|
||||
}
|
||||
assetsRelPath = filepath.ToSlash(assetsRelPath)
|
||||
|
||||
output, err := gitOutput(p.Root, "log", "--format=commit:%ct", "--name-only", "--", assetsRelPath)
|
||||
if err != nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
wanted := make(map[string]struct{}, len(p.Inventory.AssetFiles))
|
||||
for _, rel := range p.Inventory.AssetFiles {
|
||||
wanted[filepath.ToSlash(filepath.Join(assetsRelPath, filepath.FromSlash(rel)))] = struct{}{}
|
||||
}
|
||||
|
||||
var currentCommitTime time.Time
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "commit:") {
|
||||
sec, err := strconv.ParseInt(strings.TrimPrefix(line, "commit:"), 10, 64)
|
||||
if err != nil {
|
||||
currentCommitTime = time.Time{}
|
||||
continue
|
||||
}
|
||||
currentCommitTime = time.Unix(sec, 0).UTC()
|
||||
continue
|
||||
}
|
||||
if currentCommitTime.IsZero() {
|
||||
continue
|
||||
}
|
||||
if _, ok := wanted[line]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, exists := result[line]; exists {
|
||||
continue
|
||||
}
|
||||
result[line] = currentCommitTime
|
||||
if len(result) == len(wanted) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func gitOutput(dir string, args ...string) (string, error) {
|
||||
cmd := exec.Command("git", args...)
|
||||
if dir != "" {
|
||||
cmd.Dir = dir
|
||||
}
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(string(output)))
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func sortResources(resources []erf.Resource) {
|
||||
slices.SortFunc(resources, func(a, b erf.Resource) int {
|
||||
return compareResourceKeys(a, b)
|
||||
@@ -629,6 +718,7 @@ func planHAKChunks(p *project.Project, assets []assetResource) ([]hakChunk, erro
|
||||
if len(groupAssets) == 0 {
|
||||
continue
|
||||
}
|
||||
slices.SortFunc(groupAssets, compareAssetBuildOrder)
|
||||
|
||||
groupChunks, err := splitHAKGroup(cfg, groupAssets)
|
||||
if err != nil {
|
||||
@@ -708,6 +798,16 @@ func splitHAKGroup(cfg project.HAKConfig, assets []assetResource) ([]hakChunk, e
|
||||
return chunks, nil
|
||||
}
|
||||
|
||||
func compareAssetBuildOrder(a, b assetResource) int {
|
||||
if cmp := a.ChangedAt.Compare(b.ChangedAt); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
if cmp := compareResourceKeys(a.Resource, b.Resource); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return strings.Compare(a.Rel, b.Rel)
|
||||
}
|
||||
|
||||
func chunkName(base string, index int, split bool) string {
|
||||
if !split {
|
||||
return base
|
||||
@@ -868,6 +968,14 @@ func cleanupGeneratedHAKs(buildDir, moduleResRef string) error {
|
||||
}
|
||||
|
||||
func plannedModuleHAKOrder(p *project.Project) ([]string, error) {
|
||||
manifest, err := loadPreviousBuildManifest(p.BuildDir())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if manifest != nil && len(manifest.ModuleHAKs) > 0 {
|
||||
return append([]string(nil), manifest.ModuleHAKs...), nil
|
||||
}
|
||||
|
||||
assetResources, err := collectAssetResources(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user