PK ! APa a packagr/__init__.py__version__ = '0.1.0'
from packagr.packagr import application
def run():
application.run()PK ! packagr/commands/__init__.pyPK ! 1,̍e e packagr/commands/admin.pyfrom packagr.commands.base import Command
import os
import toml
import subprocess
class ConfigureClient(Command):
"""
Configure the CLI for your Packagr account
configure
{hash-id? : TYour Packagr account hash ID}
{email? : Your packagr email address}
{password? : Your packagr password}
"""
def handle(self):
"""
Creates a packagr config file at the root directory
TODO: Need to update this to actually validate the hash_id and api_access_key before setting these values
"""
hash_id: str = self.argument('hash-id')
email: str = self.argument('email')
password: str = self.argument('password')
config_path = os.path.expanduser('~')
content = {
'url': f'https://api.packagr.app/{hash_id}/',
'email': email,
'hash-id': hash_id,
'password': password
}
with open(os.path.join(config_path, 'packagr_conf.toml'), 'w') as f:
f.write(toml.dumps(content))
self.line('Successfully updated config file')
class SetValue(Command):
"""
Adds a configuration variable to your project config
set
{key? : The property to update}
{value? : The new value}
"""
def handle(self):
key, value = self.argument('key'), self.argument('value')
config = self.get_package_config()
if config:
try:
assert not isinstance(config.get(key), list)
except AssertionError:
new_type = type(value)
if not self.confirm(f'You are about to replace an array with a {new_type.__name__}. Continue?', False,
'(?i)^(y|j)'):
return
self.update_config(config, **{key: value})
self.line(f'Successfully added key "{key}" with value "{value}"')
class AddValue(Command):
"""
Appends a value to an existing array in the config
add
{key? : The property to update}
{value? : The new value}
"""
def handle(self):
key = self.argument('key')
value = self.argument('value')
config = self.get_package_config()
if not config:
return
if self.append(config, key, value):
self.line(f'Successfully added key "{key}" with value "{value}"')
class InstallCommand(Command):
"""
Installs a package and updates the config
install
{packages* : The packages to install}
{--i|ignore-errors : Continue to the next file even if errors are encountered}
"""
def handle(self):
packages = self.argument('packages')
ignore_errors = self.option('ignore-errors')
config = self.get_global_config()
url = f'https://{config["email"]}:{config["password"]}@api.packagr.app/{config["hash-id"]}/'
for package in packages:
status = subprocess.call(['pip', 'install', package, '--extra-index-url', url, '-q'])
if status == 0:
config = self.get_package_config()
if config:
self.append(config, 'install_requires', package)
self.line(f'Installed package {package} and added it to the config')
else:
self.line(f'Error installing package {package}.')
if not ignore_errors:
self.line('Stopping process')
return
class UninstallCommand(Command):
"""
Uninstalls a package and removes it from the config
uninstall
{packages* : The packages to uninstall}
{--i|ignore-errors : Continue to the next file even if errors are encountered}
{--y|skip-prompts : Skip uninstall prompts}
"""
def handle(self):
packages = self.argument('packages')
ignore_errors = self.option('ignore-errors')
skip_prompts = self.option('skip-prompts')
config = self.get_package_config()
for package in packages:
commands = ['pip', 'uninstall', package,]
if skip_prompts:
commands.append('-y')
status = subprocess.call(commands)
if status == 0:
removed = self.remove(config, 'install_requires', package)
if not removed:
return
self.line(f'Successfully uninstalled {package}')
else:
self.line(f'Error uninstalling package {package}.')
if not ignore_errors:
self.line('Stopping process')
return
class BumpVersion(Command):
"""
Increases the version number of a package, e.g. 1.0.0 > 1.0.1, assuming that semver is used
bump
{version? : The new version number}
{--a|major : Bumps the major version e.g. 1.0.0 > 2.0.0}
{--i|minor : Bumps the minor version e.g. 1.0.0 > 1.1.0}
"""
def handle(self):
config = self.get_package_config()
if not config:
return
version = config.get('version', '0.1.0')
version_opt = self.argument('version')
major_opt, minor_opt = self.option('major'), self.option('minor')
if version_opt:
if major_opt or minor_opt:
self.line(''
'Cannot use the version argument with either the --minor or --major arguments'
'')
return
new_version = version_opt
else:
try:
major, minor, bugfix = version.split('.')
except ValueError:
self.line('Cannot automatically bump version because this package does not appear to '
'use Semver. Use `packagr bump ` instead')
return
if major_opt or minor_opt:
if major_opt:
major = int(major) + 1
if minor_opt:
minor = int(minor) + 1
elif not version_opt:
bugfix = int(bugfix) + 1
new_version = f'{major}.{minor}.{bugfix}'
self.update_config(config, version=new_version)
self.line(f'Updated version to {new_version}')
class CreatePackage(Command):
"""
Creates a new Packagr config file
init
{name? : The name of the package - default to the current folder name if not provided}
{--o|overwrite : Overwrite existing without prompt}
"""
def handle(self):
"""
Creates a file called `packagr.toml` in the current directory
Checks if file exists first, prompts to replace
"""
name = self.argument('name')
if not name:
name = os.path.split(os.getcwd())[-1]
overwrite = self.option('overwrite')
template = {
'name': name,
'version': '0.1.0',
'packages': [name]
}
if not os.path.exists(name):
os.makedirs(name)
if os.path.exists('packagr.toml') and not overwrite:
if not self.confirm('A package already exists at this location. Overwrite?', False, '(?i)^(y|j)'):
return
self.write_package_content(template)
response = 'Created config file `packagr.toml`'
self.line(response)
PK ! ȎH H packagr/commands/base.pyfrom cleo import Command as BaseCommand
from packagr.utilities import get_package_config, write_package_content
from typing import Any
import os
class Command(BaseCommand):
def check_configuration(self):
"""
Check that a global config has been set with the `packagr configure` command
TODO: implement this
"""
def get_global_config(self):
"""
Returns the content of the global config file
"""
return self.get_package_config(path=os.path.expanduser('~/packagr_conf.toml'))
def get_package_config(self, path: str = 'packagr.toml'):
config = get_package_config(path=path)
if not config:
self.line(''
'Unable to perform this action because no package exists at the current location. '
'Run `packagr create` first'
'')
return config
@staticmethod
def write_package_content(config: dict, path: str = 'packagr.toml') -> None:
write_package_content(config, path=path)
def update_config(self, config, path: str = 'packagr.toml', **changes):
for key, value in changes.items():
config[key] = value
self.write_package_content(config, path=path)
def append(self, config: dict, key: str, value: Any, path: str = 'packagr.toml') -> True:
"""
Adds a value to an array, if it isn't already in there
"""
existing = config.get(key, [])
try:
assert isinstance(existing, list)
except AssertionError:
self.line(f'Cannot add to value {key} because it is not an array')
return
if value not in existing:
existing.append(value)
self.update_config(config, **{key: existing}, path=path)
return True
def remove(self, config: dict, key: str, value: Any, path: str = 'packagr.toml') -> True:
array = config.get(key, [])
try:
assert isinstance(array, list)
except AssertionError:
self.line(f'Cannot remove item because the property is not an arrary')
return
try:
array.remove(value)
except ValueError:
return
self.update_config(config, **{key: array}, path=path)
return True
PK ! =5w w packagr/commands/packaging.pyfrom packagr.commands.base import Command
from distutils.core import setup
from packagr.utilities import get_package_config
import os
import requests
import setuptools # DO NOT REMOVE THIS - IT IS IMPORTANT, EVEN THOUGH IT APPEARS TO NOT BE USED
class CreatePackage(Command):
"""
Creates sdist wheel packages
package
{--w|no-wheel : Don't create a wheel package}
{--s|no-sdist : Don't create an sdist package}
"""
def create_config(self, formats):
config = get_package_config()
output = {
'script_name': 'setup.py',
'script_args': formats + ['clean', '--all',]
}
for key, value in config.items():
output[key] = value
return output
def handle(self):
formats: list = ['bdist_wheel', 'sdist']
if self.option('no-wheel'):
formats.remove('bdist_wheel')
if self.option('no-sdist'):
formats.remove('sdist')
if not formats:
self.line('No formats to build!')
return
config = self.create_config(formats)
setup(**config)
self.line('Package built')
class UploadPackage(Command):
"""
Uploads built packages to Packagr
upload
{--i|ignore-errors : Continue to the next file even if errors are encountered}
"""
def handle(self):
self.check_configuration()
config = self.get_global_config()
package_config = self.get_package_config()
ignore_errors = self.option('ignore-errors')
upload_count = 0
for root, dirs, files in os.walk('dist'):
if len(files) == 0:
self.line('Nothing to upload. Run `packagr build` first to build a package')
return
for file in files:
self.line(f'Attempting to upload file {file} to Packagr')
files = {'content': (file, open(os.path.join(root, file), 'rb'))}
headers = {}
response = requests.post(
config['url'],
auth=(config['email'], config['password']),
data={'name': package_config['name'], 'version': package_config['version']},
files=files,
headers=headers
)
try:
assert response.status_code == 201
self.line(f'File {file} uploaded successfully')
upload_count += 1
except AssertionError:
if ignore_errors:
self.line(f'Package failed to upload. Status code: {response.status_code}'
f'\nSkipping to the next file...')
else:
self.line(f'Package failed to upload. Status code: {response.status_code}')
return
if upload_count == 0:
self.line('No files uploaded')
else:
self.line(f'Uploaded {upload_count} files successfully')
PK ! N= packagr/packagr.py#!/usr/bin/env python3
from cleo import Application
from packagr.commands import admin, packaging
application = Application()
# admin
application.add(admin.CreatePackage())
application.add(admin.ConfigureClient())
application.add(admin.SetValue())
application.add(admin.AddValue())
application.add(admin.InstallCommand())
application.add(admin.UninstallCommand())
application.add(admin.BumpVersion())
# package
application.add(packaging.CreatePackage())
application.add(packaging.UploadPackage())
def run():
application.run()PK ! 7 packagr/utilities.pyimport toml
def get_package_config(path: str = 'packagr.toml') -> dict:
"""
Returns the content of the package config file as a dict
"""
with open(path, 'r') as f:
return toml.loads(f.read())
def write_package_content(config: dict, path: str = 'packagr.toml') -> None:
"""
Writes a given dict to the package config file
"""
with open(path, 'w') as f:
f.write(toml.dumps(config))
PK !H!K# ' , packagr_cli-0.1.1.dist-info/entry_points.txtN+I/N.,()*HLNL/VEy\\ PK !HڽT U ! packagr_cli-0.1.1.dist-info/WHEEL
A
н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK !HhY B $ packagr_cli-0.1.1.dist-info/METADATAYmo8_Ad?l%izb8lpضW,`EKF$!)Ɏ;p@F93yJz9Nv!+٨dy+v^:>XoiwL<y-VƊ[n8ʛqu~~dvT\wqӺ'r՝GMUv\gVff7Mp)߈5>?o~!ƏhM_q^UNzvioQ3EoIF%:I3a:
IWA7]g,y YfKN)@aVI'V}[d.Rx͵`\C%Y6(e+
6A{;-(tO^B{]{mQΝRȂ"n(DiomB;88Uw܈ҮMЇ<=Sު,邪J\n q+qI;
~[uCtl{y+z\nFrė|;mj|v
) b-k!2)V0ۜ dVʪcg`%p!;3AFX;hU*'n5q;ey>5P:D)o`#xq("FXJ*@6%
-dUj
كhRJUq`m
~kEBW k9;(W,Kӷ~F2 Hu[
*LokzVsַ
_dJ*;=zÅt`?ң]P-gL&M-By%>@Z
%+ndN7 wĂt3=OHntaJd1] 4O(d9*Plᘫ*#+
:I!(}l$'.0A;URftҹR+מ;|92u5p`̑VHDtPUbv~x(9y0#SMKU} f Odl{ gvU8S17-Kl6gU@ ,{lhKggTt$á]<