Skip to content

Commit

Permalink
Merge pull request #2 from unparalleled-js/fix/env-var
Browse files Browse the repository at this point in the history
fix: issue preventing scoped environment variables from working correctly
  • Loading branch information
antazoey authored Sep 6, 2022
2 parents 9894e0a + a35eeb8 commit 4d166ff
Show file tree
Hide file tree
Showing 18 changed files with 380 additions and 217 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ $ ape plugins list
```

* Python Version: x.x.x
* OS: osx/linux/win
* OS: macOS/linux/win

### What went wrong?

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest] # eventually add `windows-latest`
python-version: [3.7, 3.8, 3.9]
python-version: [3.8, 3.9, "3.10"]

steps:
- uses: actions/checkout@v2
Expand Down
9 changes: 4 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v4.2.0
hooks:
- id: check-yaml

Expand All @@ -10,21 +10,20 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 22.3.0
rev: 22.6.0
hooks:
- id: black
name: black

- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
rev: 4.0.1
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.950
rev: v0.971
hooks:
- id: mypy
additional_dependencies: [types-PyYAML, types-requests]


default_language_version:
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ cd ape-keyring
python3 -m venv venv
source venv/bin/activate

# install brownie into the virtual environment
# install ape-keyring into the virtual environment
python setup.py install

# install the developer dependencies (-e is interactive mode)
pip install -e .[dev]
pip install -e .'[dev]'
```

## Pre-Commit Hooks
Expand Down
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# ape-keyring
# Quickstart

Store secrets and manage accounts for `ape` using [keyring](https://pypi.org/project/keyring/).
Avoid hard-coding secrets in your projects using `ape-keyring`
`ape-keyring` is built on top of [keyring](https://pypi.org/project/keyring/) and is an account plugin and secret manager.
By default, `keyring` uses your OS's secure storage and prompts for authorization upon request.
Thus, `keyring` is useful for securely managing local developer environments.

This guide demonstrates how to use `ape-keyring` as an account plugin and secret manager.

## Dependencies

* [python3](https://www.python.org/downloads) version 3.7 or greater, python3-dev
* [python3](https://www.python.org/downloads) version 3.8 or greater, python3-dev

## Installation

Expand All @@ -28,15 +33,43 @@ python3 setup.py install

## Quick Usage

Add accounts to keyring:
### Accounts

Use `ape-keyring` as an account plugin.
You can add existing accounts to keyring to use in your scripts, console, or tests:

```bash
ape keyring import keyring_dev_0
```

and then when it prompts you, input your private key.
It then securely prompts you for your private key.

**NOTE**: You can only add existing accounts to keyring and generate new ones.

You can delete accounts by doing:

```bash
ape keyring accounts delete <alias>
```

This only deletes the account from keyring and not the blockchain.

To add secrets to keyring:
To remove all your keyring accounts, run the command:

```bash
ape keyring accounts delete-all
```

Finally, list your accounts by doing:

```bash
ape keyring accounts list
```

### Secrets

Use `ape-keyring` as a secrets managers, such as Infura project IDs, Etherscan API keys, your mother's maiden name.
To add secrets to keyring, do:

```bash
ape keyring set WEB3_API_KEY
Expand All @@ -57,7 +90,3 @@ use the `ape-config.yaml` option `set_env_vars`:
keyring:
set_env_vars: true
```
## License
This project is licensed under the [Apache 2.0](LICENSE).
13 changes: 8 additions & 5 deletions ape_keyring/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
from pathlib import Path

from ape import plugins
import ape.plugins

from ._secrets import get_secret_manager
from ._secrets import Scope, get_secret_manager
from .accounts import KeyringAccount, KeyringAccountContainer
from .config import KeyringConfig


@plugins.register(plugins.Config)
@ape.plugins.register(ape.plugins.Config)
def config_class():
return KeyringConfig


@plugins.register(plugins.AccountPlugin)
@ape.plugins.register(ape.plugins.AccountPlugin)
def account_types():
return KeyringAccountContainer, KeyringAccount


# Sync environment variables if configured to do so.
get_secret_manager(Path.cwd()).set_environment_variables()
secret_manager = get_secret_manager(Path.cwd())
secret_manager.set_environment_variables()

__all__ = ["Scope", "secret_manager"]
9 changes: 4 additions & 5 deletions ape_keyring/_cli/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from ape import accounts
from ape.cli import ape_cli_context, existing_alias_argument, non_existing_alias_argument

from ape_keyring.accounts import KeyringAccount
from ape_keyring.utils import get_eth_account


Expand All @@ -16,7 +15,7 @@ def account_cli():
def _list(cli_ctx):
"""List accounts"""

keyring_accounts = [a for a in accounts if isinstance(a, KeyringAccount)]
keyring_accounts = list(accounts.containers["keyring"].accounts)

if not keyring_accounts:
cli_ctx.logger.warning("No accounts found.")
Expand All @@ -34,7 +33,7 @@ def _list(cli_ctx):
@non_existing_alias_argument()
@ape_cli_context()
def _import(cli_ctx, alias):
"""Add a new account"""
"""Add a private key to keyring"""

key = click.prompt("Enter the private key", hide_input=True)
eth_account = get_eth_account(key)
Expand All @@ -52,7 +51,7 @@ def _import(cli_ctx, alias):
@ape_cli_context()
@existing_alias_argument()
def delete(cli_ctx, alias):
"""Delete an account"""
"""Remove a private key from keyring"""

container = accounts.containers["keyring"]
container.delete_account(alias)
Expand All @@ -62,7 +61,7 @@ def delete(cli_ctx, alias):
@account_cli.command()
@ape_cli_context()
def delete_all(cli_ctx):
"""Delete all keyring accounts"""
"""Delete all private keys from keyring"""

container = accounts.containers["keyring"]
container.delete_all()
Expand Down
16 changes: 9 additions & 7 deletions ape_keyring/_cli/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def _list(cli_ctx):
"""List secrets"""

secret_manager = get_secret_manager(cli_ctx.project_manager.path)

if not secret_manager.secrets_exist:
cli_ctx.logger.warning("No secrets found.")
return
Expand All @@ -33,20 +32,21 @@ def output_secret_list(header: str, secret_keys: List[str]) -> bool:

return True

did_output = output_secret_list("Global secrets", secret_manager.global_secrets)
if did_output and secret_manager.project_secrets:
did_output = output_secret_list("Global secrets", secret_manager.global_keys)
if did_output and secret_manager.project_keys:
click.echo()

output_secret_list("Project secrets", secret_manager.project_secrets)
output_secret_list("Project secrets", secret_manager.project_keys)


@secrets.command(name="set")
@secret_argument()
@ape_cli_context()
@scope_option()
def _set(cli_ctx, secret, scope):
@click.option("--value", help="(insecure) the secret value, prompts if not given")
def _set(cli_ctx, secret, scope, value):
"""Add or replace a secret"""
value = click.prompt(f"Enter the secret value for '{secret}'", hide_input=True)
value = value or click.prompt(f"Enter the secret value for '{secret}'", hide_input=True)
secret_manager = get_secret_manager(cli_ctx.project_manager.path)
secret_manager.store_secret(secret, value, scope=scope)
cli_ctx.logger.success(f"Secret '{secret}' has been set.")
Expand All @@ -67,19 +67,21 @@ def delete(cli_ctx, secret, scope):
do_delete = click.confirm(f"Delete project-scoped secret '{secret}'?")
if do_delete:
did_delete = secret_manager.delete_secret(secret, scope=scope.PROJECT)

else:
project_output = (
f"(project={secret_manager.project_name})" if scope == Scope.PROJECT else ""
)
message = f"Failed to delete secret '{secret}'"
if project_output:
message = f"{message} {project_output}"

cli_ctx.logger.warning(f"{message}.")

if did_delete:
message = f"Secret '{secret}' "
if secret_manager.project_name:
message = f"{message}(project={secret_manager.project_name}) "
message = f"{message}has been unset."

message = f"{message}has been unset."
cli_ctx.logger.success(message)
Loading

0 comments on commit 4d166ff

Please sign in to comment.