ci: More release rework again (#386)

This commit is contained in:
Bernd Schorgers 2025-02-12 16:52:43 +01:00
parent 89be81f5b3
commit 77ab223e7e
No known key found for this signature in database
GPG key ID: BC5E2BD907F9A8EC
6 changed files with 298 additions and 498 deletions

View file

@ -0,0 +1,243 @@
---
name: "Chart: Release"
on:
workflow_call:
inputs:
chart:
description: >
Json encoded list of Helm charts to release.
Defaults to releasing everything.
required: true
type: string
createGithubRelease:
description: >
Should the chart be published as a GitHub release
default: false
required: false
type: boolean
publishToGhPages:
description: >
Should the charts be published to GitHub Pages.
default: false
required: false
type: boolean
deployGhPages:
description: >
Should the GitHub pages repo be deployed.
default: false
required: false
type: boolean
publishToOciRegistry:
description: >
Should the charts be published to an OCI registry.
default: false
required: false
type: boolean
helmVersion:
description: >
Helm version to use.
default: "3.11.2"
required: false
type: string
jobs:
release-chart:
name: Release chart
runs-on: ubuntu-22.04
permissions:
pages: write
id-token: write
contents: write
packages: write
steps:
# ----------------------------
# Setup
# ----------------------------
- name: Checkout source branch
uses: actions/checkout@v4
with:
path: src
- name: Install Helm
uses: azure/setup-helm@v4
with:
version: ${{ inputs.helmVersion }}
- name: Login to OCI Registry
if: ${{ inputs.publishToOciRegistry }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
# ----------------------------
# Collect chart metadata
# ----------------------------
- name: Get chart details
id: chart-details
uses: bjw-s/helm-charts-actions/get-chart-details@main
with:
path: src/charts/${{ inputs.chart }}
validateChartYaml: true
requireChangelog: true
- name: Store chart folder
id: chart-folder
shell: bash
env:
CHART_DIR: "${{ inputs.chart }}"
run: |
TARGET_DIR=$(basename $(dirname ${CHART_DIR}))
echo "chart_folder=${TARGET_DIR}" >> "$GITHUB_OUTPUT"
- name: Format changelog
id: format-changelog
uses: actions/github-script@v7
with:
script: |
let input = '${{ steps.chart-details.outputs.changes }}';
let changelog = "## Changelog:";
let inputParsed = JSON.parse(input);
var changelogGrouped = inputParsed.reduce((result, currentValue) => {
(result[currentValue['kind']] = result[currentValue['kind']] || []).push(currentValue);
return result;
}, {});
for (const key in changelogGrouped) {
changelog = changelog + `\n### ${key[0].toUpperCase() + key.slice(1)}`;
let entries = changelogGrouped[key];
entries.forEach(function (entry) {
changelog = changelog + `\n- ${entry.description}`;
if ('links' in entry) {
entry.links.forEach(function (link) {
changelog = changelog + `\n - [${link.name}](${link.url})`;
});
}
});
changelog = changelog + `\n`;
}
core.setOutput('changelog', changelog);
# ----------------------------
# Package Helm chart
# ----------------------------
- name: Dereference JSON schema before packaging
uses: bjw-s/helm-charts-actions/dereference-json-schema@main
with:
schemaFile: "src/charts/${{ inputs.chart }}/values.schema.json"
outputFile: "src/charts/${{ inputs.chart }}/values.schema.json"
allowFileNotFound: true
- name: Package Helm Chart
id: package-chart
shell: bash
env:
CHART_DIR: "src/charts/${{ inputs.chart }}"
TARGET_DIR: "${{ runner.temp }}/charts_out"
run: |
mkdir -p "${TARGET_DIR}"
helm package "${CHART_DIR}" --dependency-update --destination "${TARGET_DIR}"
echo "result=$(ls ${TARGET_DIR}/*.tgz)" >> "$GITHUB_OUTPUT"
# ----------------------------
# Add chart to GitHub Pages
# ----------------------------
- name: Checkout gh-pages branch
uses: actions/checkout@v4
if: ${{ inputs.publishToGhPages }}
with:
path: gh-pages
ref: gh-pages
- name: Copy package to gh-pages structure
id: copy-package
if: ${{ inputs.publishToGhPages }}
shell: bash
env:
CHART_DIR: "${{ inputs.chart }}"
CHART_FOLDER: ${{ steps.chart-folder.outputs.chart_folder }}
PACKAGE_FILE: ${{ steps.package-chart.outputs.result }}
run: |
TARGET_DIR=$(dirname ${CHART_DIR})
cp "${PACKAGE_FILE}" "gh-pages/${CHART_FOLDER}/"
- name: Update repository
if: ${{ inputs.publishToGhPages }}
shell: bash
working-directory: gh-pages
run: |
git pull
- name: Update Helm chart index
if: ${{ inputs.publishToGhPages && inputs.deployGhPages }}
shell: bash
working-directory: gh-pages
run: |
helm repo index . --url https://bjw-s.github.io/helm-charts/
- name: Commit Changes
if: ${{ inputs.publishToGhPages }}
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "feat: Add Chart package ${{ steps.chart-folder.outputs.chart_folder }}/${{ steps.chart-details.outputs.name }}-${{ steps.chart-details.outputs.version }}"
repository: gh-pages
branch: gh-pages
file_pattern: "index.yaml **/*.tgz"
commit_user_name: github-actions[bot]
commit_user_email: 41898282+github-actions[bot]@users.noreply.github.com
commit_author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- name: Deploy changes to GitHub Pages
if: ${{ inputs.publishToGhPages && inputs.deployGhPages }}
uses: ./src/.github/actions/publish-folder-to-pages
with:
path: gh-pages/
deleteArtifactAfterPublish: true
# ----------------------------
# Create GitHub release
# ----------------------------
- name: Create tag
if: ${{ inputs.createGithubRelease }}
uses: EndBug/latest-tag@latest
with:
ref: ${{ steps.chart-details.outputs.name }}-${{ steps.chart-details.outputs.version }}
git-directory: src
- name: Create release for tag
uses: ncipollo/release-action@v1
if: ${{ inputs.createGithubRelease }}
with:
allowUpdates: true
tag: ${{ steps.chart-details.outputs.name }}-${{ steps.chart-details.outputs.version }}
body: ${{ steps.format-changelog.outputs.changelog }}
# ----------------------------
# Publish chart to bjw-s OCI registry
# ----------------------------
- name: Install Cosign
if: ${{ inputs.publishToOciRegistry }}
uses: sigstore/cosign-installer@v3.8.0
- name: Push Helm charts to OCI registry
if: ${{ inputs.publishToOciRegistry }}
shell: bash
env:
PACKAGE_FILE: ${{ steps.package-chart.outputs.result }}
CHART_NAME: ${{ steps.chart-details.outputs.name }}
CHART_VERSION: ${{ steps.chart-details.outputs.version }}
CHART_TAG_BASE: ghcr.io/bjw-s/helm
CHART_TAG: ${{ steps.chart-details.outputs.name }}:${{ inputs.steps.chart-details.outputs.version }}
run: |
helm push "${PACKAGE_FILE}" oci://${CHART_TAG_BASE} &> push-metadata.txt
CHART_DIGEST=$(awk '/Digest: /{print $2}' push-metadata.txt)
cosign sign --yes "${CHART_TAG_BASE}/${CHART_TAG}@${CHART_DIGEST}"
cosign verify "${CHART_TAG_BASE}/${CHART_TAG}@${CHART_DIGEST}" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--certificate-identity "https://github.com/${{ github.repository }}/.github/workflows/chart-release-steps.yaml@${{ github.ref }}"

View file

@ -1,256 +0,0 @@
---
name: "Charts: Release"
on:
workflow_call:
inputs:
charts:
description: >
Json encoded list of Helm charts to release.
Defaults to releasing everything.
default: "[]"
required: false
type: string
excludedChartsRelease:
description: >
Json encoded list of Helm charts to exclude from release.
default: "[]"
required: false
type: string
publishToGhPages:
description: >
Should the charts be published to GitHub Pages.
default: true
required: false
type: boolean
ghPagesBranch:
description: >
Target branch for GitHub Pages.
default: "gh-pages"
required: false
type: string
publishToOciRegistry:
description: >
Should the charts be published to an OCI registry.
default: true
required: false
type: boolean
ociRegistry:
description: >
Target OCI registry for Helm charts.
default: "ghcr.io"
required: false
type: string
helmVersion:
description: >
Helm version to use.
default: "3.11.2"
required: false
type: string
jobs:
package-charts:
name: Package charts
runs-on: ubuntu-22.04
strategy:
matrix:
charts: ${{ fromJSON(inputs.charts) }}
fail-fast: false
steps:
- name: Checkout source branch
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
uses: actions/checkout@v4
with:
path: src
fetch-depth: 0
- name: Package Helm charts
uses: ./src/.github/actions/charts-package
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
with:
rootFolder: src/charts
chartFolder: ${{ matrix.charts }}
artifactPrefix: chart__
helmVersion: ${{ inputs.helmVersion }}
tag-charts:
name: Tag charts
runs-on: ubuntu-22.04
needs:
- package-charts
strategy:
matrix:
charts: ${{ fromJSON(inputs.charts) }}
fail-fast: false
steps:
- name: Checkout source branch
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
uses: actions/checkout@v4
- name: Grab chart details
id: chart-details
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
shell: bash
env:
ROOT_DIR: charts
CHART_DIR: "${{ matrix.charts }}"
run: |
PARENT_DIR=$(basename $(dirname "${ROOT_DIR}/${CHART_DIR}"))
echo "name=$(yq '.name' ${ROOT_DIR}/${CHART_DIR}/Chart.yaml)" >> "$GITHUB_OUTPUT"
echo "version=$(yq '.version' ${ROOT_DIR}/${CHART_DIR}/Chart.yaml)" >> "$GITHUB_OUTPUT"
echo "changelog=$(yq '.annotations["artifacthub.io/changes"]' ${ROOT_DIR}/${CHART_DIR}/Chart.yaml | yq -o=json -I=0 '.' | sed 's/\\n/ /g')" >> "$GITHUB_OUTPUT"
- name: Format changelog
id: changelog
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
uses: actions/github-script@v7
with:
script: |
let input = '${{ steps.chart-details.outputs.changelog }}';
let changelog = "## Changelog:";
if (input === 'null') {
changelog = changelog + "\nNo changelog provided.";
} else {
let inputParsed = JSON.parse(input);
var changelogGrouped = inputParsed.reduce((result, currentValue) => {
(result[currentValue['kind']] = result[currentValue['kind']] || []).push(currentValue);
return result;
}, {});
for (const key in changelogGrouped) {
changelog = changelog + `\n### ${key[0].toUpperCase() + key.slice(1)}`;
let entries = changelogGrouped[key];
entries.forEach(function (entry) {
changelog = changelog + `\n- ${entry.description}`;
if ('links' in entry) {
entry.links.forEach(function (link) {
changelog = changelog + `\n - [${link.name}](${link.url})`;
});
}
});
changelog = changelog + `\n`;
}
}
console.log(changelog);
core.setOutput('changelog', changelog);
- name: Create tag
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
uses: EndBug/latest-tag@latest
with:
ref: ${{ steps.chart-details.outputs.name }}-${{ steps.chart-details.outputs.version }}
- uses: ncipollo/release-action@v1
if: ${{ !contains(fromJSON(inputs.excludedChartsRelease), matrix.charts) }}
with:
allowUpdates: true
tag: ${{ steps.chart-details.outputs.name }}-${{ steps.chart-details.outputs.version }}
body: ${{ steps.changelog.outputs.changelog }}
release-charts-to-github-pages:
name: Release charts to GitHub Pages
runs-on: ubuntu-22.04
if: ${{ inputs.publishToGhPages }}
needs:
- package-charts
steps:
- name: Checkout source branch
uses: actions/checkout@v4
with:
path: src
- name: Checkout gh-pages branch
uses: actions/checkout@v4
with:
token: ${{ github.token }}
path: gh-pages
ref: ${{ inputs.ghPagesBranch }}
fetch-depth: 0
- name: Prepare artifacts for release to GitHub Pages
uses: ./src/.github/actions/charts-release-ghpages
with:
artifactPattern: "*"
artifactPrefix: chart__
targetFolder: gh-pages
targetBranch: gh-pages
- name: Publish changes to GitHub Pages
uses: ./src/.github/actions/publish-folder-to-pages
with:
path: gh-pages/
prepare-release-charts-to-oci:
name: Prepare releasing charts to OCI registry
runs-on: ubuntu-22.04
if: ${{ inputs.publishToOciRegistry }}
needs:
- package-charts
outputs:
artifacts: ${{ steps.artifacts.outputs.artifacts }}
steps:
- name: List artifacts
id: list
uses: yakubique/list-artifacts@v1.1
with:
name: chart__*
- name: Rewrite artifacts output
id: artifacts
shell: bash
env:
JQ_COMMAND: |-
[.[] | {artifact_name: .name, chart_name: (.name | split("__")[-2]), chart_version: (.name | split("__")[-1]) }]
run: |
echo '${{ steps.list.outputs.result }}' | jq -c -r "$JQ_COMMAND" > artifacts
echo "artifacts=$(cat artifacts)" >> "$GITHUB_OUTPUT"
release-charts-to-oci:
name: Release charts to OCI registry
runs-on: ubuntu-22.04
if: ${{ inputs.publishToOciRegistry && needs.prepare-release-charts-to-oci.outputs.artifacts != '[]' }}
strategy:
matrix:
artifacts: ${{ fromJSON(needs.prepare-release-charts-to-oci.outputs.artifacts) }}
fail-fast: false
needs:
- package-charts
- prepare-release-charts-to-oci
env:
TARGET_REGISTRY: ghcr.io
steps:
- name: Download chart artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: ${{ matrix.artifacts.artifact_name }}
- name: Checkout source branch
uses: actions/checkout@v4
with:
path: src
- name: Release chart to OCI registry
uses: ./src/.github/actions/charts-release-oci
with:
workingDir: artifacts/${{ matrix.artifacts.artifact_name }}
chartName: ${{ matrix.artifacts.chart_name }}
chartVersion: ${{ matrix.artifacts.chart_version }}
cleanup-charts-artifacts:
name: Clean up artifacts
runs-on: ubuntu-22.04
needs:
- package-charts
- release-charts-to-github-pages
- release-charts-to-oci
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
steps:
- name: Clean up artifact
uses: joernott/rm-artifact@v1
with:
name: "*"
useGlob: true
failOnError: true

View file

@ -22,12 +22,12 @@ jobs:
name: Prepare data required for workflow
runs-on: ubuntu-22.04
outputs:
repoConfiguration: ${{ steps.repo-config.outputs.config }}
libraryChartsToRelease: |-
${{ github.event_name == 'workflow_dispatch' && steps.specified-charts.outputs.libraryChartsToRelease || steps.changed-library-charts.outputs.all_changed_files }}
otherChartsToRelease: |-
${{ github.event_name == 'workflow_dispatch' && steps.specified-charts.outputs.otherChartsToRelease || steps.changed-charts.outputs.all_changed_files }}
libraryChartsToRelease: ${{ steps.filtered-charts.outputs.libraryChartsToRelease }}
otherChartsToRelease: ${{ steps.filtered-charts.outputs.otherChartsToRelease }}
steps:
# ----------------------------
# Setup
# ----------------------------
- name: Checkout
uses: actions/checkout@v4
with:
@ -69,7 +69,8 @@ jobs:
uses: actions/github-script@v7
with:
script: |
let input = '${{ github.event.inputs.charts }}';
const fs = require('fs');
let input = '${{ inputs.charts }}';
let cwd = process.cwd();
let tmpCharts = []
@ -77,28 +78,43 @@ jobs:
console.log("Empty charts input, scanning for charts in repository");
const globber = await glob.create('charts/*/*', { implicitDescendants: false });
for await (const file of globber.globGenerator()) {
relativePath = file.slice(`${cwd}/charts/`.length);
tmpCharts.push(relativePath);
if (fs.lstatSync(file).isDirectory()) {
relativePath = file.slice(`${cwd}/charts/`.length);
tmpCharts.push(relativePath);
}
}
} else {
const fs = require('fs');
tmpCharts = JSON.parse(input);
tmpCharts.forEach(function (chart) {
if (!fs.existsSync(`${cwd}/charts/${chart}`)) {
const fullPath = `${cwd}/charts/${chart}`;
if (!fs.existsSync(fullPath)) {
core.setFailed(`Chart ${chart} does not exist in repository`);
process.exit(1);
}
if (!fs.lstatSync(fullPath).isDirectory()) {
core.setFailed(`${chart} is not a valid directory`);
process.exit(1);
}
});
}
let libraryCharts = tmpCharts.filter(chart => chart.startsWith('library/'));
let otherCharts = tmpCharts.filter(chart => !chart.startsWith('library/'));
console.log("Library charts:")
console.log(JSON.stringify(libraryCharts));
core.setOutput('libraryChartsToRelease', JSON.stringify(libraryCharts));
console.log("Other charts:")
console.log(JSON.stringify(otherCharts));
let otherCharts = tmpCharts.filter(chart => !chart.startsWith('library/'));
core.setOutput('otherChartsToRelease', JSON.stringify(otherCharts));
- name: Filter out excluded charts
id: filtered-charts
uses: actions/github-script@v7
with:
script: |
let libraryChartsInput = ${{ github.event_name == 'workflow_dispatch' && steps.specified-charts.outputs.libraryChartsToRelease || steps.changed-library-charts.outputs.all_changed_files }};
let otherChartsInput = ${{ github.event_name == 'workflow_dispatch' && steps.specified-charts.outputs.otherChartsToRelease || steps.changed-charts.outputs.all_changed_files }};
let excludedFromRelease = ${{ steps.repo-config.outputs.config }}['excluded-charts-release'];
let libraryCharts = libraryChartsInput.filter(item => excludedFromRelease.indexOf(item) < 0);
core.setOutput('libraryChartsToRelease', JSON.stringify(libraryCharts));
let otherCharts = otherChartsInput.filter(item => excludedFromRelease.indexOf(item) < 0);
core.setOutput('otherChartsToRelease', JSON.stringify(otherCharts));
release-library-charts:
@ -106,17 +122,18 @@ jobs:
needs:
- prepare
if: ${{ needs.prepare.outputs.libraryChartsToRelease != '[]' && needs.prepare.outputs.libraryChartsToRelease != '' }}
uses: ./.github/workflows/charts-release-steps.yaml
permissions:
pages: write
id-token: write
contents: write
packages: write
strategy:
matrix:
chart: ${{ fromJSON(needs.prepare.outputs.libraryChartsToRelease) }}
fail-fast: false
max-parallel: 1
uses: ./.github/workflows/chart-release-steps.yaml
with:
charts: ${{ needs.prepare.outputs.libraryChartsToRelease }}
excludedChartsRelease: ${{ toJSON(fromJSON(needs.prepare.outputs.repoConfiguration).excluded-charts-release) }}
ghPagesBranch: gh-pages
chart: ${{ matrix.chart }}
createGithubRelease: true
publishToGhPages: true
publishToOciRegistry: false
deployGhPages: true
release-other-charts:
name: Release other charts
@ -126,17 +143,18 @@ jobs:
if: |-
${{
!cancelled() &&
(needs.prepare.result == 'skipped' || needs.prepare.result == 'success') &&
(needs.release-library-charts.result == 'skipped' || needs.release-library-charts.result == 'success') &&
needs.prepare.outputs.otherChartsToRelease != '[]'
!contains(needs.*.result, 'failure') &&
(needs.prepare.outputs.otherChartsToRelease != '[]' && needs.prepare.outputs.otherChartsToRelease != '')
}}
uses: ./.github/workflows/charts-release-steps.yaml
permissions:
pages: write
id-token: write
contents: write
packages: write
strategy:
matrix:
chart: ${{ fromJSON(needs.prepare.outputs.otherChartsToRelease) }}
fail-fast: false
max-parallel: 1
uses: ./.github/workflows/chart-release-steps.yaml
with:
charts: ${{ needs.prepare.outputs.otherChartsToRelease }}
excludedChartsRelease: ${{ toJSON(fromJSON(needs.prepare.outputs.repoConfiguration).excluded-charts-release) }}
ghPagesBranch: gh-pages
chart: ${{ matrix.chart }}
createGithubRelease: true
publishToGhPages: true
publishToOciRegistry: true
deployGhPages: true