Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aliases #25

Merged
merged 13 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ jobs:
run: poetry install
- name: Lint
run: |
poetry run ruff keycmd
poetry run black --check keycmd
EXIT_STATUS=0
poetry run ruff check keycmd || EXIT_STATUS=$?
poetry run ruff format --check keycmd || EXIT_STATUS=$?
exit $EXIT_STATUS

test:
name: Test
Expand Down
2 changes: 1 addition & 1 deletion keycmd/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.6.0"
__version__ = "0.7.0"
1 change: 0 additions & 1 deletion keycmd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from .logs import error, log, set_verbose
from .shell import run_cmd, run_shell


cli = argparse.ArgumentParser(
prog="keycmd",
)
Expand Down
1 change: 0 additions & 1 deletion keycmd/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from .logs import vlog


# exposed for testing
USERPROFILE = "~"

Expand Down
45 changes: 41 additions & 4 deletions keycmd/creds.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,23 @@ def b64(value):
return base64.b64encode(value.encode("utf-8")).decode("utf-8")


def expose(env, key, credential, username, password, apply_b64, format_string):
if format_string:
password = format_string.format(
credential=credential,
username=username,
password=password,
)
if apply_b64:
password = b64(password)
env[key] = password


def get_env(conf):
"""Load credentials from the OS keyring according to user configuration"""
env = environ.copy()

key_data = {}
for key, src in conf["keys"].items():
password = keyring.get_password(src["credential"], src["username"])
if password is None:
Expand All @@ -22,12 +36,35 @@ def get_env(conf):
f" with user {src['username']}"
f" as it does not exist"
)
if src.get("b64"):
password = b64(password)
env[key] = password
apply_b64 = src.get("b64", False)
format_string = src.get("format")
key_data[key] = (
src["credential"],
src["username"],
password,
apply_b64,
format_string,
)
expose(env, key, *key_data[key])
vlog(
f"exposing credential {src['credential']}"
f" with user {src['username']}"
f" as environment variable {key} (b64: {src.get('b64', False)})"
f" as environment variable {key}"
f" (b64: {apply_b64}, format: {format_string})"
)

for alias, src in conf.get("aliases", {}).items():
key = key_data.get(src["key"])
if key is None:
error(f"MISSING alias key {src['key']}")
# re-use base data but replace apply_b64 and format_string
credential, username, password, _, _ = key
apply_b64 = src.get("b64", False)
Korijn marked this conversation as resolved.
Show resolved Hide resolved
format_string = src.get("format")
expose(env, alias, credential, username, password, apply_b64, format_string)
vlog(
f"aliasing {src['key']}"
f" as environment variable {alias}"
f" (b64: {apply_b64}, format: {format_string})"
)
return env
1 change: 0 additions & 1 deletion keycmd/logs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import sys


_verbose = False


Expand Down
7 changes: 4 additions & 3 deletions keycmd/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
from subprocess import run
from sys import exit

from shellingham import detect_shell, ShellDetectionFailure
from shellingham import ShellDetectionFailure, detect_shell

from .logs import vlog, vwarn


USE_SUBPROCESS = False # exposed for testing
IS_WINDOWS = os.name == "nt"
IS_POSIX = os.name == "posix"
Expand Down Expand Up @@ -55,9 +54,11 @@ def run_shell(env=None):
def run_cmd(cmd, env=None):
"""Run a one-off command in a shell."""
shell_name, shell_path = get_shell()
opt = "-c"
if shell_name == "cmd":
opt = "/C"
else:
opt = "-c"
cmd = [" ".join(cmd)]
full_command = [shell_path, opt, *cmd]
vlog(f"running command: {pformat(full_command)}")
exec(full_command, env)
656 changes: 264 additions & 392 deletions poetry.lock

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
[tool.poetry]
name = "keycmd"
version = "0.6.0"
version = "0.7.0"
description = ""
authors = ["Korijn van Golen <[email protected]>"]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = ">=3.9"
keyring = "^24.2.0"
keyring = "^24.3.0"
tomli = "^2.0.1"
shellingham = "^1.5.0.post1"
shellingham = "^1.5.4"

[tool.poetry.scripts]
keycmd = 'keycmd.cli:main'

[tool.poetry.group.dev.dependencies]
ruff = "^0.0.278"
black = "^23.7.0"
ruff = "^0.1.8"
twine = "^4.0.2"
pytest = "^7.4.0"
pytest = "^7.4.3"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.ruff]
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"N", # pep8-naming
"ARG", # flake8-unused-arguments
"T10", # flake8-debugger
"RUF", # Ruff-specific rules
"E4",
"E5",
"E7",
"E9",
"F", # Pyflakes (default)
"I", # isort imports
"N", # pep8-naming
"T10", # flake8-debugger
"T20", # flake8-print
"RUF", # ruff
]

[tool.ruff.isort]
force-sort-within-sections = true
order-by-type = false
lines-after-imports = 2
[tool.ruff.per-file-ignores]
"keycmd/logs.py" = ["T201"]

# example config for development and testing
[tool.keycmd.keys]
ARTIFACTS_TOKEN = { credential = "azure@poetry-repository-main", username = "azure" }
ARTIFACTS_TOKEN_B64 = { credential = "azure@poetry-repository-main", username = "azure", b64 = true }

[tool.keycmd.aliases]
ARTIFACTS_TOKEN_B64 = { key = "ARTIFACTS_TOKEN", b64 = true }
9 changes: 4 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import os
from functools import cache
from pathlib import Path
from subprocess import run
from functools import cache

import pytest
import keyring
import pytest

import keycmd.conf
import keycmd.shell
from keycmd.shell import get_shell
from keycmd import __version__
from keycmd.cli import main, cli

from keycmd.cli import cli, main
from keycmd.shell import get_shell

varname = "KEYCMD_TEST"
key = "__keycmd_testß"
Expand Down
10 changes: 5 additions & 5 deletions tests/test_conf.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import os
from pathlib import Path

import tomli
import pytest
import tomli

import keycmd.conf
from keycmd.conf import (
defaults,
find_file,
load_toml,
load_conf,
load_pyproj,
defaults,
load_toml,
merge_conf,
load_conf,
)


Expand Down Expand Up @@ -66,7 +66,7 @@ def test_find_file(ch_tmpdir):
p1 = create_path("../.blabla")
p2 = create_path("../../.blabla")
p3 = create_path("../../../.blabla")
p4 = create_path("~/.blabla")
create_path("~/.blabla")
assert find_file(".blabla") == p1
assert find_file(".blabla", first_only=False) == [p3, p2, p1]
(p2.parent / ".git").mkdir(exist_ok=True, parents=True)
Expand Down
38 changes: 32 additions & 6 deletions tests/test_creds.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,40 @@ def test_get_env(credentials):
"username": username,
"b64": True,
},
}
"__FOOBAR_BASICAUTH": {
"credential": key,
"username": username,
"format": "{username}:{password}",
"b64": True,
},
},
"aliases": {
"__FOOBAR_B64_ALIAS": {
"key": "__FOOBAR",
"b64": True,
},
"__FOOBAR_BASICAUTH_ALIAS": {
"key": "__FOOBAR",
"format": "{username}:{password}",
"b64": True,
},
"__FOOBAR_BASICAUTH_ALIAS2": {
"key": "__FOOBAR_B64",
"format": "{username}:{password}",
"b64": True,
},
},
}
env = get_env(conf)
assert "__FOOBAR" not in environ
assert "__FOOBAR_B64" not in environ
all_keys = list(conf["keys"].keys())
all_keys.extend(list(conf.get("aliases", {}).keys()))
for k in all_keys:
assert k not in environ
assert env.get("__FOOBAR") == password
assert env.get("__FOOBAR_B64") == b64(password)
assert env.get("__FOOBAR_BASICAUTH") == b64(f"{username}:{password}")
assert env.get("__FOOBAR_B64_ALIAS") == b64(password)
assert env.get("__FOOBAR_BASICAUTH_ALIAS") == b64(f"{username}:{password}")
assert env.get("__FOOBAR_BASICAUTH_ALIAS2") == b64(f"{username}:{password}")
assert set(environ.keys()).intersection(set(env.keys())) == set(environ.keys())
assert set(environ.keys()).symmetric_difference(set(env.keys())) == set(
conf["keys"].keys()
)
assert set(environ.keys()).symmetric_difference(set(env.keys())) == set(all_keys)
2 changes: 1 addition & 1 deletion tests/test_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from keycmd.logs import set_verbose, log, vlog, error, vwarn
from keycmd.logs import error, log, set_verbose, vlog, vwarn


def test_logging(capsys, request):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

import keycmd.shell
from keycmd.shell import get_shell, run_shell, run_cmd
from keycmd.shell import get_shell, run_cmd, run_shell


@pytest.fixture
Expand Down
Loading