Skip to content

Commit

Permalink
feat: allow custom environment variable names (#392)
Browse files Browse the repository at this point in the history
* GH_TOKEN can now be customized by setting github_token_var
* GL_TOKEN can now be customized by setting gitlab_token_var
* PYPI_PASSWORD can now be customized by setting pypi_pass_var
* PYPI_TOKEN can now be customized by setting pypi_token_var
* PYPI_USERNAME can now be customized by setting pypi_user_var
  • Loading branch information
charlesthomas committed Oct 22, 2021
1 parent f010a15 commit 372cda3
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 30 deletions.
17 changes: 10 additions & 7 deletions semantic_release/cli.py
Expand Up @@ -42,11 +42,12 @@

logger = logging.getLogger("semantic_release")

SECRET_NAMES = [
"PYPI_USERNAME",
"PYPI_PASSWORD",
"GH_TOKEN",
"GL_TOKEN",
TOKEN_VARS = [
'github_token_var',
'gitlab_token_var',
'pypi_pass_var',
'pypi_token_var',
'pypi_user_var',
]

COMMON_OPTIONS = [
Expand Down Expand Up @@ -339,7 +340,8 @@ def publish(**kwargs):
def filter_output_for_secrets(message):
"""Remove secrets from cli output."""
output = message
for secret_name in SECRET_NAMES:
for token_var in TOKEN_VARS:
secret_name = config.get(token_var)
secret = os.environ.get(secret_name)
if secret != "" and secret is not None:
output = output.replace(secret, f"${secret_name}")
Expand Down Expand Up @@ -370,7 +372,8 @@ def entry():
def main(**kwargs):
logger.debug(f"Main args: {kwargs}")
message = ""
for secret_name in SECRET_NAMES:
for token_var in TOKEN_VARS:
secret_name = config.get(token_var)
message += f'{secret_name}="{os.environ.get(secret_name)}",'
logger.debug(f"Environment: {filter_output_for_secrets(message)}")

Expand Down
41 changes: 23 additions & 18 deletions semantic_release/defaults.cfg
@@ -1,27 +1,32 @@
[semantic_release]
minor_tag=:sparkles:
fix_tag=:nut_and_bolt:
patch_without_tag=false
major_on_zero=true
branch=master
build_command=python setup.py sdist bdist_wheel
changelog_components=semantic_release.changelog.changelog_headers
changelog_file=CHANGELOG.md
changelog_placeholder=<!--next-version-placeholder-->
changelog_scope=true
changelog_sections=feature,fix,breaking,documentation,performance,:boom:,:sparkles:,:children_crossing:,:lipstick:,:iphone:,:egg:,:chart_with_upwards_trend:,:ambulance:,:lock:,:bug:,:zap:,:goal_net:,:alien:,:wheelchair:,:speech_balloon:,:mag:,:apple:,:penguin:,:checkered_flag:,:robot:,:green_apple:,Other
check_build_status=false
hvcs=github
commit_message=Automatically generated by python-semantic-release
commit_parser=semantic_release.history.angular_parser
commit_subject={version}
dist_path=dist
fix_tag=:nut_and_bolt:
github_token_var=GH_TOKEN
gitlab_token_var=GL_TOKEN
hvcs=github
major_emoji=:boom:
major_on_zero=true
minor_emoji=:sparkles:,:children_crossing:,:lipstick:,:iphone:,:egg:,:chart_with_upwards_trend:
minor_tag=:sparkles:
patch_emoji=:ambulance:,:lock:,:bug:,:zap:,:goal_net:,:alien:,:wheelchair:,:speech_balloon:,:mag:,:apple:,:penguin:,:checkered_flag:,:robot:,:green_apple:
upload_to_pypi=true
upload_to_release=true
version_source=commit
commit_subject={version}
commit_message=Automatically generated by python-semantic-release
dist_path=dist
patch_without_tag=false
pypi_pass_var=PYPI_PASSWORD
pypi_token_var=PYPI_TOKEN
pypi_user_var=PYPI_USERNAME
remove_dist=true
build_command=python setup.py sdist bdist_wheel
branch=master
changelog_components=semantic_release.changelog.changelog_headers
changelog_sections=feature,fix,breaking,documentation,performance,:boom:,:sparkles:,:children_crossing:,:lipstick:,:iphone:,:egg:,:chart_with_upwards_trend:,:ambulance:,:lock:,:bug:,:zap:,:goal_net:,:alien:,:wheelchair:,:speech_balloon:,:mag:,:apple:,:penguin:,:checkered_flag:,:robot:,:green_apple:,Other
changelog_file=CHANGELOG.md
changelog_placeholder=<!--next-version-placeholder-->
changelog_scope=true
tag_commit=true
tag_format=v{version}
upload_to_pypi=true
upload_to_release=true
version_source=commit
4 changes: 2 additions & 2 deletions semantic_release/hvcs.py
Expand Up @@ -122,7 +122,7 @@ def token() -> Optional[str]:
:return: The Github token environment variable (GH_TOKEN) value
"""
return os.environ.get("GH_TOKEN")
return os.environ.get(config.get('github_token_var'))

@staticmethod
def auth() -> Optional[TokenAuth]:
Expand Down Expand Up @@ -369,7 +369,7 @@ def token() -> Optional[str]:
:return: The Gitlab token environment variable (GL_TOKEN) value
"""
return os.environ.get("GL_TOKEN")
return os.environ.get(config.get('gitlab_token_var'))

@staticmethod
@LoggedFunction(logger)
Expand Down
6 changes: 3 additions & 3 deletions semantic_release/pypi.py
Expand Up @@ -34,13 +34,13 @@ def upload_to_pypi(
glob_patterns = ["*"]

# Attempt to get an API token from environment
token = os.environ.get("PYPI_TOKEN")
token = os.environ.get(config.get('pypi_token_var'))
username = None
password = None
if not token:
# Look for a username and password instead
username = os.environ.get("PYPI_USERNAME")
password = os.environ.get("PYPI_PASSWORD")
username = os.environ.get(config.get('pypi_user_var'))
password = os.environ.get(config.get('pypi_pass_var'))
home_dir = os.environ.get("HOME", "")
if not (username or password) and (
not home_dir or not os.path.isfile(os.path.join(home_dir, ".pypirc"))
Expand Down
25 changes: 25 additions & 0 deletions tests/test_hvcs.py
Expand Up @@ -59,6 +59,31 @@ def test_check_token_should_return_false():
assert check_token() is False


###############################
# test custom token variables #
###############################
@mock.patch("os.environ", {"CUSTOM_GH_TOKEN": "token"})
@mock.patch("semantic_release.hvcs.config.get", wrapped_config_get(github_token_var="CUSTOM_GH_TOKEN"))
def test_check_custom_github_token_var_should_return_true():
assert check_token() is True


@mock.patch("os.environ", {"GH_TOKEN": "token"})
@mock.patch("semantic_release.hvcs.config.get", wrapped_config_get(github_token_var="CUSTOM_TOKEN"))
def test_check_custom_github_token_should_return_false():
assert check_token() is False

@mock.patch("os.environ", {"CUSTOM_GL_TOKEN": "token"})
@mock.patch("semantic_release.hvcs.config.get", wrapped_config_get(github_token_var="CUSTOM_GL_TOKEN"))
def test_check_custom_gitlab_token_var_should_return_true():
assert check_token() is True


@mock.patch("os.environ", {"GL_TOKEN": "token"})
@mock.patch("semantic_release.hvcs.config.get", wrapped_config_get(gitlab_token_var="CUSTOM_TOKEN"))
def test_check_custom_gitlab_token_should_return_false():
assert check_token() is False

@pytest.mark.parametrize(
"hvcs,hvcs_domain,expected_domain,api_url,ci_server_host",
[
Expand Down
29 changes: 29 additions & 0 deletions tests/test_pypi.py
Expand Up @@ -109,3 +109,32 @@ def test_raises_error_when_token_invalid(self):
def test_raises_error_when_missing_credentials(self):
with self.assertRaises(ImproperConfigurationError):
upload_to_pypi()

#########################
# test custom pypi vars #
#########################
@mock.patch("semantic_release.pypi.run")
@mock.patch.dict(
"os.environ",
{"CUSTOM_PYPI_USERNAME": "username", "CUSTOM_PYPI_PASSWORD": "password", "HOME": "/tmp/1234"},
)
@mock.patch("semantic_release.pypi.config.get",
wrapped_config_get(pypi_user_var="CUSTOM_PYPI_USERNAME",
pypi_pass_var="CUSTOM_PYPI_PASSWORD"))
def test_upload_with_password_using_custom_pypi_vars(self, mock_run):
upload_to_pypi()
self.assertEqual(
mock_run.call_args_list,
[mock.call("twine upload -u 'username' -p 'password' \"dist/*\"")],
)

@mock.patch("semantic_release.pypi.run")
@mock.patch.dict("os.environ", {"CUSTOM_PYPI_TOKEN": "pypi-x"})
@mock.patch("semantic_release.pypi.config.get",
wrapped_config_get(pypi_token_var="CUSTOM_PYPI_TOKEN"))
def test_upload_with_token_using_custom_pypi_vars(self, mock_run):
upload_to_pypi()
self.assertEqual(
mock_run.call_args_list,
[mock.call("twine upload -u '__token__' -p 'pypi-x' \"dist/*\"")],
)
5 changes: 5 additions & 0 deletions tests/test_settings.py
Expand Up @@ -39,6 +39,11 @@ def test_defaults(self, mock_getcwd):
self.assertFalse(config.get("check_build_status"))
self.assertEqual(config.get("hvcs"), "github")
self.assertEqual(config.get("upload_to_pypi"), True)
self.assertEqual(config.get("github_token_var"), "GH_TOKEN")
self.assertEqual(config.get("gitlab_token_var"), "GL_TOKEN")
self.assertEqual(config.get("pypi_pass_var"), "PYPI_PASSWORD")
self.assertEqual(config.get("pypi_token_var"), "PYPI_TOKEN")
self.assertEqual(config.get("pypi_user_var"), "PYPI_USERNAME")

@mock.patch("semantic_release.settings.getcwd", return_value=temp_dir)
def test_toml_override(self, mock_getcwd):
Expand Down

0 comments on commit 372cda3

Please sign in to comment.