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

chore: switch to pyproject #669

Merged
merged 1 commit into from
Jan 21, 2025
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
69 changes: 48 additions & 21 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,27 +1,54 @@
# Use the official Python 3.12 slim image
FROM python:3.12-slim
FROM python:3.8-alpine

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

# Set the working directory
WORKDIR /app
RUN apk add --no-cache \
git \
curl \
wget \
zsh \
jq \
sudo \
docker \
docker-compose \
bash \
grep \
sed \
nodejs \
npm \
# Build dependencies for Python packages
gcc \
musl-dev \
python3-dev \
libffi-dev \
openssl-dev \
cargo \
rust \
make && npm install -g pyright

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

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

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

# Switch to the non-root user for security reasons
USER appuser
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

RUN sed -i 's|/bin/ash|/bin/zsh|' /etc/passwd

RUN cp -r /root/.oh-my-zsh /home/$USERNAME/ \
&& cp /root/.zshrc /home/$USERNAME/ \
&& chown -R $USERNAME:$USERNAME /home/$USERNAME/.oh-my-zsh \
&& chown $USERNAME:$USERNAME /home/$USERNAME/.zshrc

USER $USERNAME

CMD ["zsh"]
88 changes: 70 additions & 18 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,71 @@
{
"name": "Safety-CLI Dev Container",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
"SAFETY_VERSION": "DEV"
}
},
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.debugpy"
],
"postCreateCommand": "pip install -r test_requirements.txt && pip install ruff requests pre-commit",
"remoteUser": "root",
"workspaceFolder": "/workspaces/safety",
"forwardPorts": [49152]
}
"name": "Safety CLI Development Environment",

"build": {
"dockerfile": "Dockerfile",
"context": "."
},

"remoteUser": "developer",
"workspaceFolder": "${localWorkspaceFolder}",
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",


"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/developer/.ssh,type=bind,consistency=cached"
],

"remoteEnv": {
"PYTHONPATH": "${localWorkspaceFolder}",
"TERM": "xterm-256color"
},

"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.profiles.linux": {
"zsh": {
"path": "/bin/zsh"
}
},
"python.defaultInterpreterPath": "${localWorkspaceFolder}/.hatch/bin/python",
"editor.rulers": [80],
"files.exclude": {
"**/__pycache__": true,
"**/.pytest_cache": true
},
"search.exclude": {
"**/.hatch": true,
}
},
"extensions": [
"ms-python.vscode-pylance",
"ms-python.python",
"ms-python.debugpy",
"ms-pyright.pyright",
"charliermarsh.ruff",
"tamasfe.even-better-toml",
"GitHub.copilot",
"streetsidesoftware.code-spell-checker",
"VisualStudioExptTeam.vscodeintellicode",
"VisualStudioExptTeam.intellicode-api-usage-examples",
"mechatroner.rainbow-csv",
"redhat.vscode-yaml",
"eamodio.gitlens",
"github.vscode-github-actions"
]
}
},

"postCreateCommand": "hatch env create default && git config --global core.editor nano",
"postAttachCommand": "sudo chown root:developer /var/run/docker.sock && sudo chmod 660 /var/run/docker.sock && hatch env remove default && hatch env create default",

"containerEnv": {
"SHELL": "/bin/zsh"
},

"waitFor": "postCreateCommand",
"shutdownAction": "stopContainer"
}
18 changes: 0 additions & 18 deletions .editorconfig

This file was deleted.

10 changes: 10 additions & 0 deletions .github/scripts/build_binary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pyinstaller==6.11.1"
# ]
# ///

# universal2
# windows64
# linux64
44 changes: 44 additions & 0 deletions .github/scripts/ci_pyproject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# /// script
# requires-python = ">=3.8"
# dependencies = []
# ///

def update_pyproject_toml(file_path: str) -> None:
"""
Updates the pyproject.toml file by replacing 'type = "container"' with
'type = "virtual"'

This allows to keep using the same hatch test environment configuration for
local and CI, local uses container.

This won't be needed if hatch supports a way to set the type of environment
via environment variables. This is a workaround until that is implemented.

Args:
file_path: Path to the pyproject.toml file
"""
try:
# Read the file
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()

# Replace the content
updated_content = content.replace('type = "container"',
'type = "virtual"')

# Write back to the file
with open(file_path, 'w', encoding='utf-8') as f:
f.write(updated_content)

except Exception as e:
print(f"Error updating {file_path}: {str(e)}")
raise

if __name__ == '__main__':
import sys

if len(sys.argv) != 2:
print("Usage: python update_config.py <path_to_pyproject.toml>")
sys.exit(1)

update_pyproject_toml(sys.argv[1])
90 changes: 90 additions & 0 deletions .github/scripts/matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import json
import sys
import argparse
from pathlib import Path
import tomllib

def read_toml_config(file_path: str) -> dict:
"""Read and parse TOML configuration file."""
with open(file_path, 'rb') as f:
return tomllib.load(f)

def generate_github_matrix(config: dict,
include_os_matrix: bool = True,
only_os_matrix: bool = False) -> dict:
"""Generate GitHub Actions matrix configuration from Hatch config.

Args:
config: The parsed TOML configuration
include_os_matrix: Whether to include the OS-specific matrix section
"""
test_config = config['tool']['hatch']['envs']['test']
matrix_configs = test_config['matrix']

combinations = []

if not only_os_matrix:
# First matrix: all Python versions with no target
for python_version in matrix_configs[0]['python']:
combinations.append({
"python-version": python_version,
"target": None,
"os_type": None
})

# Second matrix: specific Python versions with targets
for python_version in matrix_configs[1]['python']:
for target in matrix_configs[1]['targets']:
combinations.append({
"python-version": python_version,
"target": target,
"os_type": None
})

# Third matrix: specific Python versions with os versions
if only_os_matrix or include_os_matrix:
for python_version in matrix_configs[2]['python']:
for target in matrix_configs[2]['targets']:
for os_type in matrix_configs[2]['os_type']:
combinations.append({
"python-version": python_version,
"target": target,
"os_type": os_type
})

return {"include": combinations}

def main():
parser = argparse.ArgumentParser(description='Generate GitHub Actions matrix configuration')
parser.add_argument('toml_path', help='Path to pyproject.toml file')
parser.add_argument('--no-os-matrix', action='store_true',
help='Exclude the OS-specific matrix section')
parser.add_argument('--only-os-matrix', action='store_true',
help='Include only the OS-specific matrix section')

args = parser.parse_args()

toml_path = Path(args.toml_path)
if not toml_path.exists():
print(f"Error: File {toml_path} not found")
sys.exit(1)

try:
config = read_toml_config(str(toml_path))
matrix = generate_github_matrix(
config,
include_os_matrix=not args.no_os_matrix,
only_os_matrix=args.only_os_matrix
)
# Output single-line JSON for GitHub Actions compatibility
print(json.dumps(matrix, separators=(',', ':')))
except Exception as e:
print(f"Error processing TOML file: {e}", file=sys.stderr)
sys.exit(1)

if __name__ == "__main__":
main()
Loading
Loading