Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ Or install all Python components:
pip install --extra-index-url https://pkg.jumpstarter.dev/ jumpstarter-all
```

### Upgrade the CLI

```shell
jmp self update
```

### Deploy the Service

To install the Jumpstarter Service in your Kubernetes cluster, see the
Expand Down
8 changes: 8 additions & 0 deletions docs/source/getting-started/installation/packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ If you have the repository cloned locally:
| Release candidate | `./install.sh -s rc` | Install latest release candidate (when available) |
| Custom directory | `./install.sh -d /opt/jumpstarter` | Install to custom directory |

##### Built-in installer

If you have the jumpstarter python packages installed and want to upgrade:

```{code-block} console
jmp self update
```

##### Installation Directory Structure

After installation, the following structure is created:
Expand Down
11 changes: 11 additions & 0 deletions python/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ fi
INSTALL_DIR="${INSTALL_DIR:-${HOME}/.local/jumpstarter}"
VENV_DIR="${VENV_DIR:-${INSTALL_DIR}/venv}"
SET_SCRIPT="${INSTALL_DIR}/set"
INSTALL_SOURCE_FILE="${INSTALL_DIR}/install_source"
DEFAULT_SOURCE="release-0.8"

# Function to print colored output
Expand Down Expand Up @@ -179,6 +180,7 @@ create_venv() {
print_success "Virtual environment created"
}


# Function to install jumpstarter-all
install_jumpstarter() {
local source="$1"
Expand Down Expand Up @@ -208,6 +210,10 @@ install_jumpstarter() {
exit 1
fi

cat > "${INSTALL_SOURCE_FILE}" << EOF
${source}
EOF

print_success "jumpstarter-all==${version} installed successfully"
}

Expand Down Expand Up @@ -288,6 +294,7 @@ while [[ $# -gt 0 ]]; do
INSTALL_DIR="$2"
VENV_DIR="${INSTALL_DIR}/venv"
SET_SCRIPT="${INSTALL_DIR}/set"
INSTALL_SOURCE_FILE="${INSTALL_DIR}/install_source"
shift 2
;;
-h|--help)
Expand All @@ -302,6 +309,10 @@ while [[ $# -gt 0 ]]; do
esac
done

# Set cached source from file
if [[ -z "${SOURCE}" && -f "${INSTALL_SOURCE_FILE}" ]]; then
SOURCE=$(<"${INSTALL_SOURCE_FILE}")
fi
# Set default source if not specified
SOURCE="${SOURCE:-${DEFAULT_SOURCE}}"

Expand Down
2 changes: 2 additions & 0 deletions python/packages/jumpstarter-cli/jumpstarter_cli/jmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .get import get
from .login import login
from .run import run
from .self import self
from .shell import shell
from .update import update

Expand All @@ -37,6 +38,7 @@ def jmp():

jmp.add_command(driver)
jmp.add_command(admin)
jmp.add_command(self)
jmp.add_command(version)

try:
Expand Down
14 changes: 14 additions & 0 deletions python/packages/jumpstarter-cli/jumpstarter_cli/self.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import click

from .self_update import self_update


@click.group
def self():
"""
Manage the jumpstarter executables
"""
pass


self.add_command(self_update)
43 changes: 43 additions & 0 deletions python/packages/jumpstarter-cli/jumpstarter_cli/self_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import shutil
import subprocess
import urllib.request
from pathlib import Path

import click
from jumpstarter_cli_common.exceptions import handle_exceptions


def _determine_install_dir() -> Path | None:
jmp_path = shutil.which("jmp")
if jmp_path:
return Path(jmp_path).parent.parent
return None

def _fetch_install_script() -> str:
INSTALL_SCRIPT_URL = "https://raw.githubusercontent.com/jumpstarter-dev/jumpstarter/main/python/install.sh"
Comment thread
engelmi marked this conversation as resolved.
with urllib.request.urlopen(INSTALL_SCRIPT_URL) as response:
return response.read().decode("utf-8")


@click.command("update")
@click.option(
"--source",
type=str,
help="Overwrites the current installation source. Available: latest, rc, main and release-x.x",
default=None,
)
@handle_exceptions
def self_update(source: str):
"""
Update jumpstarter
"""
install_dir = _determine_install_dir()
script = _fetch_install_script()

cmd = ["bash", "-s", "-"]
if install_dir:
cmd.extend(["--dir", f"{install_dir}"])
if source:
cmd.extend(["--source", source])

subprocess.run(cmd, input=script, check=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from click.testing import CliRunner

from .self import self
from .self_update import _determine_install_dir, _fetch_install_script


@pytest.mark.parametrize(
"jmp_path,expected_install_dir",
[
("", None),
(None, None),
("/home/user/.local/jumpstarter/bin/jmp", Path("/home/user/.local/jumpstarter")),
],
)
@patch("shutil.which")
def test__determine_install_dir(which_mock, jmp_path, expected_install_dir):
which_mock.return_value = jmp_path
install_dir = _determine_install_dir()
assert install_dir == expected_install_dir

@patch("urllib.request.urlopen")
def test__fetch_install_script(urlopen_mock):
response = MagicMock()
response.read.return_value = b"#!/bin/sh"
urlopen_mock.return_value.__enter__.return_value = response
script = _fetch_install_script()
assert script == "#!/bin/sh"

@pytest.mark.parametrize(
"source,install_dir,expected_cmd",
[
(None, None, ["bash", "-s", "-"]),
(None, "/home/user/.local/jumpstarter", ["bash", "-s", "-", "--dir", "/home/user/.local/jumpstarter"]),
("main", None, ["bash", "-s", "-", "--source", "main"]),
("main", "/home/user/.local/jumpstarter", [
"bash", "-s", "-",
"--dir", "/home/user/.local/jumpstarter",
"--source", "main"]
),
],
)
@patch("jumpstarter_cli.self_update._determine_install_dir")
@patch("jumpstarter_cli.self_update._fetch_install_script")
@patch("subprocess.run")
def test_self_update(
subprocess_mock,
fetch_install_script_mock,
determine_install_dir_mock,
source,
install_dir,
expected_cmd
):
determine_install_dir_mock.return_value = install_dir
fetch_install_script_mock.return_value = "#!/bin/sh"

CliRunner().invoke(self, ["update", "--source", source])
Comment thread
engelmi marked this conversation as resolved.

subprocess_mock.assert_called_once_with(
expected_cmd,
input="#!/bin/sh",
check=True,
)
Loading