Skip to content

Commit 17e3e54

Browse files
committed
Back to 1.0.3
1 parent 46047ae commit 17e3e54

53 files changed

Lines changed: 10411 additions & 573 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-publish.yml

Lines changed: 258 additions & 330 deletions
Large diffs are not rendered by default.

.github/workflows/ci.yml

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@ name: CI - Test and Validate
33
on:
44
pull_request:
55
branches: [ main, develop ]
6+
paths:
7+
- 'src/**'
8+
- 'pyproject.toml'
9+
- 'test_sdk.py'
10+
- 'tests/**'
11+
- 'examples/**'
12+
- '.github/workflows/ci.yml'
613
push:
7-
branches: [ main, develop ]
14+
branches: [ main, develop, 'feature/**' ]
815
paths:
916
- 'src/**'
1017
- 'pyproject.toml'
1118
- 'test_sdk.py'
19+
- 'tests/**'
1220
- 'examples/**'
21+
- '.github/workflows/ci.yml'
1322

1423
jobs:
1524
test:
@@ -26,20 +35,51 @@ jobs:
2635
with:
2736
python-version: ${{ matrix.python-version }}
2837

38+
- name: Cache Python dependencies
39+
uses: actions/cache@v3
40+
with:
41+
path: ~/.cache/pip
42+
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
43+
restore-keys: |
44+
${{ runner.os }}-pip-${{ matrix.python-version }}-
45+
${{ runner.os }}-pip-
46+
2947
- name: Install dependencies
3048
run: |
3149
python -m pip install --upgrade pip
3250
pip install -e ".[dev]"
3351
34-
- name: Run validation tests
52+
- name: Run SDK validation tests
3553
run: python test_sdk.py
3654

55+
- name: Run pytest (if tests exist)
56+
run: |
57+
if [ -d "tests" ]; then
58+
pytest tests/ -v
59+
else
60+
echo "No pytest directory found, skipping pytest"
61+
fi
62+
3763
- name: Run type checking
3864
run: mypy src/itl_controlplane_sdk --ignore-missing-imports
3965

4066
- name: Check code formatting
4167
run: black --check src/ examples/
4268

69+
- name: Test import and basic functionality
70+
run: |
71+
python -c "
72+
from itl_controlplane_sdk import ResourceProviderRegistry, ResourceProvider
73+
from itl_controlplane_sdk.models import ResourceRequest, ProvisioningState
74+
print('All imports successful')
75+
76+
# Test basic functionality
77+
registry = ResourceProviderRegistry()
78+
print('Registry creation successful')
79+
80+
print('SDK basic functionality test passed')
81+
"
82+
4383
- name: Test examples
4484
run: |
4585
cd examples
@@ -65,5 +105,51 @@ jobs:
65105
python -m venv test-env
66106
source test-env/bin/activate
67107
pip install dist/*.whl
68-
python -c "from itl_controlplane_sdk import ResourceProviderRegistry; print('✅ Package test successful')"
69-
deactivate
108+
python -c "from itl_controlplane_sdk import ResourceProviderRegistry; print('Package test successful')"
109+
deactivate
110+
111+
lint-and-security:
112+
runs-on: ubuntu-latest
113+
steps:
114+
- name: Checkout repository
115+
uses: actions/checkout@v4
116+
117+
- name: Set up Python
118+
uses: actions/setup-python@v4
119+
with:
120+
python-version: '3.11'
121+
122+
- name: Install security tools
123+
run: |
124+
python -m pip install --upgrade pip
125+
pip install bandit safety pip-audit
126+
127+
- name: Install package
128+
run: |
129+
pip install -e ".[dev]"
130+
131+
- name: Run bandit security linter
132+
run: |
133+
bandit -r src/ -f json -o bandit-report.json || true
134+
bandit -r src/
135+
136+
- name: Run safety check for dependencies
137+
run: |
138+
safety check --json --output safety-report.json || true
139+
safety check
140+
141+
- name: Run pip-audit for vulnerability scanning
142+
run: |
143+
pip-audit --format=json --output=audit-report.json || true
144+
pip-audit
145+
146+
- name: Upload security reports
147+
uses: actions/upload-artifact@v4
148+
if: always()
149+
with:
150+
name: security-reports
151+
path: |
152+
bandit-report.json
153+
safety-report.json
154+
audit-report.json
155+
retention-days: 30

.github/workflows/security.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Security and Lint Checks
2+
3+
on:
4+
push:
5+
branches: [ main, develop, 'feature/**' ]
6+
paths:
7+
- 'src/**'
8+
- 'pyproject.toml'
9+
- '.github/workflows/security.yml'
10+
pull_request:
11+
branches: [ main, develop ]
12+
paths:
13+
- 'src/**'
14+
- 'pyproject.toml'
15+
16+
env:
17+
PYTHON_VERSION: '3.11'
18+
19+
jobs:
20+
security-and-lint:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
26+
- name: Set up Python
27+
uses: actions/setup-python@v4
28+
with:
29+
python-version: ${{ env.PYTHON_VERSION }}
30+
31+
- name: Cache Python dependencies
32+
uses: actions/cache@v3
33+
with:
34+
path: ~/.cache/pip
35+
key: ${{ runner.os }}-pip-${{ env.PYTHON_VERSION }}-security-${{ hashFiles('pyproject.toml') }}
36+
restore-keys: |
37+
${{ runner.os }}-pip-${{ env.PYTHON_VERSION }}-security-
38+
${{ runner.os }}-pip-${{ env.PYTHON_VERSION }}-
39+
40+
- name: Install security tools
41+
run: |
42+
python -m pip install --upgrade pip
43+
pip install bandit safety pip-audit
44+
45+
- name: Install package
46+
run: |
47+
pip install -e ".[dev]"
48+
49+
- name: Run bandit security linter
50+
run: |
51+
bandit -r src/ -f json -o bandit-report.json || true
52+
bandit -r src/
53+
54+
- name: Run safety check for dependencies
55+
run: |
56+
safety check --json --output safety-report.json || true
57+
safety check
58+
59+
- name: Run pip-audit for vulnerability scanning
60+
run: |
61+
pip-audit --format=json --output=audit-report.json || true
62+
pip-audit
63+
64+
- name: Upload security reports
65+
uses: actions/upload-artifact@v4
66+
if: always()
67+
with:
68+
name: security-reports
69+
path: |
70+
bandit-report.json
71+
safety-report.json
72+
audit-report.json
73+
retention-days: 30

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,8 @@ safety-report.txt
290290
# Local development files
291291
.dev/
292292
.local/
293-
config.local.*
293+
config.local.*
294+
295+
# Unsorted/draft documentation and analysis
296+
docs/999-UNSORTED/
297+
ignore/

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,65 @@ See [PIPELINE_SETUP.md](./PIPELINE_SETUP.md) for complete pipeline documentation
844844
- [**PyPI Setup Guide**](./.github/PYPI_SETUP.md) - Package publishing configuration
845845
- [**Examples Directory**](./examples/) - Working code examples and usage patterns
846846

847+
## Versioning and Releases
848+
849+
This project uses **semantic versioning** with **automatic releases on merge** to main/develop.
850+
851+
> **Automatic Releases**: When you merge to `main` or `develop`, versions are automatically bumped and released to PyPI. No manual tagging needed!
852+
853+
### Quick Start
854+
855+
Check current version:
856+
```bash
857+
python scripts/version.py --get-version
858+
```
859+
860+
### How It Works
861+
862+
1. **Push to `develop`** → Auto-bumps patch version (`1.0.0``1.0.1`) → Auto-released to PyPI
863+
2. **Push to `main`** → Auto-bumps minor version (`1.0.1``1.1.0`) → Auto-released to PyPI
864+
3. **Push to `feature/*`** → Dev version only (Test PyPI, no release)
865+
866+
See [AUTO_RELEASE_WORKFLOW.md](AUTO_RELEASE_WORKFLOW.md) for detailed flow.
867+
868+
### Documentation
869+
870+
- **[AUTO_RELEASE_WORKFLOW.md](AUTO_RELEASE_WORKFLOW.md)** - **START HERE** - How automatic releases work
871+
- **[VERSIONING_QUICKSTART.md](VERSIONING_QUICKSTART.md)** - Quick reference guide
872+
- **[VERSIONING.md](VERSIONING.md)** - Complete branching strategy and versioning guide
873+
- **[RELEASE_CHECKLIST.md](RELEASE_CHECKLIST.md)** - Pre-release verification checklist (for manual releases)
874+
- **[COMMANDS_CHEATSHEET.md](COMMANDS_CHEATSHEET.md)** - Git and version tool commands
875+
876+
### Branch Behavior
877+
878+
| Branch | Auto-Release | Version Format | PyPI | Notes |
879+
|--------|---|---|---|---|
880+
| `main` | Yes (Minor bump) |`1.1.0` | Prod | Feature release |
881+
| `develop` | Yes (Patch bump) |`1.0.1` | Prod | Bug fix release |
882+
| `feature/*` | No | `1.0.1.dev+feature...` | Test | Feature development |
883+
| Tag `v1.0.0` | Manual | `1.0.0` | Prod | Manual release |
884+
885+
### Version Management Tools
886+
887+
- **Version Script**: [scripts/version.py](scripts/version.py) - Core semantic versioning tool
888+
- **PowerShell Wrapper**: [scripts/version.ps1](scripts/version.ps1) - Windows convenience script
889+
- **Bash Wrapper**: [scripts/version.sh](scripts/version.sh) - Linux/macOS convenience script
890+
891+
### CI/CD Pipeline
892+
893+
All pushes trigger automated workflows:
894+
895+
```
896+
Push to main/develop
897+
↓ Test & Build (1-2 min)
898+
↓ Auto-Tag Job (bumps version, creates git tag)
899+
↓ Release Workflow (triggered by tag)
900+
↓ Publish to PyPI + Create GitHub Release (2 min)
901+
DONE (total: 3-5 minutes)
902+
```
903+
904+
**No manual steps needed for releases!**
905+
847906
## Support and Contributing
848907

849908
For questions, issues, or contributions:

alembic/env.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""
2+
Alembic environment configuration for ITL ControlPlane.
3+
4+
Supports both sync and async migrations. Reads the database URL from
5+
environment variables or falls back to alembic.ini.
6+
"""
7+
import os
8+
import asyncio
9+
from logging.config import fileConfig
10+
11+
from sqlalchemy import pool
12+
from sqlalchemy.engine import Connection
13+
from sqlalchemy.ext.asyncio import async_engine_from_config
14+
15+
from alembic import context
16+
17+
# Import all models so Alembic can detect them
18+
from itl_controlplane_sdk.storage.models import Base
19+
20+
# Alembic Config object
21+
config = context.config
22+
23+
# Setup logging
24+
if config.config_file_name is not None:
25+
fileConfig(config.config_file_name)
26+
27+
# Set target metadata for autogenerate
28+
target_metadata = Base.metadata
29+
30+
31+
def get_url() -> str:
32+
"""Get database URL from environment or config."""
33+
host = os.getenv("DATABASE_HOST", "localhost")
34+
port = os.getenv("DATABASE_PORT", "5432")
35+
db = os.getenv("DATABASE_NAME", "controlplane")
36+
user = os.getenv("DATABASE_USER", "controlplane")
37+
password = os.getenv("DATABASE_PASSWORD", "devpassword")
38+
return f"postgresql+asyncpg://{user}:{password}@{host}:{port}/{db}"
39+
40+
41+
def run_migrations_offline() -> None:
42+
"""Run migrations in 'offline' mode — generates SQL without connecting."""
43+
url = get_url()
44+
context.configure(
45+
url=url,
46+
target_metadata=target_metadata,
47+
literal_binds=True,
48+
dialect_opts={"paramstyle": "named"},
49+
)
50+
51+
with context.begin_transaction():
52+
context.run_migrations()
53+
54+
55+
def do_run_migrations(connection: Connection) -> None:
56+
"""Run migrations with a live connection."""
57+
context.configure(
58+
connection=connection,
59+
target_metadata=target_metadata,
60+
)
61+
62+
with context.begin_transaction():
63+
context.run_migrations()
64+
65+
66+
async def run_async_migrations() -> None:
67+
"""Run migrations in async mode."""
68+
configuration = config.get_section(config.config_ini_section, {})
69+
configuration["sqlalchemy.url"] = get_url()
70+
71+
connectable = async_engine_from_config(
72+
configuration,
73+
prefix="sqlalchemy.",
74+
poolclass=pool.NullPool,
75+
)
76+
77+
async with connectable.connect() as connection:
78+
await connection.run_sync(do_run_migrations)
79+
80+
await connectable.dispose()
81+
82+
83+
def run_migrations_online() -> None:
84+
"""Run migrations in 'online' mode — connects to the database."""
85+
asyncio.run(run_async_migrations())
86+
87+
88+
if context.is_offline_mode():
89+
run_migrations_offline()
90+
else:
91+
run_migrations_online()

0 commit comments

Comments
 (0)