Skip to content

ubuntu 25.04 SEV version 3.0-0 #39

ubuntu 25.04 SEV version 3.0-0

ubuntu 25.04 SEV version 3.0-0 #39

name: Update Certification Matrix
on:
issues:
types: [opened]
concurrency:
group: update-certification-matrix
cancel-in-progress: false
jobs:
update-matrix:
if: contains(github.event.issue.labels.*.name, 'certificate') && github.event.issue.milestone != null
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Update README
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
// Get issue details
const issue = context.payload.issue;
const milestone = issue.milestone.title;
const readmePath = 'README.md';
/// Find distribution label (format: "os-<string>", "os-<string>-<number>", or "os-<string>-<string>")
const distroLabel = issue.labels.find(label =>
label.name.match(/^os-[a-zA-Z]+(?:-[a-zA-Z0-9.]+)?$/i)
);
if (!distroLabel) {
console.log('Could not find distribution label in format "os-<string>", "os-<string>-<number>", or "os-<string>-<string>"');
return;
}
// Convert label to OS name (e.g., "os-ubuntu-25.04" to "Ubuntu 25.04" or "os-debian-forky" to "Debian forky")
const labelParts = distroLabel.name.split('-');
if (labelParts.length < 2) {
console.log('Distribution label does not have expected format "os-<string>" or "os-<string>-<number>"');
return;
}
const os = labelParts[1];
const version = labelParts.length > 2 ? labelParts.slice(2).join('-') : '';
// Capitalize first letter of OS and version (if version is alphabetical)
const capitalize = str => str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
const osName = version ? `${capitalize(os)} ${capitalize(version)}` : capitalize(os);
const osPattern = version ? new RegExp(`^\\|\\s*${os}\\s+${version}\\s*\\|`, 'i') : new RegExp(`^\\|\\s*${os}\\s*\\|`, 'i');
// Use a single branch for all certification matrix updates
const branchName = `update-certification-matrix`;
const commitMessage = `Update certification matrix for ${osName}`;
// Get the default branch
const { data: repo } = await github.rest.repos.get({
owner: context.repo.owner,
repo: context.repo.repo
});
const defaultBranch = repo.default_branch;
// Get the SHA of the default branch
const { data: refData } = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${defaultBranch}`
});
const baseSha = refData.object.sha;
// Check if branch already exists
let branchExists = false;
try {
await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${branchName}`
});
branchExists = true;
console.log(`Branch ${branchName} already exists, will fast-forward to main`);
} catch (error) {
if (error.status !== 404) throw error;
console.log(`Creating new branch ${branchName}`);
}
// Create or update branch to point to latest main
if (!branchExists) {
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/heads/${branchName}`,
sha: baseSha
});
console.log(`Created branch ${branchName} from main`);
} else {
// Try to rebase the branch onto main
try {
await github.rest.git.updateRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${branchName}`,
sha: baseSha,
force: false
});
console.log(`Rebased ${branchName} onto main`);
} catch (error) {
// If update fails, it means branch is ahead or diverged - this is fine
console.log(`Branch ${branchName} is already up to date or ahead of main`);
}
}
// Get current file content from the branch (after fast-forward)
const { data: fileData } = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: readmePath,
ref: branchName
});
const content = Buffer.from(fileData.content, 'base64').toString('utf8');
// Create new table row content
const newStatus = '✅'; // Assuming new certification issues indicate passing
const certificationCell = `[${milestone}](${issue.html_url})`;
const newRow = `| ${osName} | ${newStatus} | ${certificationCell} |`;
// Replace existing row or add new row
const lines = content.split('\n');
let tableStart = -1;
let tableEnd = -1;
let updated = false;
// Find the table boundaries
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes('| OS | Status | Certification Level |')) {
tableStart = i;
} else if (tableStart !== -1 && lines[i].trim() === '') {
tableEnd = i;
break;
}
}
if (tableStart === -1) {
console.log('Could not find certification matrix table');
return;
}
// Update existing row or add new row
for (let i = tableStart + 2; i < tableEnd; i++) {
if (osPattern.test(lines[i])) {
// Extract existing OS name from the current line
const existingName = lines[i].match(/^\|\s*([^|]+?)\s*\|/)[1];
// Create new row with existing OS name
const updatedRow = `| ${existingName} | ${newStatus} | ${certificationCell} |`;
lines[i] = updatedRow;
updated = true;
break;
}
}
if (!updated) {
// Add new row before table end
lines.splice(tableEnd, 0, newRow);
}
// Commit changes to the branch
await github.rest.repos.createOrUpdateFileContents({
owner: context.repo.owner,
repo: context.repo.repo,
path: readmePath,
message: commitMessage,
content: Buffer.from(lines.join('\n')).toString('base64'),
sha: fileData.sha,
branch: branchName
});
// Check if PR already exists for this branch
const { data: existingPRs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
head: `${context.repo.owner}:${branchName}`
});
if (existingPRs.length > 0) {
console.log(`PR already exists: #${existingPRs[0].number}`);
// Add a comment to the existing PR
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existingPRs[0].number,
body: `Updated certification matrix for **${osName}** (${milestone}) - refs #${issue.number}`
});
} else {
// Create new PR
const { data: pr } = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `Update certification matrix`,
head: branchName,
base: defaultBranch,
body: `This PR automatically updates the certification matrix based on certification issues.\n\nStarted with: **${osName}** (${milestone}) - refs #${issue.number}`
});
console.log(`Created PR #${pr.number}`);
}
close-duplicates:
if: contains(github.event.issue.labels.*.name, 'certificate') && github.event.issue.milestone != null
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Close older certification issues with same OS label
uses: actions/github-script@v7
with:
script: |
const issue = context.payload.issue;
const milestone = issue.milestone.title;
// Find the OS label (format: "os-<string>", "os-<string>-<number>", or "os-<string>-<string>")
const osLabel = issue.labels.find(label =>
label.name.match(/^os-[a-zA-Z]+(?:-[a-zA-Z0-9.]+)?$/i)
);
if (!osLabel) {
console.log('No OS label found, skipping duplicate check.');
return;
}
// Get all open issues in the same milestone
const { data: allIssues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
milestone: issue.milestone.number,
per_page: 100
});
for (const other of allIssues) {
if (other.number === issue.number) continue;
// Check if other issue has the same two labels
const hasCertLabel = other.labels.some(l => l.name === 'certificate');
const hasSameOsLabel = other.labels.some(l => l.name === osLabel.name);
if (hasCertLabel && hasSameOsLabel) {
console.log(`Closing older duplicate issue #${other.number} for ${osLabel.name}`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: other.number,
body: `Closing as duplicate of #${issue.number} (newer certification issue for **${osLabel.name}** on milestone **${milestone}**).`
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: other.number,
state: 'closed'
});
}
}