Skip to content
File

Blob: tools/update_node_version.py

python133 lines
1#!/usr/bin/env python3
2"""
3Script to update Node.js version in node-version.h based on GitHub releases.
4
5This script:
61. Fetches Node.js version tags from GitHub
72. Finds the maximum even major version
83. Looks for the latest version in the (max-2) series
94. Updates the node-version.h file if needed
10"""
11 
12import json
13import re
14import sys
15import urllib.request
16from pathlib import Path
17 
18 
19def fetch_nodejs_tags():
20 """Fetch Node.js version tags from GitHub API."""
21 url = "https://api.github.com/repos/nodejs/node/tags"
22 headers = {
23 "User-Agent": "workerd-node-version-updater",
24 "Accept": "application/vnd.github.v3+json",
25 }
26 
27 all_tags = []
28 page = 1
29 
30 while True:
31 req = urllib.request.Request(f"{url}?page={page}&per_page=100", headers=headers)
32 try:
33 with urllib.request.urlopen(req) as response:
34 data = json.loads(response.read().decode())
35 if not data:
36 break
37 all_tags.extend(data)
38 page += 1
39 except Exception as e:
40 print(f"Error fetching tags: {e}", file=sys.stderr)
41 sys.exit(1)
42 
43 return all_tags
44 
45 
46def parse_version(version_str):
47 """Parse a version string like 'v22.17.0' into (major, minor, patch)."""
48 match = re.match(r"^v(\d+)\.(\d+)\.(\d+)$", version_str)
49 if match:
50 return tuple(map(int, match.groups()))
51 return None
52 
53 
54def find_target_version(tags):
55 """Find the target Node.js version based on the algorithm."""
56 # Extract valid versions
57 versions = []
58 for tag in tags:
59 version = parse_version(tag["name"])
60 if version:
61 versions.append((version, tag["name"]))
62 
63 if not versions:
64 print("No valid versions found", file=sys.stderr)
65 sys.exit(1)
66 
67 # Sort versions
68 versions.sort(reverse=True)
69 
70 # Find maximum even major version
71 max_even_major = None
72 for (major, _minor, _patch), _ in versions:
73 if major % 2 == 0:
74 max_even_major = major
75 break
76 
77 if max_even_major is None:
78 print("No even major version found", file=sys.stderr)
79 sys.exit(1)
80 
81 # Calculate target major version (max_even - 2)
82 target_major = max_even_major - 2
83 
84 # Find latest version in target major series
85 for (major, _minor, _patch), tag_name in versions:
86 if major == target_major:
87 return tag_name[1:] # Remove 'v' prefix
88 
89 print(f"No version found for major version {target_major}", file=sys.stderr)
90 sys.exit(1)
91 
92 
93def update_header_file(file_path, new_version):
94 """Update the node-version.h file with the new version."""
95 path = Path(file_path)
96 content = path.read_text()
97 
98 # Replace the version string
99 # Match: static constexpr kj::StringPtr nodeVersion = "X.Y.Z"_kj;
100 pattern = r'static constexpr kj::StringPtr nodeVersion = "[^"]+"_kj;'
101 replacement = f'static constexpr kj::StringPtr nodeVersion = "{new_version}"_kj;'
102 new_content = re.sub(pattern, replacement, content)
103 
104 if new_content != content:
105 path.write_text(new_content)
106 return True
107 return False
108 
109 
110def main():
111 if len(sys.argv) != 2:
112 print("Usage: update_node_version.py <output_file>", file=sys.stderr)
113 sys.exit(1)
114 
115 output_file = sys.argv[1]
116 
117 print("Fetching Node.js versions from GitHub...")
118 tags = fetch_nodejs_tags()
119 
120 print("Finding target version...")
121 target_version = find_target_version(tags)
122 print(f"Target version: {target_version}")
123 
124 # Update the header file
125 if update_header_file(output_file, target_version):
126 print(f"Updated {output_file} with version {target_version}")
127 else:
128 print(f"No changes needed - already at version {target_version}")
129 
130 
131if __name__ == "__main__":
132 main()