Skip to content

Commit 7bc1d97

Browse files
committed
Make environment detection extensible
1 parent 9ffa663 commit 7bc1d97

2 files changed

Lines changed: 53 additions & 14 deletions

File tree

pyproject.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,19 @@ dependencies = [
3232
"software-card-policies @ git+https://github.com/softwarepub/software-card-policies.git",
3333
]
3434

35-
[project.urls]
36-
homepage = "https://github.com/softwarepub/hermes-plugin-software-card"
37-
repository = "https://github.com/softwarepub/hermes-plugin-software-card.git"
35+
[project.entry-points]
3836

3937
[project.entry-points."hermes.curate"]
4038
software_card = "hermes_plugin_software_card.curate:SoftwareCaRDCuratePlugin"
4139

40+
[project.entry-points."software_card.environment"]
41+
github = "hermes_plugin_software_card.environment:GitHubActionsEnvironment"
42+
gitlab = "hermes_plugin_software_card.environment:GitLabCIEnvironment"
43+
44+
[project.urls]
45+
homepage = "https://github.com/softwarepub/hermes-plugin-software-card"
46+
repository = "https://github.com/softwarepub/hermes-plugin-software-card.git"
47+
4248
[tool.ruff.lint]
4349
select = [
4450
"A",

src/hermes_plugin_software_card/environment.py

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,35 @@
33
#
44
# SPDX-License-Identifier: Apache-2.0
55

6-
"""Classes to get information about the execution environment."""
6+
"""Extensible environment detection.
7+
8+
Functions and classes to get information about the execution environment of HERMES and
9+
the Software CaRD curation plugin.
10+
11+
This plugin supports GitHub Actions (``GitHubActionsEnvironment``) and GitLab CI
12+
(``GitLabCIEnvironment``) out of the box. Support for other environments may be added by
13+
subclassing ``Environment`` and registering the new class in the Python entry point
14+
``software_card.environment``. Subclasses must make sure that ``.from_env()``
15+
returns ``None`` when HERMES is not running in their associated environment.
16+
"""
717

818
import os
919
from dataclasses import dataclass, fields
1020
from datetime import datetime
21+
from importlib import metadata
1122
from typing import Self
1223
from urllib.parse import urlencode
1324

25+
_ENTRY_POINT_GROUP = "software_card.environment"
26+
1427

1528
@dataclass(kw_only=True)
1629
class Environment:
1730
"""Base class for representing computing environments."""
1831

1932
@classmethod
2033
def from_env(cls) -> Self | None:
21-
"""Create object from environment variables.
22-
23-
If not running in GitHub Actions, ``None`` is returned instead.
24-
"""
34+
"""Create object from environment variables."""
2535
env = dict(os.environ)
2636
data = {}
2737
for field in fields(cls):
@@ -180,16 +190,39 @@ def url_data(self) -> dict[str, str]:
180190
}
181191

182192

193+
def _get_environment_classes() -> dict[str, type]:
194+
entry_points = {}
195+
for entry_point in metadata.entry_points(group=_ENTRY_POINT_GROUP):
196+
name = entry_point.name
197+
class_ = entry_point.load()
198+
if not isinstance(class_, type) or not issubclass(class_, Environment):
199+
message = (
200+
f"Entrypoint '{_ENTRY_POINT_GROUP}'.'{name}' "
201+
f"must be a subclass of '{Environment.__name__}'"
202+
)
203+
raise TypeError(message)
204+
entry_points[name] = class_
205+
return entry_points
206+
207+
183208
def get() -> Environment | None:
184209
"""Return the CI environment that we are running in, or ``None``."""
185-
github_actions = GitHubActionsEnvironment.from_env()
186-
gitlab_ci = GitLabCIEnvironment.from_env()
187-
188-
if github_actions is not None and gitlab_ci is not None:
189-
message = "More than one CI environment detected"
210+
environment_classes = _get_environment_classes()
211+
environments = {
212+
name: environment_class.from_env()
213+
for name, environment_class in environment_classes.items()
214+
}
215+
216+
num_found_environments = sum(map(bool, environments.values()))
217+
if num_found_environments > 1:
218+
names = [name for name in environments if environments.get(name) is not None]
219+
message = f"Multiple CI environments detected: {', '.join(names)}"
190220
raise RuntimeError(message)
191221

192-
return github_actions or gitlab_ci
222+
if num_found_environments == 0:
223+
return None
224+
225+
return next(env for env in environments.values() if env is not None)
193226

194227

195228
def format_app_url(base_url: str, environment: Environment) -> str:

0 commit comments

Comments
 (0)