Skip to content

Commit

Permalink
Converted interface to use click library
Browse files Browse the repository at this point in the history
  • Loading branch information
woodb committed May 12, 2014
1 parent 552e250 commit b55c67f
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 120 deletions.
68 changes: 58 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,72 @@
rural
=====

Simple command line utility for uploading files to AWS S3, copying a public link
to that file to the clipboard.
Simple command line utility for uploading files to Amazon Web Services (AWS)
Simple Storage Service (S3) and copying a public link to that file to the
clipboard.

Installation
------------
```
$ git clone https://github.com/woodb/rural.git
$ cd rural
$ sudo python setup.py install
$ rural --configure
```zsh
pip install rural
```

Usage
-----
After configuring rural, you can upload any file via the following command:
Once ```rural``` has been configured, it's a simple command line interface.

```zsh
$ rural myfile.pdf
https://my-s3-bucket.s3.amazonaws.com/myfile.pdf
$ pbpaste
https://my-s3-bucket.s3.amazonaws.com/myfile.pdf
```

To show more information, you can use multiple verbosity flags.

```zsh
$ rural -vv -b my-other-bucket myfile2.pdf
AWS Access Key ID: LsUCBGhw7qZZLUmlmhvtX
AWS Secret Access Key: mKDbRLKUmhocgVepept6QRLDv6GkBkNG1AxOnr
Bucket: my-other-bucket
[========================================] 100%
https://my-other-bucket.s3.amazonaws.com/myfile2.pdf
```

For other options, use the ```--help``` flag.


Configuration
-------------
```rural``` requires both an AWS access key ID and AWS secret access key that
allow for access to a specified S3 bucket.

You can configure rural with environment variables ```AWS_ACCESS_KEY_ID```,
```AWS_SECRET_ACCESS_KEY```, and ```RURAL_BUCKET_NAME```. We recommend that
you set these in your environment configuration files (e.g. ```~/.zshrc```).

```zsh
$ export AWS_ACCESS_KEY_ID=LsUCBGhw7qZZLUmlmhvtX
$ export AWS_SECRET_ACCESS_KEY=mKDbRLKUmhocgVepept6QRLDv6GkBkNG1AxOnr
$ export RURAL_BUCKET_NAME=my-s3-bucket
$ rural myfile.pdf
$
https://my-s3-bucket.s3.amazonaws.com/myfile.pdf
$ pbpaste
https://my-s3-bucket.s3.amazonaws.com/myfile.pdf
```

Additionally, ```rural``` configuration can be overridden at runtime.

```zsh
$ rural --aws-access-key-id=LsUCBGhw7qZZLUmlmhvtX \
> --aws-secret-access-key=mKDbRLKUmhocgVepept6QRLDv6GkBkNG1AxOnr \
> --bucket-name=my-s3-bucket \
> myfile.pdf
https://my-s3-bucket.s3.amazonaws.com/myfile.pdf
$ pbpaste
https://my-s3-bucket.s3.amazonaws.com/myfile.pdf
```

This will put the public link to the file into the clipboard and is valid for 24 hours.
Compatibility
-------------
```rural``` requires Python 2.6 or higher, and might not work on Windows.
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
boto
xerox
pyyaml
boto==2.27.0
click==0.7
xerox==0.3.1
138 changes: 36 additions & 102 deletions rural
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ rural
Simple command line utility for uploading local files to Amazon Web Services
(AWS) Simple Storage Service (S3).
"""

CONFIG_MISSING_MESSAGE = """rural has not yet been configured, or the configuration
file appears to be missing. Run the configuration wizard with `rural --configure`
"""

import click
from xerox import copy
from boto.s3.connection import S3Connection
from boto.s3.key import Key
Expand All @@ -28,7 +24,7 @@ class RuralSession(object):
self.aws_access_key_id = aws_access_key_id
self.aws_secret_access_key = aws_secret_access_key
self.bucket_name = bucket_name
self._connect()
self._check_config()

def _check_config(self):
if self.aws_access_key_id and self.aws_secret_access_key and self.bucket_name:
Expand All @@ -37,14 +33,11 @@ class RuralSession(object):
raise EnvironmentError("Rural object configuration incomplete")

def _connect(self):
if self.connection is None:
self.connection = S3Connection(
aws_access_key_id=self.aws_access_key_id,
aws_secret_access_key=self.aws_secret_access_key
)

if self.bucket is None:
self.bucket = Bucket(self.connection, self.bucket_name)
self.connection = S3Connection(
aws_access_key_id=self.aws_access_key_id,
aws_secret_access_key=self.aws_secret_access_key
)
self.bucket = Bucket(self.connection, self.bucket_name)

def upload(self, filename, cb=None):
"""
Expand All @@ -54,7 +47,6 @@ class RuralSession(object):
:param fh: file handle to upload to S3
"""
self._check_config()

self.key = Key(self.bucket)
self.key.key = filename
self.key.set_contents_from_filename(filename, cb=cb)
Expand All @@ -65,115 +57,57 @@ class RuralSession(object):
self.url = ''.join(self.url.split('?')[:-1])



def _create_config(fh):
"""
rural._create_config
~~~~~~~~~~~~~~~~~~~~
Prompts to create the configuration file.
"""
sys.stdout.write("AWS Access Key ID: ")
aws_access_key = raw_input().upper()

sys.stdout.write(" AWS Secret Key: ")
aws_secret_key = raw_input()

sys.stdout.write(" Default Bucket: ")
default_bucket_name = raw_input()

fh.write(yaml.dump({'aws_access_key' : aws_access_key,
'aws_secret_key' : aws_secret_key,
'bucket_name' : default_bucket_name},
Dumper=Dumper))


def _load_config(fh):
cfg_data = yaml.load(fh)
return cfg_data['aws_access_key'], cfg_data['aws_secret_key'], \
cfg_data['bucket_name']


def _cb_progressbar(uploaded_bytes, total_bytes):
"""Callback function that outputs a progressbar to stderr based on what's
up"""
import sys

pbw = 80 # progress bar width

progress_percent = float(uploaded_bytes) / float(total_bytes)

sys.stderr.write("\r[%s%s] %d%%" % (
'=' * int(progress_percent * pbw / 2),
' ' * (pbw / 2- int(progress_percent * pbw / 2)),
int(progress_percent * 100)))

if progress_percent == 1:
sys.stderr.write("\n")


def _main():
"""
rural._main
~~~~~~~~~~~
Handles option parsing, configuration management and command execution.
@click.command()
@click.option('--aws-access-key-id', envvar='AWS_ACCESS_KEY_ID',
help='AWS Access Key ID for the AWS API')
@click.option('--aws-secret-access-key', envvar='AWS_SECRET_ACCESS_KEY',
help='AWS Secret Access Key for the AWS API')
@click.option('--bucket-name', '-b', envvar='RURAL_BUCKET_NAME',
help='bucket name to which to upload/link files', required=True)
@click.option('--verbose', '-v', count=True)
@click.argument('filename', required=True)
def upload_copy(aws_access_key_id, aws_secret_access_key, bucket_name,
verbose, filename):
"""
# Parse command line arguments
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("filename", help="name of file to upload", nargs='?',
default=None)
parser.add_argument("--configure", dest="configure", action="store_true")
args = parser.parse_args()
if args.filename:
filename = args.filename
else:
raise IOError("filename must be defined at runtime")
Uploads a local file to AWS S3 and copy a publicly accessible link to the
file to the clipboard.

# Configuration
# ~~~~~~~~~~~~~
config_file = None
if os.getenv("HOME"):
config_file = os.path.join(os.getenv("HOME"), ".rural")
else:
raise NotImplementedError, "rural currently only supports POSIX \
compliant environments (i.e. rural does not work on Windows)"
sys.exit(1)
Environment variables can be set to configure this script, namely:
try:
config_fh = open(config_file, "r")
except IOError:
if not args.configure:
print CONFIG_MISSING_MESSAGE
sys.exit(1)
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY_ID
RURAL_BUCKET_NAME
if args.configure:
config_fh = open(config_file, 'w')
_create_config(config_fh)
sys.exit(0)

aws_access_key_id, aws_secret_access_key, bucket_name = _load_config(config_fh)
rural_session = RuralSession(aws_access_key_id=aws_access_key_id,
"""
cb = None
if verbose > 0:
cb = _cb_progressbar
if verbose > 1:
print("AWS Access Key ID:\t{}".format(aws_access_key_id))
print("AWS Secret Access Key:\t{}".format(aws_secret_access_key))
print("Bucket:\t{}".format(bucket_name))
rural_session = RuralSession(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
bucket_name=bucket_name)
rural_session.upload(filename, cb=_cb_progressbar)
rural_session.upload(filename, cb=cb)
rural_session.publicize()
copy(rural_session.url)
print rural_session.url


if __name__ == '__main__':
import os
import sys

import yaml
try:
from yaml import CLoader as Loader, CDumper as Dumper
except ImportError:
from yaml import Loader, Dumper
except Exception as exc:
raise exc
sys.exit(1)

_main()
upload_copy()
9 changes: 4 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup, find_packages

__author__ = "Brandon Wood"
__copyright__ = "Copyright 2013, Brandon Wood"
__copyright__ = "Copyright 2014, Brandon Wood"
__license__ = "BSD"

__version__ = "0.0.1"
__version__ = "0.0.2"
__maintainer__ = "Brandon Wood"
__email__ = "btwood+rural@h6o6.com"
__email__ = "btwood+rural@geometeor.com"
__status__ = "Development"

setup(name='rural',
Expand All @@ -22,5 +21,5 @@
url='https://github.com/woodb/rural',
packages=find_packages(),
scripts=['rural'],
install_requires=['boto', 'pyyaml', 'xerox'],
install_requires=['boto', 'click', 'xerox'],
)

0 comments on commit b55c67f

Please sign in to comment.