Skip to content

Bump Emulator Version #261

Bump Emulator Version

Bump Emulator Version #261

name: Bump Emulator Version
on:
schedule:
# Run daily at midnight UTC
- cron: '0 0 * * *'
# Allow manual triggering
workflow_dispatch:
jobs:
bump-emulator-version:
name: Check for new emulator version
runs-on: ubuntu-latest
permissions:
issues: write
contents: write
pull-requests: write
steps:
- name: Check out the repo
uses: actions/checkout@v6
with:
ref: master
- name: Get current version and check for newer versions
id: version-check
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const { execSync } = require('child_process');
// Read the Dockerfile
const dockerfile = fs.readFileSync('Dockerfile', 'utf8');
// Extract the current version
const versionMatch = dockerfile.match(/FROM gcr\.io\/cloud-spanner-emulator\/emulator:([0-9.]+)/);
if (!versionMatch) {
throw new Error('Could not find emulator version in Dockerfile');
}
const currentVersion = versionMatch[1];
console.log(`Current version: ${currentVersion}`);
// Set output for current version
core.setOutput('current_version', currentVersion);
// Fetch versions from Google Container Registry
const response = await fetch('https://gcr.io/v2/cloud-spanner-emulator/emulator/tags/list');
const data = await response.json();
// Filter for semantic version tags (x.y.z format)
const allVersions = data.tags.filter(tag => /^[0-9]+\.[0-9]+\.[0-9]+$/.test(tag));
// Sort versions
allVersions.sort((a, b) => {
const aParts = a.split('.').map(Number);
const bParts = b.split('.').map(Number);
for (let i = 0; i < 3; i++) {
if (aParts[i] !== bParts[i]) {
return aParts[i] - bParts[i];
}
}
return 0;
});
// Find versions newer than current version
const newerVersions = allVersions.filter(version => {
const vParts = version.split('.').map(Number);
const cParts = currentVersion.split('.').map(Number);
for (let i = 0; i < 3; i++) {
if (vParts[i] !== cParts[i]) {
return vParts[i] > cParts[i];
}
}
return false;
});
if (newerVersions.length === 0) {
console.log('No newer versions found.');
core.setOutput('has_newer_versions', 'false');
} else {
// Get only the next version (first one after sorting)
const nextVersion = newerVersions[0];
console.log(`Next version found: ${nextVersion}`);
core.setOutput('has_newer_versions', 'true');
core.setOutput('next_version', nextVersion);
// For backward compatibility
core.setOutput('newer_versions', nextVersion);
core.setOutput('latest_version', nextVersion);
}
- name: Process next version
id: process-versions
if: steps.version-check.outputs.has_newer_versions == 'true'
env:
current_version: ${{ steps.version-check.outputs.current_version }}
next_version: ${{ steps.version-check.outputs.next_version }}
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const { execSync } = require('child_process');
const currentVersion = process.env.current_version;
const nextVersion = process.env.next_version;
console.log(`Processing next version: ${nextVersion}`);
let updated = false;
const failedVersions = {};
// Update Dockerfile with the next version
console.log(`Updating Dockerfile to version ${nextVersion}...`);
const dockerfile = fs.readFileSync('Dockerfile', 'utf8');
const updatedDockerfile = dockerfile.replace(
new RegExp(`FROM gcr.io/cloud-spanner-emulator/emulator:${currentVersion}`),
`FROM gcr.io/cloud-spanner-emulator/emulator:${nextVersion}`
);
fs.writeFileSync('Dockerfile', updatedDockerfile);
// Verify the updated Dockerfile
console.log(`Verifying updated Dockerfile with version ${nextVersion}...`);
try {
execSync('bash verify.bash', { stdio: 'inherit' });
console.log(`Verification successful for version ${nextVersion}`);
updated = true;
} catch (error) {
console.log(`Verification failed for version ${nextVersion}`);
// Mark this version as failed
failedVersions[`version_${nextVersion}`] = 'failed';
// Revert the Dockerfile change
fs.writeFileSync('Dockerfile', dockerfile);
}
if (updated) {
console.log(`Successfully updated to version ${nextVersion}`);
core.setOutput('updated', 'true');
core.setOutput('updated_version', nextVersion);
} else {
console.log('No successful update');
core.setOutput('updated', 'false');
}
// Output failed version
for (const [key, value] of Object.entries(failedVersions)) {
core.setOutput(key, value);
}
- name: Create issue for failed version verification
if: steps.version-check.outputs.has_newer_versions == 'true' && steps.process-versions.outputs.updated != 'true'
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const nextVersion = process.env.NEXT_VERSION;
const issueTitle = `Emulator version update verification failed: ${nextVersion}`;
console.log(`Checking for existing issues for version: ${nextVersion}`);
// Search for existing open issues with the same title
const existingIssues = await github.rest.search.issuesAndPullRequests({
q: `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open "${issueTitle}"`
});
if (existingIssues.data.total_count > 0) {
console.log(`Found existing issue for version ${nextVersion}, skipping creation`);
return;
}
console.log(`Creating issue for failed version: ${nextVersion}`);
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: issueTitle,
body: `The verification of the next emulator version ${nextVersion} failed. Please check the [workflow run](${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}) for details.`
});
env:
NEXT_VERSION: ${{ steps.version-check.outputs.next_version }}
- name: Check for existing PRs and create new one if needed
if: steps.process-versions.outputs.updated == 'true'
env:
GH_TOKEN: ${{ secrets.CREATE_PR_PAT }}
CURRENT_VERSION: ${{ steps.version-check.outputs.current_version }}
UPDATED_VERSION: ${{ steps.process-versions.outputs.updated_version }}
run: |
# Check for existing PRs with the same title
PR_TITLE="chore: update emulator version to $UPDATED_VERSION"
EXISTING_PR=$(gh pr list --state open --search "$PR_TITLE" --json number --jq '.[0].number')
if [ -n "$EXISTING_PR" ]; then
echo "Found existing PR #$EXISTING_PR with the same title, skipping PR creation"
exit 0
fi
echo "No existing PR found, creating a new one"
# Create and checkout a new branch
git branch -d bump-base-version &>/dev/null || true
git checkout -b bump-base-version
# Commit the changes
git config --global user.name "GitHub Actions"
git config --global user.email "[email protected]"
git commit -am "chore: update emulator version to $UPDATED_VERSION"
# Push the branch (force push to handle case where branch exists remotely)
git push -f -u origin bump-base-version
# Create the PR
gh pr create \
--title "$PR_TITLE" \
--body "This PR updates the Cloud Spanner emulator version from $CURRENT_VERSION to $UPDATED_VERSION.
Original Release Notes: https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/releases/tag/v$UPDATED_VERSION
This PR was automatically generated by the check-emulator-version workflow." \
--base master