(stubs_root: &Path, lock: &StubsLock)
| 459 | } |
| 460 | |
| 461 | fn fetch_stubs(stubs_root: &Path, lock: &StubsLock) -> Result<(), Box<dyn std::error::Error>> { |
| 462 | let short = &lock.commit[..lock.commit.len().min(10)]; |
| 463 | let tarball_url = format!( |
| 464 | "https://github.com/{}/archive/{}.tar.gz", |
| 465 | lock.repo, lock.commit |
| 466 | ); |
| 467 | |
| 468 | eprintln!( |
| 469 | "cargo:warning=Downloading phpstorm-stubs from {} pinned at {}", |
| 470 | lock.repo, short |
| 471 | ); |
| 472 | |
| 473 | let mut tarball_response = ureq::get(&tarball_url) |
| 474 | .header("User-Agent", "phpantom-lsp-build") |
| 475 | .call()?; |
| 476 | |
| 477 | let mut tarball_bytes = Vec::new(); |
| 478 | tarball_response |
| 479 | .body_mut() |
| 480 | .as_reader() |
| 481 | .read_to_end(&mut tarball_bytes)?; |
| 482 | |
| 483 | // Verify the SHA-256 hash against stubs.lock. |
| 484 | let actual_hash = sha256_hex(&tarball_bytes); |
| 485 | if actual_hash != lock.sha256 { |
| 486 | return Err(format!( |
| 487 | "SHA-256 mismatch for phpstorm-stubs tarball!\n \ |
| 488 | expected: {}\n \ |
| 489 | actual: {}\n \ |
| 490 | Run scripts/update-stubs.sh to refresh stubs.lock.", |
| 491 | lock.sha256, actual_hash |
| 492 | ) |
| 493 | .into()); |
| 494 | } |
| 495 | eprintln!("cargo:warning=SHA-256 verified: {}", actual_hash); |
| 496 | |
| 497 | let decoder = GzDecoder::new(&tarball_bytes[..]); |
| 498 | let mut archive = Archive::new(decoder); |
| 499 | |
| 500 | let target_dir = stubs_root.join("jetbrains/phpstorm-stubs"); |
| 501 | fs::create_dir_all(&target_dir)?; |
| 502 | |
| 503 | // Safety: disable platform-specific features we don't need. |
| 504 | archive.set_unpack_xattrs(false); |
| 505 | archive.set_preserve_permissions(false); |
| 506 | |
| 507 | // GitHub tarballs have a top-level directory like "phpstorm-stubs-abc1234/" |
| 508 | // We need to strip that prefix when extracting. |
| 509 | for entry in archive.entries()? { |
| 510 | let mut entry = entry?; |
| 511 | let path = entry.path()?; |
| 512 | |
| 513 | let components: Vec<_> = path.components().collect(); |
| 514 | if components.len() <= 1 { |
| 515 | continue; |
| 516 | } |
| 517 | |
| 518 | let relative_path: std::path::PathBuf = components[1..].iter().collect(); |
no test coverage detected