22
33import base64
44import hashlib
5+ import itertools
56import json
67import re
78import shlex
@@ -65,33 +66,67 @@ def fetch(url: str) -> bytes:
6566 return response .read ()
6667
6768
68- def get_rust_analyzer_release_date (rust_analyzer_version : str ) -> str :
69- """Get the release date of a rust-analyzer crate version."""
69+ def get_rust_analyzer_crate_release_date (rust_analyzer_version : str ) -> str :
70+ """Get the creation date of a rust-analyzer crate version."""
7071 crate_url = f"https://crates.io/api/v1/crates/ra_ap_syntax/{ rust_analyzer_version } "
7172 crate = json .loads (fetch (crate_url ))
7273 return crate ["version" ]["created_at" ].split ("T" )[0 ]
7374
7475
75- def update_rust_analyzer_sources (rust_analyzer_version : str ) -> None :
76+ def get_rust_analyzer_release_tag (rust_analyzer_version : str ) -> str :
7677 """
77- Update the rust-analyzer source archive used by the AST generator.
78+ Get the latest rust-analyzer release tag in the git repository before or at
79+ the crate publication.
7880
79- Concretely, this updates `RUST_ANALYZER_SRC_TAG` and
80- `RUST_ANALYZER_SRC_INTEGRITY` in the MODULE.bazel file.
81+ Empirically, the rust-analyzer crates are published after the corresponding
82+ git tag. We hence look for the latest git tag that is before or at the crate
83+ publication date.
8184 """
85+ crate_release_date = get_rust_analyzer_crate_release_date (rust_analyzer_version )
86+
87+ for page in itertools .count (1 ):
88+ tags_url = (
89+ "https://api.github.com/repos/rust-lang/rust-analyzer/tags"
90+ f"?per_page=100&page={ page } "
91+ )
92+ tags = json .loads (fetch (tags_url ))
93+ if not tags :
94+ raise RuntimeError (
95+ "could not find a rust-analyzer release tag published no later than "
96+ f"ra_ap_syntax { rust_analyzer_version } "
97+ )
98+
99+ for tag in tags :
100+ name = tag ["name" ]
101+ match = re .fullmatch (r"(\d{4}-\d{2}-\d{2})(?:\.(\d+))?" , name )
102+ if match and match [1 ] <= crate_release_date :
103+ return name
104+
82105
83- release_date = get_rust_analyzer_release_date (rust_analyzer_version )
106+ def get_rust_analyzer_source_integrity (release_tag : str ) -> str :
107+ """Download and compute the integrity of a rust-analyzer source archive."""
84108 archive_url = (
85109 "https://github.com/rust-lang/rust-analyzer/archive/refs/tags/"
86- f"{ release_date } .tar.gz"
110+ f"{ release_tag } .tar.gz"
87111 )
88112 archive = fetch (archive_url )
89- integrity = "sha256-" + base64 .b64encode (hashlib .sha256 (archive ).digest ()).decode ()
113+ return "sha256-" + base64 .b64encode (hashlib .sha256 (archive ).digest ()).decode ()
114+
115+
116+ def update_rust_analyzer_sources (
117+ rust_analyzer_release_tag : str , integrity : str
118+ ) -> None :
119+ """
120+ Update the rust-analyzer source archive used by the AST generator.
121+
122+ Concretely, this updates `RUST_ANALYZER_SRC_TAG` and
123+ `RUST_ANALYZER_SRC_INTEGRITY` in the MODULE.bazel file.
124+ """
90125
91126 module = MODULE_BAZEL .read_text ()
92127 module , tag_replacements = re .subn (
93128 r'RUST_ANALYZER_SRC_TAG = "[^"]+"' ,
94- f'RUST_ANALYZER_SRC_TAG = "{ release_date } "' ,
129+ f'RUST_ANALYZER_SRC_TAG = "{ rust_analyzer_release_tag } "' ,
95130 module ,
96131 )
97132 if tag_replacements != 1 :
@@ -102,13 +137,15 @@ def update_rust_analyzer_sources(rust_analyzer_version: str) -> None:
102137 module ,
103138 )
104139 if integrity_replacements != 1 :
105- raise RuntimeError ("expected exactly one RUST_ANALYZER_SRC_INTEGRITY assignment" )
140+ raise RuntimeError (
141+ "expected exactly one RUST_ANALYZER_SRC_INTEGRITY assignment"
142+ )
106143 MODULE_BAZEL .write_text (module )
107144
108145
109- def get_compatible_rust_toolchain (rust_analyzer_version : str ) -> str :
146+ def get_compatible_rust_toolchain (rust_analyzer_release_tag : str ) -> str :
110147 """Get the latest Rust toolchain released no later than rust-analyzer."""
111- rust_analyzer_release = get_rust_analyzer_release_date ( rust_analyzer_version )
148+ rust_analyzer_release = rust_analyzer_release_tag [: 10 ]
112149
113150 # `manifests.txt` is a list of all toolchains. The one we're interested in looks like
114151 # ```
@@ -204,18 +241,25 @@ def main() -> None:
204241 print ("No new rust-analyzer version available." )
205242 return
206243
244+ rust_analyzer_release_tag = get_rust_analyzer_release_tag (new_rust_analyzer_version )
245+ rust_analyzer_source_integrity = get_rust_analyzer_source_integrity (
246+ rust_analyzer_release_tag
247+ )
248+
207249 aligned_manifest , manifest_changed = align_rust_analyzer_versions (manifest )
208250 if manifest_changed :
209251 RUST_EXTRACTOR_MANIFEST .write_text (aligned_manifest )
210252 run ("cargo" , "update" )
211253 commit_all ("Cargo: Upgrade dependencies" )
212254
213255 print_step (2 , "Update the rust-analyzer sources used by the AST generator" )
214- update_rust_analyzer_sources (new_rust_analyzer_version )
256+ update_rust_analyzer_sources (
257+ rust_analyzer_release_tag , rust_analyzer_source_integrity
258+ )
215259 commit_all ("Rust: Update rust-analyzer sources" )
216260
217261 print_step (3 , "Update the fixed Rust toolchain used by the extractor" )
218- rust_toolchain = get_compatible_rust_toolchain (new_rust_analyzer_version )
262+ rust_toolchain = get_compatible_rust_toolchain (rust_analyzer_release_tag )
219263 update_fixed_rust_toolchain_versions (rust_toolchain )
220264 commit_all ("Rust: Update fixed toolchain" )
221265
0 commit comments