ci: setup complete CI/CD with PR checks, release pipeline, and conventions
- 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:
parent
0a882d58c5
commit
0c22b31e1c
12
.commitlintrc.json
Normal file
12
.commitlintrc.json
Normal 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", "."]
|
||||||
|
}
|
||||||
|
}
|
||||||
84
.gitea/workflows/pr-check.yml
Normal file
84
.gitea/workflows/pr-check.yml
Normal 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
|
||||||
108
.gitea/workflows/release.yml
Normal file
108
.gitea/workflows/release.yml
Normal 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
|
||||||
@ -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
26
.pre-commit-config.yaml
Normal 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
105
CONTRIBUTING.md
Normal 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
|
||||||
|
```
|
||||||
129
pyproject.toml
129
pyproject.toml
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user