@oddessentials/repo-standards 4.4.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if [[ "${VERSION_GUARD_ALLOW:-}" == "1" ]]; then
5
+ echo "Version guard: bypass enabled (VERSION_GUARD_ALLOW=1)."
6
+ exit 0
7
+ fi
8
+
9
+ base_ref="${1:-}"
10
+ if [[ -z "$base_ref" ]]; then
11
+ echo "Usage: $0 <base-ref>"
12
+ echo "Example: $0 origin/main"
13
+ exit 2
14
+ fi
15
+
16
+ version_pattern='("version"\s*:|\bversion\s*=|<Version>|<VersionPrefix>|<VersionSuffix>|<PackageVersion>|<AssemblyVersion>|<FileVersion>)'
17
+
18
+ changed_files=$(git diff --name-only "${base_ref}...HEAD")
19
+ if [[ -z "$changed_files" ]]; then
20
+ echo "Version guard: no changes detected."
21
+ exit 0
22
+ fi
23
+
24
+ has_violation=0
25
+
26
+ while IFS= read -r file; do
27
+ case "$file" in
28
+ package.json|pyproject.toml|setup.cfg|setup.py|Cargo.toml|VERSION|Directory.Build.props|*.csproj)
29
+ diff_output=$(git diff -U0 "${base_ref}...HEAD" -- "$file" || true)
30
+ if echo "$diff_output" | rg -n --pcre2 "^[+-](?![+-]).*${version_pattern}" > /dev/null; then
31
+ echo "Version guard: manual version change detected in $file"
32
+ has_violation=1
33
+ fi
34
+ ;;
35
+ *)
36
+ ;;
37
+ esac
38
+ done <<< "$changed_files"
39
+
40
+ if [[ "$has_violation" -eq 1 ]]; then
41
+ cat <<'MESSAGE'
42
+
43
+ Manual version edits detected. If semantic-release or automated versioning owns
44
+ version bumps, remove manual edits and let the release pipeline update versions.
45
+ If this change is intentional (e.g., release automation), set VERSION_GUARD_ALLOW=1.
46
+ MESSAGE
47
+ exit 1
48
+ fi
49
+
50
+ echo "Version guard: no manual version changes detected."
@@ -18,11 +18,21 @@ interface ValidationResult {
18
18
 
19
19
  interface ChecklistItem {
20
20
  id: string;
21
+ executionStage?: string;
21
22
  appliesTo?: { stacks?: string[]; ciSystems?: string[] };
22
23
  ciHints?: Record<string, unknown>;
23
24
  stackHints?: Record<string, unknown>;
24
25
  }
25
26
 
27
+ const VALID_EXECUTION_STAGES = new Set([
28
+ "pre-commit",
29
+ "pre-push",
30
+ "ci-pr",
31
+ "ci-main",
32
+ "release",
33
+ "nightly",
34
+ ]);
35
+
26
36
  interface MigrationStep {
27
37
  focusIds?: string[];
28
38
  }
@@ -196,6 +206,40 @@ function validateCoverageThreshold(config: Config): ValidationResult {
196
206
  return { valid: true, errors: [] };
197
207
  }
198
208
 
209
+ /**
210
+ * Validate executionStage coverage - all items should have a valid executionStage
211
+ */
212
+ function validateExecutionStageCoverage(config: Config): ValidationResult {
213
+ const allItems = [
214
+ ...config.checklist.core,
215
+ ...config.checklist.recommended,
216
+ ...config.checklist.optionalEnhancements,
217
+ ];
218
+
219
+ const errors: string[] = [];
220
+ const stageCounts: Record<string, number> = {};
221
+
222
+ for (const item of allItems) {
223
+ if (!item.executionStage) {
224
+ errors.push(`Item "${item.id}" is missing executionStage`);
225
+ } else if (!VALID_EXECUTION_STAGES.has(item.executionStage)) {
226
+ errors.push(
227
+ `Item "${item.id}" has invalid executionStage "${item.executionStage}"`,
228
+ );
229
+ } else {
230
+ stageCounts[item.executionStage] =
231
+ (stageCounts[item.executionStage] ?? 0) + 1;
232
+ }
233
+ }
234
+
235
+ // Log stage distribution (informational, not an error)
236
+ if (errors.length === 0 && Object.keys(stageCounts).length > 0) {
237
+ // This is just for debugging, not shown in normal output
238
+ }
239
+
240
+ return { valid: errors.length === 0, errors };
241
+ }
242
+
199
243
  /**
200
244
  * Generate a normalized, deterministic string representation of the config.
201
245
  * Uses deep stable key ordering at all depths.
@@ -236,6 +280,7 @@ export function validateStandardsConfig(
236
280
  validateStackReferences(config),
237
281
  validateCiHintKeys(config),
238
282
  validateCoverageThreshold(config),
283
+ validateExecutionStageCoverage(config),
239
284
  ];
240
285
 
241
286
  const allErrors = results.flatMap((r) => r.errors);
@@ -278,6 +323,7 @@ export function validateStandardsSchema(): void {
278
323
  console.log("✓ All appliesTo.stacks reference valid stack keys");
279
324
  console.log("✓ All ciHints keys are valid ciSystems");
280
325
  console.log("✓ Coverage threshold semantics are valid");
326
+ console.log("✓ All items have valid executionStage");
281
327
  }
282
328
 
283
329
  // CLI entry point