diff --git a/.github/workflows/update-rust-analyzer.lock.yml b/.github/workflows/update-rust-analyzer.lock.yml index bc35237d9603..430687e6c7fd 100644 --- a/.github/workflows/update-rust-analyzer.lock.yml +++ b/.github/workflows/update-rust-analyzer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"6e0ebbacd3b09e9100ae46887d7192b1faa136cca70fcf787678a834fdd2521b","body_hash":"990718b1868afabca459556cc701c1d0ef315857b226b054dfe36037ea0c6371","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.80"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"89825e14c90fc1af478d2ac55eb66b5a0df973d37da033c508e957ec5cea2543","body_hash":"990718b1868afabca459556cc701c1d0ef315857b226b054dfe36037ea0c6371","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.80"}} # gh-aw-manifest: {"version":1,"secrets":["GH_AW_CI_TRIGGER_TOKEN","GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"5e508589e03a7757a7e05b26e834292f5445bfb6","version":"v0.88.7"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14","digest":"sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14","digest":"sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.14","digest":"sha256:3a379c5e96e29499c815e9dd2a71334d01c326a9b73991c76544fda9cae35c34","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.14@sha256:3a379c5e96e29499c815e9dd2a71334d01c326a9b73991c76544fda9cae35c34"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14","digest":"sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.18","digest":"sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"mcp_servers":[{"name":"safeoutputs","tools":["create_pull_request","missing_data","missing_tool","noop"]}]} # This file was automatically generated by gh-aw (v0.88.7). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -462,6 +462,8 @@ jobs: with: name: activation path: /tmp/gh-aw + - name: Set up CodeQL + uses: ./.github/actions/fetch-codeql - name: Configure Git run: | git config user.name "github-actions[bot]" diff --git a/.github/workflows/update-rust-analyzer.md b/.github/workflows/update-rust-analyzer.md index 074e9e4b7944..1b00961dd10c 100644 --- a/.github/workflows/update-rust-analyzer.md +++ b/.github/workflows/update-rust-analyzer.md @@ -33,6 +33,9 @@ network: - bazel - python steps: + - name: Set up CodeQL + uses: ./.github/actions/fetch-codeql + - name: Configure Git run: | git config user.name "github-actions[bot]" diff --git a/rust/scripts/update_rust_analyzer.py b/rust/scripts/update_rust_analyzer.py index 56b4ef5beca8..52a0ce328c98 100755 --- a/rust/scripts/update_rust_analyzer.py +++ b/rust/scripts/update_rust_analyzer.py @@ -2,6 +2,7 @@ import base64 import hashlib +import itertools import json import re import shlex @@ -65,33 +66,68 @@ def fetch(url: str) -> bytes: return response.read() -def get_rust_analyzer_release_date(rust_analyzer_version: str) -> str: - """Get the release date of a rust-analyzer crate version.""" +def get_rust_analyzer_crate_release_date(rust_analyzer_version: str) -> str: + """Get the creation date of a rust-analyzer crate version.""" crate_url = f"https://crates.io/api/v1/crates/ra_ap_syntax/{rust_analyzer_version}" crate = json.loads(fetch(crate_url)) return crate["version"]["created_at"].split("T")[0] -def update_rust_analyzer_sources(rust_analyzer_version: str) -> None: +def get_rust_analyzer_release_tag(rust_analyzer_version: str) -> str: """ - Update the rust-analyzer source archive used by the AST generator. + Get the latest rust-analyzer release tag in the git repository before or at + the crate publication. The returned string has the format `YYYY-MM-DD.N` + where the `.N` part is optional. - Concretely, this updates `RUST_ANALYZER_SRC_TAG` and - `RUST_ANALYZER_SRC_INTEGRITY` in the MODULE.bazel file. + Empirically, the rust-analyzer crates are published after the corresponding + git tag. We hence look for the latest git tag that is before or at the crate + publication date. """ + crate_release_date = get_rust_analyzer_crate_release_date(rust_analyzer_version) + + for page in itertools.count(1): + tags_url = ( + "https://api.github.com/repos/rust-lang/rust-analyzer/tags" + f"?per_page=100&page={page}" + ) + tags = json.loads(fetch(tags_url)) + if not tags: + raise RuntimeError( + "could not find a rust-analyzer release tag published no later than " + f"ra_ap_syntax {rust_analyzer_version}" + ) + + for tag in tags: + name = tag["name"] + match = re.fullmatch(r"(\d{4}-\d{2}-\d{2})(?:\.(\d+))?", name) + if match and match[1] <= crate_release_date: + return name + - release_date = get_rust_analyzer_release_date(rust_analyzer_version) +def get_rust_analyzer_source_integrity(release_tag: str) -> str: + """Download and compute the integrity of a rust-analyzer source archive.""" archive_url = ( "https://github.com/rust-lang/rust-analyzer/archive/refs/tags/" - f"{release_date}.tar.gz" + f"{release_tag}.tar.gz" ) archive = fetch(archive_url) - integrity = "sha256-" + base64.b64encode(hashlib.sha256(archive).digest()).decode() + return "sha256-" + base64.b64encode(hashlib.sha256(archive).digest()).decode() + + +def update_rust_analyzer_sources( + rust_analyzer_release_tag: str, integrity: str +) -> None: + """ + Update the rust-analyzer source archive used by the AST generator. + + Concretely, this updates `RUST_ANALYZER_SRC_TAG` and + `RUST_ANALYZER_SRC_INTEGRITY` in the MODULE.bazel file. + """ module = MODULE_BAZEL.read_text() module, tag_replacements = re.subn( r'RUST_ANALYZER_SRC_TAG = "[^"]+"', - f'RUST_ANALYZER_SRC_TAG = "{release_date}"', + f'RUST_ANALYZER_SRC_TAG = "{rust_analyzer_release_tag}"', module, ) if tag_replacements != 1: @@ -102,13 +138,17 @@ def update_rust_analyzer_sources(rust_analyzer_version: str) -> None: module, ) if integrity_replacements != 1: - raise RuntimeError("expected exactly one RUST_ANALYZER_SRC_INTEGRITY assignment") + raise RuntimeError( + "expected exactly one RUST_ANALYZER_SRC_INTEGRITY assignment" + ) MODULE_BAZEL.write_text(module) -def get_compatible_rust_toolchain(rust_analyzer_version: str) -> str: +def get_compatible_rust_toolchain(rust_analyzer_release_tag: str) -> str: """Get the latest Rust toolchain released no later than rust-analyzer.""" - rust_analyzer_release = get_rust_analyzer_release_date(rust_analyzer_version) + # Keep the `YYYY-MM-DD` part of the release tag, stripping off the optional + # incrementing suffix. + rust_analyzer_release = rust_analyzer_release_tag[:10] # `manifests.txt` is a list of all toolchains. The one we're interested in looks like # ``` @@ -204,6 +244,11 @@ def main() -> None: print("No new rust-analyzer version available.") return + rust_analyzer_release_tag = get_rust_analyzer_release_tag(new_rust_analyzer_version) + rust_analyzer_source_integrity = get_rust_analyzer_source_integrity( + rust_analyzer_release_tag + ) + aligned_manifest, manifest_changed = align_rust_analyzer_versions(manifest) if manifest_changed: RUST_EXTRACTOR_MANIFEST.write_text(aligned_manifest) @@ -211,11 +256,13 @@ def main() -> None: commit_all("Cargo: Upgrade dependencies") print_step(2, "Update the rust-analyzer sources used by the AST generator") - update_rust_analyzer_sources(new_rust_analyzer_version) + update_rust_analyzer_sources( + rust_analyzer_release_tag, rust_analyzer_source_integrity + ) commit_all("Rust: Update rust-analyzer sources") print_step(3, "Update the fixed Rust toolchain used by the extractor") - rust_toolchain = get_compatible_rust_toolchain(new_rust_analyzer_version) + rust_toolchain = get_compatible_rust_toolchain(rust_analyzer_release_tag) update_fixed_rust_toolchain_versions(rust_toolchain) commit_all("Rust: Update fixed toolchain")