Skip to content
Open
169 changes: 169 additions & 0 deletions .github/workflows/tag-stable-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# This workflow runs biweekly

name: Tag stable MicroCeph commit

# Controls when the action will run. Workflow runs when there is a new stable channel
# promoted on Snapcraft
on:
# Trigger on PRs to main
pull_request:
types: [opened, synchronize, reopened]
branches:
- main # change if your default branch is different
jobs:
tag-stable-commit:
# The type of runner that the job will run on
runs-on: ubuntu-latest
permissions:
contents: write # Needed for creating tags
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Install the MicroCeph snap
- name: Install MicroCeph snap
run: |
sudo snap install microceph
# Find the first name under "channels:" that includes "/stable:"
# and parse <code name>, <release version> and <commit ID>
- name: Extract channel information
id: snap
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_TOKEN }}
script: |
const { execSync } = require('child_process');
// Run `snap info microceph`
const info = execSync('snap info microceph', { encoding: 'utf-8' });
const lines = info.split('\n');
// Find the "channels:" header (ignoring indentation)
const headerIdx = lines.findIndex(l => l.trim().startsWith('channels:'));
if (headerIdx === -1) {
core.setFailed('Could not find "channels:" in snap info output');
return;
}
// Get the first non-empty channel line after the header
const channelRaw = lines.slice(headerIdx + 1)
.find(l => l.includes('/stable:'));
if (!channelRaw) {
core.setFailed('Could not find a "/stable:" channel line');
return;
}
core.info(`Channel line: "${channelRaw}"`);
// Parse version, and commit ID
const m = channelRaw.match(
/^\s*([a-z]+)\/stable:\s+([0-9.]+)\+snap([a-f0-9]+)\s/
);
if (!m) {
core.setFailed('Failed to parse channel line');
return;
}
const [, codeName, version, commit] = m;

const minCodeNameLength = 1; // At least 1 char
const minVersionLength = 6; // e.g. '19.2.0' or more
const minCommitLength= 7; // typical short git commit hash length

// Add minimum length requirement to validate output variables
if (!codeName || codeName.length < minCodeNameLength) {
core.setFailed(`Invalid codeName: "${codeName}"`);
return;
}
if (!version || version.length < minVersionLength) {
core.setFailed(`Invalid version: "${version}"`);
return;
}
if (!commit || commit.length < minCommitLength) {
core.setFailed(`Invalid commit: "${commit}"`);
return;
}

core.setOutput('codeName', codeName);
core.setOutput('version', version);
core.setOutput('commit', commit);
core.info(`codeName=${codeName}`);
core.info(`version=${version}`);
core.info(`commit=${commit}`);

# Verify commit exists in the repo and print commit message first line
- name: Verify commit exists
id: verify
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_TOKEN }}
script: |
const target = '${{ steps.snap.outputs.commit }}'.slice(0, 7); // 7-char prefix
core.info(`Looking for a commit starting with "${target}" ...`);
const commits = await github.paginate(
github.rest.repos.listCommits,
{
owner: 'skoech',
repo: 'skoech/microceph',
per_page: 100
}
);
const hit = commits.find(c => c.sha.startsWith(target));
if (hit) {
core.info(`Found commit: ${hit.sha} - ${hit.html_url}`);
// Print first line of the commit message
const firstLine = hit.commit.message.split('\n')[0];
core.info(`Commit message first line: "${firstLine}"`);
core.setOutput('full_sha', hit.sha); // Output full SHA
} else {
core.setFailed(`No commit starting with "${target}" found in the repository`);
}
# Create or update tag pointing to the verified commit
# If the tag doesn't exist, create it
# If the tag exists, but it points to a different commit, update it (move it forward)
# If it already exists but points to the correct commit, do nothing
- name: Create or update stable tag for verified commit
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_TOKEN }}
script: |
const commitSha = '${{ steps.verify.outputs.full_sha }}';
const codeName = '${{ steps.snap.outputs.codeName }}';
const version = '${{ steps.snap.outputs.version }}';
const stableTag = `v${version}+${codeName}`;
core.info(`Proposed stable tag: ${stableTag}`);

// Get existing tags
const tags = await github.paginate(
github.rest.repos.listTags,
{
owner: 'skoech',
repo: 'skoech/microceph',
per_page: 100
}
);

const existingTag = tags.find(t => t.name === stableTag);

if (existingTag) {
core.info(`Tag "${stableTag}" already exists.`);
if (existingTag.commit.sha === commitSha) {
core.info(`It already points to the correct commit (${commitSha}). Nothing to do.`);
return;
} else {
core.info(`Tag "${stableTag}" points to a different commit (${existingTag.commit.sha}). Updating to ${commitSha}...`);
await github.rest.git.updateRef({
owner: 'skoech',
repo: 'skoech/microceph',
ref: `tags/${stableTag}`,
sha: commitSha,
force: true
});
core.info(`Tag "${stableTag}" updated to point to ${commitSha}.`);
return;
}
}

// Create tag if it doesn't exist
core.info(`Creating new tag "${stableTag}" pointing to commit ${commitSha}`);
await github.rest.git.createRef({
owner: 'skoech',
repo: 'skoech/microceph',
ref: `refs/tags/${stableTag}`,
sha: commitSha
});
core.info(`Tag "${stableTag}" created successfully.`);

Loading