def _get_binary_url_for(system, release: str = "latest") -> Tuple[str, str]: import requests from splitgraph.cloud import get_headers if release == "latest": endpoint = "https://api.github.com/repos/splitgraph/splitgraph/releases/%s" % release else: endpoint = "https://api.github.com/repos/splitgraph/splitgraph/releases/tags/%s" % release headers = get_headers() headers.update({"Accept": "application/vnd.github.v3+json"}) response = requests.get(endpoint, headers=get_headers()) if response.status_code == 404: raise ValueError("No releases found for tag %s, system %s!" % (release, system)) response.raise_for_status() body = response.json() actual_version = body["tag_name"].lstrip("v") executable = "sgr-%s-x86_64" % system if system == "windows": executable += ".exe" asset = [a for a in body["assets"] if a["name"] == executable] if not asset: raise ValueError("No releases found for tag %s, system %s!" % (release, system)) return actual_version, asset[0]["browser_download_url"]
def curl_c(remote, request_type, image, request_params, curl_args): """ Query a Splitgraph REST API. This is a thin wrapper around curl that performs an HTTP request to Splitgraph Cloud to interact with a dataset using PostgREST (http://postgrest.org) or the Splitfile execution service. The actual invocation is: ``` curl [API endpoint][request] -H [access_token] [extra curl args]. ``` The image must be of the form `namespace/repository:[hash_or_tag (default latest)]`. The actual request parameters depend on the request type: * For PostgREST: `/table?[postgrest request]` or empty to get the OpenAPI spec for this image. For a reference on how to perform Postgrest requests, see http://postgrest.org/en/latest/api.html. * For the Splitfile executor: a JSON array to be POSTed to the executor, e.g. `'{"command": "FROM some/repo IMPORT some_table AS alias", "tag": "new_tag"}'`. `--curl-args` allows to pass extra arguments to curl. Note that every argument must be prefixed with `--curl-args`, e.g. `--curl-args --cacert --curl-args /path/to/ca.pem`. """ from splitgraph.config import CONFIG from splitgraph.cloud import AuthAPIClient, get_headers repository, hash_or_tag = image # Craft a request config = CONFIG["remotes"][remote] access_token = AuthAPIClient(remote).access_token headers = get_headers() headers.update({"Authorization": "Bearer " + access_token}) if request_type == "postgrest": if request_params and not request_params.startswith("/"): request_params = "/" + request_params full_request = (config["SG_QUERY_API"] + "/%s/%s" % (str(repository), str(hash_or_tag)) + "/-/rest" + request_params) else: full_request = (config["SG_QUERY_API"] + "/%s/%s" % (str(repository), str(hash_or_tag)) + "/-/splitfile") curl_args = ["-X", "POST", "-d", request_params] + list(curl_args) headers.update({"Content-Type": "application/json"}) header_invocation = [ h for i in headers.items() for h in ("-H", "%s: %s" % i) ] subprocess_args = ["curl", full_request ] + header_invocation + list(curl_args) logging.debug("Calling %s", " ".join(subprocess_args)) subprocess.call(subprocess_args)
def upgrade_c(skip_engine_upgrade, path, force, version): """Upgrade sgr client and engine. This will try to download the most recent stable binary for the current platform into the location this binary is running from and then upgrade the default engine. This method is only supported for single-binary installs and engines managed by `sgr engine`. """ import requests from tqdm import tqdm from splitgraph.config import CONFIG, SG_CMD_ASCII from splitgraph.cloud import get_headers # Detect if we're running from a Pyinstaller binary if not hasattr(sys, "frozen") and not force and not path: raise click.ClickException( "Not running from a single binary. Use the tool " "you originally used to install Splitgraph (e.g. pip) " "to upgrade. To download and run a single binary " "for your platform, pass --force and --path." ) system = _get_system_id() # Get link to the release actual_version, download_url = _get_binary_url_for( system, "v" + version if version != "latest" else version ) if version == "latest": click.echo("Latest version is %s." % actual_version) if Version(actual_version) < Version(__version__) and not force: click.echo( "Newer or existing version of sgr (%s) is installed, pass --force to reinstall." % __version__ ) return # Download the file temp_path, final_path = _get_download_paths(path, download_url) # Delete the temporary file at exit if we crash def _unlink(): try: temp_path.unlink() except FileNotFoundError: pass atexit.register(_unlink) click.echo("Downloading sgr %s from %s to %s" % (actual_version, download_url, temp_path)) headers = get_headers() response = requests.get(download_url, headers=headers, allow_redirects=True, stream=True) response.raise_for_status() with tqdm( total=int(response.headers["Content-Length"]), unit="B", unit_scale=True, ascii=SG_CMD_ASCII ) as pbar: with (open(str(temp_path), "wb")) as f: for chunk in response.iter_content(chunk_size=1024): f.write(chunk) pbar.update(len(chunk)) # Test the new binary st = os.stat(str(temp_path)) os.chmod(str(temp_path), st.st_mode | stat.S_IEXEC) subprocess.check_call([str(temp_path), "--version"]) # Upgrade the default engine if not skip_engine_upgrade: containers = list_engines(include_all=True, prefix=CONFIG["SG_ENGINE_PREFIX"]) if containers: click.echo("Upgrading the default engine...") subprocess.check_call([str(temp_path), "engine", "upgrade"]) def _finalize(): # On Windows we can't replace a running executable + probably should keep a backup anyway. # We hence rename ourselves to a .old file and ask the user to delete it if needed. if final_path.exists(): backup_path = final_path.with_suffix(final_path.suffix + ".old") try: # shutil.move() breaks on Windows otherwise. backup_path.unlink() except FileNotFoundError: pass shutil.move(str(final_path), str(backup_path)) click.echo("Old version has been backed up to %s." % backup_path) shutil.move(str(temp_path), str(final_path)) click.echo("New sgr has been installed at %s." % final_path) # Instead of moving the file right now, do it at exit -- that way # Pyinstaller won't break with a scary error when cleaning up because # the file it's running from has changed. atexit.register(_finalize)