upwork-learning 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- upwork_learning-0.1.0/.claude/hooks/stop-preflight.sh +30 -0
- upwork_learning-0.1.0/.claude/settings.json +17 -0
- upwork_learning-0.1.0/.env.example +24 -0
- upwork_learning-0.1.0/.githooks/pre-commit +18 -0
- upwork_learning-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
- upwork_learning-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +13 -0
- upwork_learning-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- upwork_learning-0.1.0/.github/workflows/ci.yml +93 -0
- upwork_learning-0.1.0/.github/workflows/release.yml +72 -0
- upwork_learning-0.1.0/.github/workflows/secret-scan.yml +22 -0
- upwork_learning-0.1.0/.gitignore +143 -0
- upwork_learning-0.1.0/.gitleaks.toml +171 -0
- upwork_learning-0.1.0/.pre-commit-config.yaml +54 -0
- upwork_learning-0.1.0/CHANGELOG.md +37 -0
- upwork_learning-0.1.0/CLAUDE.md +125 -0
- upwork_learning-0.1.0/CONTRIBUTING.md +85 -0
- upwork_learning-0.1.0/LICENSE +21 -0
- upwork_learning-0.1.0/Makefile +34 -0
- upwork_learning-0.1.0/PKG-INFO +247 -0
- upwork_learning-0.1.0/README.md +201 -0
- upwork_learning-0.1.0/ROADMAP.md +384 -0
- upwork_learning-0.1.0/config/.gitignore +34 -0
- upwork_learning-0.1.0/docs/cli/email.md +64 -0
- upwork_learning-0.1.0/docs/cli/index.md +52 -0
- upwork_learning-0.1.0/docs/cli/pdf.md +81 -0
- upwork_learning-0.1.0/docs/cli/sheets.md +72 -0
- upwork_learning-0.1.0/docs/development/contributing.md +76 -0
- upwork_learning-0.1.0/docs/development/testing.md +110 -0
- upwork_learning-0.1.0/docs/development/type-checking.md +101 -0
- upwork_learning-0.1.0/docs/examples/bol-com.md +52 -0
- upwork_learning-0.1.0/docs/examples/workflows.md +132 -0
- upwork_learning-0.1.0/docs/getting-started/configuration.md +56 -0
- upwork_learning-0.1.0/docs/getting-started/installation.md +53 -0
- upwork_learning-0.1.0/docs/getting-started/quick-start.md +96 -0
- upwork_learning-0.1.0/docs/index.md +64 -0
- upwork_learning-0.1.0/docs/integrations/email.md +87 -0
- upwork_learning-0.1.0/docs/integrations/google-sheets.md +67 -0
- upwork_learning-0.1.0/docs/integrations/pdf-processing.md +77 -0
- upwork_learning-0.1.0/docs/runbook.md +239 -0
- upwork_learning-0.1.0/docs/stylesheets/extra.css +11 -0
- upwork_learning-0.1.0/examples/__init__.py +1 -0
- upwork_learning-0.1.0/examples/auto_email.py +193 -0
- upwork_learning-0.1.0/examples/bol_com/README.md +53 -0
- upwork_learning-0.1.0/examples/bol_com/__init__.py +15 -0
- upwork_learning-0.1.0/examples/bol_com/cli.py +166 -0
- upwork_learning-0.1.0/examples/bol_com/client.py +322 -0
- upwork_learning-0.1.0/examples/bol_com/config.py +29 -0
- upwork_learning-0.1.0/examples/bol_com/sync.py +107 -0
- upwork_learning-0.1.0/examples/pdf_to_sheets.py +113 -0
- upwork_learning-0.1.0/mkdocs.yml +86 -0
- upwork_learning-0.1.0/noxfile.py +279 -0
- upwork_learning-0.1.0/preflight.sh +83 -0
- upwork_learning-0.1.0/pyproject.toml +146 -0
- upwork_learning-0.1.0/src/__init__.py +3 -0
- upwork_learning-0.1.0/src/cli.py +349 -0
- upwork_learning-0.1.0/src/integrations/__init__.py +9 -0
- upwork_learning-0.1.0/src/integrations/base.py +237 -0
- upwork_learning-0.1.0/src/integrations/email_handler.py +514 -0
- upwork_learning-0.1.0/src/integrations/google_sheets.py +435 -0
- upwork_learning-0.1.0/src/integrations/pdf_processor.py +372 -0
- upwork_learning-0.1.0/src/utils/__init__.py +12 -0
- upwork_learning-0.1.0/src/utils/config.py +133 -0
- upwork_learning-0.1.0/src/utils/logger.py +103 -0
- upwork_learning-0.1.0/src/utils/retry.py +84 -0
- upwork_learning-0.1.0/tests/__init__.py +1 -0
- upwork_learning-0.1.0/tests/fixtures/credentials.json +10 -0
- upwork_learning-0.1.0/tests/integrations/__init__.py +1 -0
- upwork_learning-0.1.0/tests/integrations/test_base.py +185 -0
- upwork_learning-0.1.0/tests/integrations/test_email_handler.py +449 -0
- upwork_learning-0.1.0/tests/integrations/test_google_sheets.py +428 -0
- upwork_learning-0.1.0/tests/integrations/test_pdf_processor.py +286 -0
- upwork_learning-0.1.0/tests/test_cli.py +548 -0
- upwork_learning-0.1.0/tests/utils/__init__.py +1 -0
- upwork_learning-0.1.0/tests/utils/test_config.py +62 -0
- upwork_learning-0.1.0/tests/utils/test_logger.py +76 -0
- upwork_learning-0.1.0/tests/utils/test_retry.py +128 -0
- upwork_learning-0.1.0/tsconfig.json +16 -0
- upwork_learning-0.1.0/uv.lock +1855 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# .claude/hooks/stop-preflight.sh
|
|
3
|
+
# Stop hook: запускает preflight.sh, результат возвращает как JSON systemMessage.
|
|
4
|
+
# Если lint или pytest упали — устанавливает continue:false, чтобы Claude увидел ошибки.
|
|
5
|
+
|
|
6
|
+
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
7
|
+
|
|
8
|
+
# Быстрый выход без проверок: SKIP_PREFLIGHT=1 claude
|
|
9
|
+
if [ "${SKIP_PREFLIGHT:-0}" = "1" ]; then
|
|
10
|
+
echo '{"systemMessage": "⚡ Preflight skipped (SKIP_PREFLIGHT=1)"}'
|
|
11
|
+
exit 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
OUTPUT=$(bash preflight.sh 2>&1)
|
|
15
|
+
CODE=$?
|
|
16
|
+
|
|
17
|
+
# Выводим JSON для Claude Code
|
|
18
|
+
python3 - "$OUTPUT" "$CODE" <<'PY'
|
|
19
|
+
import json, sys
|
|
20
|
+
|
|
21
|
+
output = sys.argv[1]
|
|
22
|
+
code = int(sys.argv[2])
|
|
23
|
+
|
|
24
|
+
result = {"systemMessage": output}
|
|
25
|
+
if code != 0:
|
|
26
|
+
result["continue"] = False
|
|
27
|
+
result["stopReason"] = "Preflight failed — исправь ошибки перед завершением"
|
|
28
|
+
|
|
29
|
+
print(json.dumps(result))
|
|
30
|
+
PY
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"Stop": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "bash .claude/hooks/stop-preflight.sh",
|
|
10
|
+
"timeout": 120,
|
|
11
|
+
"statusMessage": "Running preflight: lint · mypy · pytest …"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Google Sheets Integration
|
|
2
|
+
GOOGLE_SHEETS_CREDENTIALS_PATH=config/credentials.json
|
|
3
|
+
GOOGLE_SHEETS_SPREADSHEET_ID=your_spreadsheet_id_here
|
|
4
|
+
|
|
5
|
+
# Email Configuration
|
|
6
|
+
SMTP_HOST=smtp.gmail.com
|
|
7
|
+
SMTP_PORT=587
|
|
8
|
+
SMTP_USER=your_email@gmail.com
|
|
9
|
+
SMTP_PASSWORD=your_app_password
|
|
10
|
+
IMAP_HOST=imap.gmail.com
|
|
11
|
+
IMAP_PORT=993
|
|
12
|
+
IMAP_USER=your_email@gmail.com
|
|
13
|
+
IMAP_PASSWORD=your_app_password
|
|
14
|
+
|
|
15
|
+
# API Keys
|
|
16
|
+
BOL_COM_API_KEY=your_bol_com_api_key
|
|
17
|
+
BOL_COM_CLIENT_ID=your_client_id
|
|
18
|
+
BOL_COM_CLIENT_SECRET=your_client_secret
|
|
19
|
+
|
|
20
|
+
# Application Settings
|
|
21
|
+
LOG_LEVEL=INFO
|
|
22
|
+
MAX_RETRIES=3
|
|
23
|
+
RATE_LIMIT_DELAY=1.0
|
|
24
|
+
ENVIRONMENT=development
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Pre-commit hook to run linting and type checking
|
|
3
|
+
|
|
4
|
+
echo "Running pre-commit checks..."
|
|
5
|
+
|
|
6
|
+
# Run ruff
|
|
7
|
+
echo "Running ruff..."
|
|
8
|
+
ruff check src/
|
|
9
|
+
|
|
10
|
+
# Run mypy
|
|
11
|
+
echo "Running mypy..."
|
|
12
|
+
mypy src/
|
|
13
|
+
|
|
14
|
+
# Run tests
|
|
15
|
+
echo "Running tests..."
|
|
16
|
+
pytest tests/
|
|
17
|
+
|
|
18
|
+
echo "Pre-commit checks complete!"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## Bug Report
|
|
2
|
+
|
|
3
|
+
**Description**
|
|
4
|
+
A clear description of the bug.
|
|
5
|
+
|
|
6
|
+
**Steps to Reproduce**
|
|
7
|
+
1. Go to '...'
|
|
8
|
+
2. Click on '....'
|
|
9
|
+
3. See error
|
|
10
|
+
|
|
11
|
+
**Expected Behavior**
|
|
12
|
+
What you expected to happen.
|
|
13
|
+
|
|
14
|
+
**Actual Behavior**
|
|
15
|
+
What actually happened.
|
|
16
|
+
|
|
17
|
+
**Environment**
|
|
18
|
+
- OS: [e.g., Windows 11]
|
|
19
|
+
- Python version: [e.g., 3.11]
|
|
20
|
+
- Package version: [e.g., 0.1.0]
|
|
21
|
+
|
|
22
|
+
**Additional Context**
|
|
23
|
+
Any other context about the problem.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Feature Request
|
|
2
|
+
|
|
3
|
+
**Is your feature request related to a problem? Please describe.**
|
|
4
|
+
A clear description of the problem.
|
|
5
|
+
|
|
6
|
+
**Describe the solution you'd like**
|
|
7
|
+
A clear description of what you want to happen.
|
|
8
|
+
|
|
9
|
+
**Describe alternatives you've considered**
|
|
10
|
+
Any alternative solutions you've considered.
|
|
11
|
+
|
|
12
|
+
**Additional context**
|
|
13
|
+
Any other context about the feature request.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## Pull Request Description
|
|
2
|
+
|
|
3
|
+
### Type of Change
|
|
4
|
+
|
|
5
|
+
- [ ] Bug fix
|
|
6
|
+
- [ ] New feature
|
|
7
|
+
- [ ] Documentation update
|
|
8
|
+
- [ ] Code refactoring
|
|
9
|
+
- [ ] Test update
|
|
10
|
+
|
|
11
|
+
### Checklist
|
|
12
|
+
|
|
13
|
+
- [ ] My code follows the style guidelines of this project
|
|
14
|
+
- [ ] I have performed a self-review of my own code
|
|
15
|
+
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
16
|
+
- [ ] I have made corresponding changes to the documentation
|
|
17
|
+
- [ ] My changes generate no new warnings
|
|
18
|
+
- [ ] I have added tests that prove my fix is effective or that my feature works
|
|
19
|
+
- [ ] New and existing unit tests pass locally with my changes
|
|
20
|
+
- [ ] Any dependent changes have been merged and published
|
|
21
|
+
|
|
22
|
+
### Description
|
|
23
|
+
|
|
24
|
+
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
|
|
25
|
+
|
|
26
|
+
### Related Issues
|
|
27
|
+
|
|
28
|
+
Fixes #(issue number)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main, master]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
run: uv python install ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Create virtual environment
|
|
26
|
+
run: uv venv
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv pip install -e ".[dev,security]" --python .venv/bin/python
|
|
30
|
+
|
|
31
|
+
- name: Lint with ruff
|
|
32
|
+
run: .venv/bin/ruff check src/ tests/
|
|
33
|
+
|
|
34
|
+
- name: Check formatting
|
|
35
|
+
run: .venv/bin/ruff format --check src/ tests/
|
|
36
|
+
|
|
37
|
+
- name: Type check with mypy
|
|
38
|
+
run: .venv/bin/mypy src/
|
|
39
|
+
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: .venv/bin/pytest --cov=src --cov-report=xml
|
|
42
|
+
|
|
43
|
+
- name: Upload coverage to Codecov
|
|
44
|
+
uses: codecov/codecov-action@v4
|
|
45
|
+
with:
|
|
46
|
+
file: ./coverage.xml
|
|
47
|
+
fail_ci_if_error: false
|
|
48
|
+
|
|
49
|
+
audit:
|
|
50
|
+
name: Dependency Audit
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v4
|
|
55
|
+
|
|
56
|
+
- name: Install uv
|
|
57
|
+
uses: astral-sh/setup-uv@v4
|
|
58
|
+
|
|
59
|
+
- name: Set up Python
|
|
60
|
+
run: uv python install 3.12
|
|
61
|
+
|
|
62
|
+
- name: Create virtual environment
|
|
63
|
+
run: uv venv
|
|
64
|
+
|
|
65
|
+
- name: Install dependencies
|
|
66
|
+
run: uv pip install -e ".[dev]" --python .venv/bin/python
|
|
67
|
+
|
|
68
|
+
- name: Audit dependencies for known vulnerabilities
|
|
69
|
+
# CVE-2026-4539 (pygments): no patched release available yet; pinned to 2.19.2 in pyproject.toml
|
|
70
|
+
run: uv pip install pip-audit --python .venv/bin/python && .venv/bin/pip-audit --ignore-vuln CVE-2026-4539
|
|
71
|
+
|
|
72
|
+
docs:
|
|
73
|
+
name: Docs
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
if: github.ref == 'refs/heads/main'
|
|
76
|
+
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
|
|
80
|
+
- name: Install uv
|
|
81
|
+
uses: astral-sh/setup-uv@v4
|
|
82
|
+
|
|
83
|
+
- name: Set up Python
|
|
84
|
+
run: uv python install 3.12
|
|
85
|
+
|
|
86
|
+
- name: Create virtual environment
|
|
87
|
+
run: uv venv
|
|
88
|
+
|
|
89
|
+
- name: Install docs dependencies
|
|
90
|
+
run: uv pip install --python .venv/bin/python mkdocs-material mike
|
|
91
|
+
|
|
92
|
+
- name: Deploy documentation
|
|
93
|
+
run: uv run mkdocs gh-deploy --force
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
# OIDC Trusted Publishing — no API token stored in secrets.
|
|
9
|
+
# One-time setup required on PyPI:
|
|
10
|
+
# https://pypi.org/manage/account/publishing/
|
|
11
|
+
# Publisher: petro-nazarenko / Agent-Guidelines-for-Upwork-Learning-Projects
|
|
12
|
+
# Workflow: release.yml | Environment: (leave blank)
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write # create GitHub release + upload assets
|
|
15
|
+
id-token: write # OIDC token for PyPI trusted publishing
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
name: Build
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v4
|
|
27
|
+
|
|
28
|
+
- name: Set up Python
|
|
29
|
+
run: uv python install 3.12
|
|
30
|
+
|
|
31
|
+
- name: Build package
|
|
32
|
+
run: uv build
|
|
33
|
+
|
|
34
|
+
- name: Upload dist artifacts
|
|
35
|
+
uses: actions/upload-artifact@v4
|
|
36
|
+
with:
|
|
37
|
+
name: dist
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
name: Publish to PyPI
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
needs: build
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- name: Download dist artifacts
|
|
47
|
+
uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
|
|
52
|
+
- name: Publish to PyPI (trusted publishing)
|
|
53
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
54
|
+
|
|
55
|
+
github-release:
|
|
56
|
+
name: Create GitHub Release
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
needs: build
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- name: Download dist artifacts
|
|
62
|
+
uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
|
|
67
|
+
- name: Create GitHub Release
|
|
68
|
+
uses: softprops/action-gh-release@v1
|
|
69
|
+
with:
|
|
70
|
+
files: dist/*
|
|
71
|
+
env:
|
|
72
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Secret Scanning
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main, master]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
gitleaks:
|
|
10
|
+
name: Gitleaks Scan
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
|
|
18
|
+
- name: Run Gitleaks
|
|
19
|
+
uses: gitleaks/gitleaks-action@v2
|
|
20
|
+
env:
|
|
21
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
22
|
+
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
|
|
31
|
+
# Installer logs
|
|
32
|
+
pip-log.txt
|
|
33
|
+
pip-delete-this-directory.txt
|
|
34
|
+
|
|
35
|
+
# Unit test / coverage reports
|
|
36
|
+
htmlcov/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
.coverage
|
|
40
|
+
.coverage.*
|
|
41
|
+
.cache
|
|
42
|
+
nosetests.xml
|
|
43
|
+
coverage.xml
|
|
44
|
+
*.cover
|
|
45
|
+
*.py,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
|
|
49
|
+
# Translations
|
|
50
|
+
*.mo
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Django stuff:
|
|
54
|
+
*.log
|
|
55
|
+
local_settings.py
|
|
56
|
+
|
|
57
|
+
# Flask stuff:
|
|
58
|
+
instance/
|
|
59
|
+
.webassets-cache
|
|
60
|
+
|
|
61
|
+
# Scrapy stuff:
|
|
62
|
+
.scrapy
|
|
63
|
+
|
|
64
|
+
# Sphinx documentation
|
|
65
|
+
docs/_build/
|
|
66
|
+
|
|
67
|
+
# PyBuilder
|
|
68
|
+
target/
|
|
69
|
+
|
|
70
|
+
# Jupyter Notebook
|
|
71
|
+
.ipynb_checkpoints
|
|
72
|
+
|
|
73
|
+
# IPython
|
|
74
|
+
profile_default/
|
|
75
|
+
ipython_config.py
|
|
76
|
+
|
|
77
|
+
# pyenv
|
|
78
|
+
.python-version
|
|
79
|
+
|
|
80
|
+
# pipenv
|
|
81
|
+
Pipfile.lock
|
|
82
|
+
|
|
83
|
+
# uv
|
|
84
|
+
# uv.lock is intentionally tracked — see ROADMAP.md §5.6
|
|
85
|
+
|
|
86
|
+
# poetry
|
|
87
|
+
poetry.lock
|
|
88
|
+
|
|
89
|
+
# PEP 582
|
|
90
|
+
__pypackages__/
|
|
91
|
+
|
|
92
|
+
# Celery stuff
|
|
93
|
+
celerybeat-schedule
|
|
94
|
+
celerybeat.pid
|
|
95
|
+
|
|
96
|
+
# SageMath parsed files
|
|
97
|
+
*.sage.py
|
|
98
|
+
|
|
99
|
+
# Environments
|
|
100
|
+
.env
|
|
101
|
+
.venv
|
|
102
|
+
env/
|
|
103
|
+
venv/
|
|
104
|
+
ENV/
|
|
105
|
+
env.bak/
|
|
106
|
+
venv.bak/
|
|
107
|
+
|
|
108
|
+
# Spyder project settings
|
|
109
|
+
.spyderproject
|
|
110
|
+
.spyproject
|
|
111
|
+
|
|
112
|
+
# Rope project settings
|
|
113
|
+
.ropeproject
|
|
114
|
+
|
|
115
|
+
# mkdocs documentation
|
|
116
|
+
/site
|
|
117
|
+
|
|
118
|
+
# mypy
|
|
119
|
+
.mypy_cache/
|
|
120
|
+
.dmypy.json
|
|
121
|
+
dmypy.json
|
|
122
|
+
|
|
123
|
+
# Pyre type checker
|
|
124
|
+
.pyre/
|
|
125
|
+
|
|
126
|
+
# IDE
|
|
127
|
+
.idea/
|
|
128
|
+
.vscode/
|
|
129
|
+
*.swp
|
|
130
|
+
*.swo
|
|
131
|
+
|
|
132
|
+
# OS
|
|
133
|
+
.DS_Store
|
|
134
|
+
Thumbs.db
|
|
135
|
+
|
|
136
|
+
# Project specific
|
|
137
|
+
logs/
|
|
138
|
+
data/
|
|
139
|
+
config/credentials.json
|
|
140
|
+
*.sample.json
|
|
141
|
+
|
|
142
|
+
# Claude Code local settings (personal overrides)
|
|
143
|
+
.claude/settings.local.json
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Gitleaks configuration
|
|
2
|
+
# https://github.com/gitleaks/gitleaks
|
|
3
|
+
|
|
4
|
+
[allowlist]
|
|
5
|
+
description = "Allowlisted paths and patterns"
|
|
6
|
+
paths = [
|
|
7
|
+
"^examples?/",
|
|
8
|
+
"^docs?/",
|
|
9
|
+
"^config/samples?/",
|
|
10
|
+
"^\\.venv/",
|
|
11
|
+
"^venv/",
|
|
12
|
+
"^dist/",
|
|
13
|
+
"^build/",
|
|
14
|
+
"^\\.git/",
|
|
15
|
+
# Fixture files contain intentionally fake keys for unit tests
|
|
16
|
+
"^tests/fixtures/",
|
|
17
|
+
]
|
|
18
|
+
regexes = [
|
|
19
|
+
"^ пример? ",
|
|
20
|
+
"^ example ",
|
|
21
|
+
"^placeholder[_-]?",
|
|
22
|
+
"^test[_-]?",
|
|
23
|
+
# Python type annotations — field definitions, not real secrets
|
|
24
|
+
# e.g. smtp_password: SecretStr | None = None
|
|
25
|
+
"^SecretStr",
|
|
26
|
+
"^str \\| None",
|
|
27
|
+
"^None$",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[[rules]]
|
|
31
|
+
id = "aws-access-key"
|
|
32
|
+
description = "AWS Access Key"
|
|
33
|
+
regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
|
|
34
|
+
tags = ["key", "AWS"]
|
|
35
|
+
|
|
36
|
+
[[rules]]
|
|
37
|
+
id = "aws-secret-access-key"
|
|
38
|
+
description = "AWS Secret Access Key"
|
|
39
|
+
regex = '''(?i)aws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40}['"]?'''
|
|
40
|
+
tags = ["key", "AWS"]
|
|
41
|
+
|
|
42
|
+
[[rules]]
|
|
43
|
+
id = "generic-api-key"
|
|
44
|
+
description = "Generic API Key"
|
|
45
|
+
regex = '''(?i)(api[_-]?key|apikey)\s*[:=]\s*['"]?[a-zA-Z0-9]{16,64}['"]?'''
|
|
46
|
+
tags = ["key", "API"]
|
|
47
|
+
|
|
48
|
+
[[rules]]
|
|
49
|
+
id = "generic-secret-key"
|
|
50
|
+
description = "Generic Secret Key"
|
|
51
|
+
regex = '''(?i)(secret[_-]?key|private[_-]?key|client[_-]?secret)\s*[:=]\s*['"]?[a-zA-Z0-9+/=]{20,64}['"]?'''
|
|
52
|
+
tags = ["key", "secret"]
|
|
53
|
+
|
|
54
|
+
[[rules]]
|
|
55
|
+
id = "google-api-key"
|
|
56
|
+
description = "Google API Key"
|
|
57
|
+
regex = '''AIza[0-9A-Za-z\\-_]{35}'''
|
|
58
|
+
tags = ["key", "Google"]
|
|
59
|
+
|
|
60
|
+
[[rules]]
|
|
61
|
+
id = "google-oauth-access-token"
|
|
62
|
+
description = "Google OAuth Access Token"
|
|
63
|
+
regex = '''ya29\.[0-9A-Za-z\\-_]+'''
|
|
64
|
+
tags = ["key", "Google", "OAuth"]
|
|
65
|
+
|
|
66
|
+
[[rules]]
|
|
67
|
+
id = "github-token"
|
|
68
|
+
description = "GitHub Token"
|
|
69
|
+
regex = '''gh[pousr]_[A-Za-z0-9_]{36,255}'''
|
|
70
|
+
tags = ["key", "GitHub"]
|
|
71
|
+
|
|
72
|
+
[[rules]]
|
|
73
|
+
id = "gitlab-personal-access-token"
|
|
74
|
+
description = "GitLab Personal Access Token"
|
|
75
|
+
regex = '''glpat-[0-9a-zA-Z\\-_]{20}'''
|
|
76
|
+
tags = ["key", "GitLab"]
|
|
77
|
+
|
|
78
|
+
[[rules]]
|
|
79
|
+
id = "slack-token"
|
|
80
|
+
description = "Slack Token"
|
|
81
|
+
regex = '''xox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*'''
|
|
82
|
+
tags = ["key", "Slack"]
|
|
83
|
+
|
|
84
|
+
[[rules]]
|
|
85
|
+
id = "stripe-api-key"
|
|
86
|
+
description = "Stripe API Key"
|
|
87
|
+
regex = '''sk_live_[0-9a-zA-Z]{24,32}'''
|
|
88
|
+
tags = ["key", "Stripe"]
|
|
89
|
+
|
|
90
|
+
[[rules]]
|
|
91
|
+
id = "stripe-publishable-key"
|
|
92
|
+
description = "Stripe Publishable Key"
|
|
93
|
+
regex = '''pk_live_[0-9a-zA-Z]{24,32}'''
|
|
94
|
+
tags = ["key", "Stripe"]
|
|
95
|
+
|
|
96
|
+
[[rules]]
|
|
97
|
+
id = "ssh-private-key"
|
|
98
|
+
description = "SSH Private Key"
|
|
99
|
+
regex = '''-----BEGIN (RSA |DSA |EC |OPENSSH |PGP )?PRIVATE KEY-----'''
|
|
100
|
+
tags = ["key", "SSH"]
|
|
101
|
+
|
|
102
|
+
[rules.allowlist]
|
|
103
|
+
# tests/fixtures/credentials.json contains a deliberately fake RSA key for unit tests
|
|
104
|
+
paths = ['''tests/fixtures/''']
|
|
105
|
+
|
|
106
|
+
[[rules]]
|
|
107
|
+
id = "email-password-credentials"
|
|
108
|
+
description = "Email/Password credentials"
|
|
109
|
+
regex = '''(?i)(smtp|imap|email)[_-]?(password|passwd|pwd)\s*[:=]\s*['"]?[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{}|;:,.<>?]{6,}['"]?'''
|
|
110
|
+
tags = ["credentials", "email"]
|
|
111
|
+
|
|
112
|
+
[rules.allowlist]
|
|
113
|
+
# regexTarget="line" lets us match the whole source line so we can distinguish:
|
|
114
|
+
# field definition: smtp_password: SecretStr | None = None ← allowed
|
|
115
|
+
# real assignment: smtp_password = "actualpassword" ← blocked
|
|
116
|
+
regexTarget = "line"
|
|
117
|
+
regexes = [
|
|
118
|
+
# Type annotations in dataclasses / Pydantic settings (colon, not equals)
|
|
119
|
+
'''(?i)(smtp|imap)[_-]?password\s*:\s*SecretStr''',
|
|
120
|
+
# Module-level test constant — value comes from os.environ, not a literal
|
|
121
|
+
'''_TEST_SECRET\s*=\s*SecretStr\(os\.environ''',
|
|
122
|
+
# Call sites that reference the constant (no string literal on the line)
|
|
123
|
+
'''(smtp|imap)_password\s*=\s*_TEST_SECRET''',
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
[[rules]]
|
|
127
|
+
id = "basic-auth-header"
|
|
128
|
+
description = "Basic Auth Header"
|
|
129
|
+
regex = '''(?i)authorization\s*[:=]\s*['"]?(basic|bearer)\s+[a-zA-Z0-9+/=]{20,}['"]?'''
|
|
130
|
+
tags = ["auth", "header"]
|
|
131
|
+
|
|
132
|
+
[[rules]]
|
|
133
|
+
id = "authorization-header"
|
|
134
|
+
description = "Authorization Header"
|
|
135
|
+
regex = '''(?i)authorization\s*[:=]\s*['"]?[a-zA-Z0-9+/=]{20,}['"]?'''
|
|
136
|
+
tags = ["auth", "header"]
|
|
137
|
+
|
|
138
|
+
[[rules]]
|
|
139
|
+
id = "database-connection-string"
|
|
140
|
+
description = "Database Connection String"
|
|
141
|
+
regex = '''(?i)(mysql|postgres|postgresql|mongodb|redis|mssql)://[a-zA-Z0-9:@%/_\-?=&]+'''
|
|
142
|
+
tags = ["connection", "database"]
|
|
143
|
+
|
|
144
|
+
[[rules]]
|
|
145
|
+
id = "jwt-token"
|
|
146
|
+
description = "JWT Token"
|
|
147
|
+
regex = '''eyJ[A-Za-z0-9_=-]+\.eyJ[A-Za-z0-9_=-]+\.?[A-Za-z0-9_.+/=-]*'''
|
|
148
|
+
tags = ["token", "JWT"]
|
|
149
|
+
|
|
150
|
+
[[rules]]
|
|
151
|
+
id = "generic-api-token"
|
|
152
|
+
description = "Generic API Token"
|
|
153
|
+
regex = '''['"]token['"]\s*[:=]\s*['"]?[a-zA-Z0-9]{20,}['"]?'''
|
|
154
|
+
tags = ["key", "API", "token"]
|
|
155
|
+
|
|
156
|
+
[[rules]]
|
|
157
|
+
id = "connection-string-with-password"
|
|
158
|
+
description = "Connection String with Password"
|
|
159
|
+
regex = '''(?i)(password|passwd|pwd|secret)\s*[:=]\s*['"]?[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{}|;:,.<>?]{6,}['"]?'''
|
|
160
|
+
tags = ["credentials", "password"]
|
|
161
|
+
|
|
162
|
+
[rules.allowlist]
|
|
163
|
+
regexTarget = "line"
|
|
164
|
+
regexes = [
|
|
165
|
+
# Type annotations in dataclasses / Pydantic settings (colon, not equals)
|
|
166
|
+
'''(?i)(smtp|imap)[_-]?password\s*:\s*SecretStr''',
|
|
167
|
+
# Module-level test constant — value comes from os.environ, not a literal
|
|
168
|
+
'''_TEST_SECRET\s*=\s*SecretStr\(os\.environ''',
|
|
169
|
+
# Call sites that reference the constant (no string literal on the line)
|
|
170
|
+
'''(smtp|imap)_password\s*=\s*_TEST_SECRET''',
|
|
171
|
+
]
|