Skip to content

Commit

Permalink
Merge pull request #3 from smorin/feature/ci-github-action
Browse files Browse the repository at this point in the history
adding branch for new github action
  • Loading branch information
smorin authored Jan 23, 2025
2 parents f7cac15 + d9d6543 commit 3f61e6f
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 25 deletions.
80 changes: 80 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# .github/workflows/ci.yml
name: CI/CD

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
name: continuous-integration
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11"]

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install the project
run: uv sync --all-extras --dev

# - name: Run tests
# # For example, using `pytest`
# run: uv run pytest tests

# Format code
- name: Format code
run: uvx black py_launch_blueprint/ --check

- name: Sort imports
run: uvx isort py_launch_blueprint/ --check

- name: Run type checker
run: uvx mypy py_launch_blueprint/

- name: Check linters
run: uvx ruff check py_launch_blueprint/

# - name: Run tests with coverage
# run: |
# pytest --cov=./ --cov-report=xml

# - name: Upload coverage
# uses: codecov/codecov-action@v4

security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run Safety CLI to check for vulnerabilities
uses: pyupio/safety-action@v1
with:
api-key: ${{ secrets.SAFETY_API_KEY }}
args: --detailed-output # To always see detailed output from this action

# publish:
# needs: [test, security]
# if: github.event_name == 'push' && github.ref == 'refs/heads/main'
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4

# - name: Build and publish to PyPI
# env:
# TWINE_USERNAME: __token__
# TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
# run: |
# uv pip install build twine
# python -m build
# twine upload dist/*
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ ruff check py_launch_blueprint/

# Or run with our the virtual environment

# (Optional) Setup Pre-Commit Hook
uvx --with-editable . pre-commit install

# Run tests
uvx --with-editable . pytest

Expand All @@ -157,7 +160,7 @@ uvx black py_launch_blueprint/
uvx isort py_launch_blueprint/

# Run type checker
uvx mypy py_launch_blueprint/
uvx --with-editable . mypy py_launch_blueprint/

# Run linter
uvx ruff check py_launch_blueprint/
Expand Down
47 changes: 27 additions & 20 deletions py_launch_blueprint/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
A command-line interface for searching and selecting Py projects,
with support for fuzzy matching and various output formats.
"""
# TODO: remove mypy: ignore-errors and fix all type errors
# mypy: ignore-errors

import json
import os
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Any

import click
import pyperclip
Expand All @@ -21,7 +23,6 @@
from rich.console import Console
from rich.progress import Progress
from rich.table import Table
from thefuzz import fuzz, process

# Initialize Rich console for pretty output
console = Console()
Expand All @@ -46,10 +47,10 @@ class ConfigError(Exception):
class Config:
"""Configuration container."""

token: Optional[str] = None
token: str | None = None

@classmethod
def from_env(cls, env_path: Optional[str] = None) -> "Config":
def from_env(cls, env_path: str | None = None) -> "Config":
"""
Create Config from environment variables or .env file.
Expand All @@ -71,7 +72,9 @@ def from_env(cls, env_path: Optional[str] = None) -> "Config":
error_console.print("To set your Py token, you have three options:")
error_console.print("1. Set the PY_TOKEN environment variable:")
error_console.print(" export PY_TOKEN=your_token_here")
error_console.print("\n2. Create a .env file in ~/.config/py-cli/.env with:")
error_console.print(
"\n2. Create a .env file in ~/.config/py-cli/.env with:"
)
error_console.print(" PY_TOKEN=your_token_here")
error_console.print("\n3. Use the --token option when running the command:")
error_console.print(" py-cli --token your_token_here")
Expand All @@ -92,7 +95,7 @@ def get_config_path() -> Path:
return base_path / ".config" / "py-cli"


def get_config(config_path: Optional[str] = None) -> Config:
def get_config(config_path: str | None = None) -> Config:
"""
Get configuration from various sources.
Expand Down Expand Up @@ -143,7 +146,7 @@ def __init__(self, token: str):
}
)

def _request(self, method: str, path: str, **kwargs) -> Dict[str, Any]:
def _request(self, method: str, path: str, **kwargs) -> dict[str, Any]:
"""
Make a request to the Py API.
Expand Down Expand Up @@ -172,9 +175,9 @@ def _request(self, method: str, path: str, **kwargs) -> Dict[str, Any]:
error_msg = str(e)
else:
error_msg = str(e)
raise PyError(f"API request failed: {error_msg}")
raise PyError(f"API request failed: {error_msg}") from e

def get_workspaces(self) -> List[Dict[str, Any]]:
def get_workspaces(self) -> list[dict[str, Any]]:
"""
Get all accessible workspaces.
Expand All @@ -184,8 +187,8 @@ def get_workspaces(self) -> List[Dict[str, Any]]:
return self._request("GET", "/workspaces")

def get_projects(
self, workspace_name: Optional[str] = None, limit: int = 200
) -> List[Dict[str, Any]]:
self, workspace_name: str | None = None, limit: int = 200
) -> list[dict[str, Any]]:
"""
Get projects, optionally filtered by workspace.
Expand All @@ -205,7 +208,8 @@ def get_projects(
# First get workspaces and find the matching one
workspaces = self.get_workspaces()
workspace = next(
(w for w in workspaces if w["name"].lower() == workspace_name.lower()), None
(w for w in workspaces if w["name"].lower() == workspace_name.lower()),
None,
)
if not workspace:
raise PyError(f"Workspace not found: {workspace_name}")
Expand All @@ -216,7 +220,7 @@ def get_projects(


# CLI Functions
def setup_config(config_path: Optional[str] = None) -> Config:
def setup_config(config_path: str | None = None) -> Config:
"""
Set up configuration from various sources.
Expand All @@ -238,7 +242,7 @@ def setup_config(config_path: Optional[str] = None) -> Config:
sys.exit(1)


def format_output(projects: List[Dict[str, Any]], format: str) -> str:
def format_output(projects: list[dict[str, Any]], format: str) -> str:
"""
Format projects list according to specified format.
Expand All @@ -259,7 +263,7 @@ def format_output(projects: List[Dict[str, Any]], format: str) -> str:
return "\n".join(p["id"] for p in projects)


def display_projects(projects: List[Dict[str, Any]], verbose: bool = False) -> None:
def display_projects(projects: list[dict[str, Any]], verbose: bool = False) -> None:
"""
Display projects in a rich table format.
Expand Down Expand Up @@ -288,21 +292,24 @@ def display_projects(projects: List[Dict[str, Any]], verbose: bool = False) -> N
@click.option("--workspace", help="Filter projects by workspace name")
@click.option("--limit", default=200, help="Maximum number of projects to retrieve")
@click.option(
"--format", type=click.Choice(["text", "json", "csv"]), default="text", help="Output format"
"--format",
type=click.Choice(["text", "json", "csv"]),
default="text",
help="Output format",
)
@click.option("--copy", is_flag=True, help="Copy results to clipboard")
@click.option("--output", type=click.Path(), help="Write results to file")
@click.option("--no-color", is_flag=True, help="Disable colored output")
@click.option("--verbose", is_flag=True, help="Enable verbose output")
@click.version_option(version="0.1.0")
def main(
token: Optional[str],
config: Optional[str],
workspace: Optional[str],
token: str | None,
config: str | None,
workspace: str | None,
limit: int,
format: str,
copy: bool,
output: Optional[str],
output: str | None,
no_color: bool,
verbose: bool,
) -> None:
Expand Down
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ dev = [
"mypy>=1.0.0",
"ruff>=0.1.0",
"pre-commit>=4.1.0",
"types-requests",
"types-Pygments",
]

[project.scripts]
Expand All @@ -51,7 +53,7 @@ target-version = "py310"

# Line length
line-length = 88 # Match Black's default
select = [
lint.select = [
"E", # pycodestyle errors
"F", # pyflakes
"I", # isort
Expand All @@ -66,13 +68,13 @@ select = [
]

# Ignore specific rules
ignore = [
lint.ignore = [
# "E501", # line length violations
]

# Allow autofix behavior for specific rules
fix = true
unfixable = [] # Rules that should not be fixed automatically
lint.unfixable = [] # Rules that should not be fixed automatically

# Exclude files/folders
exclude = [
Expand All @@ -84,7 +86,7 @@ exclude = [
]

# Per-file-ignores
[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # Ignore unused imports in __init__.py files
"tests/*" = ["S101"] # Ignore assert statements in tests

Expand Down

0 comments on commit 3f61e6f

Please sign in to comment.