Aug 3, 2016
Who: Lucas Roesler
What: Director of Engineering
Where: Eventboard.io
Why: CLIs are fun!
# hallo.py
if __name__ == '__main__':
print('Hallo Welt!')
(.env) ➜ python hallo.py
Hallo Welt!
(.env) ➜ python hallo.py Hanz Franz
Hallo Welt!
What about arguments?
(.env) ➜ python hallo2.py Hanz Franz
Hallo Hanz!
Hallo Franz!
(.env) ➜ python hallo2.py Hanz Franz --bye
Tschüss Hanz!
Tschüss Franz!
# hallo2.py
import argparse
parser = argparse.ArgumentParser(description='Say Hello to someone')
parser.add_argument(
'names',
metavar='NAME', type=str, nargs='+',
help='a person to speak to',
)
parser.add_argument('--bye', action='store_true', default=False)
if __name__ == '__main__':
args = parser.parse_args()
if args.bye:
msg = 'Tschüss {}!'
else:
msg = 'Hallo {}!'
for name in args.names:
print(msg.format(name.capitalize()))
#! /usr/bin/env python
# -*- encoding: utf-8 -*-
# hallo3.py
import argparse
parser = argparse.ArgumentParser(description='Say Hello to someone')
parser.add_argument(
'names',
metavar='NAME', type=str, nargs='+',
help='a person to speak to',
)
parser.add_argument('--bye', action='store_true', default=False)
if __name__ == '__main__':
args = parser.parse_args()
if args.bye:
msg = 'Tschüss {}!'
else:
msg = 'Hallo {}!'
for name in args.names:
print(msg.format(name.capitalize()))
(.env) ➜ ln -sf ~/Code/slcpy-cli/beispiel/hallo3.py /usr/local/bin/hallo
(.env) ➜ hallo hanz franz
Hallo Hanz!
Hallo Franz!
➜ git -h
Unknown option: -h
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]
➜ git <TAB><TAB>
add -- add file contents to the index
bisect -- find by binary search the change that introduced a bug
branch -- list, create, or delete branches
checkout -- checkout a branch or paths to the working tree
clone -- clone a repository into a new directory
commit -- record changes to the repository
diff -- show changes between commits, commit and working tree, etc
fetch -- download objects and refs from another repository
grep -- print lines matching a pattern
init -- create an empty Git repository or reinitialize an existing one
log -- show commit logs
merge -- join two or more development histories together
mv -- move or rename a file, a directory, or a symlink
pull -- fetch from and merge with another repository or a local branch
push -- update remote refs along with associated objects
rebase -- forward-port local commits to the updated upstream head
reset -- reset current HEAD to the specified state
rm -- remove files from the working tree and from the index
show -- show various types of objects
status -- show the working tree status
tag -- create, list, delete or verify a tag object signed with GPG
➜ git add -h
usage: git add [<options>] [--] <pathspec>...
-n, --dry-run dry run
-v, --verbose be verbose
-i, --interactive interactive picking
-p, --patch select hunks interactively
-e, --edit edit current diff and apply
-f, --force allow adding otherwise ignored files
-u, --update update tracked files
-N, --intent-to-add record only the fact that the path will be added later
-A, --all add changes from all tracked and untracked files
--ignore-removal ignore paths removed in the working tree (same as --no-all)
--refresh don't add, only refresh the index
--ignore-errors just skip files which cannot be added because of errors
--ignore-missing check if - even missing - files are ignored in dry run
#! /usr/bin/env python
# -*- encoding: utf-8 -*-
# sagen.py
import argparse
def hallo(args):
for name in args.names:
print('Hallo {}!'.format(name.capitalize()))
parser = argparse.ArgumentParser(description='Speak messages to the cli')
subparsers = parser.add_subparsers(
title='subcommands',
)
parser_hallo = subparsers.add_parser(
'hallo', help='Print a German greeting to the cli')
parser_hallo.add_argument(
'names',
metavar='NAME', type=str, nargs='+',
help='a persons name',
)
parser_hallo.set_defaults(func=hallo)
def bye(args):
for name in args.names:
print('Tschüss {}!'.format(name.capitalize()))
parser_bye = subparsers.add_parser(
'bye', help='Print a German parting to the cli')
parser_bye.add_argument(
'names',
metavar='NAME', type=str, nargs='+',
help='a persons name',
)
parser_bye.set_defaults(func=bye)
if __name__ == '__main__':
args = parser.parse_args()
args.func(args)
(.env) ➜ python sagen.py -h
usage: sagen.py [-h] {hallo,bye} ...
Speak messages to the cli
optional arguments:
-h, --help show this help message and exit
subcommands:
{hallo,bye}
hallo Print a German greeting to the cli
bye Print a German parting to the cli
(.env) ➜ python sagen.py hallo hanz
Hallo Hanz!
#! /usr/bin/env python
# -*- encoding: utf-8 -*-
# sagen_click.py
import click
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option('1.0.0')
def cli():
"""Print a German phrases."""
pass
@cli.command()
@click.argument('names', nargs=-1) # See http://click.pocoo.org/6/arguments/
def hallo(names):
"""Print a German greeting to the cli"""
for name in names:
click.echo('Hallo {}!'.format(name.capitalize()))
@cli.command()
@click.argument('names', nargs=-1) # See http://click.pocoo.org/6/arguments/
def bye(names):
"""Print a German parting to the cli."""
for name in names:
click.echo('Tschüss {}!'.format(name.capitalize()))
if __name__ == '__main__':
cli()
(.env) ➜ python sagen_click.py -h
Usage: sagen_click.py [OPTIONS] COMMAND [ARGS]...
Print a German phrases.
Options:
--version Show the version and exit.
-h, --help Show this message and exit.
Commands:
bye Print a German parting to the cli.
hallo Print a German greeting to the cli
(.env) ➜ python sagen_click.py hallo -h
Usage: sagen_click.py hallo [OPTIONS] [NAMES]...
Print a German greeting to the cli
Options:
-h, --help Show this message and exit.
(.env) ➜ python sagen_click.py hallo hanz franz
Hallo Hanz!
Hallo Franz!
#! /usr/bin/env python
# -*- encoding: utf-8 -*-
# sagen_click.py
import click
CONTEXT_SETTINGS = {'help_option_names': ['-h', '--help']}
@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option('1.0.0')
def cli():
"""Print a German phrases."""
pass
@cli.command()
@click.argument('names', nargs=-1)
def hallo(names):
"""Print a German greeting to the cli"""
for name in names:
click.echo('Hallo {}!'.format(name.capitalize()))
@cli.command()
@click.argument('names', nargs=-1)
def bye(names):
"""Print a German parting to the cli."""
for name in names:
click.echo('Tschüss {}!'.format(name.capitalize()))
if __name__ == '__main__':
cli()
#! /usr/bin/env python
# -*- encoding: utf-8 -*-
# sagen.py
import argparse
def hallo(args):
for name in args.names:
print('Hallo {}!'.format(name.capitalize()))
parser = argparse.ArgumentParser(
description='Speak messages to the cli')
subparsers = parser.add_subparsers(
title='subcommands',
)
parser_hallo = subparsers.add_parser(
'hallo', help='Print a German greeting to the cli')
parser_hallo.add_argument(
'names',
metavar='NAME', type=str, nargs='+',
help='a persons name',
)
parser_hallo.set_defaults(func=hallo)
def bye(args):
for name in args.names:
print('Tschüss {}!'.format(name.capitalize()))
parser_bye = subparsers.add_parser(
'bye', help='Print a German parting to the cli')
parser_bye.add_argument(
'names',
metavar='NAME', type=str, nargs='+',
help='a persons name',
)
parser_bye.set_defaults(func=bye)
if __name__ == '__main__':
args = parser.parse_args()
args.func(args)
➜ octoeb -h
Usage: octoeb [OPTIONS] COMMAND [ARGS]...
Eventboard releases script
Options:
--log [DEBUG|INFO|WARNING|ERROR|CRITICAL]
Set the log level
--version Show the version and exit.
-h, --help Show this message and exit.
Commands:
changelog Get changelog between base branch and head...
jira (DEV) Call JiraAPI mehtod directly
method (DEV) Call GitHubAPI directly
qa Publish pre-release on GitHub for QA.
release Publish release on GitHub
review Create PR to review your code
start Start new branch for a fix, feature, or a new...
sync Sync fork with mainline Checkout each core...
update Update local branch from the upstream base...
versions Get the current release and pre-release...# cli.py
def validate_version_arg(ctx, param, version):
if version is None:
raise click.BadParameter('Version number is required')
if re.match(r'^(?:\.?\d+){4,5}$', version):
return version
raise click.BadParameter('Invalid versom format: {}'.format(version))
@cli.group()
@click.pass_obj
def start(apis):
"""Start new branch for a fix, feature, or a new release"""
pass
@start.command('release')
@click.option(
'-v', '--version',
callback=validate_version_arg,
help='Major version number of the release to start')
@click.pass_obj
def start_release(apis, version):
"""Start new version branch"""
api = apis.get('mainline')
try:
major_version = extract_major_version(version)
name = 'release-{}'.format(major_version)
branch = api.create_release_branch(name)
except GitHubAPI.DuplicateBranchError as e:
git.fetch('mainline')
git.checkout(name)
branch = api.get_branch(name)
logger.debug('Branch already started')
except Exception as e:
sys.exit(e.message)
try:
git.fetch('mainline')
log = git.log(
'mainline/master', 'mainline/{}'.format(name), merges=True)
changelog = git.changelog(log)
click.echo('Changelog:')
click.echo(changelog)
logger.info('Creating slack channel')
channel_name = 'release_{}'.format(major_version.replace('.', '_'))
slack_create_url = (
'https://zyhpsjavp8.execute-api.us-west-2.amazonaws.com'
'/slack_prod/slack/slash-commands/release-channel'
)
logger.info('Channel name: {}'.format(channel_name))
try:
resp = requests.post(
slack_create_url,
data={
'channel_id': channel_name,
'text': channel_name
})
logger.debug(resp)
except Exception as e:
sys.exit(e.message)
finally:
logger.info('Tagging new version for qa')
qa(apis, version)
except Exception as e:
sys.exit(e.message)
finally:
click.echo('Branch: {} created'.format(name))
click.echo(branch.get('url'))
click.echo('\tgit fetch --all && git checkout {}'.format(name))
sys.exit()# cli.py
def set_logging(ctx, param, level):
numeric_level = getattr(logging, level.upper(), None)
if not isinstance(numeric_level, int):
raise click.BadParameter('Invalid log level: {}'.format(level))
logging.basicConfig(level=numeric_level)
@click.group(context_settings=CONTEXT_SETTINGS)
@click.option(
'--log',
default='ERROR', help='Set the log level',
expose_value=False,
callback=set_logging,
type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']))
@click.version_option('1.2')
@click.pass_context
def cli(ctx):
"""Eventboard releases script"""
# Setup the API
config = ConfigParser.ConfigParser()
config.read([
os.path.expanduser('~/.config/octoeb'),
os.path.expanduser('~/.octoebrc'),
'.octoebrc'
])
try:
validate_config(config)
except Exception as e:
sys.exit('ERROR: {}'.format(e.message))
ctx.obj = {
'mainline': GitHubAPI(
config.get('repo', 'USER'),
config.get('repo', 'TOKEN'),
config.get('repo', 'OWNER'),
config.get('repo', 'REPO')
),
'fork': GitHubAPI(
config.get('repo', 'USER'),
config.get('repo', 'TOKEN'),
config.get('repo', 'FORK'),
config.get('repo', 'REPO')
),
'jira': JiraAPI(
config.get('bugtracker', 'BASE_URL'),
config.get('bugtracker', 'USER'),
config.get('bugtracker', 'TOKEN'),
config.items('bugtracker')
)
}# setup.py
from setuptools import find_packages
from setuptools import setup
setup(
name='octoeb',
version='1.3',
packages=find_packages(),
include_package_data=True,
install_requires=[
'click',
'python-slugify',
'requests',
],
entry_points={
'console_scripts': [
'octoeb=octoeb.cli:cli'
]
},
)Text
CONTEXT_SETTINGS = dict(
help_option_names=['-h', '--help'])
@click.group(context_settings=CONTEXT_SETTINGS)
@click.argument('args', nargs=-1)
@click.option('--opt1', '-o1', nargs=2)
@click.option('--opt2', '-o2', multiple=True)@click.group()
def cli(): pass
@cli.command()
def bar(): pass
@cli.command()
def baz(): pass@click.command()
@click.argument('bars', nargs=-1)
@click.option('--baz', '-b', default='fuzz')
def foo(bars, baz):
click.echo('bars = {}'.format(bars))
click.echo('baz = {}'.format(baz))foo a b c -o barfoo -h
foo --helpfoo bar 1 2 3
foo baz 1 2 3foo a b c -o1 10 20 -o2 x -o2 yJoin Us, We're Hiring!
https://eventboard.io/careers