Skip to content

Commit 4d3adbd

Browse files
chore: switch to pyproject (#669)
1 parent 9a6619e commit 4d3adbd

35 files changed

+1179
-575
lines changed

.devcontainer/Dockerfile

+48-21
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,54 @@
1-
# Use the official Python 3.12 slim image
2-
FROM python:3.12-slim
1+
FROM python:3.8-alpine
32

4-
# Create a non-root user and a directory for the application
5-
RUN useradd -m appuser && \
6-
mkdir /app && \
7-
chown appuser:appuser /app
3+
ENV PYTHONDONTWRITEBYTECODE=1
4+
ENV PYTHONUNBUFFERED=1
85

9-
# Set the working directory
10-
WORKDIR /app
6+
RUN apk add --no-cache \
7+
git \
8+
curl \
9+
wget \
10+
zsh \
11+
jq \
12+
sudo \
13+
docker \
14+
docker-compose \
15+
bash \
16+
grep \
17+
sed \
18+
nodejs \
19+
npm \
20+
# Build dependencies for Python packages
21+
gcc \
22+
musl-dev \
23+
python3-dev \
24+
libffi-dev \
25+
openssl-dev \
26+
cargo \
27+
rust \
28+
make && npm install -g pyright
1129

12-
# Set environment variables in a single step
13-
ENV LC_ALL=C.UTF-8 \
14-
LANG=C.UTF-8 \
15-
PYTHONPATH="/app"
30+
RUN pip install --no-cache-dir uv \
31+
&& uv pip install --system hatch hatch-containers
1632

17-
# Install necessary dependencies, clean up after installation to reduce image size
18-
RUN apt-get update && \
19-
apt-get -y install docker.io jq git && \
20-
apt-get clean && \
21-
rm -rf /var/lib/apt/lists/*
33+
ARG USERNAME=developer
34+
ARG USER_UID=1000
35+
ARG USER_GID=$USER_UID
2236

23-
# Copy project files into the container (relative to the build context)
24-
COPY . /app/
37+
RUN addgroup -g $USER_GID $USERNAME \
38+
&& adduser -u $USER_UID -G $USERNAME -s /bin/zsh -D $USERNAME \
39+
&& echo "$USERNAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME \
40+
&& chmod 0440 /etc/sudoers.d/$USERNAME \
41+
&& addgroup $USERNAME docker
2542

26-
# Switch to the non-root user for security reasons
27-
USER appuser
43+
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
44+
45+
RUN sed -i 's|/bin/ash|/bin/zsh|' /etc/passwd
46+
47+
RUN cp -r /root/.oh-my-zsh /home/$USERNAME/ \
48+
&& cp /root/.zshrc /home/$USERNAME/ \
49+
&& chown -R $USERNAME:$USERNAME /home/$USERNAME/.oh-my-zsh \
50+
&& chown $USERNAME:$USERNAME /home/$USERNAME/.zshrc
51+
52+
USER $USERNAME
53+
54+
CMD ["zsh"]

.devcontainer/devcontainer.json

+70-18
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,71 @@
11
{
2-
"name": "Safety-CLI Dev Container",
3-
"build": {
4-
"dockerfile": "Dockerfile",
5-
"context": "..",
6-
"args": {
7-
"SAFETY_VERSION": "DEV"
8-
}
9-
},
10-
"extensions": [
11-
"ms-python.python",
12-
"ms-python.vscode-pylance",
13-
"ms-python.debugpy"
14-
],
15-
"postCreateCommand": "pip install -r test_requirements.txt && pip install ruff requests pre-commit",
16-
"remoteUser": "root",
17-
"workspaceFolder": "/workspaces/safety",
18-
"forwardPorts": [49152]
19-
}
2+
"name": "Safety CLI Development Environment",
3+
4+
"build": {
5+
"dockerfile": "Dockerfile",
6+
"context": "."
7+
},
8+
9+
"remoteUser": "developer",
10+
"workspaceFolder": "${localWorkspaceFolder}",
11+
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
12+
13+
14+
"mounts": [
15+
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
16+
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/developer/.ssh,type=bind,consistency=cached"
17+
],
18+
19+
"remoteEnv": {
20+
"PYTHONPATH": "${localWorkspaceFolder}",
21+
"TERM": "xterm-256color"
22+
},
23+
24+
"customizations": {
25+
"vscode": {
26+
"settings": {
27+
"terminal.integrated.defaultProfile.linux": "zsh",
28+
"terminal.integrated.profiles.linux": {
29+
"zsh": {
30+
"path": "/bin/zsh"
31+
}
32+
},
33+
"python.defaultInterpreterPath": "${localWorkspaceFolder}/.hatch/bin/python",
34+
"editor.rulers": [80],
35+
"files.exclude": {
36+
"**/__pycache__": true,
37+
"**/.pytest_cache": true
38+
},
39+
"search.exclude": {
40+
"**/.hatch": true,
41+
}
42+
},
43+
"extensions": [
44+
"ms-python.vscode-pylance",
45+
"ms-python.python",
46+
"ms-python.debugpy",
47+
"ms-pyright.pyright",
48+
"charliermarsh.ruff",
49+
"tamasfe.even-better-toml",
50+
"GitHub.copilot",
51+
"streetsidesoftware.code-spell-checker",
52+
"VisualStudioExptTeam.vscodeintellicode",
53+
"VisualStudioExptTeam.intellicode-api-usage-examples",
54+
"mechatroner.rainbow-csv",
55+
"redhat.vscode-yaml",
56+
"eamodio.gitlens",
57+
"github.vscode-github-actions"
58+
]
59+
}
60+
},
61+
62+
"postCreateCommand": "hatch env create default && git config --global core.editor nano",
63+
"postAttachCommand": "sudo chown root:developer /var/run/docker.sock && sudo chmod 660 /var/run/docker.sock && hatch env remove default && hatch env create default",
64+
65+
"containerEnv": {
66+
"SHELL": "/bin/zsh"
67+
},
68+
69+
"waitFor": "postCreateCommand",
70+
"shutdownAction": "stopContainer"
71+
}

.editorconfig

-18
This file was deleted.

.github/scripts/build_binary.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# /// script
2+
# requires-python = ">=3.11"
3+
# dependencies = [
4+
# "pyinstaller==6.11.1"
5+
# ]
6+
# ///
7+
8+
# universal2
9+
# windows64
10+
# linux64

.github/scripts/ci_pyproject.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# /// script
2+
# requires-python = ">=3.8"
3+
# dependencies = []
4+
# ///
5+
6+
def update_pyproject_toml(file_path: str) -> None:
7+
"""
8+
Updates the pyproject.toml file by replacing 'type = "container"' with
9+
'type = "virtual"'
10+
11+
This allows to keep using the same hatch test environment configuration for
12+
local and CI, local uses container.
13+
14+
This won't be needed if hatch supports a way to set the type of environment
15+
via environment variables. This is a workaround until that is implemented.
16+
17+
Args:
18+
file_path: Path to the pyproject.toml file
19+
"""
20+
try:
21+
# Read the file
22+
with open(file_path, 'r', encoding='utf-8') as f:
23+
content = f.read()
24+
25+
# Replace the content
26+
updated_content = content.replace('type = "container"',
27+
'type = "virtual"')
28+
29+
# Write back to the file
30+
with open(file_path, 'w', encoding='utf-8') as f:
31+
f.write(updated_content)
32+
33+
except Exception as e:
34+
print(f"Error updating {file_path}: {str(e)}")
35+
raise
36+
37+
if __name__ == '__main__':
38+
import sys
39+
40+
if len(sys.argv) != 2:
41+
print("Usage: python update_config.py <path_to_pyproject.toml>")
42+
sys.exit(1)
43+
44+
update_pyproject_toml(sys.argv[1])

.github/scripts/matrix.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# /// script
2+
# requires-python = ">=3.11"
3+
# dependencies = []
4+
# ///
5+
import json
6+
import sys
7+
import argparse
8+
from pathlib import Path
9+
import tomllib
10+
11+
def read_toml_config(file_path: str) -> dict:
12+
"""Read and parse TOML configuration file."""
13+
with open(file_path, 'rb') as f:
14+
return tomllib.load(f)
15+
16+
def generate_github_matrix(config: dict,
17+
include_os_matrix: bool = True,
18+
only_os_matrix: bool = False) -> dict:
19+
"""Generate GitHub Actions matrix configuration from Hatch config.
20+
21+
Args:
22+
config: The parsed TOML configuration
23+
include_os_matrix: Whether to include the OS-specific matrix section
24+
"""
25+
test_config = config['tool']['hatch']['envs']['test']
26+
matrix_configs = test_config['matrix']
27+
28+
combinations = []
29+
30+
if not only_os_matrix:
31+
# First matrix: all Python versions with no target
32+
for python_version in matrix_configs[0]['python']:
33+
combinations.append({
34+
"python-version": python_version,
35+
"target": None,
36+
"os_type": None
37+
})
38+
39+
# Second matrix: specific Python versions with targets
40+
for python_version in matrix_configs[1]['python']:
41+
for target in matrix_configs[1]['targets']:
42+
combinations.append({
43+
"python-version": python_version,
44+
"target": target,
45+
"os_type": None
46+
})
47+
48+
# Third matrix: specific Python versions with os versions
49+
if only_os_matrix or include_os_matrix:
50+
for python_version in matrix_configs[2]['python']:
51+
for target in matrix_configs[2]['targets']:
52+
for os_type in matrix_configs[2]['os_type']:
53+
combinations.append({
54+
"python-version": python_version,
55+
"target": target,
56+
"os_type": os_type
57+
})
58+
59+
return {"include": combinations}
60+
61+
def main():
62+
parser = argparse.ArgumentParser(description='Generate GitHub Actions matrix configuration')
63+
parser.add_argument('toml_path', help='Path to pyproject.toml file')
64+
parser.add_argument('--no-os-matrix', action='store_true',
65+
help='Exclude the OS-specific matrix section')
66+
parser.add_argument('--only-os-matrix', action='store_true',
67+
help='Include only the OS-specific matrix section')
68+
69+
args = parser.parse_args()
70+
71+
toml_path = Path(args.toml_path)
72+
if not toml_path.exists():
73+
print(f"Error: File {toml_path} not found")
74+
sys.exit(1)
75+
76+
try:
77+
config = read_toml_config(str(toml_path))
78+
matrix = generate_github_matrix(
79+
config,
80+
include_os_matrix=not args.no_os_matrix,
81+
only_os_matrix=args.only_os_matrix
82+
)
83+
# Output single-line JSON for GitHub Actions compatibility
84+
print(json.dumps(matrix, separators=(',', ':')))
85+
except Exception as e:
86+
print(f"Error processing TOML file: {e}", file=sys.stderr)
87+
sys.exit(1)
88+
89+
if __name__ == "__main__":
90+
main()

0 commit comments

Comments
 (0)