From c86aa9e2deea3ebf5a606414f94f268082bbae55 Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Mon, 4 May 2026 18:06:58 +0200 Subject: [PATCH] Honor 'SOW_ASSETS_SERVER_URL and 'SOW_ASSETS_REPO' --- internal/topdata/parts_manifest.go | 39 +++++++++++-- internal/topdata/topdata_test.go | 91 ++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 6 deletions(-) diff --git a/internal/topdata/parts_manifest.go b/internal/topdata/parts_manifest.go index 2297f66..1f173da 100644 --- a/internal/topdata/parts_manifest.go +++ b/internal/topdata/parts_manifest.go @@ -75,15 +75,42 @@ func resolveReleasedPartsManifest(p *project.Project, progress func(string)) (ma } func deriveSowAssetsRepoSpec(root string) (giteaRepoSpec, error) { + serverOverride := strings.TrimSpace(os.Getenv("SOW_ASSETS_SERVER_URL")) + repoOverride := strings.TrimSpace(os.Getenv("SOW_ASSETS_REPO")) remote, err := gitOutput(root, "remote", "get-url", "origin") - if err != nil || strings.TrimSpace(remote) == "" { - return giteaRepoSpec{BaseURL: defaultGiteaBaseURL, Owner: defaultSowAssetsRepoOwner, Repo: defaultSowAssetsRepoName}, nil + spec := giteaRepoSpec{BaseURL: defaultGiteaBaseURL, Owner: defaultSowAssetsRepoOwner, Repo: defaultSowAssetsRepoName} + if err == nil && strings.TrimSpace(remote) != "" { + if derived, ok := parseRepoSpec(strings.TrimSpace(remote)); ok { + derived.Repo = defaultSowAssetsRepoName + spec = derived + } } - if spec, ok := parseRepoSpec(strings.TrimSpace(remote)); ok { - spec.Repo = defaultSowAssetsRepoName - return spec, nil + + if serverOverride != "" { + spec.BaseURL = strings.TrimRight(serverOverride, "/") } - return giteaRepoSpec{BaseURL: defaultGiteaBaseURL, Owner: defaultSowAssetsRepoOwner, Repo: defaultSowAssetsRepoName}, nil + if repoOverride != "" { + owner, repo, ok := parseOwnerRepo(repoOverride) + if !ok { + return giteaRepoSpec{}, fmt.Errorf("invalid SOW_ASSETS_REPO %q; expected owner/repo", repoOverride) + } + spec.Owner = owner + spec.Repo = repo + } + return spec, nil +} + +func parseOwnerRepo(raw string) (string, string, bool) { + parts := strings.Split(strings.Trim(strings.TrimSuffix(raw, ".git"), "/"), "/") + if len(parts) < 2 { + return "", "", false + } + owner := strings.TrimSpace(parts[len(parts)-2]) + repo := strings.TrimSpace(parts[len(parts)-1]) + if owner == "" || repo == "" { + return "", "", false + } + return owner, repo, true } func parseRepoSpec(raw string) (giteaRepoSpec, bool) { diff --git a/internal/topdata/topdata_test.go b/internal/topdata/topdata_test.go index e6c5e98..6c51da1 100644 --- a/internal/topdata/topdata_test.go +++ b/internal/topdata/topdata_test.go @@ -9189,6 +9189,97 @@ func TestBuildNativeWithReleasedPartsManifest(t *testing.T) { } } +func TestBuildNativeWithReleasedPartsManifestUsesAssetsServerOverride(t *testing.T) { + root := t.TempDir() + projRoot := filepath.Join(root, "project") + mkdirAll(t, filepath.Join(projRoot, "src")) + mkdirAll(t, filepath.Join(projRoot, "assets")) + mkdirAll(t, filepath.Join(projRoot, "build")) + mkdirAll(t, filepath.Join(projRoot, "topdata", "data", "parts")) + writeFile(t, filepath.Join(projRoot, "topdata", "base_dialog.json"), "{}\n") + writeFile(t, filepath.Join(projRoot, "topdata", "data", "parts", "belt.json"), `{ + "output": "parts_belt.2da", + "columns": ["COSTMODIFIER", "ACBONUS"], + "rows": [] +}`+"\n") + mkdirAll(t, filepath.Join(projRoot, "reference")) + writeFile(t, filepath.Join(projRoot, "reference", "build.py"), "print('ok')\n") + + manifestJSON := `{ + "repo": "ShadowsOverWestgate/sow-assets", + "ref": "test-manifest", + "generated_at": "2026-04-09T00:00:00Z", + "categories": { + "belt": [18] + } +}` + "\n" + var server *httptest.Server + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v1/repos/ShadowsOverWestgate/sow-assets/releases/tags/parts-manifest-current": + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"assets":[{"name":"sow-parts-manifest.json","browser_download_url":"` + server.URL + `/downloads/sow-parts-manifest.json"}]}`)) + case "/downloads/sow-parts-manifest.json": + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(manifestJSON)) + default: + http.NotFound(w, r) + } + })) + defer server.Close() + + runGitTest(t, "", "init", "-b", "main", projRoot) + runGitTest(t, projRoot, "remote", "add", "origin", "git@git-ssh.westgate.pw:ShadowsOverWestgate/sow-module.git") + t.Setenv("SOW_ASSETS_SERVER_URL", server.URL) + t.Setenv("SOW_ASSETS_REPO", "ShadowsOverWestgate/sow-assets") + + proj := &project.Project{ + Root: projRoot, + Config: project.Config{ + Module: project.ModuleConfig{Name: "Test", ResRef: "test"}, + Paths: project.PathConfig{Source: "src", Assets: "assets", Build: "build"}, + TopData: project.TopDataConfig{ + Source: "topdata", + Build: "build/topdata", + }, + Autogen: project.AutogenConfig{ + Consumers: []project.AutogenConsumerConfig{ + { + ID: "parts", + Producer: "parts", + Dataset: "parts", + Mode: "parts_rows", + Root: "part", + Include: []string{"**/*.mdl"}, + Derive: project.AutogenDeriveConfig{ + Kind: "trailing_numeric_suffix", + GroupFrom: "first_path_segment", + }, + Manifest: project.AutogenManifestConfig{ + ReleaseTag: "parts-manifest-current", + AssetName: "sow-parts-manifest.json", + CacheName: "sow-parts-manifest.json", + }, + }, + }, + }, + }, + } + + result, err := BuildNative(proj, nil) + if err != nil { + t.Fatalf("BuildNative failed: %v", err) + } + + partsBytes, err := os.ReadFile(filepath.Join(result.Output2DADir, "parts_belt.2da")) + if err != nil { + t.Fatalf("read parts_belt.2da: %v", err) + } + if partsText := string(partsBytes); !strings.Contains(partsText, "18\t0\t0.00") { + t.Errorf("expected autogenerated row 18 from released manifest, got:\n%s", partsText) + } +} + func TestBuildNativeWithReleasedHeadVisualeffectsManifest(t *testing.T) { root := t.TempDir() projRoot := filepath.Join(root, "project")