Skip to content

Commit

Permalink
Fully support for running in CI process
Browse files Browse the repository at this point in the history
  • Loading branch information
unfor19 committed Apr 14, 2020
1 parent aa90c90 commit 01f83d8
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 58 deletions.
35 changes: 26 additions & 9 deletions scripts/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ def create_artifacts():


class Config(object):
def __init__(self, raise_error=True):
self.verbose = False
self.home_dir = f"{Path.home()}"
self.ghs_dir = f"{self.home_dir}/.githubsecrets"
self.credentials = f"{self.ghs_dir}/credentials"
self.errors = 0
self.errors_msg = ""
# self.validate(raise_error)
self.ci = False

@staticmethod
def get_credentials_content():
config = Config()
Expand Down Expand Up @@ -60,15 +70,6 @@ def print_response(response):
res['status_code'] = response.status_code
print(json.dumps(res, indent=4, sort_keys=True))

def __init__(self, raise_error=True):
self.verbose = False
self.home_dir = f"{Path.home()}"
self.ghs_dir = f"{self.home_dir}/.githubsecrets"
self.credentials = f"{self.ghs_dir}/credentials"
self.errors = 0
self.errors_msg = ""
self.validate(raise_error)

def deserialize(self):
return {
"artifacts": {
Expand Down Expand Up @@ -100,4 +101,20 @@ def validate(self, raise_error):
error_exit(self.errors_msg)


class Validate(object):
def __init__(self, raise_error=True):
config = Config()
artifacts = config.deserialize()['artifacts']
config.errors_msg = "\nERROR: Missing files/folders\n"
for key, artifact in artifacts.items():
if not artifact['exists']:
config.errors_msg += f"{artifact['path']}\n"
config.errors += 1

if config.errors > 0 and raise_error:
config.errors_msg += "Fix it by executing: ghs init\n"
error_exit(config.errors_msg)


pass_config = click.make_pass_decorator(Config, ensure=True)
pass_validate = click.make_pass_decorator(Validate, ensure=True)
80 changes: 57 additions & 23 deletions scripts/githubsecrets.py
Original file line number Diff line number Diff line change
@@ -1,92 +1,126 @@
import click
from .config import pass_config, create_artifacts
from .config import pass_config, pass_validate, create_artifacts
from .profile import Profile
from .secret import Secret


@click.group()
def cli():
@pass_config
@click.option('--ci', is_flag=True, help="Use this flag to avoid deletion confirmation prompts") # noqa: E501
def cli(config, ci):
"""All commands can run without providing options, and then you'll be prompted to insert values.\n
Secrets' values and Personal-Access-Tokens are hidden when prompted""" # noqa: E501
pass
config.ci = ci # noqa: F821


@cli.command()
def init():
@pass_config
def init(config):
"""Create a credentials file to store your profiles"""
create_artifacts()


@cli.command()
@pass_validate
@pass_config
@click.option('--profile-name', '-p', prompt=True)
@click.option('--github-owner', '-o', prompt=True)
@click.option('--personal-access-token', '-t', prompt=True, hide_input=True)
def profile_apply(config, profile_name, github_owner, personal_access_token):
@click.option(
'--personal-access-token', '-t', prompt=True,
hide_input=True, confirmation_prompt=True
)
def profile_apply(
config, validate,
profile_name, github_owner, personal_access_token
):
"""Create or modify a profile"""
profile = Profile(profile_name)
profile = Profile(config, profile_name)
profile.apply(github_owner, personal_access_token)


@cli.command()
@pass_validate
@pass_config
@click.option('--profile-name', '-p', prompt=True)
def profile_delete(config, profile_name):
def profile_delete(
config, validate,
profile_name
):
"""Delete a profile"""
profile = Profile(profile_name)
profile = Profile(config, profile_name)
profile.delete()


@cli.command()
@pass_validate
@pass_config
def profile_list(config):
def profile_list(config, validate):
"""List all profile - truncates personal access tokens"""
Profile.lista()


@cli.command()
@pass_validate
@pass_config
@click.option('--repository', '-r', prompt=True)
@click.option('--profile-name', '-p', prompt=True)
@click.option('--secret-name', '-s', prompt=True)
@click.option('--secret-value', '-v', prompt=True, hide_input=True) # noqa: E501
def secret_apply(config, repository, profile_name, secret_name, secret_value):
@click.option(
'--secret-value', '-v', prompt=True,
hide_input=True, confirmation_prompt=True
)
def secret_apply(
config, validate,
repository, profile_name, secret_name, secret_value
):
"""Create or modify a secret in a GitHub repository"""
profile = Profile(profile_name)
secret = Secret(profile, repository, secret_name, secret_value)
profile = Profile(config, profile_name)
secret = Secret(config, profile, repository, secret_name, secret_value)
secret.apply()


@cli.command()
@pass_validate
@pass_config
@click.option('--repository', '-r', prompt=True)
@click.option('--profile-name', '-p', prompt=True)
@click.option('--secret-name', '-s', prompt=True)
def secret_delete(config, repository, profile_name, secret_name):
def secret_delete(
config, validate,
repository, profile_name, secret_name
):
"""Delete a secret in a GitHub repository"""
profile = Profile(profile_name)
secret = Secret(profile, repository, secret_name)
profile = Profile(config, profile_name)
secret = Secret(config, profile, repository, secret_name)
secret.delete()


@cli.command()
@pass_validate
@pass_config
@click.option('--repository', '-r', prompt=True)
@click.option('--profile-name', '-p', prompt=True)
@click.option('--secret-name', '-s', prompt=True)
def secret_get(config, repository, profile_name, secret_name):
def secret_get(
config, validate,
repository, profile_name, secret_name
):
"""Get a secret from a GitHub repository"""
profile = Profile(profile_name)
secret = Secret(profile, repository, secret_name)
profile = Profile(config, profile_name)
secret = Secret(config, profile, repository, secret_name)
secret.get()


@cli.command()
@pass_validate
@pass_config
@click.option('--repository', '-r', prompt=True)
@click.option('--profile-name', '-p', prompt=True)
def secret_list(config, repository, profile_name):
def secret_list(
config, validate,
repository, profile_name
):
"""List all secret in a GitHub repository"""
profile = Profile(profile_name)
secret = Secret(profile, repository)
profile = Profile(config, profile_name)
secret = Secret(config, profile, repository)
secret.lista()
11 changes: 9 additions & 2 deletions scripts/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@


class Profile():
def __init__(self, name):
def __init__(self, config, name):
self.name = name
self.credentials_content = Config.get_credentials_content()
self.github_owner = ''
self.personal_access_token = ''
self.config = config
if self.credentials_content and self.name in self.credentials_content:
self.github_owner = self.credentials_content[self.name]['github_owner'] # noqa: E501
self.personal_access_token = self.credentials_content[self.name]['personal_access_token'] # noqa: E501
Expand Down Expand Up @@ -55,7 +56,13 @@ def get(self):
def delete(self):
if self.name in self.credentials_content:
del self.credentials_content[self.name]
if click.confirm(f"Are you sure want to delete the profile {self.name} ?"): # noqa: E501
confirm = False
if self.config.ci:
confirm = True
elif click.confirm(f"Are you sure want to delete the profile {self.name} ?"): # noqa: E501
confirm = True

if confirm:
write_success = Config.set_credentials_content(
self.credentials_content)
if write_success:
Expand Down
44 changes: 26 additions & 18 deletions scripts/secret.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,28 @@
import json
from base64 import b64encode
from nacl import encoding, public
from .config import error_exit

# Reference: https://developer.github.com/v3/actions/secrets/


class Secret():
def __init__(self, config, profile, repository, name='', value=''):
self.profile = profile
if not profile.github_owner:
error_exit(f"""FAILED: Profile doesn't exist - {self.profile.name}
Create it by executing:\nghs profile-apply -p {self.profile.name}
""")
self.repository = repository
self.name = name.strip()
self.value = value.strip()
self.base_url = "/".join([
"https://api.github.com/repos",
self.profile.github_owner,
self.repository
])
self.config = config

@staticmethod
def encrypt(public_key: str, secret_value: str) -> str:
"""
Expand Down Expand Up @@ -46,22 +63,6 @@ def get_public_key(self) -> requests.request:
"""Get the repository's public key, used when creating/updating a secret""" # noqa: E501
self.public_key = self.request('get', 'actions/secrets/public-key')

def __init__(self, profile, repository, name='', value=''):
self.profile = profile
if not profile.github_owner:
click.echo(f"""FAILED: Profile doesn't exist - {self.profile.name}
Fix with: ghs profile-apply -p {self.profile.name}
""")
exit()
self.repository = repository
self.name = name.strip()
self.value = value.strip()
self.base_url = "/".join([
"https://api.github.com/repos",
self.profile.github_owner,
self.repository
])

def apply(self):
"""Create or update a secret"""

Expand Down Expand Up @@ -101,8 +102,15 @@ def lista(self):

def delete(self):
"""Delete a secret"""
response = self.request('delete', f"actions/secrets/{self.name}")
Secret.print_response(response)
confirm = False
if self.config.ci:
confirm = True
elif click.confirm(f"Are you sure want to delete the secret {self.name} ?"): # noqa: E501
confirm = True

if confirm:
response = self.request('delete', f"actions/secrets/{self.name}")
Secret.print_response(response)

def get(self):
"""Get a secret"""
Expand Down
21 changes: 15 additions & 6 deletions test_functionality.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
#!/bin/bash
PYTHON_VERSION=$(python -c 'import platform; print(platform.python_version())')
PYTHON_VERSION=$(echo $PYTHON_VERSION | sed "s|\.||g")
PROFILE_NAME=unfor19
REPOSITORY_NAME=githubsecrets

echo ">> INIT"
ghs init
ghs --ci init
echo ">> List profiles - no profiles yet"
ghs --ci profile-list
echo ">> Add profile"
ghs profile-apply -p unfor19 -o unfor19 -t $GITHUB_TOKEN
ghs --ci profile-apply -p $PROFILE_NAME -o $PROFILE_NAME -t $GITHUB_TOKEN
echo ">> List profiles - Should see $PROFILE_NAME"
ghs --ci profile-list
echo ">> Add secret - TEST_${PYTHON_VERSION}"
ghs secret-apply -p unfor19 -r githubsecrets -s "TEST_${PYTHON_VERSION}" -v oompaloompa
ghs --ci secret-apply -p $PROFILE_NAME -r $REPOSITORY_NAME -s "TEST_${PYTHON_VERSION}" -v oompaloompa
echo ">> List secrets"
ghs secret-list -p unfor19 -r githubsecrets
ghs --ci secret-list -p $PROFILE_NAME -r $REPOSITORY_NAME
echo ">> Get secret - TEST_${PYTHON_VERSION}"
ghs secret-get -p unfor19 -r githubsecrets -s "TEST_${PYTHON_VERSION}"
ghs --ci secret-get -p $PROFILE_NAME -r $REPOSITORY_NAME -s "TEST_${PYTHON_VERSION}"
echo ">> Delete secret - TEST_${PYTHON_VERSION}"
ghs secret-delete -p unfor19 -r githubsecrets -s "TEST_${PYTHON_VERSION}"
ghs --ci secret-delete -p $PROFILE_NAME -r $REPOSITORY_NAME -s "TEST_${PYTHON_VERSION}"
echo ">> Delete profile - $PROFILE_NAME"
ghs --ci profile-delete -p $PROFILE_NAME
echo ">> Remove credentials file"
rm -r ~/.githubsecrets

0 comments on commit 01f83d8

Please sign in to comment.