fraisier 0.1.1__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.
- fraisier-0.1.1/.env.example +38 -0
- fraisier-0.1.1/.github/actions/deploy/action.yml +188 -0
- fraisier-0.1.1/.github/workflows/publish.yml +197 -0
- fraisier-0.1.1/.github/workflows/python-version-matrix.yml +95 -0
- fraisier-0.1.1/.github/workflows/quality-gate.yml +162 -0
- fraisier-0.1.1/.gitignore +15 -0
- fraisier-0.1.1/CHANGELOG.md +97 -0
- fraisier-0.1.1/Dockerfile +77 -0
- fraisier-0.1.1/PKG-INFO +298 -0
- fraisier-0.1.1/README.md +254 -0
- fraisier-0.1.1/development.md +635 -0
- fraisier-0.1.1/docker-compose.yml +233 -0
- fraisier-0.1.1/docs/api-reference.md +157 -0
- fraisier-0.1.1/docs/architecture.md +623 -0
- fraisier-0.1.1/docs/cli-reference.md +585 -0
- fraisier-0.1.1/docs/deployment-guide.md +681 -0
- fraisier-0.1.1/docs/failure-modes.md +129 -0
- fraisier-0.1.1/docs/getting-started-postgres.md +493 -0
- fraisier-0.1.1/docs/index.md +26 -0
- fraisier-0.1.1/docs/notifications.md +82 -0
- fraisier-0.1.1/docs/provider-bare-metal.md +801 -0
- fraisier-0.1.1/docs/provider-docker-compose.md +722 -0
- fraisier-0.1.1/docs/security.md +81 -0
- fraisier-0.1.1/docs/testing.md +651 -0
- fraisier-0.1.1/docs/webhook-reference.md +202 -0
- fraisier-0.1.1/examples/django-celery-postgres/.github/workflows/deploy.yml +48 -0
- fraisier-0.1.1/examples/django-celery-postgres/README.md +89 -0
- fraisier-0.1.1/examples/django-celery-postgres/confiture.yaml +12 -0
- fraisier-0.1.1/examples/django-celery-postgres/fraises.yaml +67 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/__init__.py +0 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/celery.py +11 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/models.py +19 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/settings.py +36 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/tasks.py +14 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/urls.py +11 -0
- fraisier-0.1.1/examples/django-celery-postgres/myapp/views.py +34 -0
- fraisier-0.1.1/fraises.example.yaml +223 -0
- fraisier-0.1.1/fraises.yaml +31 -0
- fraisier-0.1.1/fraisier/__init__.py +21 -0
- fraisier-0.1.1/fraisier/_env.py +32 -0
- fraisier-0.1.1/fraisier/cli/__init__.py +14 -0
- fraisier-0.1.1/fraisier/cli/_helpers.py +117 -0
- fraisier-0.1.1/fraisier/cli/db.py +324 -0
- fraisier-0.1.1/fraisier/cli/health.py +158 -0
- fraisier-0.1.1/fraisier/cli/main.py +417 -0
- fraisier-0.1.1/fraisier/cli/ops.py +337 -0
- fraisier-0.1.1/fraisier/cli/providers.py +199 -0
- fraisier-0.1.1/fraisier/cli/scaffold.py +41 -0
- fraisier-0.1.1/fraisier/cli/version.py +267 -0
- fraisier-0.1.1/fraisier/config.py +564 -0
- fraisier-0.1.1/fraisier/database.py +409 -0
- fraisier-0.1.1/fraisier/db/__init__.py +44 -0
- fraisier-0.1.1/fraisier/db/adapter.py +235 -0
- fraisier-0.1.1/fraisier/db/factory.py +226 -0
- fraisier-0.1.1/fraisier/db/history.py +279 -0
- fraisier-0.1.1/fraisier/db/lock_store.py +89 -0
- fraisier-0.1.1/fraisier/db/migrations/postgresql/001_create_tables.sql +82 -0
- fraisier-0.1.1/fraisier/db/migrations/postgresql/002_create_views.sql +74 -0
- fraisier-0.1.1/fraisier/db/migrations/postgresql/003_create_indexes.sql +59 -0
- fraisier-0.1.1/fraisier/db/migrations.py +270 -0
- fraisier-0.1.1/fraisier/db/observability.py +383 -0
- fraisier-0.1.1/fraisier/db/postgres_adapter.py +373 -0
- fraisier-0.1.1/fraisier/db/state.py +145 -0
- fraisier-0.1.1/fraisier/db/webhook_store.py +134 -0
- fraisier-0.1.1/fraisier/dbops/__init__.py +5 -0
- fraisier-0.1.1/fraisier/dbops/_validation.py +120 -0
- fraisier-0.1.1/fraisier/dbops/backup.py +137 -0
- fraisier-0.1.1/fraisier/dbops/confiture.py +452 -0
- fraisier-0.1.1/fraisier/dbops/guard.py +51 -0
- fraisier-0.1.1/fraisier/dbops/operations.py +138 -0
- fraisier-0.1.1/fraisier/dbops/restore.py +96 -0
- fraisier-0.1.1/fraisier/dbops/schema.py +59 -0
- fraisier-0.1.1/fraisier/dbops/templates.py +138 -0
- fraisier-0.1.1/fraisier/deployers/__init__.py +5 -0
- fraisier-0.1.1/fraisier/deployers/api.py +468 -0
- fraisier-0.1.1/fraisier/deployers/base.py +125 -0
- fraisier-0.1.1/fraisier/deployers/docker_compose.py +166 -0
- fraisier-0.1.1/fraisier/deployers/etl.py +86 -0
- fraisier-0.1.1/fraisier/deployers/mixins.py +300 -0
- fraisier-0.1.1/fraisier/deployers/scheduled.py +154 -0
- fraisier-0.1.1/fraisier/errors.py +220 -0
- fraisier-0.1.1/fraisier/git/__init__.py +19 -0
- fraisier-0.1.1/fraisier/git/base.py +119 -0
- fraisier-0.1.1/fraisier/git/bitbucket.py +122 -0
- fraisier-0.1.1/fraisier/git/gitea.py +131 -0
- fraisier-0.1.1/fraisier/git/github.py +110 -0
- fraisier-0.1.1/fraisier/git/gitlab.py +106 -0
- fraisier-0.1.1/fraisier/git/operations.py +95 -0
- fraisier-0.1.1/fraisier/git/registry.py +64 -0
- fraisier-0.1.1/fraisier/health_check.py +701 -0
- fraisier-0.1.1/fraisier/init_templates.py +178 -0
- fraisier-0.1.1/fraisier/locking.py +219 -0
- fraisier-0.1.1/fraisier/logging.py +371 -0
- fraisier-0.1.1/fraisier/metrics.py +481 -0
- fraisier-0.1.1/fraisier/notifications/__init__.py +1 -0
- fraisier-0.1.1/fraisier/notifications/base.py +100 -0
- fraisier-0.1.1/fraisier/notifications/dispatcher.py +119 -0
- fraisier-0.1.1/fraisier/notifications/git_issue.py +69 -0
- fraisier-0.1.1/fraisier/notifications/git_issues.py +223 -0
- fraisier-0.1.1/fraisier/notifications/messaging.py +87 -0
- fraisier-0.1.1/fraisier/notifications/rendering.py +51 -0
- fraisier-0.1.1/fraisier/notifications/templates/issue_body.md.j2 +34 -0
- fraisier-0.1.1/fraisier/providers/__init__.py +41 -0
- fraisier-0.1.1/fraisier/providers/bare_metal.py +474 -0
- fraisier-0.1.1/fraisier/providers/base.py +435 -0
- fraisier-0.1.1/fraisier/providers/docker_compose/__init__.py +5 -0
- fraisier-0.1.1/fraisier/providers/docker_compose/provider.py +799 -0
- fraisier-0.1.1/fraisier/runners.py +134 -0
- fraisier-0.1.1/fraisier/scaffold/__init__.py +1 -0
- fraisier-0.1.1/fraisier/scaffold/renderer.py +259 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/backup.service.j2 +19 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/backup.sh.j2 +38 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/backup.timer.j2 +10 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/confiture.yaml.j2 +26 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/db_deploy.sh.j2 +33 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/db_reset.sh.j2 +36 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/deploy-checker.timer.j2 +10 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/gateway.conf.j2 +159 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/install.sh.j2 +87 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/poll-deploy.service.j2 +23 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/service.j2 +51 -0
- fraisier-0.1.1/fraisier/scaffold/templates/core/sudoers.j2 +13 -0
- fraisier-0.1.1/fraisier/scaffold/templates/provider/deploy.yml.j2 +103 -0
- fraisier-0.1.1/fraisier/status.py +89 -0
- fraisier-0.1.1/fraisier/strategies.py +182 -0
- fraisier-0.1.1/fraisier/systemd.py +48 -0
- fraisier-0.1.1/fraisier/timeout.py +80 -0
- fraisier-0.1.1/fraisier/validation.py +457 -0
- fraisier-0.1.1/fraisier/versioning.py +226 -0
- fraisier-0.1.1/fraisier/webhook.py +628 -0
- fraisier-0.1.1/fraisier/webhook_rate_limit.py +29 -0
- fraisier-0.1.1/monitoring/README.md +203 -0
- fraisier-0.1.1/monitoring/alert-rules.yml +117 -0
- fraisier-0.1.1/monitoring/grafana/dashboards/fraisier-deployments.json +529 -0
- fraisier-0.1.1/monitoring/grafana/provisioning/dashboards/dashboard-provider.yml +17 -0
- fraisier-0.1.1/monitoring/grafana/provisioning/datasources/prometheus.yml +17 -0
- fraisier-0.1.1/monitoring/grafana-dashboard.json +656 -0
- fraisier-0.1.1/monitoring/prometheus.yml +59 -0
- fraisier-0.1.1/pyproject.toml +96 -0
- fraisier-0.1.1/scripts/postgres-init.sql +179 -0
- fraisier-0.1.1/tests/__init__.py +1 -0
- fraisier-0.1.1/tests/conftest.py +177 -0
- fraisier-0.1.1/tests/fixtures/__init__.py +0 -0
- fraisier-0.1.1/tests/fixtures/git_env.py +130 -0
- fraisier-0.1.1/tests/integration/__init__.py +0 -0
- fraisier-0.1.1/tests/integration/test_confiture_integration.py +186 -0
- fraisier-0.1.1/tests/test_api_deploy_health.py +208 -0
- fraisier-0.1.1/tests/test_api_rollback.py +453 -0
- fraisier-0.1.1/tests/test_atomic_rollback.py +173 -0
- fraisier-0.1.1/tests/test_atomic_version.py +158 -0
- fraisier-0.1.1/tests/test_bare_metal_ssh.py +331 -0
- fraisier-0.1.1/tests/test_cli_db.py +278 -0
- fraisier-0.1.1/tests/test_cli_deploy.py +335 -0
- fraisier-0.1.1/tests/test_compose_service_validation.py +74 -0
- fraisier-0.1.1/tests/test_config.py +100 -0
- fraisier-0.1.1/tests/test_config_validation.py +165 -0
- fraisier-0.1.1/tests/test_config_wiring.py +121 -0
- fraisier-0.1.1/tests/test_coverage_gaps.py +293 -0
- fraisier-0.1.1/tests/test_database.py +343 -0
- fraisier-0.1.1/tests/test_database_lock.py +143 -0
- fraisier-0.1.1/tests/test_db_isolation.py +75 -0
- fraisier-0.1.1/tests/test_dbops.py +986 -0
- fraisier-0.1.1/tests/test_dbops_backup.py +112 -0
- fraisier-0.1.1/tests/test_dbops_operations.py +192 -0
- fraisier-0.1.1/tests/test_dbops_restore.py +112 -0
- fraisier-0.1.1/tests/test_dbops_templates.py +55 -0
- fraisier-0.1.1/tests/test_deployer_git_mixin.py +344 -0
- fraisier-0.1.1/tests/test_deployers.py +694 -0
- fraisier-0.1.1/tests/test_deployment_recording.py +324 -0
- fraisier-0.1.1/tests/test_deployment_timeout.py +74 -0
- fraisier-0.1.1/tests/test_docker_compose_deployer.py +178 -0
- fraisier-0.1.1/tests/test_dry_run.py +183 -0
- fraisier-0.1.1/tests/test_dual_logging.py +120 -0
- fraisier-0.1.1/tests/test_e2e_deployments.py +460 -0
- fraisier-0.1.1/tests/test_error_hints.py +107 -0
- fraisier-0.1.1/tests/test_errors.py +263 -0
- fraisier-0.1.1/tests/test_exception_specificity.py +117 -0
- fraisier-0.1.1/tests/test_exec_health_validation.py +42 -0
- fraisier-0.1.1/tests/test_file_lock.py +105 -0
- fraisier-0.1.1/tests/test_git_deploy_env.py +75 -0
- fraisier-0.1.1/tests/test_git_issue_client.py +150 -0
- fraisier-0.1.1/tests/test_git_issue_notifier.py +106 -0
- fraisier-0.1.1/tests/test_git_operations.py +193 -0
- fraisier-0.1.1/tests/test_git_providers.py +294 -0
- fraisier-0.1.1/tests/test_hardening.py +667 -0
- fraisier-0.1.1/tests/test_health_check.py +338 -0
- fraisier-0.1.1/tests/test_health_validation.py +794 -0
- fraisier-0.1.1/tests/test_init.py +170 -0
- fraisier-0.1.1/tests/test_integration.py +807 -0
- fraisier-0.1.1/tests/test_lock_timeout_config.py +87 -0
- fraisier-0.1.1/tests/test_messaging_notifiers.py +111 -0
- fraisier-0.1.1/tests/test_metrics.py +98 -0
- fraisier-0.1.1/tests/test_notification_config_validation.py +115 -0
- fraisier-0.1.1/tests/test_notification_dispatcher.py +117 -0
- fraisier-0.1.1/tests/test_notification_wiring.py +58 -0
- fraisier-0.1.1/tests/test_notifications.py +175 -0
- fraisier-0.1.1/tests/test_observability.py +345 -0
- fraisier-0.1.1/tests/test_preflight_validation.py +90 -0
- fraisier-0.1.1/tests/test_provider_integration.py +561 -0
- fraisier-0.1.1/tests/test_providers.py +1512 -0
- fraisier-0.1.1/tests/test_rollback_everywhere.py +118 -0
- fraisier-0.1.1/tests/test_rollback_integration.py +212 -0
- fraisier-0.1.1/tests/test_runners.py +194 -0
- fraisier-0.1.1/tests/test_safe_env_parsing.py +99 -0
- fraisier-0.1.1/tests/test_scaffold.py +976 -0
- fraisier-0.1.1/tests/test_security.py +201 -0
- fraisier-0.1.1/tests/test_security_hardening.py +236 -0
- fraisier-0.1.1/tests/test_security_validation.py +70 -0
- fraisier-0.1.1/tests/test_ship.py +380 -0
- fraisier-0.1.1/tests/test_status.py +257 -0
- fraisier-0.1.1/tests/test_strategies.py +256 -0
- fraisier-0.1.1/tests/test_systemd.py +93 -0
- fraisier-0.1.1/tests/test_timeout_rollback.py +96 -0
- fraisier-0.1.1/tests/test_validate.py +321 -0
- fraisier-0.1.1/tests/test_validation.py +175 -0
- fraisier-0.1.1/tests/test_version.py +597 -0
- fraisier-0.1.1/tests/test_versioning.py +171 -0
- fraisier-0.1.1/tests/test_webhook.py +1180 -0
- fraisier-0.1.1/tests/test_webhook_integration.py +212 -0
- fraisier-0.1.1/tests/test_webhook_rate_limit.py +38 -0
- fraisier-0.1.1/tests/test_webhook_security.py +95 -0
- fraisier-0.1.1/uv.lock +1170 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Fraisier Environment Configuration
|
|
2
|
+
# Copy this file to .env and update values for your environment
|
|
3
|
+
|
|
4
|
+
# ============================================================================
|
|
5
|
+
# Database Configuration
|
|
6
|
+
# ============================================================================
|
|
7
|
+
DATABASE_URL=postgresql://fraisier:fraisier_password@postgres:5432/fraisier
|
|
8
|
+
|
|
9
|
+
# ============================================================================
|
|
10
|
+
# Fraisier Application Settings
|
|
11
|
+
# ============================================================================
|
|
12
|
+
FRAISIER_LOG_LEVEL=INFO
|
|
13
|
+
FRAISIER_WEBHOOK_SECRET=dev-webhook-secret-change-me
|
|
14
|
+
PROMETHEUS_PORT=8001
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Monitoring and Observability
|
|
18
|
+
# ============================================================================
|
|
19
|
+
|
|
20
|
+
# Grafana Admin Credentials
|
|
21
|
+
GF_SECURITY_ADMIN_USER=admin
|
|
22
|
+
GF_SECURITY_ADMIN_PASSWORD=admin
|
|
23
|
+
|
|
24
|
+
# Prometheus Configuration
|
|
25
|
+
PROMETHEUS_RETENTION=15d
|
|
26
|
+
|
|
27
|
+
# Redis Cache (optional, for distributed deployments)
|
|
28
|
+
REDIS_PASSWORD=redis_password
|
|
29
|
+
|
|
30
|
+
# ============================================================================
|
|
31
|
+
# Development/Production Settings
|
|
32
|
+
# ============================================================================
|
|
33
|
+
|
|
34
|
+
# Environment: development, staging, production
|
|
35
|
+
ENVIRONMENT=development
|
|
36
|
+
|
|
37
|
+
# Enable verbose logging for debugging
|
|
38
|
+
DEBUG=false
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
name: "Fraisier Deploy"
|
|
2
|
+
description: "Trigger a Fraisier deployment via webhook and poll for completion"
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
webhook_url:
|
|
6
|
+
description: "Fraisier webhook URL (e.g., https://deploy.example.com/webhook)"
|
|
7
|
+
required: true
|
|
8
|
+
webhook_secret:
|
|
9
|
+
description: "Webhook HMAC secret for signature verification"
|
|
10
|
+
required: true
|
|
11
|
+
fraise_name:
|
|
12
|
+
description: "Name of the fraise to deploy"
|
|
13
|
+
required: true
|
|
14
|
+
status_url:
|
|
15
|
+
description: "Base URL for status endpoint (e.g., https://deploy.example.com)"
|
|
16
|
+
required: true
|
|
17
|
+
environment:
|
|
18
|
+
description: "Target environment (development, staging, production)"
|
|
19
|
+
required: false
|
|
20
|
+
default: "production"
|
|
21
|
+
timeout:
|
|
22
|
+
description: "Max seconds to wait for deployment (0 = use environment default)"
|
|
23
|
+
required: false
|
|
24
|
+
default: "0"
|
|
25
|
+
poll_interval:
|
|
26
|
+
description: "Seconds between status polls"
|
|
27
|
+
required: false
|
|
28
|
+
default: "10"
|
|
29
|
+
create_issue:
|
|
30
|
+
description: "Create GitHub Issue on failure (true/false)"
|
|
31
|
+
required: false
|
|
32
|
+
default: "true"
|
|
33
|
+
|
|
34
|
+
outputs:
|
|
35
|
+
version:
|
|
36
|
+
description: "Deployed version after successful deployment"
|
|
37
|
+
value: ${{ steps.poll.outputs.version }}
|
|
38
|
+
state:
|
|
39
|
+
description: "Final deployment state"
|
|
40
|
+
value: ${{ steps.poll.outputs.state }}
|
|
41
|
+
|
|
42
|
+
runs:
|
|
43
|
+
using: "composite"
|
|
44
|
+
steps:
|
|
45
|
+
- name: Trigger deployment
|
|
46
|
+
shell: bash
|
|
47
|
+
env:
|
|
48
|
+
WEBHOOK_URL: ${{ inputs.webhook_url }}
|
|
49
|
+
WEBHOOK_SECRET: ${{ inputs.webhook_secret }}
|
|
50
|
+
COMMIT_SHA: ${{ github.sha }}
|
|
51
|
+
BRANCH: ${{ github.ref_name }}
|
|
52
|
+
run: |
|
|
53
|
+
# Compute HMAC-SHA256 signature
|
|
54
|
+
PAYLOAD='{"ref":"refs/heads/'"$BRANCH"'","after":"'"$COMMIT_SHA"'"}'
|
|
55
|
+
SIGNATURE="sha256=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | cut -d' ' -f2)"
|
|
56
|
+
|
|
57
|
+
HTTP_CODE=$(curl -sf -o /dev/null -w '%{http_code}' \
|
|
58
|
+
-X POST "$WEBHOOK_URL" \
|
|
59
|
+
-H "Content-Type: application/json" \
|
|
60
|
+
-H "X-GitHub-Event: push" \
|
|
61
|
+
-H "X-Hub-Signature-256: $SIGNATURE" \
|
|
62
|
+
-d "$PAYLOAD")
|
|
63
|
+
|
|
64
|
+
if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
|
|
65
|
+
echo "::error::Webhook trigger failed with HTTP $HTTP_CODE"
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
echo "Webhook triggered successfully (HTTP $HTTP_CODE)"
|
|
69
|
+
|
|
70
|
+
- name: Poll for completion
|
|
71
|
+
id: poll
|
|
72
|
+
shell: bash
|
|
73
|
+
env:
|
|
74
|
+
STATUS_URL: ${{ inputs.status_url }}/api/status/${{ inputs.fraise_name }}
|
|
75
|
+
EXPECTED_SHA: ${{ github.sha }}
|
|
76
|
+
TIMEOUT: ${{ inputs.timeout }}
|
|
77
|
+
POLL_INTERVAL: ${{ inputs.poll_interval }}
|
|
78
|
+
ENVIRONMENT: ${{ inputs.environment }}
|
|
79
|
+
run: |
|
|
80
|
+
# Use environment default if timeout is 0
|
|
81
|
+
if [ "$TIMEOUT" = "0" ]; then
|
|
82
|
+
case "$ENVIRONMENT" in
|
|
83
|
+
development) TIMEOUT=120 ;;
|
|
84
|
+
staging) TIMEOUT=600 ;;
|
|
85
|
+
*) TIMEOUT=300 ;;
|
|
86
|
+
esac
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
echo "Polling $STATUS_URL (timeout: ${TIMEOUT}s, interval: ${POLL_INTERVAL}s)"
|
|
90
|
+
DEADLINE=$((SECONDS + TIMEOUT))
|
|
91
|
+
|
|
92
|
+
while [ "$SECONDS" -lt "$DEADLINE" ]; do
|
|
93
|
+
RESPONSE=$(curl -sf "$STATUS_URL" 2>/dev/null) || {
|
|
94
|
+
echo "Status endpoint unreachable, retrying in ${POLL_INTERVAL}s..."
|
|
95
|
+
sleep "$POLL_INTERVAL"
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
STATE=$(echo "$RESPONSE" | jq -r '.state // empty')
|
|
100
|
+
SHA=$(echo "$RESPONSE" | jq -r '.commit_sha // empty')
|
|
101
|
+
VERSION=$(echo "$RESPONSE" | jq -r '.version // empty')
|
|
102
|
+
|
|
103
|
+
# Check FAILURE first (key insight: fail fast, don't wait for SHA match)
|
|
104
|
+
if [ "$STATE" = "failed" ]; then
|
|
105
|
+
echo "::error::Deployment failed"
|
|
106
|
+
echo "state=failed" >> "$GITHUB_OUTPUT"
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Check SUCCESS with SHA match
|
|
111
|
+
if [ "$STATE" = "success" ] && [ "$SHA" = "$EXPECTED_SHA" ]; then
|
|
112
|
+
echo "Deployment succeeded: version=$VERSION"
|
|
113
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
114
|
+
echo "state=success" >> "$GITHUB_OUTPUT"
|
|
115
|
+
exit 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
echo "Waiting: state=$STATE sha=${SHA:0:7}..."
|
|
119
|
+
sleep "$POLL_INTERVAL"
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
echo "::error::Deployment timed out after ${TIMEOUT}s"
|
|
123
|
+
echo "state=timeout" >> "$GITHUB_OUTPUT"
|
|
124
|
+
exit 1
|
|
125
|
+
|
|
126
|
+
- name: Create issue on failure
|
|
127
|
+
if: failure() && inputs.create_issue == 'true'
|
|
128
|
+
shell: bash
|
|
129
|
+
env:
|
|
130
|
+
STATUS_URL: ${{ inputs.status_url }}/api/status/${{ inputs.fraise_name }}
|
|
131
|
+
WEBHOOK_SECRET: ${{ inputs.webhook_secret }}
|
|
132
|
+
ENVIRONMENT: ${{ inputs.environment }}
|
|
133
|
+
COMMIT_SHA: ${{ github.sha }}
|
|
134
|
+
BRANCH: ${{ github.ref_name }}
|
|
135
|
+
ACTOR: ${{ github.actor }}
|
|
136
|
+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
137
|
+
GH_TOKEN: ${{ github.token }}
|
|
138
|
+
run: |
|
|
139
|
+
# Fetch failure details from authenticated endpoint
|
|
140
|
+
DETAILS=$(curl -sf "${STATUS_URL}/details" \
|
|
141
|
+
-H "X-Deployment-Token: ${WEBHOOK_SECRET}" 2>/dev/null) || DETAILS="{}"
|
|
142
|
+
|
|
143
|
+
ERROR_MSG=$(echo "$DETAILS" | jq -r '.error_message // "No error details available"')
|
|
144
|
+
MIGRATION=$(echo "$DETAILS" | jq -r '.migration_report // empty')
|
|
145
|
+
|
|
146
|
+
SHORT_SHA="${COMMIT_SHA:0:7}"
|
|
147
|
+
|
|
148
|
+
# Build issue body
|
|
149
|
+
BODY="## Deployment Failure
|
|
150
|
+
|
|
151
|
+
| Field | Value |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| Environment | \`${ENVIRONMENT}\` |
|
|
154
|
+
| Commit | \`${SHORT_SHA}\` (${COMMIT_SHA}) |
|
|
155
|
+
| Branch | \`${BRANCH}\` |
|
|
156
|
+
| Triggered by | @${ACTOR} |
|
|
157
|
+
| Workflow run | [View logs](${RUN_URL}) |
|
|
158
|
+
|
|
159
|
+
## Error Details
|
|
160
|
+
|
|
161
|
+
\`\`\`
|
|
162
|
+
${ERROR_MSG}
|
|
163
|
+
\`\`\`"
|
|
164
|
+
|
|
165
|
+
if [ -n "$MIGRATION" ] && [ "$MIGRATION" != "null" ]; then
|
|
166
|
+
BODY="${BODY}
|
|
167
|
+
|
|
168
|
+
## Migration Report
|
|
169
|
+
|
|
170
|
+
\`\`\`json
|
|
171
|
+
${MIGRATION}
|
|
172
|
+
\`\`\`"
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
BODY="${BODY}
|
|
176
|
+
|
|
177
|
+
## Remediation Checklist
|
|
178
|
+
|
|
179
|
+
- [ ] Review error details and workflow logs above
|
|
180
|
+
- [ ] Check database state and migration status
|
|
181
|
+
- [ ] Fix the root cause in a new commit
|
|
182
|
+
- [ ] Verify fix deploys successfully
|
|
183
|
+
- [ ] Close this issue"
|
|
184
|
+
|
|
185
|
+
gh issue create \
|
|
186
|
+
--title "Deploy failed: ${ENVIRONMENT} @ ${SHORT_SHA}" \
|
|
187
|
+
--label "deployment-failure" \
|
|
188
|
+
--body "$BODY"
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Tests (Required for Release)
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
services:
|
|
14
|
+
postgres:
|
|
15
|
+
image: postgres:16-alpine
|
|
16
|
+
env:
|
|
17
|
+
POSTGRES_USER: fraisier
|
|
18
|
+
POSTGRES_PASSWORD: fraisier
|
|
19
|
+
POSTGRES_DB: fraisier_test
|
|
20
|
+
options: >-
|
|
21
|
+
--health-cmd pg_isready
|
|
22
|
+
--health-interval 10s
|
|
23
|
+
--health-timeout 5s
|
|
24
|
+
--health-retries 5
|
|
25
|
+
ports:
|
|
26
|
+
- 5432:5432
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
uses: actions/setup-python@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: '3.12'
|
|
35
|
+
|
|
36
|
+
- name: Install uv
|
|
37
|
+
uses: astral-sh/setup-uv@v7
|
|
38
|
+
|
|
39
|
+
- name: Install dependencies
|
|
40
|
+
run: |
|
|
41
|
+
uv venv
|
|
42
|
+
uv pip install ".[dev]"
|
|
43
|
+
|
|
44
|
+
- name: Run tests
|
|
45
|
+
env:
|
|
46
|
+
FRAISIER_TEST_PG_URL: postgresql://fraisier:fraisier@localhost:5432/fraisier_test
|
|
47
|
+
run: |
|
|
48
|
+
uv run pytest tests/ \
|
|
49
|
+
--cov=fraisier \
|
|
50
|
+
--cov-report=xml \
|
|
51
|
+
--cov-report=term-missing \
|
|
52
|
+
-v
|
|
53
|
+
|
|
54
|
+
lint:
|
|
55
|
+
name: Lint (Required for Release)
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- name: Set up Python
|
|
60
|
+
uses: actions/setup-python@v5
|
|
61
|
+
with:
|
|
62
|
+
python-version: '3.12'
|
|
63
|
+
- name: Install uv
|
|
64
|
+
uses: astral-sh/setup-uv@v7
|
|
65
|
+
- name: Install dependencies
|
|
66
|
+
run: |
|
|
67
|
+
uv venv
|
|
68
|
+
uv pip install ruff
|
|
69
|
+
- name: Run ruff check
|
|
70
|
+
run: uv run ruff check .
|
|
71
|
+
- name: Run ruff format check
|
|
72
|
+
run: uv run ruff format --check .
|
|
73
|
+
|
|
74
|
+
type-check:
|
|
75
|
+
name: Type Check (Advisory)
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
continue-on-error: true
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
- name: Set up Python
|
|
81
|
+
uses: actions/setup-python@v5
|
|
82
|
+
with:
|
|
83
|
+
python-version: '3.12'
|
|
84
|
+
- name: Install uv
|
|
85
|
+
uses: astral-sh/setup-uv@v7
|
|
86
|
+
- name: Install ty
|
|
87
|
+
run: uv tool install ty
|
|
88
|
+
- name: Run ty
|
|
89
|
+
run: ty check fraisier/ || echo "Type check warnings (non-blocking)"
|
|
90
|
+
|
|
91
|
+
security:
|
|
92
|
+
name: Security (Required for Release)
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
steps:
|
|
95
|
+
- uses: actions/checkout@v4
|
|
96
|
+
- name: Set up Python
|
|
97
|
+
uses: actions/setup-python@v5
|
|
98
|
+
with:
|
|
99
|
+
python-version: '3.12'
|
|
100
|
+
- name: Install uv
|
|
101
|
+
uses: astral-sh/setup-uv@v7
|
|
102
|
+
- name: Install dependencies
|
|
103
|
+
run: |
|
|
104
|
+
uv venv
|
|
105
|
+
uv pip install bandit
|
|
106
|
+
- name: Run bandit
|
|
107
|
+
run: uv run bandit -r fraisier/ -f json || true
|
|
108
|
+
|
|
109
|
+
build:
|
|
110
|
+
name: Build distribution
|
|
111
|
+
runs-on: ubuntu-latest
|
|
112
|
+
needs: [test, lint, type-check, security]
|
|
113
|
+
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/checkout@v4
|
|
116
|
+
with:
|
|
117
|
+
fetch-depth: 0
|
|
118
|
+
|
|
119
|
+
- name: Install uv
|
|
120
|
+
uses: astral-sh/setup-uv@v7
|
|
121
|
+
|
|
122
|
+
- name: Build sdist and wheel
|
|
123
|
+
run: uv build
|
|
124
|
+
|
|
125
|
+
- name: Upload artifacts
|
|
126
|
+
uses: actions/upload-artifact@v5
|
|
127
|
+
with:
|
|
128
|
+
name: dist
|
|
129
|
+
path: dist/*
|
|
130
|
+
|
|
131
|
+
validate:
|
|
132
|
+
name: Validate built artifacts
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
needs: [build]
|
|
135
|
+
|
|
136
|
+
steps:
|
|
137
|
+
- name: Download artifacts
|
|
138
|
+
uses: actions/download-artifact@v5
|
|
139
|
+
with:
|
|
140
|
+
name: dist
|
|
141
|
+
path: dist
|
|
142
|
+
|
|
143
|
+
- name: Install validation tools
|
|
144
|
+
run: pip install twine
|
|
145
|
+
|
|
146
|
+
- name: Validate with twine
|
|
147
|
+
run: twine check dist/*
|
|
148
|
+
|
|
149
|
+
- name: List artifacts
|
|
150
|
+
run: |
|
|
151
|
+
echo "=== Built Artifacts ==="
|
|
152
|
+
ls -lh dist/
|
|
153
|
+
|
|
154
|
+
publish:
|
|
155
|
+
name: Publish to PyPI
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
needs: [validate]
|
|
158
|
+
environment: release
|
|
159
|
+
permissions:
|
|
160
|
+
id-token: write
|
|
161
|
+
|
|
162
|
+
steps:
|
|
163
|
+
- name: Download artifacts
|
|
164
|
+
uses: actions/download-artifact@v5
|
|
165
|
+
with:
|
|
166
|
+
name: dist
|
|
167
|
+
path: dist
|
|
168
|
+
|
|
169
|
+
- name: Install uv
|
|
170
|
+
uses: astral-sh/setup-uv@v7
|
|
171
|
+
|
|
172
|
+
- name: Publish to PyPI
|
|
173
|
+
run: uv publish --trusted-publishing always
|
|
174
|
+
|
|
175
|
+
create-release:
|
|
176
|
+
name: Create GitHub Release
|
|
177
|
+
runs-on: ubuntu-latest
|
|
178
|
+
needs: [publish]
|
|
179
|
+
permissions:
|
|
180
|
+
contents: write
|
|
181
|
+
|
|
182
|
+
steps:
|
|
183
|
+
- uses: actions/checkout@v4
|
|
184
|
+
|
|
185
|
+
- name: Download artifacts
|
|
186
|
+
uses: actions/download-artifact@v5
|
|
187
|
+
with:
|
|
188
|
+
name: dist
|
|
189
|
+
path: dist
|
|
190
|
+
|
|
191
|
+
- name: Create Release
|
|
192
|
+
uses: softprops/action-gh-release@v2
|
|
193
|
+
with:
|
|
194
|
+
generate_release_notes: true
|
|
195
|
+
files: dist/*
|
|
196
|
+
draft: false
|
|
197
|
+
prerelease: false
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
name: Python Version Matrix Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: python-matrix-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test-matrix:
|
|
16
|
+
name: Test Python ${{ matrix.python-version }}
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
python-version: ['3.11', '3.12', '3.13']
|
|
23
|
+
|
|
24
|
+
services:
|
|
25
|
+
postgres:
|
|
26
|
+
image: postgres:16-alpine
|
|
27
|
+
env:
|
|
28
|
+
POSTGRES_USER: fraisier
|
|
29
|
+
POSTGRES_PASSWORD: fraisier
|
|
30
|
+
POSTGRES_DB: fraisier_test
|
|
31
|
+
options: >-
|
|
32
|
+
--health-cmd pg_isready
|
|
33
|
+
--health-interval 10s
|
|
34
|
+
--health-timeout 5s
|
|
35
|
+
--health-retries 5
|
|
36
|
+
ports:
|
|
37
|
+
- 5432:5432
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
43
|
+
uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: ${{ matrix.python-version }}
|
|
46
|
+
|
|
47
|
+
- name: Install uv
|
|
48
|
+
uses: astral-sh/setup-uv@v7
|
|
49
|
+
|
|
50
|
+
- name: Install dependencies
|
|
51
|
+
run: |
|
|
52
|
+
uv venv
|
|
53
|
+
uv pip install ".[dev]"
|
|
54
|
+
|
|
55
|
+
- name: Verify environment
|
|
56
|
+
run: |
|
|
57
|
+
uv run python --version
|
|
58
|
+
pg_isready -h localhost -p 5432 -U fraisier && echo 'PostgreSQL ready' || echo 'PostgreSQL not ready'
|
|
59
|
+
|
|
60
|
+
- name: Run tests with coverage
|
|
61
|
+
env:
|
|
62
|
+
FRAISIER_TEST_PG_URL: postgresql://fraisier:fraisier@localhost:5432/fraisier_test
|
|
63
|
+
run: |
|
|
64
|
+
uv run pytest tests/ \
|
|
65
|
+
--cov=fraisier \
|
|
66
|
+
--cov-report=xml \
|
|
67
|
+
--cov-report=term-missing \
|
|
68
|
+
-v \
|
|
69
|
+
--tb=short
|
|
70
|
+
|
|
71
|
+
- name: Upload coverage to Codecov
|
|
72
|
+
uses: codecov/codecov-action@v5
|
|
73
|
+
with:
|
|
74
|
+
files: ./coverage.xml
|
|
75
|
+
flags: python-${{ matrix.python-version }}
|
|
76
|
+
name: Python-${{ matrix.python-version }}
|
|
77
|
+
fail_ci_if_error: false
|
|
78
|
+
continue-on-error: true
|
|
79
|
+
|
|
80
|
+
matrix-summary:
|
|
81
|
+
name: Python Version Matrix Summary
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
needs: [test-matrix]
|
|
84
|
+
if: always()
|
|
85
|
+
|
|
86
|
+
steps:
|
|
87
|
+
- name: Check matrix results
|
|
88
|
+
run: |
|
|
89
|
+
if [[ "${{ needs.test-matrix.result }}" == "success" ]]; then
|
|
90
|
+
echo "Python 3.11+ tests passed"
|
|
91
|
+
exit 0
|
|
92
|
+
else
|
|
93
|
+
echo "Python version testing failed"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
name: Quality Gate
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: quality-gate-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
name: Tests
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
services:
|
|
19
|
+
postgres:
|
|
20
|
+
image: postgres:16-alpine
|
|
21
|
+
env:
|
|
22
|
+
POSTGRES_USER: fraisier
|
|
23
|
+
POSTGRES_PASSWORD: fraisier
|
|
24
|
+
POSTGRES_DB: fraisier_test
|
|
25
|
+
options: >-
|
|
26
|
+
--health-cmd pg_isready
|
|
27
|
+
--health-interval 10s
|
|
28
|
+
--health-timeout 5s
|
|
29
|
+
--health-retries 5
|
|
30
|
+
ports:
|
|
31
|
+
- 5432:5432
|
|
32
|
+
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- name: Set up Python
|
|
37
|
+
uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: '3.12'
|
|
40
|
+
|
|
41
|
+
- name: Install uv
|
|
42
|
+
uses: astral-sh/setup-uv@v7
|
|
43
|
+
|
|
44
|
+
- name: Install dependencies
|
|
45
|
+
run: |
|
|
46
|
+
uv venv
|
|
47
|
+
uv pip install ".[dev]"
|
|
48
|
+
|
|
49
|
+
- name: Verify environment
|
|
50
|
+
run: |
|
|
51
|
+
uv run python --version
|
|
52
|
+
uv run pytest --version
|
|
53
|
+
pg_isready -h localhost -p 5432 -U fraisier && echo 'PostgreSQL ready' || echo 'PostgreSQL not ready'
|
|
54
|
+
|
|
55
|
+
- name: Run tests with coverage
|
|
56
|
+
env:
|
|
57
|
+
FRAISIER_TEST_PG_URL: postgresql://fraisier:fraisier@localhost:5432/fraisier_test
|
|
58
|
+
run: |
|
|
59
|
+
uv run pytest tests/ \
|
|
60
|
+
--cov=fraisier \
|
|
61
|
+
--cov-report=xml \
|
|
62
|
+
--cov-report=term-missing \
|
|
63
|
+
-v \
|
|
64
|
+
--tb=short
|
|
65
|
+
|
|
66
|
+
- name: Upload coverage
|
|
67
|
+
uses: codecov/codecov-action@v5
|
|
68
|
+
with:
|
|
69
|
+
files: ./coverage.xml
|
|
70
|
+
flags: unittests
|
|
71
|
+
name: Python-3.12
|
|
72
|
+
fail_ci_if_error: false
|
|
73
|
+
continue-on-error: true
|
|
74
|
+
|
|
75
|
+
lint:
|
|
76
|
+
name: Lint
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
- name: Set up Python
|
|
81
|
+
uses: actions/setup-python@v5
|
|
82
|
+
with:
|
|
83
|
+
python-version: '3.12'
|
|
84
|
+
- name: Install uv
|
|
85
|
+
uses: astral-sh/setup-uv@v7
|
|
86
|
+
- name: Install dependencies
|
|
87
|
+
run: |
|
|
88
|
+
uv venv
|
|
89
|
+
uv pip install ruff
|
|
90
|
+
- name: Run ruff check
|
|
91
|
+
run: uv run ruff check .
|
|
92
|
+
- name: Run ruff format check
|
|
93
|
+
run: uv run ruff format --check .
|
|
94
|
+
|
|
95
|
+
type-check:
|
|
96
|
+
name: Type Check
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
- name: Set up Python
|
|
101
|
+
uses: actions/setup-python@v5
|
|
102
|
+
with:
|
|
103
|
+
python-version: '3.12'
|
|
104
|
+
- name: Install uv
|
|
105
|
+
uses: astral-sh/setup-uv@v7
|
|
106
|
+
- name: Install ty
|
|
107
|
+
run: uv tool install ty
|
|
108
|
+
- name: Run ty type check
|
|
109
|
+
run: ty check fraisier/
|
|
110
|
+
timeout-minutes: 5
|
|
111
|
+
continue-on-error: true
|
|
112
|
+
|
|
113
|
+
security:
|
|
114
|
+
name: Security
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
steps:
|
|
117
|
+
- uses: actions/checkout@v4
|
|
118
|
+
- name: Set up Python
|
|
119
|
+
uses: actions/setup-python@v5
|
|
120
|
+
with:
|
|
121
|
+
python-version: '3.12'
|
|
122
|
+
- name: Install uv
|
|
123
|
+
uses: astral-sh/setup-uv@v7
|
|
124
|
+
- name: Install dependencies
|
|
125
|
+
run: |
|
|
126
|
+
uv venv
|
|
127
|
+
uv pip install bandit
|
|
128
|
+
- name: Run bandit security scan
|
|
129
|
+
run: uv run bandit -r fraisier/ -f json || true
|
|
130
|
+
- name: Run Trivy vulnerability scanner
|
|
131
|
+
uses: aquasecurity/trivy-action@master
|
|
132
|
+
with:
|
|
133
|
+
scan-type: 'fs'
|
|
134
|
+
scan-ref: '.'
|
|
135
|
+
format: 'table'
|
|
136
|
+
severity: 'CRITICAL,HIGH'
|
|
137
|
+
continue-on-error: true
|
|
138
|
+
|
|
139
|
+
quality-gate:
|
|
140
|
+
name: Quality Gate
|
|
141
|
+
runs-on: ubuntu-latest
|
|
142
|
+
needs: [test, lint, type-check, security]
|
|
143
|
+
if: always()
|
|
144
|
+
steps:
|
|
145
|
+
- name: Check quality gate status
|
|
146
|
+
run: |
|
|
147
|
+
if [[ "${{ needs.test.result }}" != "success" ]]; then
|
|
148
|
+
echo "Tests failed"
|
|
149
|
+
exit 1
|
|
150
|
+
fi
|
|
151
|
+
if [[ "${{ needs.lint.result }}" != "success" ]]; then
|
|
152
|
+
echo "Lint checks failed"
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
if [[ "${{ needs.type-check.result }}" == "failure" ]]; then
|
|
156
|
+
echo "Type check has warnings (non-blocking)"
|
|
157
|
+
fi
|
|
158
|
+
if [[ "${{ needs.security.result }}" != "success" ]]; then
|
|
159
|
+
echo "Security checks failed"
|
|
160
|
+
exit 1
|
|
161
|
+
fi
|
|
162
|
+
echo "All quality checks passed"
|