Skip to content

Commit 1b8e5b5

Browse files
committed
Update scripts for just vc, vu
Signed-off-by: hahwul <[email protected]>
1 parent fd5fdc8 commit 1b8e5b5

3 files changed

Lines changed: 341 additions & 333 deletions

File tree

Lines changed: 31 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env crystal
22
# noir/scripts/check_version_consistency.cr
3-
# Check version consistency across multiple files using shard.yml as source of truth
3+
# Check version consistency across multiple files using shard.yml as source of truth.
44
#
55
# Usage:
66
# crystal run scripts/check_version_consistency.cr
@@ -9,189 +9,43 @@
99
# Exit codes:
1010
# 0 - All versions match
1111
# 1 - Version mismatches detected
12-
# 2 - Error reading files or invalid format
1312

14-
require "yaml"
13+
require "./version_common"
1514

16-
class VersionChecker
17-
getter shard_version : String
18-
getter? quiet : Bool
15+
entries = collect_versions
1916

20-
struct CheckResult
21-
getter file_path : String
22-
getter pattern : String
23-
getter expected : String
24-
getter actual : String?
25-
getter? matches : Bool
17+
label_width = entries.map { |label, _, _| label.size }.max
2618

27-
def initialize(@file_path : String, @pattern : String, @expected : String, @actual : String?, @matches : Bool)
28-
end
29-
end
30-
31-
def initialize(@quiet : Bool = false)
32-
@shard_version = read_shard_version
33-
end
34-
35-
def run : Int32
36-
puts "Checking version consistency across files..." unless quiet?
37-
puts "Source of truth: shard.yml version = #{@shard_version}" unless quiet?
38-
puts unless quiet?
39-
40-
results = [] of CheckResult
41-
42-
# Check each file
43-
results << check_flake_nix
44-
results << check_dockerfile
45-
results << check_noir_cr
46-
results << check_sarif_cr
47-
results << check_snapcraft_yaml
48-
results << check_docs_index_md
49-
results << check_docs_index_ko_md
50-
results << check_github_action_dockerfile
51-
results << check_github_action_readme
52-
results << check_sarif_spec
53-
results << check_copilot_instructions
54-
results << check_how_to_release_md
55-
results << check_how_to_release_ko_md
56-
57-
# Print results
58-
mismatches = [] of CheckResult
59-
results.each do |result|
60-
if result.matches?
61-
puts "#{result.file_path}" unless quiet?
62-
else
63-
puts "#{result.file_path}" unless quiet?
64-
puts " Expected: #{result.expected}" unless quiet?
65-
puts " Found: #{result.actual || "NOT FOUND"}" unless quiet?
66-
mismatches << result
67-
end
68-
end
69-
70-
puts unless quiet?
71-
if mismatches.empty?
72-
puts "🎉 All versions match! (#{@shard_version})" unless quiet?
73-
0
74-
else
75-
puts "❌ Version mismatch detected in #{mismatches.size} file(s):" unless quiet?
76-
mismatches.each do |r|
77-
puts " - #{r.file_path}" unless quiet?
78-
end
79-
1
80-
end
81-
end
19+
puts "Current versions:"
20+
entries.each do |label, _, version|
21+
puts " #{label.ljust(label_width)} #{version || "Not found"}"
22+
end
23+
puts
8224

83-
private def read_shard_version : String
84-
shard_yml = YAML.parse(File.read("shard.yml"))
85-
shard_yml["version"].as_s
86-
rescue ex
87-
STDERR.puts "Error reading version from shard.yml: #{ex.message}"
88-
exit 2
89-
end
25+
versions = entries.map { |_, _, v| v }.compact
26+
if versions.empty?
27+
puts "No versions found!"
28+
exit 1
29+
end
9030

91-
private def check_file(file_path : String, pattern : Regex, expected_version : String) : CheckResult
92-
if File.exists?(file_path)
93-
content = File.read(file_path)
94-
if match = content.match(pattern)
95-
actual = match[1]
96-
CheckResult.new(file_path, pattern.source, expected_version, actual, actual == expected_version)
97-
else
98-
CheckResult.new(file_path, pattern.source, expected_version, nil, false)
31+
unique = versions.uniq
32+
if unique.size == 1
33+
puts "✅ All versions match: #{unique.first}"
34+
exit 0
35+
else
36+
puts "❌ Versions do not match!"
37+
puts " Unique versions found: #{unique.join(", ")}"
38+
puts
39+
shard_v = get_shard_version
40+
if shard_v
41+
puts " Source of truth (shard.yml): #{shard_v}"
42+
mismatches = entries.reject { |_, _, v| v.nil? || v == shard_v }
43+
unless mismatches.empty?
44+
puts " Mismatched files:"
45+
mismatches.each do |label, _, v|
46+
puts " - #{label} (#{v})"
9947
end
100-
else
101-
CheckResult.new(file_path, pattern.source, expected_version, nil, false)
10248
end
103-
rescue ex
104-
STDERR.puts "Error checking #{file_path}: #{ex.message}"
105-
CheckResult.new(file_path, pattern.source, expected_version, nil, false)
106-
end
107-
108-
private def check_flake_nix : CheckResult
109-
check_file("flake.nix", /version\s*=\s*"([^"]+)"/, @shard_version)
110-
end
111-
112-
private def check_dockerfile : CheckResult
113-
check_file("Dockerfile", /org\.opencontainers\.image\.version="([^"]+)"/, @shard_version)
114-
end
115-
116-
private def check_noir_cr : CheckResult
117-
check_file("src/noir.cr", /VERSION\s*=\s*"([^"]+)"/, @shard_version)
118-
end
119-
120-
private def check_sarif_cr : CheckResult
121-
# SARIF output now uses Noir::VERSION constant, no hardcoded version to check.
122-
# Version consistency is ensured by check_noir_cr via src/noir/version.cr.
123-
CheckResult.new("src/output_builder/sarif.cr", "Noir::VERSION (indirect)", @shard_version, @shard_version, true)
12449
end
125-
126-
private def check_snapcraft_yaml : CheckResult
127-
check_file("snap/snapcraft.yaml", /^version:\s*([\d.]+)\s*$/m, @shard_version)
128-
end
129-
130-
private def check_docs_index_file(file_path : String) : CheckResult
131-
# Check hero-badge version in documentation index files
132-
check_file(file_path, /class="hero-badge">v([\d.]+)</, @shard_version)
133-
end
134-
135-
private def check_docs_index_md : CheckResult
136-
check_docs_index_file("docs/content/_index.md")
137-
end
138-
139-
private def check_docs_index_ko_md : CheckResult
140-
check_docs_index_file("docs/content/_index.ko.md")
141-
end
142-
143-
private def check_github_action_dockerfile : CheckResult
144-
check_file("github-action/Dockerfile", /FROM\s+ghcr\.io\/owasp-noir\/noir:v([^\s]+)/, @shard_version)
145-
end
146-
147-
private def check_github_action_readme : CheckResult
148-
check_file("github-action/README.md", /uses:\s+owasp-noir\/noir@v([\d.]+)/, @shard_version)
149-
end
150-
151-
private def check_sarif_spec : CheckResult
152-
# SARIF spec now uses Noir::VERSION constant, no hardcoded version to check.
153-
# Version consistency is ensured by check_noir_cr via src/noir/version.cr.
154-
CheckResult.new("spec/unit_test/output_builder/sarif_spec.cr", "Noir::VERSION (indirect)", @shard_version, @shard_version, true)
155-
end
156-
157-
private def check_copilot_instructions : CheckResult
158-
# AGENTS.md no longer contains a hardcoded version string.
159-
# Version consistency is maintained through shard.yml only.
160-
CheckResult.new("AGENTS.md", "N/A (no version)", @shard_version, @shard_version, true)
161-
end
162-
163-
private def check_how_to_release_md : CheckResult
164-
# Check for example version in brew command
165-
check_file("docs/content/development/how_to_release/index.md", /brew bump-formula-pr --strict --version\s+([\d.]+)\s+noir/, @shard_version)
166-
end
167-
168-
private def check_how_to_release_ko_md : CheckResult
169-
# Check for example version in brew command
170-
check_file("docs/content/development/how_to_release/index.ko.md", /brew bump-formula-pr --strict --version\s+([\d.]+)\s+noir/, @shard_version)
171-
end
172-
end
173-
174-
# Entry point
175-
quiet = ARGV.includes?("-q") || ARGV.includes?("--quiet")
176-
show_help = ARGV.includes?("-h") || ARGV.includes?("--help")
177-
178-
if show_help
179-
puts "Usage: crystal run scripts/check_version_consistency.cr [options]"
180-
puts ""
181-
puts "Options:"
182-
puts " -q, --quiet Suppress detailed output"
183-
puts " -h, --help Show this help message"
184-
puts ""
185-
puts "Description:"
186-
puts " Checks version consistency across all files using shard.yml as source of truth."
187-
puts ""
188-
puts "Exit codes:"
189-
puts " 0 - All versions match"
190-
puts " 1 - Version mismatches detected"
191-
puts " 2 - Error reading files"
192-
exit 0
50+
exit 1
19351
end
194-
195-
checker = VersionChecker.new(quiet)
196-
exit_code = checker.run
197-
exit(exit_code)

scripts/version_common.cr

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
require "yaml"
2+
3+
# Version file locations (shard.yml is the source of truth)
4+
SHARD_FILE = "shard.yml"
5+
NOIR_FILE = "src/noir.cr"
6+
FLAKE_FILE = "flake.nix"
7+
DOCKERFILE = "Dockerfile"
8+
SNAPCRAFT_FILE = "snap/snapcraft.yaml"
9+
DOCS_INDEX = "docs/content/_index.md"
10+
DOCS_INDEX_KO = "docs/content/_index.ko.md"
11+
ACTION_DOCKER = "github-action/Dockerfile"
12+
ACTION_README = "github-action/README.md"
13+
RELEASE_DOC = "docs/content/development/how_to_release/index.md"
14+
RELEASE_DOC_KO = "docs/content/development/how_to_release/index.ko.md"
15+
PKGBUILD_FILE = "aur/PKGBUILD"
16+
17+
# Extract version from shard.yml
18+
def get_shard_version : String?
19+
YAML.parse(File.read(SHARD_FILE))["version"].as_s
20+
rescue
21+
nil
22+
end
23+
24+
# Extract VERSION from src/noir.cr
25+
def get_noir_version : String?
26+
match = File.read(NOIR_FILE).match(/VERSION\s*=\s*"([^"]+)"/)
27+
match ? match[1] : nil
28+
rescue
29+
nil
30+
end
31+
32+
# Extract version from flake.nix
33+
def get_flake_version : String?
34+
match = File.read(FLAKE_FILE).match(/version\s*=\s*"([^"]+)"/)
35+
match ? match[1] : nil
36+
rescue
37+
nil
38+
end
39+
40+
# Extract org.opencontainers.image.version from Dockerfile
41+
def get_dockerfile_version : String?
42+
match = File.read(DOCKERFILE).match(/org\.opencontainers\.image\.version="([^"]+)"/)
43+
match ? match[1] : nil
44+
rescue
45+
nil
46+
end
47+
48+
# Extract version from snap/snapcraft.yaml
49+
def get_snapcraft_version : String?
50+
YAML.parse(File.read(SNAPCRAFT_FILE))["version"].to_s
51+
rescue
52+
nil
53+
end
54+
55+
# Extract hero-badge version from a docs index file
56+
def get_docs_index_version(path : String) : String?
57+
match = File.read(path).match(/class="hero-badge">v([\d.]+)</)
58+
match ? match[1] : nil
59+
rescue
60+
nil
61+
end
62+
63+
# Extract version from github-action/Dockerfile
64+
def get_action_dockerfile_version : String?
65+
match = File.read(ACTION_DOCKER).match(/FROM\s+ghcr\.io\/owasp-noir\/noir:v([\d.]+)/)
66+
match ? match[1] : nil
67+
rescue
68+
nil
69+
end
70+
71+
# Extract version from github-action/README.md
72+
def get_action_readme_version : String?
73+
match = File.read(ACTION_README).match(/uses:\s+owasp-noir\/noir@v([\d.]+)/)
74+
match ? match[1] : nil
75+
rescue
76+
nil
77+
end
78+
79+
# Extract example version from a how_to_release doc (brew bump-formula-pr line)
80+
def get_release_doc_version(path : String) : String?
81+
match = File.read(path).match(/brew bump-formula-pr --strict --version\s+([\d.]+)\s+noir/)
82+
match ? match[1] : nil
83+
rescue
84+
nil
85+
end
86+
87+
# Extract pkgver from aur/PKGBUILD
88+
def get_pkgbuild_version : String?
89+
match = File.read(PKGBUILD_FILE).match(/^pkgver=([\d.]+)/m)
90+
match ? match[1] : nil
91+
rescue
92+
nil
93+
end
94+
95+
# Collect (label, path, version) for every tracked file.
96+
def collect_versions : Array(Tuple(String, String, String?))
97+
[
98+
{"shard.yml", SHARD_FILE, get_shard_version},
99+
{"src/noir.cr", NOIR_FILE, get_noir_version},
100+
{"flake.nix", FLAKE_FILE, get_flake_version},
101+
{"Dockerfile", DOCKERFILE, get_dockerfile_version},
102+
{"snap/snapcraft.yaml", SNAPCRAFT_FILE, get_snapcraft_version},
103+
{"docs/_index.md", DOCS_INDEX, get_docs_index_version(DOCS_INDEX)},
104+
{"docs/_index.ko.md", DOCS_INDEX_KO, get_docs_index_version(DOCS_INDEX_KO)},
105+
{"github-action/Dockerfile", ACTION_DOCKER, get_action_dockerfile_version},
106+
{"github-action/README.md", ACTION_README, get_action_readme_version},
107+
{"docs/.../how_to_release/index.md", RELEASE_DOC, get_release_doc_version(RELEASE_DOC)},
108+
{"docs/.../how_to_release/index.ko.md", RELEASE_DOC_KO, get_release_doc_version(RELEASE_DOC_KO)},
109+
{"aur/PKGBUILD", PKGBUILD_FILE, get_pkgbuild_version},
110+
]
111+
end
112+
113+
# Validate semver-like version (X.Y.Z)
114+
def valid_version?(version : String) : Bool
115+
!!(version =~ /^\d+\.\d+\.\d+$/)
116+
end

0 commit comments

Comments
 (0)