Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b872448
Signal already-translated vocab with a custom exception
Oct 14, 2025
7a90a93
Add backend: Utility functions for language pair file paths
Oct 15, 2025
90ac9ec
Add backend: Vocabulary statistics calculation
Oct 15, 2025
fd65b45
Add backend: Language pair rename functionality
Oct 15, 2025
b5fba29
Add CLI infrastructure: Command aliasing support
Oct 15, 2025
03bfb0d
Refactor CLI: Introduce pairs command group
Oct 15, 2025
de7166f
Update `README.md`
Oct 15, 2025
c066bc8
Refactor test: Normalize string quote style
Oct 15, 2025
9ddac1f
Normalize string quote style
sderev Oct 15, 2025
986a7f8
Fix test: Correct test_pairs_add_canceled_keeps_state expectations
Oct 15, 2025
69a0ca2
Migrate from `setup.py` to `pyproject.toml`
Oct 15, 2025
99feff4
Update token estimation with improved pricing and UX
Oct 15, 2025
8652183
Fix path traversal vuln in language pair names
Oct 16, 2025
c368076
Add mode detection utilities for same-language pairs
Oct 15, 2025
5950d84
Update prompt system and Anki handling for definition mode
Oct 15, 2025
f51c74a
Add unit tests for mode detection utilities
Oct 15, 2025
f8499fd
Add unit tests for prompt generation and Anki headers
Oct 16, 2025
1fabaf4
Add integration tests for same-language definition workflow
Oct 16, 2025
fa5a3c0
Update README with same-language definition mode documentation
Oct 16, 2025
1c95011
Fix CSV header validation bug in get_words_to_translate
Oct 16, 2025
1c1ea06
Relax definition length constraints in prompt
sderev Oct 16, 2025
f540a93
Add data protection against corruption and injection
Oct 16, 2025
9e939ed
Add input validation to prevent corruption and injection in CLI
Oct 16, 2025
a3a1851
Apply ruff formatting and linting fixes
Oct 16, 2025
a2cd138
Fix test isolation bug in `test_path_traversal_write_outside_data_dir`
Oct 19, 2025
0a14ce1
Track `uv.lock` in version control
Oct 22, 2025
2760392
Add development dependencies and linting configuration
Oct 16, 2025
69f6551
Add comprehensive CI workflow for automated testing
Oct 16, 2025
4f6a36e
Add automated release workflow with SLSA provenance
Oct 16, 2025
fc4d074
Add weekly dependency update monitoring workflow
Oct 16, 2025
a75e37d
Add documentation for GitHub Actions workflows
Oct 16, 2025
c42c537
Add standalone security scanning workflow
Nov 14, 2025
f8af614
Remove integrated security job from CI workflow
Nov 14, 2025
f9117cb
Update documentation for standalone security workflow
Nov 14, 2025
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
139 changes: 139 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# GitHub Actions CI/CD Workflows

This directory contains the CI/CD pipeline configuration for VocabMaster.

## Workflows

### 1. CI (`ci.yml`)

**Triggers:** Push to `main`, Pull requests to `main`

**Jobs:**
* **Lint** - Runs `ruff` linter and format checker
* **Test** - Runs test suite across multiple Python versions (3.10, 3.11, 3.12) and OS (Ubuntu, macOS, Windows)
* **Build** - Builds the package distribution
* **Install Test** - Verifies the package can be installed and the CLI works

**Coverage:** Uploads coverage reports to Codecov (requires `CODECOV_TOKEN` secret)

### 2. Release (`release.yml`)

**Triggers:** Git tags matching `v*` (e.g., `v0.2.1`)

**Jobs:**
* **Test** - Runs full test suite before release
* **Build** - Builds distribution packages (wheel and sdist)
* **Publish to PyPI** - Publishes package to PyPI using trusted publishing
* **Create GitHub Release** - Creates a GitHub release with auto-generated changelog

**Required Setup:**
1. Configure PyPI trusted publishing:
* Go to https://pypi.org/manage/account/publishing/
* Add GitHub as a trusted publisher for this repository
* Specify the workflow: `release.yml`
* Specify the environment: `pypi`

2. The workflow uses GitHub's OIDC token for authentication (no API token needed)

### 3. Dependency Check (`dependencies.yml`)

**Triggers:**
* Weekly schedule (Mondays at 9:00 UTC)
* Manual dispatch

**Jobs:**
* Checks for outdated dependencies
* Creates or updates a GitHub issue with outdated packages

### 4. Security Scan (`security.yml`)

**Triggers:**
* Push to `main`, Pull requests to `main`
* Daily schedule (2:00 UTC)
* Manual dispatch

**Jobs:**
* **Dependency Scan** - Uses `pip-audit` and `safety` to detect vulnerable dependencies
* **Secret Scan** - Uses Gitleaks to detect leaked credentials
* **CodeQL Analysis** - GitHub semantic security vulnerability analysis

The standalone security workflow provides daily automated scans for newly disclosed vulnerabilities.

## Manual Workflow Triggers

You can manually trigger workflows from the Actions tab:

```bash
# Using GitHub CLI
gh workflow run ci.yml
gh workflow run dependencies.yml
gh workflow run security.yml
```

## Creating a Release

To create a new release:

1. Update the version in `pyproject.toml`
2. Commit the changes
3. Create and push a tag:
```bash
git tag v0.2.1
git push origin v0.2.1
```
4. The release workflow will automatically:
* Run tests
* Build the package
* Publish to PyPI
* Create a GitHub release

## Status Badges

Add these badges to your README.md:

```markdown
[![CI](https://github.com/sderev/vocabmaster/actions/workflows/ci.yml/badge.svg)](https://github.com/sderev/vocabmaster/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/sderev/vocabmaster/graph/badge.svg)](https://codecov.io/gh/sderev/vocabmaster)
```

## Local Testing

Test the CI pipeline locally before pushing:

```bash
# Lint and format check
uv run ruff check .
uv run ruff format --check .

# Run tests
uv run pytest -v

# Run tests with coverage
uv run pytest --cov=vocabmaster --cov-report=term

# Build package
uv build

# Test installation
uv pip install dist/*.whl
vocabmaster --help
```

## Troubleshooting

### Coverage Upload Fails
* Ensure `CODECOV_TOKEN` secret is set in repository settings
* The workflow continues even if upload fails (`fail_ci_if_error: false`)

### PyPI Publishing Fails
* Verify trusted publishing is configured correctly on PyPI
* Check the `pypi` environment is configured in repository settings
* Ensure the tag format matches `v*` pattern

### Security Scan Issues
* Security scans run daily at 2:00 UTC to detect newly disclosed vulnerabilities
* Review security scan results in the Actions tab under `security.yml`
* Dependency scans use `pip-audit` and `safety` for vulnerability detection
* Secret scanning uses Gitleaks to detect leaked credentials
* CodeQL provides semantic analysis for security issues
* Update dependencies if vulnerabilities are found
199 changes: 199 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
name: CI

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

# Cancel in-progress runs when a new run is triggered
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
PYTHON_VERSION: "3.12"
UV_CACHE_DIR: /tmp/.uv-cache

jobs:
lint:
name: Lint and format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Restore uv cache
uses: actions/cache@v4
with:
path: ${{ env.UV_CACHE_DIR }}
key: uv-${{ runner.os }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-${{ runner.os }}-

- name: Install dependencies
run: uv sync --frozen --dev

- name: Run ruff linter
run: uv run ruff check . --output-format=github

- name: Run ruff formatter check
run: uv run ruff format --check .

test:
name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}

- name: Restore uv cache
uses: actions/cache@v4
with:
path: ${{ env.UV_CACHE_DIR }}
key: uv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-${{ runner.os }}-${{ matrix.python-version }}-
uv-${{ runner.os }}-

- name: Install dependencies
run: uv sync --frozen --dev

- name: Run tests
run: uv run pytest -v --tb=short --junitxml=pytest-results.xml
env:
OPENAI_API_KEY: test-key-for-ci

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-${{ matrix.os }}-${{ matrix.python-version }}
path: pytest-results.xml
retention-days: 30

- name: Run tests with coverage
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
run: uv run pytest --cov=vocabmaster --cov-report=xml --cov-report=term --cov-report=html
env:
OPENAI_API_KEY: test-key-for-ci

- name: Upload coverage reports to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

- name: Upload coverage HTML report
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
uses: actions/upload-artifact@v4
with:
name: coverage-html-report
path: htmlcov/
retention-days: 30

build:
name: Build package
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4

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

- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Build package
run: uv build

- name: Check package metadata
run: |
uv pip install twine
uv run twine check dist/*

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 90

- name: Generate build provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: dist/*

install-test:
name: Test installation (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: build
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

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

- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Install package from wheel
run: uv pip install --system dist/*.whl

- name: Test CLI entry point
run: vocabmaster --help

- name: Verify package version
shell: bash
run: |
INSTALLED_VERSION=$(vocabmaster --version 2>&1 || echo "unknown")
echo "Installed version: $INSTALLED_VERSION"

all-checks-pass:
name: All checks pass
runs-on: ubuntu-latest
needs: [lint, test, build, install-test]
if: always()
steps:
- name: Decide whether all checks passed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
Loading
Loading