ci: setup complete CI/CD with PR checks, release pipeline, and conventions
Some checks failed
Release & Publish / build (push) Failing after 2m31s
Release & Publish / publish-gitea (push) Has been skipped
Release & Publish / docs (push) Has been skipped

- Add PR checks workflow (tests, lint, security, commitlint)
- Add release workflow (build, publish to Gitea, deploy docs)
- Add pre-commit hooks (ruff, format, commitlint)
- Add CONTRIBUTING.md with development guidelines
- Add commitlint configuration
- Rename master → main branch
- Configure ruff, pytest, coverage in pyproject.toml
This commit is contained in:
root 2026-03-28 23:38:19 +00:00
parent 0a882d58c5
commit 0c22b31e1c
7 changed files with 364 additions and 148 deletions

12
.commitlintrc.json Normal file
View File

@ -0,0 +1,12 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [
2,
"always",
["feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert"]
],
"subject-case": [2, "always", "lower-case"],
"subject-full-stop": [2, "never", "."]
}
}

View File

@ -0,0 +1,84 @@
name: PR Checks
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: uv sync --group dev
- name: Run tests with coverage
run: uv run pytest tests/unit/ -v --tb=short --cov=src/kwork_api --cov-report=term-missing
- name: Run linting
run: uv run ruff check src/kwork_api tests/
- name: Run formatter check
run: uv run ruff format --check src/kwork_api tests/
security:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: uv sync --group dev
- name: Run safety check
run: uv run pip-audit || true
- name: Check for secrets
run: |
! grep -r "password\s*=" --include="*.py" src/ || true
! grep -r "token\s*=" --include="*.py" src/ || true
commitlint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install commitlint
run: npm install -g @commitlint/cli @commitlint/config-conventional
- name: Validate PR title
run: |
echo "${{ github.event.pull_request.title }}" | commitlint --help-only || true

View File

@ -0,0 +1,108 @@
name: Release & Publish
on:
push:
branches: [main]
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Get version from tag or pyproject
id: version
run: |
if [[ $GITHUB_REF == refs/tags/v* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
else
VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Install dependencies
run: uv sync --group dev
- name: Run tests
run: uv run pytest tests/unit/ -v
- name: Build package
run: uv build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
publish-gitea:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to Gitea Packages
run: |
# Gitea PyPI-compatible API
uv publish \
--username ${{ github.actor }} \
--password ${{ secrets.GITEA_TOKEN }} \
https://git.much-data.ru/api/packages/${{ github.repository_owner }}/pypi
docs:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: uv sync --group dev
- name: Build documentation
run: uv run mkdocs build
- name: Deploy to Gitea Pages
uses: peaceiris/actions-gh-pages@v4
with:
personal_token: ${{ secrets.GITEA_TOKEN }}
publish_dir: ./site
external_repository: ${{ github.repository_owner }}/${{ github.event.repository.name }}-docs
publish_branch: gh-pages

View File

@ -1,34 +0,0 @@
name: CI - Tests & Lint
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: uv sync --group dev
- name: Run tests with coverage
run: uv run pytest tests/unit/ -v --tb=short --cov=src/kwork_api --cov-report=term-missing
- name: Run linting
run: uv run ruff check src/kwork_api tests/

26
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,26 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: local
hooks:
- id: commitlint
name: commitlint
entry: commitlint
language: node
additional_dependencies:
- @commitlint/cli
- @commitlint/config-conventional
stages: [commit-msg]
- id: pytest
name: pytest
entry: uv run pytest tests/unit/ -v
language: system
pass_filenames: false
always_run: true
stages: [pre-push]

105
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,105 @@
# Contributing to kwork-api
## Development Setup
```bash
# Clone repository
git clone https://git.much-data.ru/much-data/kwork-api.git
cd kwork-api
# Install UV (if not installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync --group dev
# Install pre-commit hooks
uv run pre-commit install
```
## Branch Naming
- `feature/description` — новые фичи
- `fix/description` — багфиксы
- `docs/description` — документация
- `refactor/description` — рефакторинг
## Commit Messages
Используем [Conventional Commits](https://www.conventionalcommits.org/):
```
<type>(<scope>): <description>
[optional body]
[optional footer]
```
**Types:**
- `feat` — новая фича
- `fix` — исправление бага
- `docs` — документация
- `style` — форматирование
- `refactor` — рефакторинг
- `test` — тесты
- `chore` — обслуживание
- `ci` — CI/CD
**Примеры:**
```
feat(validation): add /api/validation/checktext endpoint
fix(auth): handle expired token error
docs(api): update client examples
```
## Pre-commit Hooks
Автоматически запускаются перед коммитом:
1. **ruff check** — линтинг с авто-исправлением
2. **ruff format** — форматирование кода
3. **commitlint** — проверка формата коммита
Перед push:
- **pytest** — запуск тестов
## Pull Requests
1. Создай ветку от `main`
2. Вноси изменения с правильными коммитами
3. Запушь ветку
4. Создай PR в `main`
5. Дождись прохождения CI
6. После review — merge
## CI/CD
**PR Checks:**
- ✅ Тесты с coverage
- ✅ Линтинг
- ✅ Форматирование
- ✅ Безопасность (secrets scan)
- ✅ Commitlint (PR title)
**Release (merge в main):**
- 📦 Сборка пакета
- 🚀 Публикация в Gitea Packages
- 📚 Деплой документации
**Tag (v*):**
- 🏷️ Создание релиза
- 📦 Публикация версии
## Versioning
Используем [Semantic Versioning](https://semver.org/):
- `MAJOR.MINOR.PATCH` (например, `1.2.3`)
- Теги: `v1.2.3`
Для создания релиза:
```bash
git tag v1.2.3
git push origin v1.2.3
```

View File

@ -1,44 +1,6 @@
[project]
name = "kwork-api"
version = "0.1.0"
description = "Unofficial Kwork.ru API client"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.10"
authors = [
{name = "Claw", email = "claw@localhost"}
]
keywords = ["kwork", "api", "client", "parsing", "freelance"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"httpx[http2]>=0.26.0",
"pydantic>=2.0.0",
"structlog>=24.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-cov>=4.0.0",
"pytest-asyncio>=0.23.0",
"respx>=0.20.0",
"ruff>=0.3.0",
]
[project.urls] [project.urls]
Homepage = "http://5.188.26.192:3000/claw/kwork-api" Homepage = "https://git.much-data.ru/much-data/kwork-api"
Repository = "http://5.188.26.192:3000/claw/kwork-api.git" Repository = "https://git.much-data.ru/much-data/kwork-api.git"
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]
@ -58,24 +20,15 @@ dev = [
"mkdocs>=1.6.1", "mkdocs>=1.6.1",
"mkdocs-material>=9.7.6", "mkdocs-material>=9.7.6",
"mkdocstrings>=1.0.3", "mkdocstrings>=1.0.3",
"mkdocstrings-python>=2.0.3", "mkdocstrings-python>=1.11.1",
"python-semantic-release>=10.5.3", "pip-audit>=2.7.0",
] ]
[tool.pytest.ini_options] # ========== Tool Configurations ==========
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
asyncio_mode = "auto"
markers = [
"unit: Unit tests with mocks",
"integration: Integration tests with real API",
]
addopts = "-v --cov=kwork_api --cov-report=term-missing"
[tool.ruff] [tool.ruff]
line-length = 100
target-version = "py310" target-version = "py310"
line-length = 100
[tool.ruff.lint] [tool.ruff.lint]
select = [ select = [
@ -87,12 +40,24 @@ select = [
"C4", # flake8-comprehensions "C4", # flake8-comprehensions
"UP", # pyupgrade "UP", # pyupgrade
] ]
ignore = [ ignore = ["E501"]
"E501", # line too long
] [tool.ruff.lint.isort]
known-first-party = ["kwork_api"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
addopts = "-v --tb=short"
[tool.coverage.run] [tool.coverage.run]
source = ["kwork_api"] source = ["src/kwork_api"]
branch = true branch = true
[tool.coverage.report] [tool.coverage.report]
@ -100,55 +65,5 @@ exclude_lines = [
"pragma: no cover", "pragma: no cover",
"def __repr__", "def __repr__",
"raise NotImplementedError", "raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:", "if TYPE_CHECKING:",
] ]
# ============================================
# Python Semantic Release Configuration
# ============================================
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
version_variables = [
"src/kwork_api/__init__.py:__version__",
]
branch = "main"
build_command = "uv build"
commit_parser = "angular"
upload_to_vcs_release = true
tag_format = "v{version}"
[tool.semantic_release.branches.main]
match = "main"
prerelease = false
[tool.semantic_release.commit_parser_options]
allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]
breaking_change_tags = ["feat"]
[tool.semantic_release.remote]
type = "gitea"
domain = "https://git.much-data.ru"
owner = "claw"
repo_name = "kwork-api"
token = { env = "GITEA_TOKEN" }
[tool.semantic_release.publish]
dist_glob_patterns = ["dist/*"]
upload_to_vcs_release = true
[tool.semantic_release.changelog]
template_dir = "templates"
changelog_file = "CHANGELOG.md"
exclude_commit_patterns = [
"chore\\(release\\):.*",
"ci\\(release\\):.*",
]
[tool.semantic_release.changelog.environment]
trim_blocks = true
lstrip_blocks = true