Skip to content

Install script #318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0563c02
Install script and Dockerfile for testing on Linux
tcdent Feb 18, 2025
4c50c65
Cleanup.
tcdent Feb 18, 2025
dea4109
Cleanup.
tcdent Feb 18, 2025
f62cd43
Allow version from cli argument. Fix print_quiet logic.
tcdent Feb 18, 2025
40b5e32
Better formatting, better comments, note future improvements.
tcdent Feb 18, 2025
718146a
Properly handle uv error exits where exit code is still 0.
tcdent Feb 18, 2025
b8c86f7
GitHub action for calculating checksums on release.
tcdent Feb 18, 2025
7222a18
Merge branch 'main' into install
tcdent Feb 18, 2025
6743a1b
Allow installation from dev branches. Better error messaging.
tcdent Feb 18, 2025
f028ede
Upgrade system version and user project versions. Check for updates e…
tcdent Feb 18, 2025
df9c1ee
Exit installer if already installed.
tcdent Feb 18, 2025
9e5d61f
Specify target so uv will install in user site-packages.
tcdent Feb 18, 2025
75e5fdd
Decrement version for testing; I can't figure out a better way to do …
tcdent Feb 18, 2025
6ca050c
Re-enable setup_app
tcdent Feb 18, 2025
2b772fb
Restore version number.
tcdent Feb 18, 2025
30fc1f2
Wack type error.
tcdent Feb 18, 2025
a6c8f8d
Show activity during installs.
tcdent Feb 19, 2025
ef67d5c
Tests for install script.
tcdent Feb 19, 2025
abb4cd4
Merge branch 'main' into install
tcdent Feb 19, 2025
588b127
MacOS fixes. More robust dots.
tcdent Feb 19, 2025
bd85edb
Uninstall
tcdent Feb 19, 2025
418aa7a
Insert newline when adding PATH to user profile.
tcdent Feb 19, 2025
56fe099
Remove stale TODO comments.
tcdent Feb 19, 2025
41b1114
Merge branch 'alias-test-fix' into install
tcdent Feb 20, 2025
d627753
Initialize new project via config vars.
tcdent Feb 20, 2025
54ceaec
Merge branch 'main' into install
bboynton97 Feb 21, 2025
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: 5 additions & 1 deletion agentstack/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@
PATH: Path = Path()


class NoProjectError(Exception):
pass


def assert_project() -> None:
try:
ConfigFile()
return
except FileNotFoundError:
raise Exception("Could not find agentstack.json, are you in an AgentStack project directory?")
raise NoProjectError("Could not find agentstack.json, are you in an AgentStack project directory?")


def set_path(path: Union[str, Path, None]):
Expand Down
48 changes: 32 additions & 16 deletions agentstack/packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import re
import subprocess
import select
import site
from packaging.requirements import Requirement
from agentstack import conf, log

Expand All @@ -20,10 +21,17 @@
# In testing, when this was not set, packages could end up in the pyenv's
# site-packages directory; it's possible an environment variable can control this.

_python_executable = ".venv/bin/python"

def set_python_executable(path: str):
global _python_executable

_python_executable = path


def install(package: str):
"""Install a package with `uv` and add it to pyproject.toml."""

global _python_executable
from agentstack.cli.spinner import Spinner

def on_progress(line: str):
Expand All @@ -35,15 +43,15 @@ def on_error(line: str):

with Spinner(f"Installing {package}") as spinner:
_wrap_command_with_callbacks(
[get_uv_bin(), 'add', '--python', '.venv/bin/python', package],
[get_uv_bin(), 'add', '--python', _python_executable, package],
on_progress=on_progress,
on_error=on_error,
)


def install_project():
"""Install all dependencies for the user's project."""

global _python_executable
from agentstack.cli.spinner import Spinner

def on_progress(line: str):
Expand All @@ -56,14 +64,14 @@ def on_error(line: str):
try:
with Spinner(f"Installing project dependencies.") as spinner:
result = _wrap_command_with_callbacks(
[get_uv_bin(), 'pip', 'install', '--python', '.venv/bin/python', '.'],
[get_uv_bin(), 'pip', 'install', '--python', _python_executable, '.'],
on_progress=on_progress,
on_error=on_error,
)
if result is False:
spinner.clear_and_log("Retrying uv installation with --no-cache flag...", 'info')
_wrap_command_with_callbacks(
[get_uv_bin(), 'pip', 'install', '--no-cache', '--python', '.venv/bin/python', '.'],
[get_uv_bin(), 'pip', 'install', '--no-cache', '--python', _python_executable, '.'],
on_progress=on_progress,
on_error=on_error,
)
Expand All @@ -87,13 +95,13 @@ def on_error(line: str):

log.info(f"Uninstalling {requirement.name}")
_wrap_command_with_callbacks(
[get_uv_bin(), 'remove', '--python', '.venv/bin/python', requirement.name],
[get_uv_bin(), 'remove', '--python', _python_executable, requirement.name],
on_progress=on_progress,
on_error=on_error,
)


def upgrade(package: str):
def upgrade(package: str, use_venv: bool = True):
"""Upgrade a package with `uv`."""

# TODO should we try to update the project's pyproject.toml as well?
Expand All @@ -104,11 +112,17 @@ def on_progress(line: str):
def on_error(line: str):
log.error(f"uv: [error]\n {line.strip()}")

extra_args = []
if not use_venv:
# uv won't let us install without a venv if we don't specify a target
extra_args = ['--target', site.getusersitepackages()]

log.info(f"Upgrading {package}")
_wrap_command_with_callbacks(
[get_uv_bin(), 'pip', 'install', '-U', '--python', '.venv/bin/python', package],
[get_uv_bin(), 'pip', 'install', '-U', '--python', _python_executable, *extra_args, package],
on_progress=on_progress,
on_error=on_error,
use_venv=use_venv,
)


Expand Down Expand Up @@ -156,19 +170,21 @@ def _wrap_command_with_callbacks(
on_progress: Callable[[str], None] = lambda x: None,
on_complete: Callable[[str], None] = lambda x: None,
on_error: Callable[[str], None] = lambda x: None,
use_venv: bool = True,
) -> bool:
"""Run a command with progress callbacks. Returns bool for cmd success."""
process = None
try:
all_lines = ''
process = subprocess.Popen(
command,
cwd=conf.PATH.absolute(),
env=_setup_env(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
sub_args = {
'cwd': conf.PATH.absolute(),
'stdout': subprocess.PIPE,
'stderr': subprocess.PIPE,
'text': True,
}
if use_venv:
sub_args['env'] = _setup_env()
process = subprocess.Popen(command, **sub_args) # type: ignore
assert process.stdout and process.stderr # appease type checker

readable = [process.stdout, process.stderr]
Expand Down
14 changes: 11 additions & 3 deletions agentstack/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from pathlib import Path
from packaging.version import parse as parse_version, Version
import inquirer
from agentstack import log
from agentstack import conf, log
from agentstack.utils import term_color, get_version, get_framework, get_base_dir
from agentstack import packaging

Expand All @@ -24,7 +24,7 @@
USER_GUID_FILE_PATH = get_base_dir() / ".cli-user-guid"
INSTALL_PATH = Path(sys.executable).parent.parent
ENDPOINT_URL = "https://pypi.org/simple"
CHECK_EVERY = 3600 # hour
CHECK_EVERY = 12 * 60 * 60 # 12 hours


def _is_ci_environment():
Expand Down Expand Up @@ -113,7 +113,15 @@ def check_for_updates(update_requested: bool = False):
if inquirer.confirm(
f"New version of {AGENTSTACK_PACKAGE} available: {latest_version}! Do you want to install?"
):
packaging.upgrade(f'{AGENTSTACK_PACKAGE}[{get_framework()}]')
try:
# handle update inside a user project
conf.assert_project()
packaging.upgrade(f'{AGENTSTACK_PACKAGE}[{get_framework()}]')
except conf.NoProjectError:
# handle update for system version of agentstack
packaging.set_python_executable(sys.executable)
packaging.upgrade(AGENTSTACK_PACKAGE, use_venv=False)

log.success(f"{AGENTSTACK_PACKAGE} updated. Re-run your command to use the latest version.")
else:
log.info("Skipping update. Run `agentstack update` to install the latest version.")
Expand Down
Loading
Loading