Skip to content

Commit a281141

Browse files
fix(e2e): use fixtures.stackit.project_id in storage-bucket test; exclude .terraform from validate-modules scan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 24519af commit a281141

5 files changed

Lines changed: 154 additions & 4 deletions

File tree

ci/validate_modules.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,16 @@ fi
115115

116116
modules_glob="$modules_path/*/*/buildingblock"
117117

118-
for readme_file in $(find $modules_glob -name 'README.md'); do
118+
for readme_file in $(find $modules_glob -name 'README.md' -not -path '*/.terraform/*'); do
119119
check_readme_format "$readme_file"
120120
done
121121

122-
for png_file in $(find $modules_glob -name '*.png'); do
122+
for png_file in $(find $modules_glob -name '*.png' -not -path '*/.terraform/*'); do
123123
check_png_naming "$png_file"
124124
check_png_minimization "$png_file"
125125
done
126126

127-
for buildingblock_dir in $(find $modules_glob -type d -name 'buildingblock'); do
127+
for buildingblock_dir in $(find $modules_glob -type d -name 'buildingblock' -not -path '*/.terraform/*'); do
128128
check_terraform_files "$buildingblock_dir"
129129
done
130130

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
variable "test_context" {
2+
type = object({
3+
hub_git_ref = string
4+
workspace = string
5+
project = string
6+
name_suffix = string
7+
8+
fixtures = object({
9+
stackit = object({
10+
project_id = string
11+
mesh_tenant_id = string
12+
})
13+
})
14+
})
15+
nullable = false
16+
}
17+
18+
module "stackit_storage_bucket" {
19+
source = "../"
20+
meshstack = {
21+
owning_workspace_identifier = var.test_context.workspace
22+
tags = {}
23+
}
24+
hub = {
25+
git_ref = var.test_context.hub_git_ref
26+
bbd_draft = true
27+
}
28+
29+
stackit_project_id = var.test_context.fixtures.stackit.project_id
30+
}
31+
32+
resource "meshstack_building_block_v2" "this" {
33+
wait_for_completion = true
34+
spec = {
35+
building_block_definition_version_ref = module.stackit_storage_bucket.building_block_definition.version_ref
36+
37+
display_name = "smoke-test-stackit-storage-bucket-${var.test_context.name_suffix}"
38+
target_ref = {
39+
kind = "meshWorkspace"
40+
name = var.test_context.workspace
41+
}
42+
43+
inputs = {
44+
bucket_name = { value_string = "smoke-test-bucket-${var.test_context.name_suffix}" }
45+
}
46+
}
47+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
meshstack = {
6+
source = "meshcloud/meshstack"
7+
}
8+
}
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
run "building_block_stackit_storage_bucket_hub" {
2+
assert {
3+
condition = meshstack_building_block_v2.this.status.status == "SUCCEEDED"
4+
error_message = "stackit storage-bucket hub building block expected SUCCEEDED, got ${meshstack_building_block_v2.this.status.status}"
5+
}
6+
7+
assert {
8+
condition = meshstack_building_block_v2.this.status.outputs["bucket_name"].value_string == "smoke-test-bucket-${var.test_context.name_suffix}"
9+
error_message = "stackit storage-bucket hub building block expected bucket_name to be 'smoke-test-bucket-${var.test_context.name_suffix}', got ${meshstack_building_block_v2.this.status.outputs["bucket_name"].value_string}"
10+
}
11+
12+
assert {
13+
condition = strcontains(meshstack_building_block_v2.this.status.outputs["bucket_url_path_style"].value_string, "smoke-test-bucket-${var.test_context.name_suffix}")
14+
error_message = "stackit storage-bucket hub building block expected bucket_url_path_style to contain bucket name, got ${meshstack_building_block_v2.this.status.outputs["bucket_url_path_style"].value_string}"
15+
}
16+
17+
assert {
18+
condition = strcontains(meshstack_building_block_v2.this.status.outputs["bucket_url_path_style"].value_string, "object.storage.eu01.onstackit.cloud")
19+
error_message = "stackit storage-bucket hub building block expected bucket_url_path_style to contain STACKIT domain, got ${meshstack_building_block_v2.this.status.outputs["bucket_url_path_style"].value_string}"
20+
}
21+
22+
assert {
23+
condition = length(meshstack_building_block_v2.this.status.outputs["s3_access_key"].value_string) > 0
24+
error_message = "stackit storage-bucket hub building block expected non-empty s3_access_key"
25+
}
26+
}

tools/scorecard/scorecard.mjs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ const detectors = [
494494
category: "testing",
495495
name: "e2e/ contains .tftest.hcl files",
496496
emoji: "✅",
497-
fixRef: E2E("e2etests-tftesthcl-conventions"),
497+
fixRef: E2E("e2eteststftesthcl-conventions"),
498498
fn: (mod) => {
499499
const e2eDir = join(mod.path, "e2e", "tests");
500500
if (!existsSync(e2eDir)) return { pass: false };
@@ -577,6 +577,67 @@ function discoverModules() {
577577
return modules.sort((a, b) => a.id.localeCompare(b.id));
578578
}
579579

580+
// ─── fixRef validator ────────────────────────────────────────────────────────
581+
582+
function headingToAnchor(heading) {
583+
return heading
584+
.replace(/^#+\s*/, "")
585+
.replace(/`([^`]*)`/g, "$1")
586+
.toLowerCase()
587+
.replace(/[^\w\s-]/g, "")
588+
.replace(/\s+/g, "-");
589+
}
590+
591+
function validateFixRefs() {
592+
const filesToParse = [...new Set(detectors.filter((d) => d.fixRef).map((d) => d.fixRef.file))];
593+
const markerMap = new Map(); // "file#section" → Set<checkId>
594+
const allMarkerCheckIds = new Set();
595+
596+
for (const relFile of filesToParse) {
597+
const filePath = join(ROOT, relFile);
598+
if (!existsSync(filePath)) continue;
599+
600+
const lines = readFileSync(filePath, "utf-8").split("\n");
601+
let pendingIds = null;
602+
603+
for (const line of lines) {
604+
const m = line.match(/<!--\s*scorecard-checks:\s*([^-]+?)\s*-->/);
605+
if (m) {
606+
pendingIds = m[1].split(",").map((s) => s.trim()).filter(Boolean);
607+
continue;
608+
}
609+
if (pendingIds && /^#{1,6}\s/.test(line)) {
610+
const section = headingToAnchor(line);
611+
markerMap.set(`${relFile}#${section}`, new Set(pendingIds));
612+
for (const id of pendingIds) allMarkerCheckIds.add(id);
613+
pendingIds = null;
614+
}
615+
}
616+
}
617+
618+
const errors = [];
619+
const detectorIds = new Set(detectors.map((d) => d.id));
620+
621+
for (const d of detectors) {
622+
if (!d.fixRef) continue;
623+
const key = `${d.fixRef.file}#${d.fixRef.section}`;
624+
const checksInSection = markerMap.get(key);
625+
if (!checksInSection) {
626+
errors.push(`fixRef "${key}" not found — no <!-- scorecard-checks: ... --> marker before that heading`);
627+
} else if (!checksInSection.has(d.id)) {
628+
errors.push(`check "${d.id}" missing from marker at "${key}" (has: ${[...checksInSection].join(", ")})`);
629+
}
630+
}
631+
632+
for (const id of allMarkerCheckIds) {
633+
if (!detectorIds.has(id)) {
634+
errors.push(`marker references unknown check "${id}" — no detector with that id`);
635+
}
636+
}
637+
638+
return errors;
639+
}
640+
580641
// ─── Fix prompt generator ────────────────────────────────────────────────────
581642

582643
function generateFixPrompt(mod, failingChecks) {
@@ -620,6 +681,13 @@ function main() {
620681
const filterModules = args.filter((a) => a.startsWith("--module=")).map((a) => a.split("=")[1]);
621682
const fixMode = args.includes("--fix");
622683

684+
const fixRefErrors = validateFixRefs();
685+
if (fixRefErrors.length > 0) {
686+
process.stderr.write("❌ fixRef/marker validation failed:\n");
687+
for (const e of fixRefErrors) process.stderr.write(` • ${e}\n`);
688+
process.exit(1);
689+
}
690+
623691
let modules = discoverModules();
624692
if (filterProvider) {
625693
modules = modules.filter((m) => m.provider === filterProvider);

0 commit comments

Comments
 (0)