mirror of
https://github.com/httpie/cli.git
synced 2025-02-17 18:10:50 +01:00
Implement httpie upgrade
for upgrading plugins (#1241)
* Implement `httpie upgrade` for upgrading plugins * Support upgrades for every installation type * Fix decoding problems
This commit is contained in:
parent
2b78d04410
commit
a5d8b51e47
@ -2224,6 +2224,14 @@ httpie_converter (1.0.0)
|
|||||||
httpie_konsole_konverter (httpie.plugins.converter.v1)
|
httpie_konsole_konverter (httpie.plugins.converter.v1)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `httpie plugins upgrade`
|
||||||
|
|
||||||
|
For upgrading already installed plugins, use `httpie plugins upgrade`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ httpie plugins upgrade httpie-plugin
|
||||||
|
```
|
||||||
|
|
||||||
#### `httpie plugins uninstall`
|
#### `httpie plugins uninstall`
|
||||||
|
|
||||||
Uninstall plugins from the isolated plugins directory. If the plugin is not installed
|
Uninstall plugins from the isolated plugins directory. If the plugin is not installed
|
||||||
|
@ -14,6 +14,14 @@ COMMANDS = {
|
|||||||
'help': 'targets to install'
|
'help': 'targets to install'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
'upgrade': [
|
||||||
|
'Upgrade the given plugins',
|
||||||
|
{
|
||||||
|
'dest': 'targets',
|
||||||
|
'nargs': '+',
|
||||||
|
'help': 'targets to upgrade'
|
||||||
|
}
|
||||||
|
],
|
||||||
'uninstall': [
|
'uninstall': [
|
||||||
'Uninstall the given HTTPie plugins.',
|
'Uninstall the given HTTPie plugins.',
|
||||||
{
|
{
|
||||||
|
@ -3,15 +3,20 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List
|
from typing import Tuple, Optional, List
|
||||||
|
|
||||||
from httpie.manager.cli import parser, missing_subcommand
|
from httpie.manager.cli import parser, missing_subcommand
|
||||||
from httpie.compat import importlib_metadata, get_dist_name
|
from httpie.compat import importlib_metadata, get_dist_name
|
||||||
from httpie.context import Environment
|
from httpie.context import Environment
|
||||||
from httpie.status import ExitStatus
|
from httpie.status import ExitStatus
|
||||||
|
from httpie.utils import as_site
|
||||||
|
|
||||||
|
PEP_503 = re.compile(r"[-_.]+")
|
||||||
|
|
||||||
|
|
||||||
class PluginInstaller:
|
class PluginInstaller:
|
||||||
@ -68,16 +73,22 @@ class PluginInstaller:
|
|||||||
**options
|
**options
|
||||||
)
|
)
|
||||||
|
|
||||||
def install(self, targets: List[str]) -> Optional[ExitStatus]:
|
def _install(self, targets: List[str], mode='install', **process_options) -> Tuple[
|
||||||
self.env.stdout.write(f"Installing {', '.join(targets)}...\n")
|
Optional[bytes], ExitStatus
|
||||||
self.env.stdout.flush()
|
]:
|
||||||
|
pip_args = [
|
||||||
|
'install',
|
||||||
|
f'--prefix={self.dir}',
|
||||||
|
'--no-warn-script-location',
|
||||||
|
]
|
||||||
|
if mode == 'upgrade':
|
||||||
|
pip_args.append('--upgrade')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.pip(
|
process = self.pip(
|
||||||
'install',
|
*pip_args,
|
||||||
f'--prefix={self.dir}',
|
|
||||||
'--no-warn-script-location',
|
|
||||||
*targets,
|
*targets,
|
||||||
|
**process_options,
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
reason = None
|
reason = None
|
||||||
@ -94,7 +105,58 @@ class PluginInstaller:
|
|||||||
if severity == 'ERROR':
|
if severity == 'ERROR':
|
||||||
reason = message
|
reason = message
|
||||||
|
|
||||||
return self.fail('install', ', '.join(targets), reason)
|
stdout = error.stdout
|
||||||
|
exit_status = self.fail(mode, ', '.join(targets), reason)
|
||||||
|
else:
|
||||||
|
stdout = process.stdout
|
||||||
|
exit_status = ExitStatus.SUCCESS
|
||||||
|
|
||||||
|
return stdout, exit_status
|
||||||
|
|
||||||
|
def install(self, targets: List[str]) -> ExitStatus:
|
||||||
|
self.env.stdout.write(f"Installing {', '.join(targets)}...\n")
|
||||||
|
self.env.stdout.flush()
|
||||||
|
_, exit_status = self._install(targets)
|
||||||
|
return exit_status
|
||||||
|
|
||||||
|
def _clear_metadata(self, targets: List[str]) -> None:
|
||||||
|
# Due to an outstanding pip problem[0], we have to get rid of
|
||||||
|
# existing metadata for old versions manually.
|
||||||
|
# [0]: https://github.com/pypa/pip/issues/10727
|
||||||
|
result_deps = defaultdict(list)
|
||||||
|
for child in as_site(self.dir).iterdir():
|
||||||
|
if child.suffix in {'.dist-info', '.egg-info'}:
|
||||||
|
name, _, version = child.stem.rpartition('-')
|
||||||
|
result_deps[name].append((version, child))
|
||||||
|
|
||||||
|
for target in targets:
|
||||||
|
name, _, version = target.rpartition('-')
|
||||||
|
name = PEP_503.sub("-", name).lower().replace('-', '_')
|
||||||
|
if name not in result_deps:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for result_version, meta_path in result_deps[name]:
|
||||||
|
if version != result_version:
|
||||||
|
shutil.rmtree(meta_path)
|
||||||
|
|
||||||
|
def upgrade(self, targets: List[str]) -> ExitStatus:
|
||||||
|
self.env.stdout.write(f"Upgrading {', '.join(targets)}...\n")
|
||||||
|
self.env.stdout.flush()
|
||||||
|
|
||||||
|
raw_stdout, exit_status = self._install(
|
||||||
|
targets,
|
||||||
|
mode='upgrade',
|
||||||
|
stdout=subprocess.PIPE
|
||||||
|
)
|
||||||
|
if not raw_stdout:
|
||||||
|
return exit_status
|
||||||
|
|
||||||
|
stdout = raw_stdout.decode()
|
||||||
|
self.env.stdout.write(stdout)
|
||||||
|
|
||||||
|
installation_line = stdout.splitlines()[-1]
|
||||||
|
if installation_line.startswith('Successfully installed'):
|
||||||
|
self._clear_metadata(installation_line.split()[2:])
|
||||||
|
|
||||||
def _uninstall(self, target: str) -> Optional[ExitStatus]:
|
def _uninstall(self, target: str) -> Optional[ExitStatus]:
|
||||||
try:
|
try:
|
||||||
@ -178,6 +240,8 @@ class PluginInstaller:
|
|||||||
with enable_plugins(self.dir):
|
with enable_plugins(self.dir):
|
||||||
if action == 'install':
|
if action == 'install':
|
||||||
status = self.install(args.targets)
|
status = self.install(args.targets)
|
||||||
|
elif action == 'upgrade':
|
||||||
|
status = self.upgrade(args.targets)
|
||||||
elif action == 'uninstall':
|
elif action == 'uninstall':
|
||||||
status = self.uninstall(args.targets)
|
status = self.uninstall(args.targets)
|
||||||
elif action == 'list':
|
elif action == 'list':
|
||||||
|
@ -93,6 +93,18 @@ def test_plugins_double_uninstall(httpie_plugins, httpie_plugins_success, dummy_
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugins_upgrade(httpie_plugins, httpie_plugins_success, dummy_plugin):
|
||||||
|
httpie_plugins_success("install", dummy_plugin.path)
|
||||||
|
|
||||||
|
# Make a new version of the plugin
|
||||||
|
dummy_plugin.version = '2.0.0'
|
||||||
|
dummy_plugin.build()
|
||||||
|
|
||||||
|
httpie_plugins_success("upgrade", dummy_plugin.path)
|
||||||
|
data = parse_listing(httpie_plugins_success('list'))
|
||||||
|
assert data[dummy_plugin.name]['version'] == '2.0.0'
|
||||||
|
|
||||||
|
|
||||||
def test_broken_plugins(httpie_plugins, httpie_plugins_success, dummy_plugin, broken_plugin):
|
def test_broken_plugins(httpie_plugins, httpie_plugins_success, dummy_plugin, broken_plugin):
|
||||||
httpie_plugins_success("install", dummy_plugin.path, broken_plugin.path)
|
httpie_plugins_success("install", dummy_plugin.path, broken_plugin.path)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user