Skip to content

updated readme

updated readme #7

name: Version Check
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
version-consistency:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
submodules: true
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq
npm install -g @openapitools/openapi-generator-cli @asyncapi/cli @asyncapi/modelina-cli
pip install tomli
- name: Check specs submodule points to a tag
run: |
cd specs
# Get the current commit hash
CURRENT_COMMIT=$(git rev-parse HEAD)
echo "Current specs commit: $CURRENT_COMMIT"
# Check if this commit is tagged
TAGS=$(git tag --points-at $CURRENT_COMMIT)
if [ -z "$TAGS" ]; then
echo "❌ ERROR: specs submodule is not pointing to a tagged commit"
echo "Current commit $CURRENT_COMMIT has no tags"
exit 1
fi
# Get the first tag (in case there are multiple)
SPECS_TAG=$(echo "$TAGS" | head -n1)
echo "Specs is pointing to tag: $SPECS_TAG"
echo "SPECS_TAG=$SPECS_TAG" >> $GITHUB_ENV
- name: Check if PR and detect specs tag changes
if: github.event_name == 'pull_request'
run: |
# Get base branch specs submodule commit
git fetch origin ${{ github.base_ref }}
BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
# Get the specs submodule commit hash from base branch
echo "DEBUG: BASE_SHA = $BASE_SHA"
echo "DEBUG: git ls-tree output for base:"
git ls-tree $BASE_SHA specs
echo "DEBUG: git ls-tree output for HEAD:"
git ls-tree HEAD specs
BASE_SPECS_COMMIT=$(git ls-tree $BASE_SHA specs | awk '{print $3}')
CURRENT_SPECS_COMMIT=$(git ls-tree HEAD specs | awk '{print $3}')
echo "Base specs commit: $BASE_SPECS_COMMIT"
echo "Current specs commit: $CURRENT_SPECS_COMMIT"
# Get specs tag from base branch commit
cd specs
git fetch --tags origin # Make sure we have all tags
# Get tag for base commit
BASE_TAGS=$(git tag --points-at $BASE_SPECS_COMMIT 2>/dev/null || echo "")
if [ -z "$BASE_TAGS" ]; then
echo "Base specs commit $BASE_SPECS_COMMIT has no tags"
BASE_SPECS_TAG="unknown"
else
BASE_SPECS_TAG=$(echo "$BASE_TAGS" | head -n1)
fi
cd ..
echo "Base specs tag: $BASE_SPECS_TAG"
echo "Current specs tag: $SPECS_TAG"
echo "BASE_SPECS_TAG=$BASE_SPECS_TAG" >> $GITHUB_ENV
# Check if specs commit changed (more reliable than just tag comparison)
if [ "$BASE_SPECS_COMMIT" = "$CURRENT_SPECS_COMMIT" ]; then
echo "SPECS_TAG_CHANGED=false" >> $GITHUB_ENV
echo "Specs commit unchanged: $CURRENT_SPECS_COMMIT (tag: $SPECS_TAG)"
else
echo "SPECS_TAG_CHANGED=true" >> $GITHUB_ENV
echo "Specs commit changed from $BASE_SPECS_COMMIT to $CURRENT_SPECS_COMMIT"
echo "Specs tag changed from $BASE_SPECS_TAG to $SPECS_TAG"
fi
- name: Check if version was manually modified in PR
if: github.event_name == 'pull_request'
run: |
# Check if pyproject.toml version line was modified in this PR
BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
if git diff $BASE_SHA HEAD -- pyproject.toml | grep -q '^\+.*version = '; then
echo "VERSION_MANUALLY_MODIFIED=true" >> $GITHUB_ENV
echo "Version was manually modified in this PR"
else
echo "VERSION_MANUALLY_MODIFIED=false" >> $GITHUB_ENV
echo "Version was not manually modified in this PR"
fi
- name: Extract version components
run: |
# Extract version from pyproject.toml (this is the source of truth)
SDK_VERSION=$(python -c "
import tomli
with open('pyproject.toml', 'rb') as f:
data = tomli.load(f)
print(data['project']['version'])
")
echo "SDK_VERSION: $SDK_VERSION"
echo "SDK_VERSION=$SDK_VERSION" >> $GITHUB_ENV
# If this is a PR, get the base version for comparison
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
BASE_SDK_VERSION=$(git show $BASE_SHA:pyproject.toml | python -c "
import sys, tomli
try:
data = tomli.loads(sys.stdin.read())
print(data['project']['version'])
except:
print('unknown')
" 2>/dev/null || echo "unknown")
echo "BASE_SDK_VERSION: $BASE_SDK_VERSION"
echo "BASE_SDK_VERSION=$BASE_SDK_VERSION" >> $GITHUB_ENV
fi
# Extract first 3 components from specs tag (remove 'v' prefix if present)
SPECS_TAG_CLEAN=$(echo "$SPECS_TAG" | sed 's/^v//')
SPECS_VERSION_PREFIX=$(echo "$SPECS_TAG_CLEAN" | cut -d. -f1-3)
echo "SPECS_VERSION_PREFIX: $SPECS_VERSION_PREFIX"
echo "SPECS_VERSION_PREFIX=$SPECS_VERSION_PREFIX" >> $GITHUB_ENV
# Extract first 3 components from SDK_VERSION
SDK_VERSION_PREFIX=$(echo "$SDK_VERSION" | cut -d. -f1-3)
echo "SDK_VERSION_PREFIX: $SDK_VERSION_PREFIX"
echo "SDK_VERSION_PREFIX=$SDK_VERSION_PREFIX" >> $GITHUB_ENV
- name: Automatic version bump (specs unchanged)
if: github.event_name == 'pull_request' && env.SPECS_TAG_CHANGED == 'false' && env.VERSION_MANUALLY_MODIFIED == 'false'
run: |
echo "Conditions met for automatic version bump:"
echo "- This is a PR: ✅"
echo "- Specs tag unchanged: ✅ ($SPECS_TAG)"
echo "- Version not manually modified: ✅"
# Parse current version (X.Y.Z.W)
IFS='.' read -r MAJOR MINOR PATCH BUILD <<< "$SDK_VERSION"
# Increment the last digit (build number)
NEW_BUILD=$((BUILD + 1))
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}.${NEW_BUILD}"
echo "Bumping version from $SDK_VERSION to $NEW_VERSION"
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
# Update pyproject.toml
sed -i "s/^version = \".*\"$/version = \"$NEW_VERSION\"/" pyproject.toml
echo "✅ Updated pyproject.toml to version $NEW_VERSION"
- name: Automatic version update for specs tag change
if: github.event_name == 'pull_request' && env.SPECS_TAG_CHANGED == 'true' && env.VERSION_MANUALLY_MODIFIED == 'false'
run: |
echo "Conditions met for automatic version update due to specs tag change:"
echo "- This is a PR: ✅"
echo "- Specs tag changed: ✅ (from $BASE_SPECS_TAG to $SPECS_TAG)"
echo "- Version not manually modified: ✅"
# Update SDK version to match new specs version with .0 build number
NEW_VERSION="${SPECS_VERSION_PREFIX}.0"
echo "Updating version from $SDK_VERSION to $NEW_VERSION to match specs tag $SPECS_TAG"
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
# Update pyproject.toml
sed -i "s/^version = \".*\"$/version = \"$NEW_VERSION\"/" pyproject.toml
echo "✅ Updated pyproject.toml to version $NEW_VERSION"
- name: Commit version bump
if: github.event_name == 'pull_request' && env.NEW_VERSION != ''
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add pyproject.toml
git commit -m "chore: bump SDK version to $NEW_VERSION"
git push origin HEAD:${{ github.head_ref }}
echo "✅ Committed and pushed version bump to $NEW_VERSION"
- name: Validate version progression (for manual changes)
if: github.event_name == 'pull_request' && env.VERSION_MANUALLY_MODIFIED == 'true'
run: |
echo "Manual version change detected, validating progression..."
echo "Previous version: $BASE_SDK_VERSION"
echo "Current version: $SDK_VERSION"
# Check for version regression using version comparison
if [ "$BASE_SDK_VERSION" != "unknown" ]; then
# Parse versions for comparison
IFS='.' read -r BASE_MAJOR BASE_MINOR BASE_PATCH BASE_BUILD <<< "$BASE_SDK_VERSION"
IFS='.' read -r CURR_MAJOR CURR_MINOR CURR_PATCH CURR_BUILD <<< "$SDK_VERSION"
# Convert to comparable integers (assuming reasonable version numbers < 1000)
BASE_NUM=$((BASE_MAJOR*1000000000 + BASE_MINOR*1000000 + BASE_PATCH*1000 + BASE_BUILD))
CURR_NUM=$((CURR_MAJOR*1000000000 + CURR_MINOR*1000000 + CURR_PATCH*1000 + CURR_BUILD))
if [ "$CURR_NUM" -lt "$BASE_NUM" ]; then
echo "❌ ERROR: Version regression detected!"
echo "Previous version: $BASE_SDK_VERSION"
echo "Current version: $SDK_VERSION"
echo "New version must be higher than or equal to the previous version."
exit 1
elif [ "$CURR_NUM" -eq "$BASE_NUM" ]; then
echo "⚠️ Version unchanged: $SDK_VERSION"
else
# Check that the bump is reasonable (no more than 1 in any component)
MAJOR_DIFF=$((CURR_MAJOR - BASE_MAJOR))
MINOR_DIFF=$((CURR_MINOR - BASE_MINOR))
PATCH_DIFF=$((CURR_PATCH - BASE_PATCH))
BUILD_DIFF=$((CURR_BUILD - BASE_BUILD))
if [ "$MAJOR_DIFF" -gt 1 ] || [ "$MINOR_DIFF" -gt 1 ] || [ "$PATCH_DIFF" -gt 1 ] || [ "$BUILD_DIFF" -gt 1 ]; then
echo "❌ ERROR: Version bump is too large!"
echo "Previous: $BASE_SDK_VERSION"
echo "Current: $SDK_VERSION"
echo "Version should only be incremented by 1 in a single component."
exit 1
fi
echo "✅ Version progression verified: $BASE_SDK_VERSION → $SDK_VERSION"
fi
else
echo "⚠️ Cannot determine previous version, skipping progression check"
fi
- name: Check version consistency
run: |
# If version was auto-bumped, use the new version
if [ -n "$NEW_VERSION" ]; then
CURRENT_SDK_VERSION="$NEW_VERSION"
else
CURRENT_SDK_VERSION="$SDK_VERSION"
fi
echo "Checking version consistency..."
echo "Specs tag: $SPECS_TAG"
echo "Specs version prefix: $SPECS_VERSION_PREFIX"
echo "SDK version (from pyproject.toml): $CURRENT_SDK_VERSION"
# Check 1: SDK_VERSION must have exactly 4 digits (X.Y.Z.W format)
SDK_VERSION_PARTS=$(echo "$CURRENT_SDK_VERSION" | tr '.' '\n' | wc -l)
if [ "$SDK_VERSION_PARTS" -ne 4 ]; then
echo "❌ ERROR: SDK_VERSION must have exactly 4 digits (X.Y.Z.W format)"
echo "Found $SDK_VERSION_PARTS parts in version: $CURRENT_SDK_VERSION"
echo "Expected format: X.Y.Z.W (e.g., 2.0.3.0)"
exit 1
fi
echo "✅ SDK version has 4 digits: $CURRENT_SDK_VERSION"
# Check 2: First 3 digits of SDK version must match specs tag
CURRENT_SDK_VERSION_PREFIX=$(echo "$CURRENT_SDK_VERSION" | cut -d. -f1-3)
if [ "$CURRENT_SDK_VERSION_PREFIX" != "$SPECS_VERSION_PREFIX" ]; then
echo "❌ ERROR: First 3 digits of SDK_VERSION ($CURRENT_SDK_VERSION_PREFIX) do not match specs tag ($SPECS_VERSION_PREFIX)"
echo "SDK version $CURRENT_SDK_VERSION should start with $SPECS_VERSION_PREFIX"
exit 1
fi
echo "✅ SDK version prefix matches specs tag"
- name: Store initial git state
run: |
echo "Storing initial git state..."
# Store the current state of tracked files
git ls-files | sort > /tmp/files_before.txt
git status --porcelain > /tmp/status_before.txt
# Create checksums for all tracked files (filter out directories)
git ls-files -z | xargs -0 -I {} sh -c 'test -f "{}" && sha256sum "{}"' | sort > /tmp/checksums_before.txt
- name: Run API generation script
run: |
echo "Running API generation script..."
chmod +x scripts/generate-api.sh
scripts/generate-api.sh
- name: Run WS generation script
run: |
echo "Running WebSocket generation script..."
chmod +x scripts/generate-ws.sh
scripts/generate-ws.sh
- name: Check for changes after script execution
run: |
echo "Storing state after running scripts..."
# Store the state after running scripts
git ls-files | sort > /tmp/files_after.txt
git status --porcelain > /tmp/status_after.txt
git ls-files -z | xargs -0 -I {} sh -c 'test -f "{}" && sha256sum "{}"' | sort > /tmp/checksums_after.txt
echo "Checking for file changes..."
# Check if any tracked files were modified
if ! cmp -s /tmp/status_before.txt /tmp/status_after.txt; then
echo "❌ ERROR: Git status changed after running generation scripts"
echo "Status before:"
cat /tmp/status_before.txt
echo "Status after:"
cat /tmp/status_after.txt
echo ""
echo "Detailed changes:"
git status
git diff --name-only
exit 1
fi
# Check if file list changed (new files created)
if ! cmp -s /tmp/files_before.txt /tmp/files_after.txt; then
echo "❌ ERROR: File list changed after running generation scripts"
echo "New or removed files detected:"
diff /tmp/files_before.txt /tmp/files_after.txt || true
exit 1
fi
# Check if any file contents changed
if ! cmp -s /tmp/checksums_before.txt /tmp/checksums_after.txt; then
echo "❌ ERROR: File contents changed after running generation scripts"
echo "Files with different checksums:"
diff /tmp/checksums_before.txt /tmp/checksums_after.txt || true
echo ""
echo "Git diff:"
git diff
exit 1
fi
# Check for any untracked files that might have been created
UNTRACKED_FILES=$(git ls-files --others --exclude-standard)
if [ -n "$UNTRACKED_FILES" ]; then
echo "❌ ERROR: Generation scripts created new untracked files:"
echo "$UNTRACKED_FILES"
exit 1
fi
echo "✅ No changes detected after running generation scripts"
- name: Add PR comment about version changes
uses: actions/github-script@v7
if: github.event_name == 'pull_request' && always()
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
// Remove existing bot comments
for (const comment of comments) {
if (comment.body.includes('🤖 SDK Version Check')) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
});
}
}
const newVersion = '${{ env.NEW_VERSION }}';
const versionModified = '${{ env.VERSION_MANUALLY_MODIFIED }}' === 'true';
const specsTagChanged = '${{ env.SPECS_TAG_CHANGED }}' === 'true';
const sdkVersion = '${{ env.SDK_VERSION }}';
const specsTag = '${{ env.SPECS_TAG }}';
let message = '🤖 **SDK Version Check**\n\n';
if (newVersion && !specsTagChanged) {
message += `✅ **Version automatically bumped to \`${newVersion}\`**\n`;
message += `- Previous version: \`${sdkVersion}\`\n`;
message += `- Reason: Specs tag unchanged (${specsTag}), no manual version changes\n`;
message += `- Change committed automatically\n`;
} else if (newVersion && specsTagChanged) {
message += `✅ **Version automatically updated to \`${newVersion}\`**\n`;
message += `- Previous version: \`${sdkVersion}\`\n`;
message += `- Reason: Specs tag changed to ${specsTag}, version updated to match\n`;
message += `- Change committed automatically\n`;
} else if (versionModified) {
message += '📝 **Manual version changes detected**\n\n';
message += `✅ Version validation passed: \`${sdkVersion}\`\n`;
message += '- Version progression validated (no regression)\n';
message += '- Version bump is reasonable (≤ 1 increment per component)\n';
} else if (specsTagChanged) {
message += '📋 **Specs tag updated**\n\n';
message += `✅ No automatic version bump needed\n`;
message += `- Specs tag changed to: \`${specsTag}\`\n`;
message += `- SDK version: \`${sdkVersion}\`\n`;
message += '- Version alignment with specs verified\n';
} else {
message += '✅ **Version check passed**\n\n';
message += `- SDK version: \`${sdkVersion}\`\n`;
message += `- Specs tag: \`${specsTag}\`\n`;
message += '- All consistency checks passed\n';
}
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});
- name: Summary
run: |
# Determine final version (could be auto-bumped version)
if [ -n "$NEW_VERSION" ]; then
FINAL_VERSION="$NEW_VERSION"
VERSION_STATUS="auto-bumped to $NEW_VERSION"
else
FINAL_VERSION="$SDK_VERSION"
VERSION_STATUS="$SDK_VERSION"
fi
echo "🎉 All version consistency checks passed!"
echo "✅ Specs submodule points to tag: $SPECS_TAG"
echo "✅ SDK version: $VERSION_STATUS"
echo "✅ SDK version has required 4-digit format (X.Y.Z.W)"
echo "✅ First 3 digits match specs version: $SPECS_VERSION_PREFIX"
echo "✅ Generation scripts produced no changes"
if [ "${{ github.event_name }}" = "pull_request" ]; then
if [ -n "$NEW_VERSION" ]; then
echo "🚀 Version automatically bumped and committed"
elif [ "$VERSION_MANUALLY_MODIFIED" = "true" ]; then
echo "📝 Manual version changes validated"
elif [ "$SPECS_TAG_CHANGED" = "true" ]; then
echo "📋 Specs tag change detected and validated"
else
echo "✅ No version changes needed"
fi
fi