Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
with:
python-version: 3.12
- run: pip install poetry
- run: poetry install
- run: poetry install --all-extras
- run: poetry run make test
docker:
runs-on: ubuntu-latest
Expand All @@ -28,7 +28,7 @@ jobs:
- name: Install dependencies
run: |
docker run --name tagbot-deps --mount type=bind,source=$(pwd),target=/repo tagbot:test sh -c '
pip install poetry && cd /repo && poetry install'
pip install poetry && cd /repo && poetry install --all-extras'
docker commit tagbot-deps tagbot:ready
docker rm tagbot-deps
- name: Run pytest
Expand Down
2 changes: 1 addition & 1 deletion DEVGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Or view in [AWS Console](https://console.aws.amazon.com/cloudwatch/home?region=u
```bash
# Setup
python3 -m venv .venv && source .venv/bin/activate
pip install . && pip install pytest pytest-cov black flake8 mypy boto3
pip install '.[all]' && pip install pytest pytest-cov black flake8 mypy boto3

# Run all checks
make test
Expand Down
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
FROM python:3.14-slim as builder
FROM python:3.14-slim AS builder

RUN pip install --no-cache-dir poetry poetry-plugin-export

COPY pyproject.toml .
COPY poetry.lock .
RUN poetry export --format requirements.txt --output /root/requirements.txt
RUN poetry export --all-extras --format requirements.txt --output /root/requirements.txt

FROM python:3.14-slim
LABEL org.opencontainers.image.source https://github.com/JuliaRegistries/TagBot
LABEL org.opencontainers.image.source="https://github.com/JuliaRegistries/TagBot"
RUN apt-get update && apt-get install -y git gnupg make openssh-client
COPY --from=builder /root/requirements.txt /root/requirements.txt
RUN pip install --no-cache-dir --requirement /root/requirements.txt
COPY pyproject.toml /root/pyproject.toml
COPY action.yml /root/action.yml
COPY tagbot /root/tagbot
RUN pip install --no-cache-dir --no-deps /root
CMD python -m tagbot.action
CMD ["python", "-m", "tagbot.action"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ You can also run the code outside of Docker, but you'll just need to install [Po
```sh
$ git clone https://github.com/JuliaRegistries/TagBot # Consider --branch vA.B.C
$ cd TagBot
$ poetry install
$ poetry install --all-extras
$ poetry run python -m tagbot.local --help
```

Expand Down
55 changes: 36 additions & 19 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 22 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,36 @@ license = "MIT"

[tool.poetry.dependencies]
python = "^3.12"
Flask = "3.1.3"
Jinja2 = "^3"
PyGithub = "^2.9.0"
click = "^8"
docker = "^7.1.0"
pexpect = "^4.8.0"
pylev = "^1.3.0"
python-gnupg = "^0.5.6"
pyyaml = "^6"
semver = "^3.0.4"
toml = "^0.10.0"
werkzeug = "3.1.7"
# Optional: only needed when using SSH key passwords
pexpect = { version = "^4.8.0", optional = true }
# Optional: only needed when using GPG signing
python-gnupg = { version = "^0.5.6", optional = true }
# Optional: only needed for error reporting to julia-tagbot.com
docker = { version = "^7.1.0", optional = true }
requests = { version = "^2.28.0", optional = true }
# Optional: only needed for the web service
Flask = { version = "3.1.3", optional = true }
werkzeug = { version = "3.1.7", optional = true }
pylev = { version = "^1.3.0", optional = true }
# Optional: only needed for the local CLI
click = { version = "^8", optional = true }
pyyaml = { version = "^6", optional = true }
# Optional: only needed for GitLab repos
python-gitlab = { version = "^8.2.0", optional = true }

[tool.poetry.extras]
gitlab = ["python-gitlab"]
ssh = ["pexpect"]
gpg = ["python-gnupg"]
reporting = ["docker", "requests"]
web = ["Flask", "werkzeug", "pylev"]
local = ["click", "pyyaml"]
# Install everything (matches previous behavior)
all = ["pexpect", "python-gnupg", "docker", "requests", "Flask", "werkzeug", "pylev", "click", "pyyaml", "python-gitlab"]

[tool.poetry.requires-plugins]
poetry-plugin-export = ">=1.8"
Expand Down
35 changes: 24 additions & 11 deletions tagbot/action/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

from importlib.metadata import version as pkg_version, PackageNotFoundError

import docker
import pexpect
import requests
import toml

from base64 import b64decode
Expand All @@ -36,7 +33,6 @@

from github import Github, Auth, GithubException, UnknownObjectException
from github.PullRequest import PullRequest
from gnupg import GPG
from semver import VersionInfo

from .. import logger
Expand Down Expand Up @@ -65,7 +61,10 @@
if GitlabUnknown is not None:
UnknownObjectExceptions = (UnknownObjectException, GitlabUnknown)

RequestException = requests.RequestException
try:
from requests import RequestException
except ImportError:
RequestException = OSError # type: ignore[assignment,misc]

# Maximum number of PRs to check when looking for registry PR
# This prevents excessive API calls on large registries
Expand Down Expand Up @@ -1038,9 +1037,14 @@ def _image_id(self) -> str:
if not host:
logger.warning("HOSTNAME is not set")
return "Unknown"
client = docker.from_env()
container = client.containers.get(host)
return container.image.id
try:
import docker

client = docker.from_env()
container = client.containers.get(host)
return container.image.id
except Exception:
return "Unknown"

def _tag_exists(self, version: str) -> bool:
"""Check if a tag already exists."""
Expand Down Expand Up @@ -1384,9 +1388,14 @@ def _report_error(self, trace: str) -> None:
}
if self._manual_intervention_issue_url:
data["manual_intervention_url"] = self._manual_intervention_issue_url
resp = requests.post(f"{TAGBOT_WEB}/report", json=data)
output = json.dumps(resp.json(), indent=2)
logger.info(f"Response ({resp.status_code}): {output}")
try:
import requests

resp = requests.post(f"{TAGBOT_WEB}/report", json=data)
output = json.dumps(resp.json(), indent=2)
logger.info(f"Response ({resp.status_code}): {output}")
except ImportError:
logger.debug("requests not installed, skipping error reporting")

def is_registered(self) -> bool:
"""Check whether or not the repository belongs to a registered package."""
Expand Down Expand Up @@ -1477,6 +1486,8 @@ def configure_ssh(self, key: str, password: Optional[str], repo: str = "") -> No
for k, v in re.findall(r"\s*(.+)=(.+?);", proc.stdout):
logger.debug(f"Setting environment variable {k}={v}")
os.environ[k] = v
import pexpect

child = pexpect.spawn(f"ssh-add {priv}")
child.expect("Enter passphrase")
child.sendline(password)
Expand All @@ -1486,6 +1497,8 @@ def configure_ssh(self, key: str, password: Optional[str], repo: str = "") -> No

def configure_gpg(self, key: str, password: Optional[str]) -> None:
"""Configure the repo to sign tags with GPG."""
from gnupg import GPG

home = os.environ["GNUPGHOME"] = mkdtemp(prefix="tagbot_gpg_")
os.chmod(home, S_IREAD | S_IWRITE | S_IEXEC)
logger.debug(f"Set GNUPGHOME to {home}")
Expand Down
Loading