admission-controller should have access to get replicationcontrollers
#191
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Bump Chart Version | |
on: | |
pull_request: | |
types: [labeled, unlabeled, opened, edited, synchronize] | |
# Permission forced by repo-level setting; only elevate on job-level | |
permissions: | |
pull-requests: write | |
contents: write | |
# packages: read | |
jobs: | |
bump-chart-version: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Check out repository | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
- name: Extract all chart label information and update Chart.yaml and CHANGELOG.md | |
id: update_charts | |
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | |
with: | |
script: | | |
// Based on script from: https://github.com/DataDog/k8s-datadog-agent-ops/blob/main/.github/workflows/automatically-bump.yaml | |
const fs = require('fs'); | |
const pr = context.payload.pull_request; | |
if (!pr) { | |
core.setFailed("No pull request found in context payload."); | |
return; | |
} | |
const { data: labels } = await github.rest.issues.listLabelsOnIssue({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: pr.number | |
}); | |
const labelNames = labels.map(l => l.name); | |
// Gather all file changes and individual commit messages. | |
const fileChanges = []; | |
const commitMessages = []; | |
// Helper to parse a semver string (e.g., "1.2.3") into an object {major, minor, patch} | |
function parseVersion(versionStr) { | |
const parts = versionStr.split('.'); | |
return { | |
major: parseInt(parts[0], 10), | |
minor: parseInt(parts[1], 10), | |
patch: parseInt(parts[2], 10) | |
}; | |
} | |
// Helper to produce a semver string | |
function makeVersion({ major, minor, patch }) { | |
return `${major}.${minor}.${patch}`; | |
} | |
// Get the list of charts that need a version bump (or changelog update) | |
const chartsToBump = []; | |
for (const label of labelNames) { | |
const match = label.match(/^(?<chartName>[^/]+)\/(?<versionType>minor-version|patch-version|no-version-bump)$/); | |
if (match) { | |
chartsToBump.push({ | |
chartName: match.groups.chartName, | |
bumpType: match.groups.versionType | |
}); | |
} | |
} | |
if (chartsToBump.length === 0) { | |
core.info("No charts to bump found in labels."); | |
return; | |
} | |
// Compare the base and head branches to find their merge base | |
const comparison = await github.rest.repos.compareCommits({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
base: pr.base.ref, | |
head: pr.head.ref | |
}); | |
// Use the merge_base_commit SHA | |
const mergeBaseSHA = comparison.data.merge_base_commit.sha; | |
for (const info of chartsToBump) { | |
const { chartName, bumpType } = info; | |
core.info(`Examining '${chartName}' for a ${bumpType} update…`); | |
// Get the base Chart.yaml (from the PR base branch) | |
let baseChartFile; | |
try { | |
baseChartFile = await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: `charts/${chartName}/Chart.yaml`, | |
ref: mergeBaseSHA | |
}); | |
} catch (error) { | |
core.setFailed(`Could not get base Chart.yaml for ${chartName}: ${error.message}`); | |
return; | |
} | |
const baseContent = Buffer.from(baseChartFile.data.content, baseChartFile.data.encoding).toString(); | |
const baseVersionMatch = baseContent.match(/^version:\s+(\S+)/m); | |
if (!baseVersionMatch) { | |
core.setFailed(`No 'version:' found in base branch Chart.yaml for ${chartName}. Skipping…`); | |
return; | |
} | |
const baseVersion = baseVersionMatch[1].trim(); | |
core.info(`Base version for '${chartName}' is '${baseVersion}'.`); | |
// Read the PR Chart.yaml on the PR head branch. | |
let prChartFile; | |
try { | |
prChartFile = await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: `charts/${chartName}/Chart.yaml`, | |
ref: pr.head.ref | |
}); | |
} catch (error) { | |
core.setFailed(`Could not get PR Chart.yaml for ${chartName}: ${error.message}`); | |
return; | |
} | |
const prContent = Buffer.from(prChartFile.data.content, prChartFile.data.encoding).toString(); | |
const prVersionMatch = prContent.match(/^version:\s+(\S+)/m); | |
if (!prVersionMatch) { | |
core.setFailed(`No 'version:' found in PR Chart.yaml for ${chartName}. Skipping…`); | |
return; | |
} | |
const prVersion = prVersionMatch[1].trim(); | |
core.info(`PR version for '${chartName}' is '${prVersion}'.`); | |
// Calculate the desired version based on bump type. | |
const baseParsed = parseVersion(baseVersion); | |
let desiredParsed = { ...baseParsed }; | |
if (bumpType === 'patch-version') { | |
desiredParsed.patch += 1; | |
} else if (bumpType === 'minor-version') { | |
desiredParsed.minor += 1; | |
desiredParsed.patch = 0; | |
} | |
const desiredVersion = makeVersion(desiredParsed); | |
core.info(`Desired version for '${chartName}' is '${desiredVersion}'.`); | |
// If the Chart.yaml version is not what we expect, update it. | |
if (prVersion !== desiredVersion) { | |
core.info(`For '${chartName}', base was '${baseVersion}' but PR had '${prVersion}'. Changing version to '${desiredVersion}'.`); | |
const newChartContent = prContent.replace( | |
/^version:\s+\S+/m, | |
`version: ${desiredVersion}` | |
); | |
// Replace file content locally so that helm-docs.sh script can properly update READMEs | |
fs.writeFileSync(`charts/${chartName}/Chart.yaml`, newChartContent, 'utf8'); | |
fileChanges.push({ | |
path: `charts/${chartName}/Chart.yaml`, | |
content: newChartContent | |
}); | |
commitMessages.push(`bump version for ${chartName} to ${desiredVersion} (${bumpType})`); | |
} else { | |
core.info(`'${chartName}' version is already correct ('${prVersion}').`); | |
} | |
// Unless the bump type is no-version-bump, prepare CHANGELOG update. | |
if (bumpType === 'no-version-bump') { | |
core.info(`Skipping CHANGELOG update for '${chartName}' (no-version-bump).`); | |
continue; | |
} | |
// Get base and head CHANGELOG.md files. | |
let baseChangelog, prChangelog; | |
try { | |
baseChangelog = await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: `charts/${chartName}/CHANGELOG.md`, | |
ref: mergeBaseSHA | |
}); | |
} catch (error) { | |
core.setFailed(`Could not get base CHANGELOG.md for ${chartName}: ${error.message}`); | |
return; | |
} | |
try { | |
prChangelog = await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: `charts/${chartName}/CHANGELOG.md`, | |
ref: pr.head.ref | |
}); | |
} catch (error) { | |
core.setFailed(`Could not get head CHANGELOG.md for ${chartName}: ${error.message}`); | |
return; | |
} | |
// Get the changelog content and check if it has already been modified in this branch. | |
let newChangelogContent; | |
const changelogContent = Buffer.from(prChangelog.data.content, prChangelog.data.encoding).toString(); | |
const lines = changelogContent.split('\n'); | |
const versionHeaderIdx = lines.findIndex(line => line.trim().startsWith('##')); | |
if (prChangelog.data.sha !== baseChangelog.data.sha) { | |
core.info(`CHANGELOG.md for '${chartName}' has already been modified in this branch. Updating the latest changelog entry version.`); | |
// Update the version header to the desired version. | |
if (versionHeaderIdx !== -1) { | |
lines[versionHeaderIdx] = `## ${desiredVersion}`; | |
newChangelogContent = lines.join('\n'); | |
// Check for diff between newChangelogContent and HEAD prChangelog content | |
if (newChangelogContent !== Buffer.from(prChangelog.data.content, prChangelog.data.encoding).toString()) { | |
fileChanges.push({ | |
path: `charts/${chartName}/CHANGELOG.md`, | |
content: newChangelogContent | |
}); | |
commitMessages.push(`update changelog version for ${chartName} to ${desiredVersion}`); | |
// Replace file content locally so that helm-docs.sh script can properly update READMEs | |
fs.writeFileSync(`charts/${chartName}/CHANGELOG.md`, newChangelogContent, 'utf8'); | |
} else { | |
core.info(`CHANGELOG.md for '${chartName}' is already updated with latest entry version.`) | |
} | |
} | |
continue; | |
} | |
// If the changelog has not been modified, add a new entry. | |
const prLink = `https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${pr.number}`; | |
const newEntry = `## ${desiredVersion} \n\n* ${pr.title} ([#${pr.number}](${prLink})).\n\n`; | |
if (versionHeaderIdx !== -1) { | |
const headerSection = lines.slice(0, versionHeaderIdx).join('\n').trimEnd(); | |
const remaining = lines.slice(versionHeaderIdx).join('\n'); | |
newChangelogContent = `${headerSection}\n\n${newEntry}${remaining}`; | |
} else { | |
newChangelogContent = newEntry + changelogContent; | |
} | |
// Also replace file content locally so that helm-docs.sh script can properly update READMEs | |
fs.writeFileSync(`charts/${chartName}/CHANGELOG.md`, newChangelogContent, 'utf8'); | |
fileChanges.push({ | |
path: `charts/${chartName}/CHANGELOG.md`, | |
content: newChangelogContent | |
}); | |
commitMessages.push(`update changelog for ${chartName} with version ${desiredVersion}`); | |
core.info(`CHANGELOG.md for '${chartName}' updated with a new entry for version ${desiredVersion}.`); | |
} | |
core.info("Done checking all labeled charts."); | |
// Update README.mds using the .github/helm-docs.sh script | |
try { | |
await exec.exec('/bin/bash', ['.github/helm-docs.sh']); | |
} catch (exitCode) { | |
// Do nothing | |
// Exit code 1 from the helm-docs.sh script when there is a diff is OK | |
} | |
// Get head README.md file | |
for (const chart of chartsToBump) { | |
let prReadme; | |
try { | |
prReadme = await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: `charts/${chart.chartName}/README.md`, | |
ref: pr.head.ref | |
}); | |
} catch (error) { | |
core.setFailed(`Could not get head README.md for ${chart.chartName}: ${error.message}`); | |
return; | |
} | |
// Compare local README.md file with HEAD to check if it has been modified by helm-docs.sh | |
const localReadmeContent = fs.readFileSync(`charts/${chart.chartName}/README.md`, { encoding: 'utf-8' }); | |
if (localReadmeContent !== Buffer.from(prReadme.data.content, prReadme.data.encoding).toString()) { | |
fileChanges.push({ | |
path: `charts/${chart.chartName}/README.md`, | |
content: localReadmeContent | |
}); | |
commitMessages.push(`update readme for ${chart.chartName}`); | |
} | |
} | |
core.info("Done updating README.md files."); | |
// If no file changes were collected, nothing to commit. | |
if (fileChanges.length === 0) { | |
core.info("No file changes to commit."); | |
return; | |
} | |
// Get the current commit of the PR head branch. | |
core.info("Getting current commit of the PR head branch…"); | |
let branchData; | |
try { | |
const response = await github.rest.repos.getBranch({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
branch: pr.head.ref | |
}); | |
branchData = response.data; | |
} catch (error) { | |
core.setFailed(`Could not get branch data: ${error.message}`); | |
return; | |
} | |
const baseCommitSha = branchData.commit.sha; | |
const baseTreeSha = branchData.commit.commit.tree.sha; | |
core.info(`Base commit for branch '${pr.head.ref}' is ${baseCommitSha}`); | |
// Prepare tree entries from each file change. | |
// (Mode "100644" means a normal non‐executable file.) | |
const treeItems = fileChanges.map(change => ({ | |
path: change.path, | |
mode: "100644", | |
type: "blob", | |
content: change.content | |
})); | |
// Create a new tree with these modifications. | |
core.info("Creating new tree with modified files…"); | |
let newTreeResponse; | |
try { | |
const response = await github.rest.git.createTree({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
tree: treeItems, | |
base_tree: baseTreeSha | |
}); | |
newTreeResponse = response; | |
} catch (error) { | |
core.setFailed(`Could not create new tree: ${error.message}`); | |
return; | |
} | |
const newTreeSha = newTreeResponse.data.sha; | |
core.info(`Created new tree ${newTreeSha}`); | |
// Create a combined commit message. | |
const commitMessage = `chore: update charts\n\n` + | |
commitMessages.map(msg => `- ${msg}`).join('\n'); | |
// Create a new commit object. | |
core.info("Creating new commit object…"); | |
let newCommitResponse; | |
try { | |
const response = await github.rest.git.createCommit({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
message: commitMessage, | |
tree: newTreeSha, | |
parents: [baseCommitSha] | |
}); | |
newCommitResponse = response; | |
} catch (error) { | |
core.setFailed(`Could not create new commit: ${error.message}`); | |
return; | |
} | |
const newCommitSha = newCommitResponse.data.sha; | |
core.info(`Created new commit ${newCommitSha}`); | |
// Update the head branch reference to point to the new commit. | |
core.info(`Updating branch reference to point to new commit ${newCommitSha}…`); | |
try { | |
await github.rest.git.updateRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: `heads/${pr.head.ref}`, | |
sha: newCommitSha | |
}); | |
core.info(`Branch '${pr.head.ref}' has been updated with a combined commit.`); | |
} catch (error) { | |
core.setFailed(`Could not update branch reference: ${error.message}`); | |
return; | |
} |