Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/update-rust-analyzer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .github/workflows/update-rust-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]"
Expand Down
77 changes: 62 additions & 15 deletions rust/scripts/update_rust_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import base64
import hashlib
import itertools
import json
import re
import shlex
Expand Down Expand Up @@ -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:
Expand All @@ -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]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be helpful to comment exptected format of rust_analyzer_release_tag parameter to make it clear that the first 10 chars is a date on the form YYYY-MM-DD

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Done.


# `manifests.txt` is a list of all toolchains. The one we're interested in looks like
# ```
Expand Down Expand Up @@ -204,18 +244,25 @@ 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)
run("cargo", "update")
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")

Expand Down
Loading