Skip to content

Commit 8ad0436

Browse files
authored
feat: Add continuous deployment pipeline with manual version control (#13)
Implements comprehensive CD pipeline with manual version control, multi-architecture Docker builds, and automated GitHub Container Registry publishing. Includes complete documentation and project changelog.
1 parent ac822a8 commit 8ad0436

File tree

4 files changed

+371
-1
lines changed

4 files changed

+371
-1
lines changed

.github/workflows/cd.yml

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
name: CD - Build and Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version_type:
7+
description: 'Version bump type'
8+
required: true
9+
default: 'patch'
10+
type: choice
11+
options:
12+
- patch
13+
- minor
14+
- major
15+
16+
env:
17+
REGISTRY: ghcr.io
18+
IMAGE_NAME: ${{ github.repository }}
19+
20+
jobs:
21+
version:
22+
name: Calculate Version
23+
runs-on: ubuntu-latest
24+
outputs:
25+
version: ${{ steps.version.outputs.version }}
26+
previous_version: ${{ steps.version.outputs.previous_version }}
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 0 # Need full history for tags
32+
33+
- name: Get latest version tag
34+
id: get_version
35+
run: |
36+
# Get the latest semantic version tag
37+
LATEST_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || echo "v0.0.0")
38+
echo "Latest tag: $LATEST_TAG"
39+
echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
40+
41+
- name: Calculate new version
42+
id: version
43+
run: |
44+
LATEST_TAG="${{ steps.get_version.outputs.latest_tag }}"
45+
echo "previous_version=$LATEST_TAG" >> $GITHUB_OUTPUT
46+
47+
# Extract version components
48+
VERSION_WITHOUT_V="${LATEST_TAG#v}"
49+
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION_WITHOUT_V"
50+
51+
# Default to 0.0.0 if no valid version found
52+
MAJOR=${MAJOR:-0}
53+
MINOR=${MINOR:-0}
54+
PATCH=${PATCH:-0}
55+
56+
# Increment based on input
57+
case "${{ github.event.inputs.version_type }}" in
58+
major)
59+
MAJOR=$((MAJOR + 1))
60+
MINOR=0
61+
PATCH=0
62+
;;
63+
minor)
64+
MINOR=$((MINOR + 1))
65+
PATCH=0
66+
;;
67+
patch)
68+
PATCH=$((PATCH + 1))
69+
;;
70+
esac
71+
72+
NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
73+
echo "New version: $NEW_VERSION"
74+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
75+
76+
build-and-push:
77+
name: Build and Push Docker Image
78+
runs-on: ubuntu-latest
79+
needs: version
80+
permissions:
81+
contents: read
82+
packages: write
83+
steps:
84+
- name: Checkout code
85+
uses: actions/checkout@v4
86+
87+
- name: Set up Docker Buildx
88+
uses: docker/setup-buildx-action@v3
89+
90+
- name: Log in to GitHub Container Registry
91+
uses: docker/login-action@v3
92+
with:
93+
registry: ${{ env.REGISTRY }}
94+
username: ${{ github.actor }}
95+
password: ${{ secrets.GITHUB_TOKEN }}
96+
97+
- name: Extract metadata
98+
id: meta
99+
uses: docker/metadata-action@v5
100+
with:
101+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
102+
tags: |
103+
type=raw,value=${{ needs.version.outputs.version }}
104+
type=raw,value=latest
105+
106+
- name: Build and push Docker image
107+
uses: docker/build-push-action@v5
108+
with:
109+
context: .
110+
platforms: linux/amd64,linux/arm64
111+
push: true
112+
tags: ${{ steps.meta.outputs.tags }}
113+
labels: ${{ steps.meta.outputs.labels }}
114+
build-args: |
115+
VERSION=${{ needs.version.outputs.version }}
116+
cache-from: type=gha
117+
cache-to: type=gha,mode=max
118+
119+
create-release:
120+
name: Create GitHub Release
121+
runs-on: ubuntu-latest
122+
needs: [version, build-and-push]
123+
permissions:
124+
contents: write
125+
steps:
126+
- name: Checkout code
127+
uses: actions/checkout@v4
128+
with:
129+
fetch-depth: 0
130+
131+
- name: Create tag
132+
run: |
133+
git config user.name "github-actions[bot]"
134+
git config user.email "github-actions[bot]@users.noreply.github.com"
135+
git tag ${{ needs.version.outputs.version }}
136+
git push origin ${{ needs.version.outputs.version }}
137+
138+
- name: Generate release notes
139+
id: changelog
140+
run: |
141+
# Generate changelog between versions
142+
PREV_TAG="${{ needs.version.outputs.previous_version }}"
143+
NEW_TAG="${{ needs.version.outputs.version }}"
144+
145+
{
146+
echo "## What's Changed"
147+
echo ""
148+
149+
if [ "$PREV_TAG" = "v0.0.0" ]; then
150+
echo "Initial release 🎉"
151+
else
152+
# Get PR merge commits
153+
git log ${PREV_TAG}..HEAD --merges --pretty=format:"* %s" | grep -E "Merge pull request #[0-9]+" || true
154+
155+
# Get direct commits (non-merge)
156+
echo ""
157+
echo "### Direct commits"
158+
git log ${PREV_TAG}..HEAD --no-merges --pretty=format:"* %s (%an)" || true
159+
fi
160+
161+
echo ""
162+
echo "## Docker Image"
163+
echo ""
164+
echo "Pull the latest image:"
165+
echo '```bash'
166+
echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.version }}"
167+
echo '```'
168+
echo ""
169+
echo "Or use docker-compose:"
170+
echo '```yaml'
171+
echo "services:"
172+
echo " service-quality-oracle:"
173+
echo " image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.version }}"
174+
echo '```'
175+
} > release_notes.md
176+
177+
- name: Create Release
178+
uses: softprops/action-gh-release@v1
179+
with:
180+
tag_name: ${{ needs.version.outputs.version }}
181+
name: Release ${{ needs.version.outputs.version }}
182+
body_path: release_notes.md
183+
draft: false
184+
prerelease: false
185+

CHANGELOG.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Changelog
2+
3+
All notable changes to the Service Quality Oracle project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
- Continuous deployment pipeline with manual version control
12+
- Multi-architecture Docker builds (amd64/arm64)
13+
- GitHub Container Registry publishing
14+
- Automated semantic versioning and Git tagging
15+
- Release notes generation with changelogs
16+
- CD process documentation
17+
18+
### Changed
19+
- Dockerfile now accepts VERSION build argument for dynamic versioning
20+
21+
## [0.1.0] - 2025-07-25
22+
23+
### Added
24+
- BigQuery caching system with 30-minute freshness threshold
25+
- Cache directory initialization in scheduler
26+
- Force refresh capability via environment variable
27+
- Comprehensive cache test coverage
28+
29+
### Changed
30+
- Container restart performance improved from ~5 minutes to ~30 seconds
31+
- BigQuery costs reduced by eliminating redundant expensive queries
32+
33+
### Technical Details
34+
- Cache location: `/app/data/cache/bigquery_cache.json`
35+
- Configurable via `CACHE_MAX_AGE_MINUTES` environment variable
36+
- Override caching with `FORCE_BIGQUERY_REFRESH=true`
37+
38+
## [0.0.1] - Initial Release
39+
40+
### Added
41+
- Daily BigQuery performance data fetching from Google BigQuery
42+
- Indexer eligibility processing based on threshold algorithms
43+
- On-chain oracle updates to ServiceQualityOracle contract
44+
- RPC provider failover with circuit breaker pattern
45+
- Slack notifications for monitoring
46+
- Docker containerization with health checks
47+
- Scheduled execution at 10:00 UTC daily
48+
- Data persistence and CSV output generation
49+
- Comprehensive test coverage
50+
51+
### Technical Implementation
52+
- Python 3.11 with slim Docker image
53+
- Google BigQuery integration
54+
- Web3 blockchain client with automatic failover
55+
- Circuit breaker prevents infinite restart loops
56+
- Retry mechanisms with exponential backoff
57+
- Configuration management via TOML and environment variables

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
# Use Python 3.9 slim as the base image for a lightweight container
44
FROM python:3.11-slim
55

6+
# Accept version as build argument
7+
ARG VERSION=dev
8+
69
# Add metadata labels
710
LABEL description="Service Quality Oracle" \
8-
version="0.1.0"
11+
version="${VERSION}"
912

1013
# Set working directory
1114
WORKDIR /app

docs/CD_PROCESS.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Continuous Deployment (CD) Process
2+
3+
This document explains the CD process for the Service Quality Oracle project.
4+
5+
## Overview
6+
7+
The CD process automates building, versioning, and releasing Docker images when you're ready to deploy. It uses semantic versioning (v0.0.1, v0.0.2, etc.) and publishes images to GitHub Container Registry.
8+
9+
## How It Works
10+
11+
### 1. Merge PRs to Main
12+
Continue your normal development workflow:
13+
```bash
14+
# Create feature branch
15+
git checkout -b feat/new-feature
16+
17+
# Make changes and commit
18+
git commit -m "Add new feature"
19+
20+
# Push and create PR
21+
git push origin feat/new-feature
22+
```
23+
24+
### 2. Trigger a Release
25+
After one or more PRs are merged to main:
26+
27+
1. Go to the repository's **Actions** tab
28+
2. Select **"CD - Build and Release"** from the left sidebar
29+
3. Click **"Run workflow"** button
30+
4. Choose version type:
31+
- **patch**: Bug fixes (0.0.1 → 0.0.2)
32+
- **minor**: New features (0.0.2 → 0.1.0)
33+
- **major**: Breaking changes (0.1.0 → 1.0.0)
34+
5. Click **"Run workflow"**
35+
36+
### 3. What Happens
37+
38+
The CD workflow will:
39+
1. Calculate the new version number
40+
2. Build multi-architecture Docker image (amd64/arm64)
41+
3. Push to GitHub Container Registry
42+
4. Create a Git tag
43+
5. Generate a GitHub Release with changelog
44+
45+
## Docker Images
46+
47+
Images are published to GitHub Container Registry:
48+
```bash
49+
# Pull specific version
50+
docker pull ghcr.io/graphprotocol/service-quality-oracle:v0.1.0
51+
52+
# Pull latest
53+
docker pull ghcr.io/graphprotocol/service-quality-oracle:latest
54+
```
55+
56+
## Release Strategy Examples
57+
58+
### Single PR Release
59+
```
60+
Monday: PR #1 merged → Trigger CD (patch) → v0.0.1
61+
```
62+
63+
### Batched Release
64+
```
65+
Monday: PR #1 merged (bug fix)
66+
Tuesday: PR #2 merged (bug fix)
67+
Wednesday: PR #3 merged (new feature)
68+
Thursday: → Trigger CD (minor) → v0.1.0
69+
```
70+
71+
### Hotfix Release
72+
```
73+
v0.1.0 released
74+
Critical bug found → PR merged → Trigger CD (patch) → v0.1.1
75+
```
76+
77+
## First Time Setup
78+
79+
### 1. Container Registry Access
80+
The GitHub Container Registry (ghcr.io) is automatically available. Images will be published to:
81+
```
82+
ghcr.io/graphprotocol/service-quality-oracle
83+
```
84+
85+
### 2. Update docker-compose.yml
86+
After your first release, update your `docker-compose.yml`:
87+
```yaml
88+
services:
89+
service-quality-oracle:
90+
image: ghcr.io/graphprotocol/service-quality-oracle:v0.1.0
91+
# or use :latest for auto-updates
92+
```
93+
94+
## Version History
95+
96+
View all releases:
97+
- Go to the repository's main page
98+
- Click **"Releases"** on the right sidebar
99+
- See all versions with changelogs and Docker pull commands
100+
101+
## Rollback
102+
103+
To rollback to a previous version:
104+
```bash
105+
# List available versions
106+
docker images ghcr.io/graphprotocol/service-quality-oracle
107+
108+
# Pull and run specific version
109+
docker pull ghcr.io/graphprotocol/service-quality-oracle:v0.0.9
110+
docker-compose up -d
111+
```
112+
113+
## Troubleshooting
114+
115+
### Permission Denied on Docker Pull
116+
For private repositories, authenticate with GitHub:
117+
```bash
118+
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
119+
```
120+
121+
### CD Workflow Fails
122+
Check the Actions tab for detailed logs. Common issues:
123+
- No previous tags (first run will create v0.0.1)
124+
- Docker build failures (check Dockerfile syntax)
125+
- Registry permission issues (automatic for public repos)

0 commit comments

Comments
 (0)