Release Integration Guides
Purpose: GitHub Actions and CI/CD pipeline integration examples for automated releases
Version: 1.0.0
Last Updated: 2026-03-10
🚨 Critical Policy Reminder
NEVER use manual git commit/push commands! Always use Release Workflow (RW) automation.
GitHub Actions Integration
Basic Release Workflow
# .github/workflows/release.yml
name: Release
on:
workflow_dispatch:
inputs:
mode:
description: 'Release mode'
required: true
default: 'registry'
type: choice
options:
- registry
- task_touch
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install pyyaml
- name: Configure SemVer Strategy
run: |
sed -i "s/semver_mapping_strategy: .*/semver_mapping_strategy: ${{ github.event.inputs.mode }}/" rw-config.yaml
echo "Configured strategy: ${{ github.event.inputs.mode }}"
- name: Validate Configuration
run: |
python3 -c "
import yaml
with open('rw-config.yaml', 'r') as f:
config = yaml.safe_load(f)
print(f'Strategy: {config[\"semver_mapping_strategy\"]}')
print('Configuration validation: PASSED')
"
- name: Execute Release Workflow
run: |
echo "RW" | python3 -c "
import sys
import os
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
# Set environment variables for CI
os.environ['CI'] = 'true'
os.environ['GITHUB_TOKEN'] = '${{ secrets.GITHUB_TOKEN }}'
# Import and execute workflow
try:
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f'Release result: {result}')
except Exception as e:
print(f'Release failed: {e}')
sys.exit(1)
"
- name: Verify Release
run: |
# Check if tag was created
if [ "${{ github.event.inputs.mode }}" = "task_touch" ]; then
TAG=$(python3 -c "
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts/version')
from semver_converter import convert_version_string
import re
with open('src/fynd_deals/version.py', 'r') as f:
content = f.read()
version_match = re.search(r'VERSION_STRING = \"([^\"]+)\"', content)
if version_match:
internal_version = version_match.group(1)
semver = convert_version_string(internal_version, 'task_touch')
print(semver)
")
else
TAG=$(python3 -c "
import re
with open('src/fynd_deals/version.py', 'r') as f:
content = f.read()
version_match = re.search(r'VERSION_STRING = \"([^\"]+)\"', content)
if version_match:
print(version_match.group(1))
")
fi
echo "Expected tag: $TAG"
git tag -l | grep "$TAG" || {
echo "❌ Tag $TAG not found"
exit 1
}
echo "✅ Release verification passed"
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get-tag.outputs.tag }}
release_name: Release ${{ steps.get-tag.outputs.tag }}
draft: false
prerelease: false
Advanced Release Workflow with Validation
# .github/workflows/release-advanced.yml
name: Advanced Release
on:
workflow_dispatch:
inputs:
mode:
description: 'Release mode'
required: true
default: 'registry'
type: choice
options:
- registry
- task_touch
skip_validation:
description: 'Skip validation steps'
required: false
default: false
type: boolean
create_github_release:
description: 'Create GitHub release'
required: false
default: true
type: boolean
jobs:
pre-release:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
strategy: ${{ steps.config.outputs.strategy }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install pyyaml
- name: Configure Release
run: |
# Backup current config
cp rw-config.yaml rw-config.yaml.backup
# Update strategy
sed -i "s/semver_mapping_strategy: .*/semver_mapping_strategy: ${{ github.event.inputs.mode }}/" rw-config.yaml
# Add CI-specific settings
if [ "${{ github.event.inputs.skip_validation }}" = "true" ]; then
echo "skip_validation: true" >> rw-config.yaml
fi
if [ "${{ github.event.inputs.create_github_release }}" = "false" ]; then
echo "skip_github_release: true" >> rw-config.yaml
fi
- name: Get Version and Tag
id: version
run: |
python3 -c "
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts/version')
from semver_converter import convert_version_string
import re
import yaml
# Get configuration
with open('rw-config.yaml', 'r') as f:
config = yaml.safe_load(f)
strategy = config['semver_mapping_strategy']
# Get current version
with open('src/fynd_deals/version.py', 'r') as f:
content = f.read()
version_match = re.search(r'VERSION_STRING = \"([^\"]+)\"', content)
if version_match:
internal_version = version_match.group(1)
print(f'internal_version={internal_version}')
if strategy == 'task_touch':
semver = convert_version_string(internal_version, 'task_touch')
print(f'tag={semver}')
else:
print(f'tag={internal_version}')
print(f'version={internal_version}')
print(f'strategy={strategy}')
"
- name: Validate Configuration
id: config
run: |
python3 -c "
import yaml
with open('rw-config.yaml', 'r') as f:
config = yaml.safe_load(f)
print(f'strategy={config[\"semver_mapping_strategy\"]}')
print('Configuration validation: PASSED')
"
- name: Pre-release Checks
run: |
echo "🔍 Running pre-release checks..."
# Check branch
BRANCH=$(git branch --show-current)
echo "Current branch: $BRANCH"
# Validate branch format
if [[ ! "$BRANCH" =~ ^epic/[0-9] ]]; then
echo "❌ Not on epic branch: $BRANCH"
exit 1
fi
# Check git status
if [ -n "$(git status --porcelain)" ]; then
echo "❌ Working directory not clean"
exit 1
fi
echo "✅ Pre-release checks passed"
release:
runs-on: ubuntu-latest
needs: pre-release
permissions:
contents: write
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install pyyaml
- name: Restore Configuration
run: |
# Configure with outputs from pre-release
sed -i "s/semver_mapping_strategy: .*/semver_mapping_strategy: ${{ needs.pre-release.outputs.strategy }}/" rw-config.yaml
if [ "${{ github.event.inputs.skip_validation }}" = "true" ]; then
echo "skip_validation: true" >> rw-config.yaml
fi
if [ "${{ github.event.inputs.create_github_release }}" = "false" ]; then
echo "skip_github_release: true" >> rw-config.yaml
fi
- name: Execute Release Workflow
run: |
echo "🚀 Executing release workflow..."
echo "Mode: ${{ needs.pre-release.outputs.strategy }}"
echo "Expected tag: ${{ needs.pre-release.outputs.tag }}"
# Set environment variables
export CI=true
export GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}"
# Execute release workflow
python3 -c "
import sys
import os
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
try:
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f'Release result: {result}')
if not result.get('success', False):
print('❌ Release workflow failed')
sys.exit(1)
print('✅ Release workflow completed')
except Exception as e:
print(f'❌ Release workflow error: {e}')
sys.exit(1)
"
- name: Verify Release
run: |
echo "🔍 Verifying release..."
# Check tag exists
if git tag -l | grep -q "${{ needs.pre-release.outputs.tag }}"; then
echo "✅ Tag ${{ needs.pre-release.outputs.tag }} found"
else
echo "❌ Tag ${{ needs.pre-release.outputs.tag }} not found"
exit 1
fi
# Check tag points to correct commit
TAG_COMMIT=$(git rev-list -n 1 ${{ needs.pre-release.outputs.tag }})
HEAD_COMMIT=$(git rev-parse HEAD)
if [ "$TAG_COMMIT" = "$HEAD_COMMIT" ]; then
echo "✅ Tag points to correct commit"
else
echo "❌ Tag does not point to HEAD"
exit 1
fi
echo "✅ Release verification passed"
- name: Create GitHub Release
if: ${{ github.event.inputs.create_github_release == 'true' }}
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ needs.pre-release.outputs.tag }}
release_name: Release ${{ needs.pre-release.outputs.tag }}
body: |
## Release ${{ needs.pre-release.outputs.tag }}
**Mode:** ${{ needs.pre-release.outputs.strategy }}
**Internal Version:** ${{ needs.pre-release.outputs.version }}
### Changes
[Release notes and changelog will be added here]
### Verification
- ✅ Release workflow completed
- ✅ Tag created successfully
- ✅ GitHub release created
draft: false
prerelease: false
- name: Notify Release Complete
run: |
echo "🎉 Release completed successfully!"
echo "📋 Release details:"
echo " Mode: ${{ needs.pre-release.outputs.strategy }}"
echo " Tag: ${{ needs.pre-release.outputs.tag }}"
echo " Version: ${{ needs.pre-release.outputs.version }}"
echo " GitHub Release: ${{ github.event.inputs.create_github_release }}"
CI/CD Pipeline Integration
Jenkins Pipeline Example
// Jenkinsfile
pipeline {
agent any
parameters {
choice(
name: 'RELEASE_MODE',
choices: ['registry', 'task_touch'],
description: 'Release mode'
)
booleanParam(
name: 'SKIP_VALIDATION',
defaultValue: false,
description: 'Skip validation steps'
)
booleanParam(
name: 'CREATE_GITHUB_RELEASE',
defaultValue: true,
description: 'Create GitHub release'
)
}
environment {
GITHUB_TOKEN = credentials('github-token')
CI = 'true'
}
stages {
stage('Pre-Release Checks') {
steps {
script {
echo "🔍 Running pre-release checks..."
// Check branch
def branch = sh(script: 'git branch --show-current', returnStdout: true).trim()
echo "Current branch: ${branch}"
if (!branch.matches(/^epic\/\d+.*$/)) {
error "❌ Not on epic branch: ${branch}"
}
// Check git status
def status = sh(script: 'git status --porcelain', returnStdout: true).trim()
if (status) {
error "❌ Working directory not clean"
}
echo "✅ Pre-release checks passed"
}
}
}
stage('Configure Release') {
steps {
script {
echo "🔧 Configuring release..."
// Backup current config
sh 'cp rw-config.yaml rw-config.yaml.backup'
// Update strategy
sh "sed -i 's/semver_mapping_strategy: .*/semver_mapping_strategy: ${params.RELEASE_MODE}/' rw-config.yaml"
// Add CI-specific settings
if (params.SKIP_VALIDATION) {
sh 'echo "skip_validation: true" >> rw-config.yaml'
}
if (!params.CREATE_GITHUB_RELEASE) {
sh 'echo "skip_github_release: true" >> rw-config.yaml'
}
echo "✅ Configuration updated"
}
}
}
stage('Get Version Info') {
steps {
script {
// Get version and tag information
def versionInfo = sh(script: '''
python3 -c "
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts/version')
from semver_converter import convert_version_string
import re
import yaml
# Get configuration
with open('rw-config.yaml', 'r') as f:
config = yaml.safe_load(f)
strategy = config['semver_mapping_strategy']
# Get current version
with open('src/fynd_deals/version.py', 'r') as f:
content = f.read()
version_match = re.search(r'VERSION_STRING = \"([^\"]+)\"', content)
if version_match:
internal_version = version_match.group(1)
print(f'internal_version={internal_version}')
if strategy == 'task_touch':
semver = convert_version_string(internal_version, 'task_touch')
print(f'tag={semver}')
else:
print(f'tag={internal_version}')
print(f'strategy={strategy}')
"
''', returnStdout: true).trim()
// Parse version info
def versionMap = [:]
versionInfo.eachLine { line ->
def (key, value) = line.split('=')
versionMap[key] = value
}
env.INTERNAL_VERSION = versionMap.internal_version
env.RELEASE_TAG = versionMap.tag
env.RELEASE_STRATEGY = versionMap.strategy
echo "📋 Version info:"
echo " Internal: ${env.INTERNAL_VERSION}"
echo " Tag: ${env.RELEASE_TAG}"
echo " Strategy: ${env.RELEASE_STRATEGY}"
}
}
}
stage('Execute Release') {
steps {
script {
echo "🚀 Executing release workflow..."
sh """
python3 -c "
import sys
import os
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
try:
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f'Release result: {result}')
if not result.get('success', False):
print('❌ Release workflow failed')
sys.exit(1)
print('✅ Release workflow completed')
except Exception as e:
print(f'❌ Release workflow error: {e}')
sys.exit(1)
"
"""
}
}
}
stage('Verify Release') {
steps {
script {
echo "🔍 Verifying release..."
// Check tag exists
def tagExists = sh(script: "git tag -l | grep -q '${env.RELEASE_TAG}'", returnStatus: true) == 0
if (!tagExists) {
error "❌ Tag ${env.RELEASE_TAG} not found"
}
echo "✅ Tag ${env.RELEASE_TAG} found"
// Check tag points to correct commit
def tagCommit = sh(script: "git rev-list -n 1 ${env.RELEASE_TAG}", returnStdout: true).trim()
def headCommit = sh(script: "git rev-parse HEAD", returnStdout: true).trim()
if (tagCommit != headCommit) {
error "❌ Tag does not point to HEAD"
}
echo "✅ Tag points to correct commit"
echo "✅ Release verification passed"
}
}
}
stage('Create GitHub Release') {
when {
expression { params.CREATE_GITHUB_RELEASE }
}
steps {
script {
echo "🌐 Creating GitHub release..."
// Create GitHub release using gh CLI
sh """
gh release create ${env.RELEASE_TAG} \\
--title "Release ${env.RELEASE_TAG}" \\
--notes "## Release ${env.RELEASE_TAG}
**Mode:** ${env.RELEASE_STRATEGY}
**Internal Version:** ${env.INTERNAL_VERSION}
### Changes
[Release notes and changelog will be added here]
### Verification
- ✅ Release workflow completed
- ✅ Tag created successfully
- ✅ GitHub release created
"""
echo "✅ GitHub release created"
}
}
}
}
post {
success {
echo "🎉 Release completed successfully!"
echo "📋 Release details:"
echo " Mode: ${env.RELEASE_STRATEGY}"
echo " Tag: ${env.RELEASE_TAG}"
echo " Version: ${env.INTERNAL_VERSION}"
echo " GitHub Release: ${params.CREATE_GITHUB_RELEASE}"
}
failure {
echo "❌ Release failed!"
// Restore configuration
sh 'cp rw-config.yaml.backup rw-config.yaml || true'
// Clean up any partial releases
sh """
if git tag -l | grep -q '${env.RELEASE_TAG}'; then
git tag -d ${env.RELEASE_TAG}
git push origin :refs/tags/${env.RELEASE_TAG} || true
fi
"""
}
always {
// Clean up
sh 'rm -f rw-config.yaml.backup'
}
}
}
GitLab CI/CD Example
# .gitlab-ci.yml
stages:
- validate
- release
- deploy
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0
release:
stage: release
image: python:3.9
rules:
- if: $CI_PIPELINE_SOURCE == "web"
variables:
RELEASE_MODE: $RELEASE_MODE
SKIP_VALIDATION: $SKIP_VALIDATION
CREATE_GITHUB_RELEASE: $CREATE_GITHUB_RELEASE
before_script:
- apt-get update -y
- apt-get install -y git
- pip install pyyaml
- git config --global user.email "ci@example.com"
- git config --global user.name "CI Pipeline"
script:
- |
echo "🔍 Running pre-release checks..."
# Check branch
BRANCH=$(git branch --show-current)
echo "Current branch: $BRANCH"
if [[ ! "$BRANCH" =~ ^epic/[0-9] ]]; then
echo "❌ Not on epic branch: $BRANCH"
exit 1
fi
# Check git status
if [ -n "$(git status --porcelain)" ]; then
echo "❌ Working directory not clean"
exit 1
fi
echo "✅ Pre-release checks passed"
- |
echo "🔧 Configuring release..."
# Backup current config
cp rw-config.yaml rw-config.yaml.backup
# Update strategy
sed -i "s/semver_mapping_strategy: .*/semver_mapping_strategy: $RELEASE_MODE/" rw-config.yaml
# Add CI-specific settings
if [ "$SKIP_VALIDATION" = "true" ]; then
echo "skip_validation: true" >> rw-config.yaml
fi
if [ "$CREATE_GITHUB_RELEASE" = "false" ]; then
echo "skip_github_release: true" >> rw-config.yaml
fi
echo "✅ Configuration updated"
- |
echo "📋 Getting version info..."
python3 -c "
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts/version')
from semver_converter import convert_version_string
import re
import yaml
# Get configuration
with open('rw-config.yaml', 'r') as f:
config = yaml.safe_load(f)
strategy = config['semver_mapping_strategy']
# Get current version
with open('src/fynd_deals/version.py', 'r') as f:
content = f.read()
version_match = re.search(r'VERSION_STRING = \"([^\"]+)\"', content)
if version_match:
internal_version = version_match.group(1)
print(f'internal_version={internal_version}')
if strategy == 'task_touch':
semver = convert_version_string(internal_version, 'task_touch')
print(f'tag={semver}')
else:
print(f'tag={internal_version}')
print(f'strategy={strategy}')
" > version_info.txt
# Parse version info
export INTERNAL_VERSION=$(grep internal_version version_info.txt | cut -d= -f2)
export RELEASE_TAG=$(grep tag version_info.txt | cut -d= -f2)
export RELEASE_STRATEGY=$(grep strategy version_info.txt | cut -d= -f2)
echo "📋 Version info:"
echo " Internal: $INTERNAL_VERSION"
echo " Tag: $RELEASE_TAG"
echo " Strategy: $RELEASE_STRATEGY"
- |
echo "🚀 Executing release workflow..."
python3 -c "
import sys
import os
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
try:
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f'Release result: {result}')
if not result.get('success', False):
print('❌ Release workflow failed')
sys.exit(1)
print('✅ Release workflow completed')
except Exception as e:
print(f'❌ Release workflow error: {e}')
sys.exit(1)
"
- |
echo "🔍 Verifying release..."
# Check tag exists
if git tag -l | grep -q "$RELEASE_TAG"; then
echo "✅ Tag $RELEASE_TAG found"
else
echo "❌ Tag $RELEASE_TAG not found"
exit 1
fi
# Check tag points to correct commit
TAG_COMMIT=$(git rev-list -n 1 $RELEASE_TAG)
HEAD_COMMIT=$(git rev-parse HEAD)
if [ "$TAG_COMMIT" = "$HEAD_COMMIT" ]; then
echo "✅ Tag points to correct commit"
else
echo "❌ Tag does not point to HEAD"
exit 1
fi
echo "✅ Release verification passed"
- |
if [ "$CREATE_GITHUB_RELEASE" = "true" ]; then
echo "🌐 Creating GitHub release..."
# Create GitHub release (requires gh CLI and GitHub token)
if command -v gh &> /dev/null; then
gh release create $RELEASE_TAG \
--title "Release $RELEASE_TAG" \
--notes "## Release $RELEASE_TAG
**Mode:** $RELEASE_STRATEGY
**Internal Version:** $INTERNAL_VERSION
### Changes
[Release notes and changelog will be added here]
### Verification
- ✅ Release workflow completed
- ✅ Tag created successfully
- ✅ GitHub release created"
echo "✅ GitHub release created"
else
echo "⚠️ gh CLI not available, skipping GitHub release"
fi
fi
after_script:
- |
echo "🎉 Release completed successfully!"
echo "📋 Release details:"
echo " Mode: $RELEASE_STRATEGY"
echo " Tag: $RELEASE_TAG"
echo " Version: $INTERNAL_VERSION"
echo " GitHub Release: $CREATE_GITHUB_RELEASE"
artifacts:
reports:
junit: test-results.xml
paths:
- version_info.txt
only:
- web
Docker Integration
Dockerfile with Release Automation
# Dockerfile.release
FROM python:3.9-slim
# Install dependencies
RUN apt-get update && apt-get install -y \
git \
&& rm -rf /var/lib/apt/lists/*
# Set up Python
RUN pip install pyyaml
# Copy repository
WORKDIR /app
COPY . .
# Set environment variables
ENV CI=true
ENV PYTHONPATH=/app/packages/frameworks/workflow mgt/scripts
# Release script
RUN echo '#!/bin/bash\n\
echo "🚀 Docker Release Automation"\n\
\n\
# Check arguments\n\
if [ $# -lt 1 ]; then\n\
echo "Usage: docker run release-automation <mode> [skip_validation] [create_github_release]"\n\
echo "Modes: registry, task_touch"\n\
exit 1\n\
fi\n\
\n\
MODE=$1\n\
SKIP_VALIDATION=${2:-false}\n\
CREATE_GITHUB_RELEASE=${3:-true}\n\
\n\
echo "📋 Release configuration:"\n\
echo " Mode: $MODE"\n\
echo " Skip validation: $SKIP_VALIDATION"\n\
echo " Create GitHub release: $CREATE_GITHUB_RELEASE"\n\
\n\
# Configure release\n\
cp rw-config.yaml rw-config.yaml.backup\n\
sed -i "s/semver_mapping_strategy: .*/semver_mapping_strategy: $MODE/" rw-config.yaml\n\
\n\
if [ "$SKIP_VALIDATION" = "true" ]; then\n\
echo "skip_validation: true" >> rw-config.yaml\n\
fi\n\
\n\
if [ "$CREATE_GITHUB_RELEASE" = "false" ]; then\n\
echo "skip_github_release: true" >> rw-config.yaml\n\
fi\n\
\n\
# Execute release\n\
python3 -c "\n\
import sys\n\
sys.path.insert(0, \"packages/frameworks/workflow mgt/scripts\")\n\
\n\
try:\n\
from workflow_orchestrator import WorkflowOrchestrator\n\
wo = WorkflowOrchestrator()\n\
result = wo.execute_workflow(\"release-workflow.yaml\")\n\
print(f\"Release result: {result}\")\n\
\n\
if not result.get(\"success\", False):\n\
print(\"❌ Release workflow failed\")\n\
sys.exit(1)\n\
\n\
print(\"✅ Release workflow completed\")\n\
except Exception as e:\n\
print(f\"❌ Release workflow error: {e}\")\n\
sys.exit(1)\n\
"\n\
\n\
echo "🎉 Release completed!"\n\
' > /usr/local/bin/release-automation && chmod +x /usr/local/bin/release-automation
ENTRYPOINT ["/usr/local/bin/release-automation"]
Docker Compose Example
# docker-compose.release.yml
version: '3.8'
services:
release:
build:
context: .
dockerfile: Dockerfile.release
environment:
- GITHUB_TOKEN=${GITHUB_TOKEN}
- CI=true
volumes:
- .:/app
- ~/.gitconfig:/root/.gitconfig
working_dir: /app
command: ["${RELEASE_MODE:-registry}", "${SKIP_VALIDATION:-false}", "${CREATE_GITHUB_RELEASE:-true}"]
release-task-touch:
extends: release
command: ["task_touch", "false", "true"]
release-registry:
extends: release
command: ["registry", "false", "true"]
Testing Integration
Release Integration Tests
# test_release_integration.py
import pytest
import subprocess
import tempfile
import os
import yaml
from pathlib import Path
class TestReleaseIntegration:
"""Test release automation integration"""
@pytest.fixture
def temp_repo(self):
"""Create a temporary repository for testing"""
with tempfile.TemporaryDirectory() as temp_dir:
repo_path = Path(temp_dir)
# Initialize git repo
subprocess.run(['git', 'init'], cwd=repo_path, check=True)
subprocess.run(['git', 'config', 'user.name', 'Test User'], cwd=repo_path, check=True)
subprocess.run(['git', 'config', 'user.email', 'test@example.com'], cwd=repo_path, check=True)
# Create basic structure
(repo_path / 'src' / 'fynd_deals').mkdir(parents=True, exist_ok=True)
(repo_path / 'packages' / 'frameworks' / 'workflow mgt' / 'scripts').mkdir(parents=True, exist_ok=True)
(repo_path / 'docs' / 'changelog-and-release-notes' / 'changelog-archive').mkdir(parents=True, exist_ok=True)
# Create version file
version_content = '''
VERSION_STRING = "0.5.1.48+1"
VERSION_EPIC = 5
VERSION_STORY = 1
VERSION_TASK = 48
VERSION_BUILD = 1
'''
(repo_path / 'src' / 'fynd_deals' / 'version.py').write_text(version_content)
# Create rw-config.yaml
config_content = {
'version_file': 'src/fynd_deals/version.py',
'main_changelog': 'CHANGELOG.md',
'changelog_dir': 'docs/changelog-and-release-notes/changelog-archive',
'scripts_path': 'packages/frameworks/workflow mgt/scripts',
'readme_file': 'README.md',
'semver_mapping_strategy': 'registry',
'use_kanban': False
}
with open(repo_path / 'rw-config.yaml', 'w') as f:
yaml.dump(config_content, f)
# Create initial commit
subprocess.run(['git', 'add', '.'], cwd=repo_path, check=True)
subprocess.run(['git', 'commit', '-m', 'Initial commit'], cwd=repo_path, check=True)
# Create epic branch
subprocess.run(['git', 'checkout', '-b', 'epic/5'], cwd=repo_path, check=True)
yield repo_path
def test_registry_mode_release(self, temp_repo):
"""Test registry mode release"""
# Configure for registry mode
config_path = temp_repo / 'rw-config.yaml'
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
config['semver_mapping_strategy'] = 'registry'
with open(config_path, 'w') as f:
yaml.dump(config, f)
# Execute release workflow
result = subprocess.run([
'python3', '-c',
'''
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f"success={result.get('success', False)}")
'''
], cwd=temp_repo, capture_output=True, text=True)
assert result.returncode == 0
assert 'success=True' in result.stdout
# Verify tag was created
tag_result = subprocess.run(['git', 'tag', '-l'], cwd=temp_repo, capture_output=True, text=True)
assert 'v0.5.1.48+1' in tag_result.stdout
def test_task_touch_mode_release(self, temp_repo):
"""Test task-touch mode release"""
# Configure for task-touch mode
config_path = temp_repo / 'rw-config.yaml'
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
config['semver_mapping_strategy'] = 'task_touch'
with open(config_path, 'w') as f:
yaml.dump(config, f)
# Execute release workflow
result = subprocess.run([
'python3', '-c',
'''
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f"success={result.get('success', False)}")
'''
], cwd=temp_repo, capture_output=True, text=True)
assert result.returncode == 0
assert 'success=True' in result.stdout
# Verify SemVer tag was created
tag_result = subprocess.run(['git', 'tag', '-l'], cwd=temp_repo, capture_output=True, text=True)
# Should contain SemVer tag (exact format depends on semver-registry.yaml)
assert len(tag_result.stdout.strip().split('\n')) >= 1
def test_configuration_validation(self, temp_repo):
"""Test configuration validation"""
# Test invalid configuration
config_path = temp_repo / 'rw-config.yaml'
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
config['semver_mapping_strategy'] = 'invalid'
with open(config_path, 'w') as f:
yaml.dump(config, f)
# Should fail validation
result = subprocess.run([
'python3', '-c',
'''
import sys
sys.path.insert(0, 'packages/frameworks/workflow mgt/scripts')
from workflow_orchestrator import WorkflowOrchestrator
wo = WorkflowOrchestrator()
result = wo.execute_workflow('release-workflow.yaml')
print(f"success={result.get('success', False)}")
'''
], cwd=temp_repo, capture_output=True, text=True)
assert result.returncode != 0
assert 'success=False' in result.stdout
if __name__ == '__main__':
pytest.main([__file__])
Monitoring and Alerting
Release Monitoring Script
# monitor_releases.py
import subprocess
import json
import time
from datetime import datetime
from pathlib import Path
class ReleaseMonitor:
"""Monitor release automation and send alerts"""
def __init__(self, config_file='monitor-config.json'):
self.config = self.load_config(config_file)
self.alerts_sent = []
def load_config(self, config_file):
"""Load monitoring configuration"""
default_config = {
'check_interval': 300, # 5 minutes
'max_release_time': 1800, # 30 minutes
'alert_channels': ['email', 'slack'],
'email_recipients': ['devops@example.com'],
'slack_webhook': 'https://hooks.slack.com/services/...',
'repositories': ['origin']
}
if Path(config_file).exists():
with open(config_file, 'r') as f:
return json.load(f)
else:
with open(config_file, 'w') as f:
json.dump(default_config, f, indent=2)
return default_config
def check_release_status(self):
"""Check current release status"""
try:
# Get current branch
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
# Get current version
version = subprocess.check_output([
'python3', '-c',
'''
import re
with open('src/fynd_deals/version.py', 'r') as f:
content = f.read()
version_match = re.search(r'VERSION_STRING = \"([^\"]+)\"', content)
if version_match:
print(version_match.group(1))
'''
], text=True).strip()
# Get recent tags
tags = subprocess.check_output(['git', 'tag', '--sort=-version:refname'], text=True).strip().split('\n')[:5]
# Check for in-progress releases
in_progress = self.check_in_progress_releases()
return {
'timestamp': datetime.now().isoformat(),
'branch': branch,
'current_version': version,
'recent_tags': tags,
'in_progress': in_progress,
'status': 'healthy'
}
except Exception as e:
return {
'timestamp': datetime.now().isoformat(),
'status': 'error',
'error': str(e)
}
def check_in_progress_releases(self):
"""Check for releases in progress"""
# Check for release workflow processes
try:
result = subprocess.check_output(['ps', 'aux'], text=True)
return 'workflow_orchestrator' in result or 'release-workflow' in result
except:
return False
def send_alert(self, message, level='warning'):
"""Send alert to configured channels"""
alert_data = {
'timestamp': datetime.now().isoformat(),
'level': level,
'message': message,
'repository': 'ai-dev-kit'
}
# Avoid duplicate alerts
alert_key = f"{level}:{message}"
if alert_key in self.alerts_sent:
return
if 'email' in self.config['alert_channels']:
self.send_email_alert(alert_data)
if 'slack' in self.config['alert_channels']:
self.send_slack_alert(alert_data)
self.alerts_sent.append(alert_key)
def send_email_alert(self, alert_data):
"""Send email alert (placeholder implementation)"""
print(f"📧 Email Alert: {alert_data['level'].upper()} - {alert_data['message']}")
# Implement actual email sending here
def send_slack_alert(self, alert_data):
"""Send Slack alert (placeholder implementation)"""
print(f"💬 Slack Alert: {alert_data['level'].upper()} - {alert_data['message']}")
# Implement actual Slack webhook call here
def monitor_loop(self):
"""Main monitoring loop"""
print("🔍 Starting release monitoring...")
while True:
status = self.check_release_status()
if status['status'] == 'error':
self.send_alert(f"Release monitoring error: {status['error']}", 'error')
elif status['in_progress']:
# Check if release is taking too long
if self.is_release_stale(status):
self.send_alert("Release appears to be stuck or taking too long", 'warning')
# Clean old alerts (older than 24 hours)
self.cleanup_alerts()
time.sleep(self.config['check_interval'])
def is_release_stale(self, status):
"""Check if release is stale"""
# Implement logic to detect stale releases
# This could check tag timestamps, process ages, etc.
return False # Placeholder
def cleanup_alerts(self):
"""Clean up old alerts"""
# Remove alerts older than 24 hours
current_time = datetime.now()
self.alerts_sent = [
alert for alert in self.alerts_sent
if (current_time - datetime.fromisoformat(alert.split(':')[0])).total_seconds() < 86400
]
if __name__ == '__main__':
monitor = ReleaseMonitor()
monitor.monitor_loop()
Security Considerations
Secure Release Automation
# .github/workflows/release-secure.yml
name: Secure Release
on:
workflow_dispatch:
inputs:
mode:
description: 'Release mode'
required: true
default: 'registry'
type: choice
options:
- registry
- task_touch
jobs:
security-scan:
runs-on: ubuntu-latest
outputs:
scan-passed: ${{ steps.scan.outputs.passed }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Security Scan
id: scan
run: |
echo "🔒 Running security scan..."
# Check for secrets in configuration
if grep -r "password\|secret\|token" rw-config.yaml 2>/dev/null; then
echo "❌ Secrets found in configuration"
echo "passed=false" >> $GITHUB_OUTPUT
exit 1
fi
# Check file permissions
if [ -x "rw-config.yaml" ]; then
echo "❌ Configuration file is executable"
echo "passed=false" >> $GITHUB_OUTPUT
exit 1
fi
# Validate YAML syntax
if ! python3 -c "import yaml; yaml.safe_load(open('rw-config.yaml'))"; then
echo "❌ Invalid YAML syntax"
echo "passed=false" >> $GITHUB_OUTPUT
exit 1
fi
echo "✅ Security scan passed"
echo "passed=true" >> $GITHUB_OUTPUT
release:
runs-on: ubuntu-latest
needs: security-scan
if: needs.security-scan.outputs.scan-passed == 'true'
permissions:
contents: write
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install pyyaml
- name: Execute Release
run: |
echo "🚀 Executing secure release..."
# Release automation code here
Remember: Always test integration changes in a development environment before applying to production!