diff --git a/.github/workflows/release-pypi.yaml b/.github/workflows/release-pypi.yaml index bd6c6ab..16111e2 100644 --- a/.github/workflows/release-pypi.yaml +++ b/.github/workflows/release-pypi.yaml @@ -12,7 +12,7 @@ permissions: packages: none id-token: write jobs: - release-krayt: + pypi-release-krayt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -22,3 +22,26 @@ jobs: env: # required for gh release GH_TOKEN: ${{ github.token }} + - run: sudo rm -rf dist + - name: Install just + run: | + curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + shell: bash + - name: Install uv + run: | + curl -LsSf https://astral.sh/uv/0.6.16/install.sh | sh + shell: bash + - name: Install hatch + run: | + uv tool install hatch + shell: bash + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + shell: bash + - name: GitHub Release (just release) + run: just create-release + env: + GH_TOKEN: ${{ github.token }} + shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f04f65..29ffba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## 0.4.3 + +- working out binary release process + +## 0.4.2 + +- working out binary release process + +## 0.4.1 + +- Automated release for both pypi and github + +## 0.4.0 + +- create now has --apply to apply the generated manifest to the cluster +- generic templates endpoint for cli +- better motd for volume mounts + +## 0.3.0 + +- created pypi release +- updated releases to use pyapp +- all new package +- port forward support +- additional_packages support + ## 0.2.0 ### Added diff --git a/justfile b/justfile index 8b9c260..f62f5c9 100644 --- a/justfile +++ b/justfile @@ -3,7 +3,7 @@ delete-tag: set -euo pipefail # Get the version - VERSION=$(cat version) + VERSION=$(hatch version) # Delete the tag git tag -d "v$VERSION" @@ -14,83 +14,65 @@ delete-release: set -euo pipefail # Get the version - VERSION=$(cat version) + VERSION=$(hatch version) # Delete the release gh release delete "v$VERSION" create-tag: #!/usr/bin/env bash - VERSION=$(cat version) + VERSION=$(hatch version) git tag -a "v$VERSION" -m "Release v$VERSION" git push origin "v$VERSION" create-archives: #!/usr/bin/env bash - VERSION=$(cat version) + VERSION=$(hatch version) rm -rf dist build - mkdir -p dist + hatch build -t binary + + krayt_bin=dist/binary/krayt-${VERSION} # Create the binary for each platform for platform in "x86_64-unknown-linux-gnu" "aarch64-unknown-linux-gnu"; do - outdir="krayt-${VERSION}-${platform}" - mkdir -p "dist/${outdir}" - + outbin="krayt-${VERSION}-${platform}" # Copy the Python script and update version - cp krayt.py "dist/${outdir}/krayt.py" - sed -i "s/NIGHTLY/${VERSION}/" "dist/${outdir}/krayt.py" - - cd dist - tar czf "${outdir}.tar.gz" "${outdir}" - sha256sum "${outdir}.tar.gz" > "${outdir}.tar.gz.sha256" - cd .. + cp ${krayt_bin} "dist/binary/${outbin}" done # Generate install.sh - ./scripts/generate_install_script.py "$VERSION" - chmod +x dist/install.sh + # ./scripts/generate_install_script.py "$VERSION" + # chmod +x dist/install.sh create-release: create-tag create-archives #!/usr/bin/env bash - VERSION=$(cat version) + VERSION=$(hatch version) ./scripts/get_release_notes.py "$VERSION" > release_notes.tmp - gh release create "v$VERSION" \ - --title "v$VERSION" \ - --notes-file release_notes.tmp \ - dist/krayt-${VERSION}-x86_64-unknown-linux-gnu.tar.gz \ - dist/krayt-${VERSION}-x86_64-unknown-linux-gnu.tar.gz.sha256 \ - dist/krayt-${VERSION}-aarch64-unknown-linux-gnu.tar.gz \ - dist/krayt-${VERSION}-aarch64-unknown-linux-gnu.tar.gz.sha256 \ - dist/install.sh + + # Check if release already exists + if gh release view "v$VERSION" &>/dev/null; then + echo "Release v$VERSION already exists. Uploading binaries..." + # Upload binaries to existing release + gh release upload "v$VERSION" \ + dist/binary/krayt-${VERSION} \ + dist/binary/krayt-${VERSION}-aarch64-unknown-linux-gnu \ + dist/binary/krayt-${VERSION}-x86_64-unknown-linux-gnu || true + else + echo "Creating new release v$VERSION" + # Create new release with binaries + gh release create "v$VERSION" \ + --title "v$VERSION" \ + --notes-file release_notes.tmp \ + dist/binary/krayt-${VERSION} \ + dist/binary/krayt-${VERSION}-aarch64-unknown-linux-gnu \ + dist/binary/krayt-${VERSION}-x86_64-unknown-linux-gnu + fi rm release_notes.tmp preview-release-notes: #!/usr/bin/env bash - VERSION=$(cat version) + VERSION=$(hatch version) ./scripts/get_release_notes.py "$VERSION" | less -R release: create-release -build-pyapp: - export PYAPP_PROJECT_NAME=krayt - export PYAPP_PROJECT_VERSION=`hatch version` - export PYAPP_DISTRIBUTION_SOURCE=~/git/krayt/dist/krayt-${PYAPP_PROJECT_VERSION}.tar.gz - export PYAPP_DISTRIBUTION_EMBED=true - - - echo "linting" - hatch run lint-format - - echo "Building pyapp" - hatch build - - echo "Uploading pyapp" - hatch publish - - cd ~/git/pyapp - cargo build --release --quiet - - - echo "Done" - - diff --git a/krayt/__about__.py b/krayt/__about__.py index 493f741..f6b7e26 100644 --- a/krayt/__about__.py +++ b/krayt/__about__.py @@ -1 +1 @@ -__version__ = "0.3.0" +__version__ = "0.4.3" diff --git a/krayt/cli/__init__.py b/krayt/cli/__init__.py index d5c6f0c..035a6de 100644 --- a/krayt/cli/__init__.py +++ b/krayt/cli/__init__.py @@ -6,7 +6,7 @@ from typer import Typer app = Typer() -app.add_typer(templates_app, name="templates", no_args_is_help=True) +app.add_typer(templates_app, name="template", no_args_is_help=True) app.add_typer(pod_app, name="pod", no_args_is_help=True) app.command(name="create")(create) app.command(name="c")(create) diff --git a/krayt/cli/pod.py b/krayt/cli/pod.py index 6cf9aa3..d8ac87a 100644 --- a/krayt/cli/pod.py +++ b/krayt/cli/pod.py @@ -1,5 +1,6 @@ import iterfzf from krayt.templates import env +from kubernetes.stream import stream from kubernetes import client, config import logging import os @@ -8,6 +9,12 @@ import typer from typing import Any, List, Optional import yaml from krayt.__about__ import __version__ +import sys +import tty +import termios +import select +import signal +import json logging.basicConfig(level=logging.WARNING) @@ -35,8 +42,8 @@ def format_volume_mount(vm: client.V1VolumeMount) -> dict[str, Any]: return clean_dict( { "name": vm.name, - "mountPath": vm.mount_path, - "readOnly": vm.read_only if vm.read_only else None, + "mount_path": vm.mount_path, + "read_only": vm.read_only if vm.read_only else None, } ) @@ -144,6 +151,17 @@ def get_pods( raise typer.Exit(1) +def get_namespaces( + namespace=None, + label_selector: str = "app=krayt", +): + config.load_kube_config() + api = client.CoreV1Api() + + all_namespaces = [n.metadata.name for n in api.list_namespace().items] + return all_namespaces + + def get_pod_spec(pod_name, namespace): config.load_kube_config() v1 = client.CoreV1Api() @@ -260,92 +278,81 @@ def create_inspector_job( pre_init_hooks: Optional[List[str]] = None, post_init_hooks: Optional[List[str]] = None, ): - """Create a Krayt inspector job with the given mounts""" timestamp = int(time.time()) job_name = f"{pod_name}-krayt-{timestamp}" - # Get environment variables and secret volumes from the target pod env_vars, secret_volumes = get_env_vars_and_secret_volumes(api, namespace) - - # Add secret volumes to our volumes list volumes.extend(secret_volumes) - # Create corresponding volume mounts for secrets - secret_mounts = [] - for vol in secret_volumes: - secret_mounts.append( - { - "name": vol.name, - "mountPath": f"/mnt/secrets/{vol.secret.secret_name}", - "readOnly": True, - } + secret_mounts = [ + client.V1VolumeMount( + name=vol.name, + mount_path=f"/mnt/secrets/{vol.secret.secret_name}", + read_only=True, ) + for vol in secret_volumes + ] - # Convert volume mounts to dictionaries formatted_mounts = [format_volume_mount(vm) for vm in volume_mounts] + formatted_mounts = [client.V1VolumeMount(**vm) for vm in formatted_mounts if vm] formatted_mounts.extend(secret_mounts) - # Format mount and PVC info for MOTD - mount_info = [] - for vm in formatted_mounts: - if vm: - mount_info.append(f"{vm['name']}:{vm['mountPath']}") + pvc_info = [ + f"{v.name}:{v.persistent_volume_claim.claim_name}" + for v in volumes + if hasattr(v, "persistent_volume_claim") and v.persistent_volume_claim + ] - pvc_info = [] - for v in volumes: - if hasattr(v, "persistent_volume_claim") and v.persistent_volume_claim: - pvc_info.append(f"{v.name}:{v.persistent_volume_claim.claim_name}") - - template_name = "base.sh" - template = env.get_template(template_name) - pvcs = None - pre_init_scripts = None - post_init_scripts = None - pre_init_hooks = None - post_init_hooks = None + template = env.get_template("base.sh") command = template.render( volumes=volumes, - pvcs=pvcs, + pvcs=None, additional_packages=additional_packages, - pre_init_scripts=pre_init_scripts, - post_init_scripts=post_init_scripts, - pre_init_hooks=pre_init_hooks, - post_init_hooks=post_init_hooks, + pre_init_scripts=None, + post_init_scripts=None, + pre_init_hooks=None, + post_init_hooks=None, ) - inspector_job = { - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": job_name, - "namespace": namespace, - "labels": {"app": "krayt"}, - "annotations": {"pvcs": ",".join(pvc_info) if pvc_info else "none"}, - }, - "spec": { - "ttlSecondsAfterFinished": 600, - "template": { - "metadata": {"labels": {"app": "krayt"}}, - "spec": { - "containers": [ - { - "name": "inspector", - "image": image, - "command": ["sh", "-c", command], - "env": env_vars, - "volumeMounts": formatted_mounts, - } - ], - "volumes": [format_volume(v) for v in volumes if format_volume(v)], - "imagePullSecrets": [{"name": imagepullsecret}] - if imagepullsecret - else None, - "restartPolicy": "Never", - }, - }, - }, - } - return inspector_job + container = client.V1Container( + name="inspector", + image=image, + command=["sh", "-c", command], + env=env_vars, + volume_mounts=formatted_mounts, + ) + + spec = client.V1PodSpec( + containers=[container], + volumes=[format_volume(v) for v in volumes if format_volume(v)], + restart_policy="Never", + image_pull_secrets=[client.V1LocalObjectReference(name=imagepullsecret)] + if imagepullsecret + else None, + ) + + template = client.V1PodTemplateSpec( + metadata=client.V1ObjectMeta(labels={"app": "krayt"}), spec=spec + ) + + job_spec = client.V1JobSpec( + template=template, + ttl_seconds_after_finished=600, + ) + + job = client.V1Job( + api_version="batch/v1", + kind="Job", + metadata=client.V1ObjectMeta( + name=job_name, + namespace=namespace, + labels={"app": "krayt"}, + annotations={"pvcs": ",".join(pvc_info) if pvc_info else "none"}, + ), + spec=job_spec, + ) + + return job PROTECTED_NAMESPACES = { @@ -438,32 +445,164 @@ def get_pod(namespace: Optional[str] = None): return pod_name, pod_namespace +def interactive_exec(pod_name: str, namespace: str): + # Load kubeconfig from local context (or use load_incluster_config if running inside the cluster) + print(f"Connecting to pod {pod_name} in namespace {namespace}...") + try: + config.load_kube_config() + except Exception as e: + print(f"Error loading kubeconfig: {e}", file=sys.stderr) + return + + core_v1 = client.CoreV1Api() + command = ["/bin/bash", "-l"] + resp = None + + # Save the current terminal settings + oldtty = termios.tcgetattr(sys.stdin) + + # Function to handle window resize events + def handle_resize(signum, frame): + if resp and resp.is_open(): + # Get the current terminal size + cols, rows = os.get_terminal_size() + # Send terminal resize command via websocket + # Format matches kubectl's resize message format + resize_msg = json.dumps({"Width": cols, "Height": rows}) + resp.write_channel(4, resize_msg) + + # Function to handle exit signals + def handle_exit(signum, frame): + if resp and resp.is_open(): + # Send Ctrl+C to the remote process + resp.write_stdin("\x03") + + try: + # Put terminal into raw mode but don't handle local echo ourselves + # Let the remote terminal handle echoing and control characters + tty.setraw(sys.stdin.fileno()) + + # Set up signal handlers + signal.signal(signal.SIGWINCH, handle_resize) # Window resize + signal.signal(signal.SIGINT, handle_exit) # Ctrl+C + + # Create a TTY-enabled exec connection to the pod + try: + resp = stream( + core_v1.connect_get_namespaced_pod_exec, + pod_name, + namespace, + command=command, + stderr=True, + stdin=True, + stdout=True, + tty=True, + _preload_content=False, + ) + print(f"Connected to {pod_name}") + except Exception as e: + print(f"\nError connecting to pod: {e}", file=sys.stderr) + return + + # Wait for the connection to be ready + time.sleep(0.2) + + # Send initial terminal size + cols, rows = os.get_terminal_size() + resize_msg = json.dumps({"Width": cols, "Height": rows}) + resp.write_channel(4, resize_msg) + + # Make sure the size is set by sending a resize event + handle_resize(None, None) + + # Set up a simple select-based event loop to handle I/O + try: + while resp and resp.is_open(): + # Update the websocket connection + resp.update(timeout=0.1) + + # Handle output from the pod + if resp.peek_stdout(): + sys.stdout.write(resp.read_stdout()) + sys.stdout.flush() + if resp.peek_stderr(): + sys.stderr.write(resp.read_stderr()) + sys.stderr.flush() + + # Check for input from the user + rlist, _, _ = select.select([sys.stdin], [], [], 0.01) + if sys.stdin in rlist: + # Read input and forward it to the pod without local echo + data = os.read(sys.stdin.fileno(), 1024) + if not data: # EOF (e.g., user pressed Ctrl+D) + break + resp.write_stdin(data.decode()) + except Exception as e: + print(f"\nConnection error: {e}", file=sys.stderr) + + except KeyboardInterrupt: + # Handle Ctrl+C gracefully + print("\nSession terminated by user", file=sys.stderr) + except Exception as e: + print(f"\nError in interactive session: {e}", file=sys.stderr) + finally: + # Reset signal handlers + signal.signal(signal.SIGWINCH, signal.SIG_DFL) + signal.signal(signal.SIGINT, signal.SIG_DFL) + + # Close the connection if it's still open + if resp and resp.is_open(): + try: + resp.close() + except Exception: + pass + + # Always restore terminal settings + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) + print("\nConnection closed", file=sys.stderr) + + @app.command() def exec( namespace: Optional[str] = typer.Option( None, help="Kubernetes namespace. If not specified, will search for inspectors across all namespaces.", ), + shell: Optional[str] = typer.Option( + "/bin/bash", + "--shell", + "-s", + help="Shell to use for the inspector pod", + ), ): """ Enter the Krayt dragon's lair! Connect to a running inspector pod. If multiple inspectors are found, you'll get to choose which one to explore. """ + config.load_kube_config() # or config.load_incluster_config() if running inside a pod + client.CoreV1Api() pod_name, pod_namespace = get_pod(namespace) - exec_command = [ - "kubectl", - "exec", - "-it", - "-n", - pod_namespace, - pod_name, - "--", - "/bin/bash", - "-l", - ] - os.execvp("kubectl", exec_command) + try: + pod_name, pod_namespace = get_pod(namespace) + exec_command = [ + "kubectl", + "exec", + "-it", + "-n", + pod_namespace, + pod_name, + "--", + shell, + "-l", + ] + + os.execvp("kubectl", exec_command) + except Exception as e: + print(f"Error executing command with kubectl trying python api: {e}") + + interactive_exec(pod_name, pod_namespace) @app.command() @@ -640,6 +779,11 @@ def create( "--post-init-hooks", help="additional hooks to execute at the start of container initialization", ), + apply: bool = typer.Option( + False, + "--apply", + help="Automatically apply the changes instead of just echoing them.", + ), ): """ Krack open a Krayt dragon! Create an inspector pod to explore what's inside your volumes. @@ -647,16 +791,15 @@ def create( The inspector will be created in the same namespace as the selected pod. """ # For create, we want to list all pods, not just Krayt pods - selected_namespace = None - selected_pod = None + selected_namespace = namespace + selected_pod = clone if namespace is None and clone is not None and "/" in clone: selected_namespace, selected_pod = clone.split("/", 1) - elif namespace is not None and clone is not None: - selected_namespace = namespace - selected_pod = clone - pods = get_pods(namespace, label_selector=None) + get_namespaces(namespace) + pods = get_pods(namespace, label_selector="app!=krayt") + if not pods: typer.echo("No pods found.") raise typer.Exit(1) @@ -672,9 +815,6 @@ def create( typer.echo("No pod selected.") raise typer.Exit(1) - # typer.echo(f"Selected pod exists: {selected_pod in (p[0] for p in pods)}") - # typer.echo(f"Selected pod: {selected_pod} ({selected_namespace})") - pod_spec = get_pod_spec(selected_pod, selected_namespace) volume_mounts, volumes = get_pod_volumes_and_mounts(pod_spec) @@ -694,7 +834,21 @@ def create( ) # Output the job manifest - typer.echo(yaml.dump(clean_dict(inspector_job), sort_keys=False)) + api_client = client.ApiClient() + job_dict = api_client.sanitize_for_serialization(inspector_job) + job_yaml = yaml.dump(job_dict, sort_keys=False) + + if apply: + batch_api = client.BatchV1Api() + job = batch_api.create_namespaced_job( + namespace=selected_namespace, + body=inspector_job, + ) + print(f"Job {job.metadata.name} created.") + return job + else: + # Just echo the YAML + typer.echo(job_yaml) @app.command() @@ -762,10 +916,10 @@ def list_pods(): typer.echo(f"{pod} ({namespace})") -def main(): - setup_environment() - app() - - -if __name__ == "__main__": - main() +# def main(): +# setup_environment() +# app() +# +# +# if __name__ == "__main__": +# main() diff --git a/krayt/cli/templates.py b/krayt/cli/templates.py index 841cd54..7416db6 100644 --- a/krayt/cli/templates.py +++ b/krayt/cli/templates.py @@ -13,7 +13,8 @@ def list(): @app.command() -def base( +def render( + template_name: Optional[str] = typer.Option("base.sh", "--template-name", "-t"), volumes: Optional[List[str]] = typer.Option( None, "--volume", @@ -48,7 +49,6 @@ def base( help="additional hooks to execute at the start of container initialization", ), ): - template_name = "base.sh" template = env.get_template(template_name) rendered = template.render( volumes=volumes, @@ -62,37 +62,37 @@ def base( print(rendered) -@app.command() -def install( - additional_packages: Optional[List[str]] = typer.Option( - ..., "--additional-packages", "-ap" - ), -): - template_name = "install.sh" - template = env.get_template(template_name) - rendered = template.render(additional_packages=additional_packages) - print(rendered) - - -@app.command() -def motd( - volumes: Optional[List[str]] = typer.Option( - None, - "--volume", - ), - pvcs: Optional[List[str]] = typer.Option( - None, - "--pvc", - ), - additional_packages: Optional[List[str]] = typer.Option( - ..., "--additional-packages", "-ap" - ), -): - template_name = "motd.sh" - template = env.get_template(template_name) - rendered = template.render( - volumes=volumes, - pvcs=pvcs, - additional_packages=additional_packages, - ) - print(rendered) +# @app.command() +# def install( +# additional_packages: Optional[List[str]] = typer.Option( +# ..., "--additional-packages", "-ap" +# ), +# ): +# template_name = "install.sh" +# template = env.get_template(template_name) +# rendered = template.render(additional_packages=additional_packages) +# print(rendered) +# +# +# @app.command() +# def motd( +# volumes: Optional[List[str]] = typer.Option( +# None, +# "--volume", +# ), +# pvcs: Optional[List[str]] = typer.Option( +# None, +# "--pvc", +# ), +# additional_packages: Optional[List[str]] = typer.Option( +# ..., "--additional-packages", "-ap" +# ), +# ): +# template_name = "motd.sh" +# template = env.get_template(template_name) +# rendered = template.render( +# volumes=volumes, +# pvcs=pvcs, +# additional_packages=additional_packages, +# ) +# print(rendered) diff --git a/krayt/templates/motd.sh b/krayt/templates/motd.sh index 9de1e85..5838161 100644 --- a/krayt/templates/motd.sh +++ b/krayt/templates/motd.sh @@ -5,11 +5,11 @@ cat </etc/motd └───────────────────────────────────┘ "Inside every volume lies a pearl of wisdom waiting to be discovered." -{%- if volumes %} +{%- if mounts %} Mounted Volumes: -{%- for volume in volumes %} -- {{ volume }} +{%- for mount in mounts %} +- {{ mount.name }}:{{ mount.mount_path }} {%- endfor %} {%- endif %} diff --git a/scripts/get_release_notes.py b/scripts/get_release_notes.py index d782a15..f564136 100755 --- a/scripts/get_release_notes.py +++ b/scripts/get_release_notes.py @@ -21,7 +21,11 @@ def get_release_notes(version): You can install krayt using one of these methods: -> !krayt requires [uv](https://docs.astral.sh/uv/getting-started/installation/) to be installed +## pypi + +``` bash +pip install krayt +``` ### Using i.jpillora.com (recommended) @@ -37,8 +41,8 @@ curl -fsSL https://github.com/waylonwalker/krayt/releases/download/v{version}/in ### Manual download You can also manually download the archive for your platform from the releases page: -- [x86_64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-x86_64-unknown-linux-gnu.tar.gz) -- [aarch64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-aarch64-unknown-linux-gnu.tar.gz)""" +- [x86_64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-x86_64-unknown-linux-gnu) +- [aarch64-unknown-linux-gnu](https://github.com/waylonwalker/krayt/releases/download/v{version}/krayt-{version}-aarch64-unknown-linux-gnu)""" # Get help output for main command and all subcommands try: @@ -46,17 +50,23 @@ You can also manually download the archive for your platform from the releases p # Get main help output main_help = subprocess.check_output( - ["./krayt.py", "--help"], + ["krayt", "--help"], stderr=subprocess.STDOUT, universal_newlines=True, ) help_outputs.append(("Main Command", main_help)) # Get help for each subcommand - subcommands = ["create", "exec", "clean", "version"] + subcommands = [ + "create", + "exec", + "clean", + "version", + "pod", + ] for cmd in subcommands: cmd_help = subprocess.check_output( - ["./krayt.py", cmd, "--help"], + ["krayt", cmd, "--help"], stderr=subprocess.STDOUT, universal_newlines=True, ) diff --git a/scripts/install.sh.template b/scripts/install.sh.template index 5fc0fb5..8e930c9 100644 --- a/scripts/install.sh.template +++ b/scripts/install.sh.template @@ -148,11 +148,11 @@ function install { FTYPE="" case "${OS}_${ARCH}" in "linux_amd64") - URL="https://github.com/WaylonWalker/nvim-manager/releases/download/v${RELEASE}/nvim-manager-${RELEASE}-x86_64-unknown-linux-gnu.tar.gz" + URL="https://github.com/WaylonWalker/krayt/releases/download/v${RELEASE}/krayt-${RELEASE}-x86_64-unknown-linux-gnu.tar.gz" FTYPE=".tar.gz" ;; "linux_arm64") - URL="https://github.com/WaylonWalker/nvim-manager/releases/download/v${RELEASE}/nvim-manager-${RELEASE}-aarch64-unknown-linux-gnu.tar.gz" + URL="https://github.com/WaylonWalker/krayt/releases/download/v${RELEASE}/krayt-${RELEASE}-aarch64-unknown-linux-gnu.tar.gz" FTYPE=".tar.gz" ;; *) fail "No asset for platform ${OS}-${ARCH}" ;; @@ -193,7 +193,7 @@ function install { unzip -o -qq tmp.zip || fail "unzip failed" rm tmp.zip || fail "cleanup failed" elif [[ $FTYPE = ".bin" ]]; then - bash -c "$GET $URL" >"nvim-manager_${OS}_${ARCH}" || fail "download failed" + bash -c "$GET $URL" >"krayt_${OS}_${ARCH}" || fail "download failed" else fail "unknown file type: $FTYPE" fi