Skip to content

Commit d159a04

Browse files
committed
fix(release): select historical chart sources
Prefer tagged source charts during historical inventory bootstrap, and fall back to published charts only when the source path is unavailable in the selected tag. Refs #1838
1 parent 916fbd2 commit d159a04

7 files changed

Lines changed: 77 additions & 20 deletions

File tree

‎.github/workflows/release-tags.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ jobs:
189189
else
190190
echo "WARNING: ${INVENTORY_TAG} predates its per-stack inventory states; using ${bootstrap_config}." >&2
191191
inventory_config="${bootstrap_config}"
192-
inventory_args+=(--use-published-charts)
192+
inventory_args+=(--allow-unavailable-source-charts)
193193
fi
194194
"${RUNNER_TEMP}/docs-version-sync" \
195195
--generate-stack-inventory "${RUNNER_TEMP}/stack-inventory.json" \

‎deploy/stacks/INVENTORY.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,10 @@ The manual `inventory_tag` preflight uploads the rendered JSON as a workflow
195195
artifact. New releases use the inventory config stored in the immutable tag.
196196
For a release that lacks a config with per-stack states, the preflight uses the
197197
config from the workflow ref as a bootstrap. The inventory still records and
198-
renders the selected tag, using the published charts referenced there instead
199-
of source chart paths that might not exist in the historical commit. This
200-
fallback is only for validating historical releases. Tags created after this
201-
workflow lands are self-contained.
198+
renders the selected tag. It uses source charts available in the historical
199+
commit and falls back to a published chart when its source path did not exist
200+
yet. This fallback is only for validating historical releases. Tags created
201+
after this workflow lands are self-contained.
202202

203203
## Development Documentation Sync
204204

‎tools/ci/test-github-release.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1768,7 +1768,7 @@ def test_stack_inventory_preflight_renders_without_release_publication(self):
17681768
self.assertIn("--inventory-config", preflight_workflow)
17691769
self.assertIn("grep -q '^states:'", preflight_workflow)
17701770
self.assertIn("predates its per-stack inventory states", preflight_workflow)
1771-
self.assertIn("--use-published-charts", preflight_workflow)
1771+
self.assertIn("--allow-unavailable-source-charts", preflight_workflow)
17721772
self.assertIn("actions/upload-artifact@v4", preflight_workflow)
17731773
self.assertIn("if-no-files-found: error", preflight_workflow)
17741774
self.assertNotIn("github-release tag", preflight_workflow)

‎tools/docs-version-sync/README.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ directory normally contains three separated inventories.
189189
Use the `inventory_tag` input on `release-tags.yml` to reproduce an inventory
190190
without publishing a release. The preflight uploads the JSON as a workflow
191191
artifact. Tags that lack a config with per-stack states use the config from the
192-
workflow ref as a one-time bootstrap and render their published chart versions.
193-
New tags carry their own config.
192+
workflow ref as a one-time bootstrap. They use an available tagged source chart
193+
and otherwise render the published chart version. New tags carry their own
194+
config.
194195

195196
## Validation
196197

‎tools/docs-version-sync/main.go‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func run(args []string) error {
4545
qualificationVersion := flags.String("qualification-version", "", "documentation version for an exact QA-qualified three-stack release set")
4646
inventoryOutput := flags.String("generate-stack-inventory", "", "write a resolved stack inventory to this path")
4747
inventoryConfig := flags.String("inventory-config", "", "release inventory config path; defaults to the stack checkout")
48-
usePublishedCharts := flags.Bool("use-published-charts", false, "render inventory with published charts instead of configured source charts")
48+
allowUnavailableSourceCharts := flags.Bool("allow-unavailable-source-charts", false, "use published charts when configured source paths are unavailable in a historical tag")
4949
stackSourceTag := flags.String("stack-source-tag", "", "immutable owning stack source tag for inventory generation")
5050
stackSourceCommit := flags.String("stack-source-commit", "", "immutable owning stack source commit for inventory generation")
5151
compareFrom := flags.String("compare-release-set-from", "", "directory containing previous release-set inventory JSON files")
@@ -71,7 +71,7 @@ func run(args []string) error {
7171
}
7272
if *updateCatalog || *check || *inventoryOutput != "" || *qualificationVersion != "" ||
7373
*stackVersion != "" || *computeStackVersion != "" || *observabilityStackVersion != "" ||
74-
*inventoryConfig != "" || *usePublishedCharts || *stackSourceTag != "" || *stackSourceCommit != "" {
74+
*inventoryConfig != "" || *allowUnavailableSourceCharts || *stackSourceTag != "" || *stackSourceCommit != "" {
7575
return fmt.Errorf("release-set comparison cannot be combined with catalog update, check, or inventory generation flags")
7676
}
7777
fromPath := resolveRepoPath(repoRoot, *compareFrom)
@@ -105,7 +105,7 @@ func run(args []string) error {
105105
Tag: *stackSourceTag,
106106
Commit: *stackSourceCommit,
107107
}, resolvedInventoryGenerationOptions{
108-
UsePublishedCharts: *usePublishedCharts,
108+
AllowUnavailableSourceCharts: *allowUnavailableSourceCharts,
109109
})
110110
}
111111
if !*updateCatalog && (*computeStackVersion != "" || *observabilityStackVersion != "") {
@@ -122,8 +122,8 @@ func run(args []string) error {
122122
return fmt.Errorf("--qualification-version requires exact control-plane, compute-plane, and observability stack versions")
123123
}
124124
}
125-
if *stackSourceTag != "" || *stackSourceCommit != "" || *inventoryConfig != "" || *usePublishedCharts {
126-
return fmt.Errorf("--stack-source-tag, --stack-source-commit, --inventory-config, and --use-published-charts require --generate-stack-inventory")
125+
if *stackSourceTag != "" || *stackSourceCommit != "" || *inventoryConfig != "" || *allowUnavailableSourceCharts {
126+
return fmt.Errorf("--stack-source-tag, --stack-source-commit, --inventory-config, and --allow-unavailable-source-charts require --generate-stack-inventory")
127127
}
128128
if *catalogPath == "" {
129129
*catalogPath = filepath.Join(repoRoot, "docs", "version-catalog", *target+".yaml")

‎tools/docs-version-sync/resolved_inventory_publish.go‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ type resolvedInventoryConfig struct {
162162
}
163163

164164
type resolvedInventoryGenerationOptions struct {
165-
UsePublishedCharts bool
165+
AllowUnavailableSourceCharts bool
166166
}
167167

168168
type resolvedInventorySourceChart struct {
@@ -245,9 +245,6 @@ func collectResolvedStackInventory(repoRoot, configPath string, source stackSour
245245
if err != nil {
246246
return resolvedStackInventory{}, err
247247
}
248-
if options.UsePublishedCharts {
249-
config.SourceCharts = nil
250-
}
251248
states := config.States
252249
if len(states) == 0 {
253250
return resolvedStackInventory{}, fmt.Errorf("resolved inventory config must declare at least one stack-owned state")
@@ -292,6 +289,7 @@ func collectResolvedStackInventory(repoRoot, configPath string, source stackSour
292289
renderSource,
293290
config.PublishedChartRepository,
294291
config.SourceCharts,
292+
options.AllowUnavailableSourceCharts,
295293
ngcAPIKey,
296294
runner,
297295
)
@@ -334,9 +332,11 @@ func collectResolvedStackInventory(repoRoot, configPath string, source stackSour
334332
plane.ManifestByRelease[release] = manifest
335333
}
336334
}
337-
for chart := range config.SourceCharts {
338-
if _, used := usedSourceCharts[chart]; !used {
339-
return resolvedStackInventory{}, fmt.Errorf("source chart %s is not used by any resolved Helmfile state", chart)
335+
if !options.AllowUnavailableSourceCharts {
336+
for chart := range config.SourceCharts {
337+
if _, used := usedSourceCharts[chart]; !used {
338+
return resolvedStackInventory{}, fmt.Errorf("source chart %s is not used by any resolved Helmfile state", chart)
339+
}
340340
}
341341
}
342342

@@ -708,6 +708,7 @@ func collectResolvedInventoryState(
708708
renderSource resolvedInventoryHelmSource,
709709
publishedChartRepository string,
710710
sourceCharts map[string]resolvedInventorySourceChart,
711+
allowUnavailableSourceCharts bool,
711712
ngcAPIKey string,
712713
runner resolvedInventoryCommandRunner,
713714
) ([]helmfileRelease, map[string][]byte, []string, error) {
@@ -728,6 +729,7 @@ func collectResolvedInventoryState(
728729
source,
729730
fullList,
730731
sourceCharts,
732+
allowUnavailableSourceCharts,
731733
)
732734
if err != nil {
733735
return nil, nil, nil, err
@@ -795,6 +797,7 @@ func materializeResolvedInventorySourceCharts(
795797
stackSource stackSourceRelease,
796798
fullRaw []byte,
797799
sourceCharts map[string]resolvedInventorySourceChart,
800+
allowUnavailableSourceCharts bool,
798801
) ([]string, error) {
799802
if len(sourceCharts) == 0 {
800803
return nil, nil
@@ -820,6 +823,12 @@ func materializeResolvedInventorySourceCharts(
820823
return nil, fmt.Errorf("source chart %s release %s version %q is not semantic", name, release.Name, release.Version)
821824
}
822825
tag := sourceChart.TagPrefix + release.Version
826+
if _, err := gitOutput(repoRoot, "cat-file", "-e", tag+":"+sourceChart.Path); err != nil {
827+
if allowUnavailableSourceCharts {
828+
continue
829+
}
830+
return nil, fmt.Errorf("source chart %s path %s is unavailable at %s: %w", name, sourceChart.Path, tag, err)
831+
}
823832
if _, err := gitOutput(repoRoot, "merge-base", "--is-ancestor", tag, stackSource.Commit); err != nil {
824833
return nil, fmt.Errorf("source chart %s tag %s is not an ancestor of stack commit %s: %w", name, tag, stackSource.Commit, err)
825834
}

‎tools/docs-version-sync/resolved_inventory_publish_test.go‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,53 @@ releases:
102102
}
103103
}
104104

105+
func TestMaterializeResolvedInventorySourceChartsAllowsUnavailableHistoricalPath(t *testing.T) {
106+
repo := initTestGitRepo(t)
107+
source := commitTestStackSource(t, repo, "1.2.3", map[string]string{
108+
"deploy/stacks/self-managed/helmfile.d/state.yaml.gotmpl": "releases: []\n",
109+
})
110+
fullRaw := mustJSON(t, []helmfileRelease{{
111+
Name: "example",
112+
Chart: "nvcf/helm-example",
113+
Version: "1.0.0",
114+
}})
115+
sourceCharts := map[string]resolvedInventorySourceChart{
116+
"helm-example": {
117+
TagPrefix: "deploy/helm/example/v",
118+
Path: "deploy/helm/example",
119+
},
120+
}
121+
122+
used, err := materializeResolvedInventorySourceCharts(
123+
repo,
124+
filepath.Join(repo, "deploy/stacks/self-managed/helmfile.d/state.yaml.gotmpl"),
125+
t.TempDir(),
126+
source,
127+
fullRaw,
128+
sourceCharts,
129+
true,
130+
)
131+
if err != nil {
132+
t.Fatalf("historical source-chart fallback failed: %v", err)
133+
}
134+
if len(used) != 0 {
135+
t.Fatalf("used source charts = %v, want published chart fallback", used)
136+
}
137+
138+
_, err = materializeResolvedInventorySourceCharts(
139+
repo,
140+
filepath.Join(repo, "deploy/stacks/self-managed/helmfile.d/state.yaml.gotmpl"),
141+
t.TempDir(),
142+
source,
143+
fullRaw,
144+
sourceCharts,
145+
false,
146+
)
147+
if err == nil || !strings.Contains(err.Error(), "is unavailable") {
148+
t.Fatalf("strict source-chart error = %v, want unavailable path", err)
149+
}
150+
}
151+
105152
func TestCollectResolvedStackInventoryBuildsConfiguredSelfManagedStates(t *testing.T) {
106153
t.Setenv("NVCF_RELEASE_NGC_API_KEY", "test-api-key")
107154
t.Setenv("NVCF_RELEASE_HELM_REGISTRY", "registry.example.test/release/charts")

0 commit comments

Comments
 (0)