diff options
author | Christian Cleberg <hello@cleberg.net> | 2025-04-05 13:36:49 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-05 13:36:49 -0500 |
commit | c8617ce8aecb40ccf1d13c02dcd7d7e74e79b56b (patch) | |
tree | cf901030197768b2aa21d2d3fcf0426948a94111 | |
parent | 1fc982100a13de26bfca3419d1a6c5a65acad3d2 (diff) | |
download | yoshi-cli-c8617ce8aecb40ccf1d13c02dcd7d7e74e79b56b.tar.gz yoshi-cli-c8617ce8aecb40ccf1d13c02dcd7d7e74e79b56b.tar.bz2 yoshi-cli-c8617ce8aecb40ccf1d13c02dcd7d7e74e79b56b.zip |
migrate from pylint to ruff-action (#4)
* migrate from pylint to ruff-action
* temporarily disable push param
* Commit from GitHub Actions (Pylint)
* re-enable push param
---------
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
-rw-r--r-- | .github/workflows/pylint.yml | 26 | ||||
-rw-r--r-- | yoshi/__main__.py | 1 | ||||
-rw-r--r-- | yoshi/account.py | 24 | ||||
-rw-r--r-- | yoshi/cli.py | 90 | ||||
-rw-r--r-- | yoshi/crypto.py | 12 | ||||
-rw-r--r-- | yoshi/database.py | 60 | ||||
-rw-r--r-- | yoshi/process.py | 105 |
7 files changed, 169 insertions, 149 deletions
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 6f918b6..caef813 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -1,23 +1,37 @@ name: Pylint -on: [push] +on: + push: + paths: + - '**.py' jobs: build: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.x"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + ref: ${{ github.event.pull_request.head.ref }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pylint cryptography prettytable - - name: Analysing the code with pylint + pip install cryptography prettytable + - name: Install Ruff + uses: astral-sh/ruff-action@v3.2.2 + - name: Ruff Actions run: | - pylint -d R0801 $(git ls-files '*.py') + ruff check --fix + ruff format + - name: Add and Commit + uses: EndBug/add-and-commit@v9 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + default_author: github_actions + pathspec_error_handling: ignore
\ No newline at end of file diff --git a/yoshi/__main__.py b/yoshi/__main__.py index b7e6c6f..a94bac4 100644 --- a/yoshi/__main__.py +++ b/yoshi/__main__.py @@ -4,4 +4,5 @@ Entry point for the yoshi cli app. if __name__ == "__main__": from yoshi.cli import yoshi + yoshi() diff --git a/yoshi/account.py b/yoshi/account.py index fb42008..e92c304 100644 --- a/yoshi/account.py +++ b/yoshi/account.py @@ -11,9 +11,14 @@ from yoshi import database class Account: """Represents a login account.""" - def __init__(self, uuid: str, application: str, #pylint: disable=R0913,R0917 - username: str, #pylint: disable=R0913,R0917 - password: str, url: str) -> None: #pylint: disable=R0913,R0917 + def __init__( + self, + uuid: str, + application: str, # pylint: disable=R0913,R0917 + username: str, # pylint: disable=R0913,R0917 + password: str, + url: str, + ) -> None: # pylint: disable=R0913,R0917 self.uuid = uuid self.application = application self.username = username @@ -22,16 +27,17 @@ class Account: def display_account(self) -> None: """Print the account details.""" - print('ID:', self.uuid) - print('Application:', self.application) - print('Username:', self.username) - print('Password:', self.password) - print('URL:', self.url) + print("ID:", self.uuid) + print("Application:", self.application) + print("Username:", self.username) + print("Password:", self.password) + print("URL:", self.url) def save_account(self) -> None: """Save the account details to the database.""" database.add_account( - self.uuid, self.application, self.username, self.password, self.url) + self.uuid, self.application, self.username, self.password, self.url + ) def delete_account(self) -> bool: """Delete the account from the database. diff --git a/yoshi/cli.py b/yoshi/cli.py index e140098..2e9ce49 100644 --- a/yoshi/cli.py +++ b/yoshi/cli.py @@ -7,91 +7,76 @@ It imports the required modules and sets up a parser with basic options for demo import argparse from yoshi import crypto, database, process + def yoshi(): """ CLI entry point logic, used to parse user inputs. """ parser = argparse.ArgumentParser( - description='Manage your username and passwords via a convenient CLI vault.' + description="Manage your username and passwords via a convenient CLI vault." ) # Top-level arguments group_one = parser.add_mutually_exclusive_group() group_one.add_argument( - '-n', '--new', - help='Create a new account.', - action='store_true' + "-n", "--new", help="Create a new account.", action="store_true" ) group_one.add_argument( - '-l', '--list', - help='List all saved accounts.', - action='store_true' + "-l", "--list", help="List all saved accounts.", action="store_true" ) group_one.add_argument( - '-e', '--edit', - help='Edit a saved account.', - action='store_true' + "-e", "--edit", help="Edit a saved account.", action="store_true" ) group_one.add_argument( - '-d', '--delete', - help='Delete a saved account.', - action='store_true' + "-d", "--delete", help="Delete a saved account.", action="store_true" ) group_one.add_argument( - '--purge', + "--purge", help=( - 'Purge all accounts and delete the vault. ' - '(Caution: this will irreversibly destroy your data.)' + "Purge all accounts and delete the vault. " + "(Caution: this will irreversibly destroy your data.)" ), - action='store_true' - ) - group_one.add_argument( - '--encrypt', - help='Encrypt the vault.', - action='store_true' - ) - group_one.add_argument( - '--decrypt', - help='Decrypt the vault.', - action='store_true' + action="store_true", ) + group_one.add_argument("--encrypt", help="Encrypt the vault.", action="store_true") + group_one.add_argument("--decrypt", help="Decrypt the vault.", action="store_true") # Encryption flags group_two = parser.add_mutually_exclusive_group() group_two.add_argument( - '-g', '--generate', - help=( - 'When using the --encrypt option, generate a new encryption key.' - ), - action='store_true' + "-g", + "--generate", + help=("When using the --encrypt option, generate a new encryption key."), + action="store_true", ) group_two.add_argument( - '-k', '--keyfile', - help='Path to existing key file.', - action='store', + "-k", + "--keyfile", + help="Path to existing key file.", + action="store", nargs=1, - type=str + type=str, ) # Edit flags group_three = parser.add_argument_group() group_three.add_argument( - '-u', '--uuid', - help=( - 'When using the --edit or --delete options, provide the account UUID.' - ), - action='store', + "-u", + "--uuid", + help=("When using the --edit or --delete options, provide the account UUID."), + action="store", nargs=1, - type=str + type=str, ) group_three.add_argument( - '-f', '--field', + "-f", + "--field", help=( - 'When using the --edit option, specify the field to edit (integer index).' + "When using the --edit option, specify the field to edit (integer index)." ), - action='store', + action="store", nargs=1, - type=int + type=int, ) args = parser.parse_args() @@ -100,22 +85,22 @@ def yoshi(): if args.keyfile: key = crypto.load_key(args.keyfile[0]) else: - key = input('Please enter your decryption key: ') + key = input("Please enter your decryption key: ") crypto.decrypt(key) elif args.encrypt: if args.generate: key = crypto.generate_key() print( - 'WRITE THIS KEY DOWN SOMEWHERE SAFE. YOU WILL NOT BE ABLE TO DECRYPT ' - 'YOUR DATA WITHOUT IT!' + "WRITE THIS KEY DOWN SOMEWHERE SAFE. YOU WILL NOT BE ABLE TO DECRYPT " + "YOUR DATA WITHOUT IT!" ) print(key.decode()) - print('\n') + print("\n") else: if args.keyfile: key = crypto.load_key(args.keyfile[0]) else: - key = input('Please enter your encryption key: ') + key = input("Please enter your encryption key: ") crypto.encrypt(key) elif database.check_table(): if args.new: @@ -130,8 +115,9 @@ def yoshi(): process.purge_accounts() else: raise TypeError( - 'Please specify a command or use the --help flag for more information.' + "Please specify a command or use the --help flag for more information." ) + if __name__ == "__main__": yoshi() diff --git a/yoshi/crypto.py b/yoshi/crypto.py index 9b0a423..d85fbe9 100644 --- a/yoshi/crypto.py +++ b/yoshi/crypto.py @@ -6,7 +6,7 @@ It allows for secure encryption and decryption of data using a secret key. from cryptography.fernet import Fernet -VAULT_FILE = 'vault.sqlite' +VAULT_FILE = "vault.sqlite" def generate_key() -> bytes: @@ -21,25 +21,25 @@ def load_key(key_file: str) -> bytes: Args: key_file (str): Path to the key file. """ - with open(key_file, 'rb') as key: + with open(key_file, "rb") as key: return key.read() def encrypt(key: bytes, filename: str = VAULT_FILE) -> None: """Encrypts the data in the specified file using the provided key.""" f = Fernet(key) - with open(filename, 'rb') as vault: + with open(filename, "rb") as vault: data = vault.read() encrypted_data = f.encrypt(data) - with open(filename, 'wb') as vault: + with open(filename, "wb") as vault: vault.write(encrypted_data) def decrypt(key: bytes, filename: str = VAULT_FILE) -> None: """Decrypts the data in the specified file using the provided key.""" f = Fernet(key) - with open(filename, 'rb') as vault: + with open(filename, "rb") as vault: encrypted_data = vault.read() decrypted_data = f.decrypt(encrypted_data) - with open(filename, 'wb') as vault: + with open(filename, "wb") as vault: vault.write(decrypted_data) diff --git a/yoshi/database.py b/yoshi/database.py index e1e2e78..4591955 100644 --- a/yoshi/database.py +++ b/yoshi/database.py @@ -7,8 +7,8 @@ import sqlite3 import sys import os -VAULT_DECRYPTED = 'vault.sqlite' -VAULT_ENCRYPTED = 'vault.sqlite.aes' +VAULT_DECRYPTED = "vault.sqlite" +VAULT_ENCRYPTED = "vault.sqlite.aes" def create_table() -> None: @@ -16,8 +16,8 @@ def create_table() -> None: db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() cursor.execute( - ''' CREATE TABLE IF NOT EXISTS accounts (uuid text, application text, - username text, password text, url text) ''' + """ CREATE TABLE IF NOT EXISTS accounts (uuid text, application text, + username text, password text, url text) """ ) db_connection.commit() db_connection.close() @@ -29,17 +29,18 @@ def check_table() -> bool: db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() cursor.execute( - ''' SELECT count(name) FROM sqlite_master WHERE type='table' - AND name='accounts' ''' + """ SELECT count(name) FROM sqlite_master WHERE type='table' + AND name='accounts' """ ) if cursor.fetchone()[0] != 1: user_choice = input( - 'Password vault does not exist. Would you like to create it now? (y/n): ') - if user_choice.lower() == 'y': + "Password vault does not exist. Would you like to create it now? (y/n): " + ) + if user_choice.lower() == "y": create_table() check = True else: - sys.exit('Program aborted upon user request.') + sys.exit("Program aborted upon user request.") else: check = True db_connection.commit() @@ -47,17 +48,22 @@ def check_table() -> bool: return check -def add_account(uuid: str, application: str, username: str, password: str, - url: str) -> None: +def add_account( + uuid: str, application: str, username: str, password: str, url: str +) -> None: """Add a new account within the vault database.""" db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() cursor.execute( - ''' INSERT INTO accounts VALUES (:uuid,:application,:username, - :password,:url) ''', { - 'uuid': uuid, 'application': application, 'username': username, - 'password': password, 'url': url - } + """ INSERT INTO accounts VALUES (:uuid,:application,:username, + :password,:url) """, + { + "uuid": uuid, + "application": application, + "username": username, + "password": password, + "url": url, + }, ) db_connection.commit() db_connection.close() @@ -67,9 +73,7 @@ def delete_account(uuid: str) -> None: """Delete an account within the vault database by its unique ID.""" db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() - cursor.execute( - ''' DELETE FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid} - ) + cursor.execute(""" DELETE FROM accounts WHERE uuid = :uuid """, {"uuid": uuid}) db_connection.commit() db_connection.close() @@ -78,9 +82,7 @@ def find_account(uuid: str) -> list: """Find an account within the vault database by its unique ID.""" db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() - cursor.execute( - ''' SELECT * FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid} - ) + cursor.execute(""" SELECT * FROM accounts WHERE uuid = :uuid """, {"uuid": uuid}) account = cursor.fetchall() db_connection.close() return account @@ -90,7 +92,7 @@ def find_accounts() -> list: """Return all accounts stored within the vault database.""" db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() - cursor.execute(''' SELECT * FROM accounts ''') + cursor.execute(""" SELECT * FROM accounts """) accounts = cursor.fetchall() db_connection.close() return accounts @@ -99,14 +101,14 @@ def find_accounts() -> list: def update_account(field_name: str, new_value: str, uuid: str) -> None: """Update an account within the vault database by its unique ID.""" queries = { - 'application': 'UPDATE accounts SET application = :new_value WHERE uuid = :uuid', - 'username': 'UPDATE accounts SET username = :new_value WHERE uuid = :uuid', - 'password': 'UPDATE accounts SET password = :new_value WHERE uuid = :uuid', - 'url': 'UPDATE accounts SET url = :new_value WHERE uuid = :uuid' + "application": "UPDATE accounts SET application = :new_value WHERE uuid = :uuid", + "username": "UPDATE accounts SET username = :new_value WHERE uuid = :uuid", + "password": "UPDATE accounts SET password = :new_value WHERE uuid = :uuid", + "url": "UPDATE accounts SET url = :new_value WHERE uuid = :uuid", } db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() - cursor.execute(queries[field_name], {'new_value': new_value, 'uuid': uuid}) + cursor.execute(queries[field_name], {"new_value": new_value, "uuid": uuid}) db_connection.commit() db_connection.close() @@ -115,7 +117,7 @@ def purge_table() -> None: """Purge the 'accounts' table within the vault database.""" db_connection = sqlite3.connect(VAULT_DECRYPTED) cursor = db_connection.cursor() - cursor.execute(''' DROP TABLE accounts ''') + cursor.execute(""" DROP TABLE accounts """) db_connection.commit() db_connection.close() diff --git a/yoshi/process.py b/yoshi/process.py index f09c036..ed9f456 100644 --- a/yoshi/process.py +++ b/yoshi/process.py @@ -32,6 +32,7 @@ from yoshi.account import Account from yoshi import database from yoshi.wordlist import WORDLIST + def generate_characters(n: int) -> list: """ Generates a list of n random characters from the set of ASCII letters, @@ -61,7 +62,7 @@ def shuffle_characters(characters: list) -> str: str: A string representation of the shuffled characters """ random.shuffle(characters) - character_string = ''.join(characters) + character_string = "".join(characters) return character_string @@ -96,7 +97,7 @@ def list_accounts() -> None: None """ accounts = database.find_accounts() - t = PrettyTable(['UUID', 'Application', 'Username', 'Password', 'URL']) + t = PrettyTable(["UUID", "Application", "Username", "Password", "URL"]) for account in accounts: t.add_row([account[0], account[1], account[2], account[3], account[4]]) print(t) @@ -113,13 +114,15 @@ def delete_account(account_uuid: str) -> None: None """ account_record = database.find_account(account_uuid) - account = Account(account_record[0][0], - account_record[0][1], - account_record[0][2], - account_record[0][3], - account_record[0][4]) + account = Account( + account_record[0][0], + account_record[0][1], + account_record[0][2], + account_record[0][3], + account_record[0][4], + ) if account.delete_account(): - print('Account successfully deleted.') + print("Account successfully deleted.") def purge_accounts() -> None: @@ -130,12 +133,15 @@ def purge_accounts() -> None: None """ check = input( - '''Are you absolutely sure you want to delete your password vault? - This action is irreversible. (y/n): ''') - if check.lower() == 'y': + """Are you absolutely sure you want to delete your password vault? + This action is irreversible. (y/n): """ + ) + if check.lower() == "y": database.purge_table() database.purge_database() - print('The password vault has been purged. You may now exit or create a new one.') + print( + "The password vault has been purged. You may now exit or create a new one." + ) def create_account() -> None: @@ -145,46 +151,50 @@ def create_account() -> None: Returns: None """ - application_string = input('Please enter a name for this account: ') - username_string = input('Please enter your username for this account: ') - url_string = input('(Optional) Please enter a URL for this account: ') + application_string = input("Please enter a name for this account: ") + username_string = input("Please enter your username for this account: ") + url_string = input("(Optional) Please enter a URL for this account: ") password_type = input( - '''Do you want a random character password (p), an XKCD-style passphrase -(x), or a custom password (c)? (p|x|c): ''' + """Do you want a random character password (p), an XKCD-style passphrase +(x), or a custom password (c)? (p|x|c): """ ) - if password_type not in ['p', 'x', 'c']: - print('Error: Invalid choice. Please choose p, x, or c.') + if password_type not in ["p", "x", "c"]: + print("Error: Invalid choice. Please choose p, x, or c.") return - if password_type == 'x': + if password_type == "x": password_length = int( - input('Please enter number of words to include (min. 2): ') + input("Please enter number of words to include (min. 2): ") ) if password_length < 3: - print('Error: Your passphrase length must be at least 3 words.') + print("Error: Your passphrase length must be at least 3 words.") return password_separator = input( - 'Please enter your desired separator symbol (_,-, ~, etc.): ' + "Please enter your desired separator symbol (_,-, ~, etc.): " ) password_string = generate_passphrase(password_length, password_separator) - elif password_type == 'p': + elif password_type == "p": password_length = int( - input('Please enter your desired password length (min. 8): ') + input("Please enter your desired password length (min. 8): ") ) if password_length < 8: - print('Error: Your password length must be at least 8 characters.') + print("Error: Your password length must be at least 8 characters.") return password_characters = generate_characters(password_length) password_string = shuffle_characters(password_characters) else: - password_string = input('Please enter your desired password: ') - - account = Account(str(uuid.uuid4()), application_string, - username_string, password_string, url_string) + password_string = input("Please enter your desired password: ") + + account = Account( + str(uuid.uuid4()), + application_string, + username_string, + password_string, + url_string, + ) account.save_account() - print('Account saved to the vault. Use `--list` to see all saved accounts.') - + print("Account saved to the vault. Use `--list` to see all saved accounts.") def edit_account(account_uuid: str, edit_parameter: int) -> None: @@ -197,29 +207,30 @@ def edit_account(account_uuid: str, edit_parameter: int) -> None: Valid values are 1 for application name, 2 for username, 3 for password, and 4 for URL. """ - field_name, new_value = '' + field_name, new_value = "" if edit_parameter == 1: - field_name = 'application' - new_value = input('Please enter your desired Application name: ') + field_name = "application" + new_value = input("Please enter your desired Application name: ") elif edit_parameter == 2: - field_name = 'username' - new_value = input('Please enter your desired username: ') + field_name = "username" + new_value = input("Please enter your desired username: ") elif edit_parameter == 3: - field_name = 'password' + field_name = "password" type_check = input( - 'Do you want a new random password or to enter a custom password? ' - '(random/custom): ').lower() - if type_check == 'random': - password_length = int(input('Please enter your desired password length: ')) + "Do you want a new random password or to enter a custom password? " + "(random/custom): " + ).lower() + if type_check == "random": + password_length = int(input("Please enter your desired password length: ")) if password_length < 8: - print('Error: Your password length must be at least 8 characters.') + print("Error: Your password length must be at least 8 characters.") else: password_characters = generate_characters(password_length) new_value = shuffle_characters(password_characters) else: - new_value = input('Please enter your desired password: ') + new_value = input("Please enter your desired password: ") elif edit_parameter == 4: - field_name = 'url' - new_value = input('Please enter your desired URL: ') + field_name = "url" + new_value = input("Please enter your desired URL: ") database.update_account(field_name, new_value, account_uuid) - print('Account successfully updated.') + print("Account successfully updated.") |