diff --git a/ports/arrow/portfile.cmake b/ports/arrow/portfile.cmake index bdcf4553..2072d97b 100644 --- a/ports/arrow/portfile.cmake +++ b/ports/arrow/portfile.cmake @@ -1,8 +1,10 @@ +# GITHUB_REPO apache/arrow +# GITHUB_REF apache-arrow-${VERSION} vcpkg_download_distfile( ARCHIVE_PATH URLS "https://archive.apache.org/dist/arrow/arrow-${VERSION}/apache-arrow-${VERSION}.tar.gz" FILENAME apache-arrow-${VERSION}.tar.gz - SHA512 c687e50dfcdbf7e0e39710224360d35d9aa734452b3a47adc8c101f3019b6b4116310c05b9f3cd0a5ed4ad9b7bd8fb88edb70e79b3cbd413a57e5e35e4554a6c + SHA512 e75d384b4fdbdee29eb8ad29800c731843e7c43d90a43995dcc77390008723537791e212333178625345c718bdab15e0f3d8c12aa86b336918598c7d3fefc6e5 ) vcpkg_extract_source_archive( SOURCE_PATH diff --git a/ports/arrow/vcpkg.json b/ports/arrow/vcpkg.json index 4c1904e0..96597bea 100644 --- a/ports/arrow/vcpkg.json +++ b/ports/arrow/vcpkg.json @@ -1,7 +1,6 @@ { "name": "arrow", - "version": "23.0.1", - "port-version": 1, + "version": "25.0.1", "description": "Cross-language development platform for in-memory analytics", "homepage": "https://arrow.apache.org", "license": "Apache-2.0", diff --git a/scripts/update-ports.py b/scripts/update-ports.py index bc9e3db7..28b3aca9 100644 --- a/scripts/update-ports.py +++ b/scripts/update-ports.py @@ -259,6 +259,14 @@ def get_latest_tag(repo, ref_pattern, headers): return None, None +def get_version_key(vcpkg_data): + """Return the version field a manifest uses (version, version-date, ...).""" + for key in ("version-date", "version-semver", "version-string"): + if key in vcpkg_data: + return key + return "version" + + def update_github_ports(): updated = [] failed = [] @@ -340,17 +348,10 @@ def update_github_ports(): continue # Determine version key and new version value + version_key = get_version_key(vcpkg_data) if is_version_date: - version_key = "version-date" new_version = version_from_tag.replace(".", "-") - elif "version-semver" in vcpkg_data: - version_key = "version-semver" - new_version = version_from_tag - elif "version-string" in vcpkg_data: - version_key = "version-string" - new_version = version_from_tag else: - version_key = "version" new_version = version_from_tag # vcpkg relaxed versions only allow digits and dots @@ -420,6 +421,141 @@ def update_github_ports(): return updated, failed, unchanged +def update_distfile_ports(): + """Update ports that fetch a release archive with vcpkg_download_distfile. + + Such ports have no REPO to derive the upstream from, so the GitHub + repository holding the releases is declared in the portfile: + + # GITHUB_REPO apache/arrow + # GITHUB_REF apache-arrow-${VERSION} + + GITHUB_REF is the tag pattern (defaults to "${VERSION}"). The download URL + is taken from the URLS argument, with ${VERSION} substituted. + """ + updated = [] + failed = [] + unchanged = [] + + headers = {} + token = os.environ.get("GITHUB_TOKEN") + if token: + headers["Authorization"] = f"token {token}" + + for dir_name in sorted(os.listdir(PORTS_DIR)): + dir_path = os.path.join(PORTS_DIR, dir_name) + portfile_cmake = os.path.join(dir_path, "portfile.cmake") + vcpkg_json = os.path.join(dir_path, "vcpkg.json") + + if not os.path.isfile(portfile_cmake): + continue + + with open(portfile_cmake, "r") as f: + portfile_content = f.read() + + if "vcpkg_download_distfile" not in portfile_content: + continue + + # Only ports that opt in by declaring their upstream repository + repo_match = re.search(r"#\s*GITHUB_REPO\s+(\S+)", portfile_content) + if not repo_match: + continue + repo = repo_match.group(1) + + ref_match = re.search(r"#\s*GITHUB_REF\s+(\S+)", portfile_content) + ref_pattern = ref_match.group(1) if ref_match else "${VERSION}" + + print(f"Updating {dir_name} (GitHub release: {repo})") + + if not os.path.isfile(vcpkg_json): + print(f" vcpkg.json not found for {dir_name}") + failed.append(f"{dir_name} (vcpkg.json not found)") + continue + + with open(vcpkg_json, "r") as f: + vcpkg_data = json.load(f) + + url_match = re.search(r'URLS\s+"?(\S+?)"?\s*$', portfile_content, re.MULTILINE) + if not url_match: + print(f" Could not find URLS in {portfile_cmake}") + failed.append(f"{dir_name} (no URLS)") + continue + url_template = url_match.group(1) + + if "${VERSION}" not in url_template: + print(f" URLS in {portfile_cmake} is not version-templated") + failed.append(f"{dir_name} (URLS not version-templated)") + continue + + tag_name, version_from_tag = get_latest_tag(repo, ref_pattern, headers) + + if not tag_name: + print(f" No matching tag found for {repo}") + failed.append(f"{dir_name} (no matching tag)") + continue + + version_key = get_version_key(vcpkg_data) + if version_key == "version-date": + new_version = version_from_tag.replace(".", "-") + else: + new_version = version_from_tag + + # vcpkg relaxed versions only allow digits and dots + if version_key in ("version", "version-semver") and not re.fullmatch( + r"\d+(\.\d+)*", new_version + ): + print( + f" Skipping {dir_name}: version '{new_version}' is not a valid vcpkg relaxed version" + ) + unchanged.append(dir_name) + continue + + current_version = vcpkg_data.get(version_key) + if current_version == new_version: + print(f" {dir_name} is already up to date ({current_version}). Skipping...") + unchanged.append(dir_name) + continue + + print(f" Updating {dir_name} from {current_version} to {new_version} (tag: {tag_name})") + + # Download the release archive and compute SHA512 + source_url = url_template.replace("${VERSION}", new_version) + resp = requests.get(source_url, stream=True) + if resp.status_code != 200: + print(f" Failed to download archive from {source_url} (status {resp.status_code})") + failed.append(f"{dir_name} (download failed)") + continue + + temp_file_path = os.path.join(dir_path, "temp_source_file") + with open(temp_file_path, "wb") as f: + for chunk in resp.iter_content(chunk_size=8192): + f.write(chunk) + sha512_checksum = calculate_sha512(temp_file_path) + os.remove(temp_file_path) + print(f" Calculated SHA512 checksum: {sha512_checksum}") + + vcpkg_data.pop("port-version", None) + vcpkg_data[version_key] = new_version + with open(vcpkg_json, "w") as f: + json.dump(vcpkg_data, f, indent=2) + print(f" Updated {vcpkg_json} to version {new_version}") + + # Update the SHA512 of the main source archive (first occurrence) + new_portfile_content = re.sub( + r"(SHA512\s+)[a-f0-9]+", + f"\\g<1>{sha512_checksum}", + portfile_content, + count=1, + ) + + with open(portfile_cmake, "w") as f: + f.write(new_portfile_content) + print(f" Updated {portfile_cmake}") + updated.append(f"{dir_name} ({current_version} -> {new_version})") + + return updated, failed, unchanged + + def print_report(title, updated, failed, unchanged): print(f"\n{'=' * 60}") print(f" {title}") @@ -440,12 +576,18 @@ def main(): parser = argparse.ArgumentParser(description="Update vcpkg ports to latest versions") parser.add_argument("--pypi", action="store_true", help="Update ports using vcpkg_from_pythonhosted") parser.add_argument("--github", action="store_true", help="Update ports using vcpkg_from_github") + parser.add_argument( + "--distfile", + action="store_true", + help="Update ports using vcpkg_download_distfile with a # GITHUB_REPO marker", + ) args = parser.parse_args() - # Default to both if neither specified - if not args.pypi and not args.github: + # Default to all if none specified + if not args.pypi and not args.github and not args.distfile: args.pypi = True args.github = True + args.distfile = True all_updated = [] all_failed = [] @@ -461,6 +603,11 @@ def main(): all_updated.extend(updated) all_failed.extend(failed) all_unchanged.extend(unchanged) + if args.distfile: + updated, failed, unchanged = update_distfile_ports() + all_updated.extend(updated) + all_failed.extend(failed) + all_unchanged.extend(unchanged) print_report("Summary", all_updated, all_failed, all_unchanged)