From bade488bdfff27f4b233a870b6c7110969eb1343 Mon Sep 17 00:00:00 2001 From: Luis Neto Date: Fri, 11 Oct 2024 02:44:29 -0700 Subject: [PATCH] TUN-8631: Abort release on version mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes TUN-8631 Approved-by: Gonçalo Garcia Approved-by: Devin Carr MR: https://gitlab.cfdata.org/cloudflare/tun/cloudflared/-/merge_requests/1579 --- github_release.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/github_release.py b/github_release.py index 8773fc43..33c47648 100755 --- a/github_release.py +++ b/github_release.py @@ -11,8 +11,9 @@ import hashlib import requests import tarfile from os import listdir -from os.path import isfile, join +from os.path import isfile, join, splitext import re +import subprocess from github import Github, GithubException, UnknownObjectException @@ -210,6 +211,61 @@ def move_asset(filepath, filename): except shutil.SameFileError: pass # the macOS release copy fails with being the same file (already in the artifacts directory) +def get_binary_version(binary_path): + """ + Sample output from go version -m : + ... + build -compiler=gc + build -ldflags="-X \"main.Version=2024.8.3-6-gec072691\" -X \"main.BuildTime=2024-09-10-1027 UTC\" " + build CGO_ENABLED=1 + ... + + This function parses the above output to retrieve the following substring 2024.8.3-6-gec072691. + To do this a start and end indexes are computed and the a slice is extracted from the output using them. + """ + needle = "main.Version=" + cmd = ['go','version', '-m', binary_path] + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, _ = process.communicate() + version_info = output.decode() + + # Find start of needle + needle_index = version_info.find(needle) + # Find backward slash relative to the beggining of the needle + relative_end_index = version_info[needle_index:].find("\\") + # Calculate needle position plus needle length to find version beggining + start_index = needle_index + len(needle) + # Calculate needle position plus relative position of the backward slash + end_index = needle_index + relative_end_index + return version_info[start_index:end_index] + +def assert_asset_version(binary_path, release_version): + """ + Asserts that the artifacts have the correct release_version. + The artifacts that are checked must not have an extension expecting .exe and .tgz. + In the occurrence of any other extension the function exits early. + """ + try: + shutil.rmtree('tmp') + except OSError: + pass + _, ext = os.path.splitext(binary_path) + if ext == '.exe' or ext == '': + binary_version = get_binary_version(binary_path) + elif ext == '.tgz': + tar = tarfile.open(binary_path, "r:gz") + tar.extractall("tmp", filter='data') + tar.close() + binary_path = os.path.join(os.getcwd(), 'tmp', 'cloudflared') + binary_version = get_binary_version(binary_path) + else: + return + + if binary_version != release_version: + logging.error(f"Version mismatch {binary_path}, binary_version {binary_version} release_version {release_version}") + exit(1) + + def main(): """ Attempts to upload Asset to Github Release. Creates Release if it doesn't exist """ try: @@ -221,6 +277,7 @@ def main(): for filename in onlyfiles: binary_path = os.path.join(args.path, filename) logging.info("binary: " + binary_path) + assert_asset_version(binary_path, args.release_version) elif os.path.isfile(args.path): logging.info("binary: " + binary_path) else: @@ -233,6 +290,9 @@ def main(): if os.path.isdir(args.path): onlyfiles = [f for f in listdir(args.path) if isfile(join(args.path, f))] + for filename in onlyfiles: + binary_path = os.path.join(args.path, filename) + assert_asset_version(binary_path, args.release_version) for filename in onlyfiles: binary_path = os.path.join(args.path, filename) upload_asset(release, binary_path, filename, args.release_version, args.kv_account_id, args.namespace_id,