ci: added helm cosign verification and renovate app workflow to bump chart versions (#2064)

* ci: added helm cosign verification and renovate app workflow to bump chart versions

* docs: add helm artifacts verification

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>

* fix: update app id

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>

* docs: add documentation link in helm chart and seerr docs

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>

---------

Signed-off-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
Co-authored-by: Ludovic Ortega <ludovic.ortega@adminafk.fr>
This commit is contained in:
Joe Harrison
2025-10-19 04:22:28 +01:00
committed by GitHub
parent a975ab25c3
commit 082ba3d037
8 changed files with 376 additions and 34 deletions

View File

@@ -55,7 +55,7 @@ jobs:
# get current version # get current version
current_version=$(grep '^version:' "$chart_path/Chart.yaml" | awk '{print $2}') current_version=$(grep '^version:' "$chart_path/Chart.yaml" | awk '{print $2}')
# try to get current release version # try to get current release version
if oras manifest fetch "ghcr.io/${GITHUB_REPOSITORY@L}/${chart_name}:${current_version}" >/dev/null 2>&1; then if oras manifest fetch "ghcr.io/${{ github.repository }}/${chart_name}:${current_version}" >/dev/null 2>&1; then
echo "No version change for $chart_name. Skipping." echo "No version change for $chart_name. Skipping."
else else
helm dependency build "$chart_path" helm dependency build "$chart_path"
@@ -87,8 +87,8 @@ jobs:
name: Publish to ghcr.io name: Publish to ghcr.io
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: permissions:
packages: write # needed for pushing to github registry packages: write
id-token: write # needed for signing the images with GitHub OIDC Token id-token: write
needs: [package-helm-chart] needs: [package-helm-chart]
if: needs.package-helm-chart.outputs.has_artifacts == 'true' if: needs.package-helm-chart.outputs.has_artifacts == 'true'
steps: steps:
@@ -128,17 +128,59 @@ jobs:
# push chart to OCI # push chart to OCI
chart_release_file=$(basename "$chart_path") chart_release_file=$(basename "$chart_path")
chart_name=${chart_release_file%-*} chart_name=${chart_release_file%-*}
helm push ${chart_path} oci://ghcr.io/${GITHUB_REPOSITORY@L} |& tee helm-push-output.log helm push ${chart_path} oci://ghcr.io/${{ github.repository }} |& tee helm-push-output.log
chart_digest=$(awk -F "[, ]+" '/Digest/{print $NF}' < helm-push-output.log) chart_digest=$(awk -F "[, ]+" '/Digest/{print $NF}' < helm-push-output.log)
# sign chart # sign chart
cosign sign "ghcr.io/${GITHUB_REPOSITORY@L}/${chart_name}@${chart_digest}" cosign sign "ghcr.io/${{ github.repository }}/${chart_name}@${chart_digest}"
# push artifacthub-repo.yml to OCI # push artifacthub-repo.yml to OCI
oras push \ oras push \
ghcr.io/${GITHUB_REPOSITORY@L}/${chart_name}:artifacthub.io \ ghcr.io/${{ github.repository }}/${chart_name}:artifacthub.io \
--config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \ --config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \
charts/$chart_name/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml \ charts/$chart_name/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml \
|& tee oras-push-output.log |& tee oras-push-output.log
artifacthub_digest=$(grep "Digest:" oras-push-output.log | awk '{print $2}') artifacthub_digest=$(grep "Digest:" oras-push-output.log | awk '{print $2}')
# sign artifacthub-repo.yml # sign artifacthub-repo.yml
cosign sign "ghcr.io/${GITHUB_REPOSITORY@L}/${chart_name}:artifacthub.io@${artifacthub_digest}" cosign sign "ghcr.io/${{ github.repository }}/${chart_name}:artifacthub.io@${artifacthub_digest}"
done
verify:
name: Verify signatures for each chart tag
needs: [publish]
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
- name: Downloads artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: artifacts
path: .cr-release-packages/
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Verify signatures for each chart tag
run: |
for chart_path in $(find .cr-release-packages -name '*.tgz' -print); do
chart_release_file=$(basename "$chart_path")
chart_name=${chart_release_file%-*}
version=${chart_release_file#$chart_name-}
version=${version%.tgz}
cosign verify "ghcr.io/${{ github.repository }}/${chart_name}:${version}" \
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
done done

View File

@@ -1,3 +1,4 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Seerr Release name: Seerr Release

View File

@@ -0,0 +1,181 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Renovate Helm Hooks
on:
pull_request:
branches:
- develop
paths:
- 'charts/**'
permissions: {}
concurrency:
group: renovate-helm-hooks-${{ github.ref }}
cancel-in-progress: true
jobs:
renovate-post-run:
name: Renovate Bump Chart Version
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
if: github.actor == 'renovate[bot]'
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: app-token
with:
app-id: 2138788
private-key: ${{ secrets.APP_SEERR_HELM_PRIVATE_KEY }}
- name: Set up chart-testing
uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0
- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed="$(ct list-changed --target-branch ${TARGET_BRANCH})"
if [[ -n "$changed" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
echo "changed_list=${changed//$'\n'/ }" >> "$GITHUB_OUTPUT"
fi
env:
TARGET_BRANCH: ${{ github.event.repository.default_branch }}
- name: Bump chart version
if: steps.list-changed.outputs.changed == 'true'
env:
CHART: ${{ steps.list-changed.outputs.changed_list }}
run: |
if [[ ! -d "${CHART}" ]]; then
echo "${CHART} directory not found"
exit 0
fi
# Extract current appVersion and chart version from Chart.yaml
APP_VERSION=$(grep -e "^appVersion:" "$CHART/Chart.yaml" | cut -d ":" -f 2 | tr -d '[:space:]' | tr -d '"')
CHART_VERSION=$(grep -e "^version:" "$CHART/Chart.yaml" | cut -d ":" -f 2 | tr -d '[:space:]' | tr -d '"')
# Extract major, minor and patch versions of appVersion
APP_MAJOR_VERSION=$(printf '%s' "$APP_VERSION" | cut -d "." -f 1)
APP_MINOR_VERSION=$(printf '%s' "$APP_VERSION" | cut -d "." -f 2)
APP_PATCH_VERSION=$(printf '%s' "$APP_VERSION" | cut -d "." -f 3)
# Extract major, minor and patch versions of chart version
CHART_MAJOR_VERSION=$(printf '%s' "$CHART_VERSION" | cut -d "." -f 1)
CHART_MINOR_VERSION=$(printf '%s' "$CHART_VERSION" | cut -d "." -f 2)
CHART_PATCH_VERSION=$(printf '%s' "$CHART_VERSION" | cut -d "." -f 3)
# Get previous appVersion from the base commit of the pull request
BASE_COMMIT=$(git merge-base origin/main HEAD)
PREV_APP_VERSION=$(git show "$BASE_COMMIT":"$CHART/Chart.yaml" | grep -e "^appVersion:" | cut -d ":" -f 2 | tr -d '[:space:]' | tr -d '"')
# Extract major, minor and patch versions of previous appVersion
PREV_APP_MAJOR_VERSION=$(printf '%s' "$PREV_APP_VERSION" | cut -d "." -f 1)
PREV_APP_MINOR_VERSION=$(printf '%s' "$PREV_APP_VERSION" | cut -d "." -f 2)
PREV_APP_PATCH_VERSION=$(printf '%s' "$PREV_APP_VERSION" | cut -d "." -f 3)
# Check if the major, minor, or patch version of appVersion has changed
if [[ "$APP_MAJOR_VERSION" != "$PREV_APP_MAJOR_VERSION" ]]; then
# Bump major version of the chart and reset minor and patch versions to 0
CHART_MAJOR_VERSION=$((CHART_MAJOR_VERSION+1))
CHART_MINOR_VERSION=0
CHART_PATCH_VERSION=0
elif [[ "$APP_MINOR_VERSION" != "$PREV_APP_MINOR_VERSION" ]]; then
# Bump minor version of the chart and reset patch version to 0
CHART_MINOR_VERSION=$((CHART_MINOR_VERSION+1))
CHART_PATCH_VERSION=0
elif [[ "$APP_PATCH_VERSION" != "$PREV_APP_PATCH_VERSION" ]]; then
# Bump patch version of the chart
CHART_PATCH_VERSION=$((CHART_PATCH_VERSION+1))
fi
# Update the chart version in Chart.yaml
CHART_NEW_VERSION="${CHART_MAJOR_VERSION}.${CHART_MINOR_VERSION}.${CHART_PATCH_VERSION}"
sed -i "s/^version:.*/version: ${CHART_NEW_VERSION}/" "$CHART/Chart.yaml"
- name: Ensure documentation is updated
if: steps.list-changed.outputs.changed == 'true'
uses: docker://jnorwood/helm-docs:v1.14.2@sha256:7e562b49ab6b1dbc50c3da8f2dd6ffa8a5c6bba327b1c6335cc15ce29267979c
- name: Commit changes
if: steps.list-changed.outputs.changed == 'true'
env:
CHART: ${{ steps.list-changed.outputs.changed_list }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
GITHUB_HEAD_REF: ${{ github.head_ref }}
run: |
# Define the target directory
TARGET_DIR="$CHART"
# Fetch deleted files in the target directory
DELETED_FILES=$(git diff --diff-filter=D --name-only HEAD -- "$TARGET_DIR")
# Fetch added/modified files in the target directory
MODIFIED_FILES=$(git diff --diff-filter=ACM --name-only HEAD -- "$TARGET_DIR")
# Create a temporary file for JSON output
FILE_CHANGES_JSON_FILE=$(mktemp)
# Initialize JSON structure in the file
echo '{ "deletions": [], "additions": [] }' > "$FILE_CHANGES_JSON_FILE"
# Add deletions
for file in $DELETED_FILES; do
jq --arg path "$file" '.deletions += [{"path": $path}]' "$FILE_CHANGES_JSON_FILE" > "$FILE_CHANGES_JSON_FILE.tmp"
mv "$FILE_CHANGES_JSON_FILE.tmp" "$FILE_CHANGES_JSON_FILE"
done
# Add additions (new or modified files)
for file in $MODIFIED_FILES; do
BASE64_CONTENT=$(base64 -w 0 <"$file") # Encode file content
jq --arg path "$file" --arg content "$BASE64_CONTENT" \
'.additions += [{"path": $path, "contents": $content}]' "$FILE_CHANGES_JSON_FILE" > "$FILE_CHANGES_JSON_FILE.tmp"
mv "$FILE_CHANGES_JSON_FILE.tmp" "$FILE_CHANGES_JSON_FILE"
done
# Create a temporary file for the final JSON payload
JSON_PAYLOAD_FILE=$(mktemp)
# Construct the final JSON using jq and store it in a file
jq -n --arg repo "$GITHUB_REPOSITORY" \
--arg branch "$GITHUB_HEAD_REF" \
--arg message "fix: post upgrade changes from renovate" \
--arg expectedOid "$GITHUB_SHA" \
--slurpfile fileChanges "$FILE_CHANGES_JSON_FILE" \
'{
query: "mutation ($input: CreateCommitOnBranchInput!) {
createCommitOnBranch(input: $input) {
commit {
url
}
}
}",
variables: {
input: {
branch: {
repositoryNameWithOwner: $repo,
branchName: $branch
},
message: { headline: $message },
fileChanges: $fileChanges[0],
expectedHeadOid: $expectedOid
}
}
}' > "$JSON_PAYLOAD_FILE"
# Call GitHub API
curl https://api.github.com/graphql -f \
-sSf -H "Authorization: Bearer $GITHUB_TOKEN" \
--data "@$JSON_PAYLOAD_FILE"
# Clean up temporary files
rm "$FILE_CHANGES_JSON_FILE" "$JSON_PAYLOAD_FILE"

View File

@@ -20,6 +20,10 @@ Seerr helm chart for Kubernetes
Kubernetes: `>=1.23.0-0` Kubernetes: `>=1.23.0-0`
## Installation
Refer to [https://docs.seerr.dev/getting-started/kubernetes](Seerr kubernetes documentation)
## Update Notes ## Update Notes
### Updating to 3.0.0 ### Updating to 3.0.0

View File

@@ -14,11 +14,15 @@
{{ template "chart.requirementsSection" . }} {{ template "chart.requirementsSection" . }}
## Installation
Refer to [https://docs.seerr.dev/getting-started/kubernetes](Seerr kubernetes documentation)
## Update Notes ## Update Notes
### Updating to 3.0.0 ### Updating to 3.0.0
Nothing change we just rebranded `jellyseerr` helm-chart to `seerr` :) Nothing has changed; we just rebranded the `jellyseerr` Helm chart to `seerr` 🥳.
### Updating to 2.7.0 ### Updating to 2.7.0

View File

@@ -15,6 +15,12 @@ Refer to [Configuring Databases](/extending-jellyseerr/database-config#postgresq
An alternative Docker image is available on Docker Hub for this project. You can find it at [Docker Hub Repository Link](https://hub.docker.com/r/seerr/seerr) An alternative Docker image is available on Docker Hub for this project. You can find it at [Docker Hub Repository Link](https://hub.docker.com/r/seerr/seerr)
::: :::
:::info
All official Seerr images are cryptographically signed and include a verified [Software Bill of Materials (SBOM)](https://cyclonedx.org/).
To confirm that the container image you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-jellyseerr/advanced/verifying-signed-artifacts#verifying-signed-images) guide.
:::
## Unix (Linux, macOS) ## Unix (Linux, macOS)
:::warning :::warning
Be sure to replace `/path/to/appdata/config` in the below examples with a valid host directory path. If this volume mount is not configured correctly, your Jellyseerr settings/data will not be persisted when the container is recreated (e.g., when updating the image or rebooting your machine). Be sure to replace `/path/to/appdata/config` in the below examples with a valid host directory path. If this volume mount is not configured correctly, your Jellyseerr settings/data will not be persisted when the container is recreated (e.g., when updating the image or rebooting your machine).
@@ -72,11 +78,6 @@ Finally, run the container with the same parameters originally used to create th
```bash ```bash
docker run -d ... docker run -d ...
``` ```
:::info
All official Seerr images are cryptographically signed and include a verified [Software Bill of Materials (SBOM)](https://cyclonedx.org/).
To confirm that the container image you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-jellyseerr/advanced/verifying-signed-artifacts) guide.
:::
:::tip :::tip
You may alternatively use a third-party updating mechanism, such as [Watchtower](https://github.com/containrrr/watchtower) or [Ouroboros](https://github.com/pyouroboros/ouroboros), to keep Jellyseerr up-to-date automatically. You may alternatively use a third-party updating mechanism, such as [Watchtower](https://github.com/containrrr/watchtower) or [Ouroboros](https://github.com/pyouroboros/ouroboros), to keep Jellyseerr up-to-date automatically.

View File

@@ -8,6 +8,12 @@ sidebar_position: 5
This method is not recommended for most users. It is intended for advanced users who are using Kubernetes. This method is not recommended for most users. It is intended for advanced users who are using Kubernetes.
::: :::
:::info
All official Seerr charts are cryptographically signed and include a verified [Software Bill of Materials (SBOM)](https://cyclonedx.org/).
To confirm that the chart you are using is authentic and unmodified, please refer to the [Verifying Signed Artifacts](/using-jellyseerr/advanced/verifying-signed-artifacts#verifying-signed-helm-charts) guide.
:::
## Installation ## Installation
```console ```console
helm install jellyseerr oci://ghcr.io/fallenbagel/jellyseerr/jellyseerr-chart helm install jellyseerr oci://ghcr.io/fallenbagel/jellyseerr/jellyseerr-chart

View File

@@ -12,6 +12,7 @@ import TabItem from '@theme/TabItem';
These artifacts are cryptographically signed using [Sigstore Cosign](https://docs.sigstore.dev/quickstart/quickstart-cosign/): These artifacts are cryptographically signed using [Sigstore Cosign](https://docs.sigstore.dev/quickstart/quickstart-cosign/):
- Container images - Container images
- Helm charts
This ensures that the images you pull are authentic, tamper-proof, and built by the official Seerr release pipeline. This ensures that the images you pull are authentic, tamper-proof, and built by the official Seerr release pipeline.
@@ -27,19 +28,11 @@ You will need the following tools installed:
To verify images: To verify images:
- [Docker](https://docs.docker.com/get-docker/) **or** - [Docker](https://docs.docker.com/get-docker/) **or** [Podman](https://podman.io/getting-started/installation) (including [Skopeo](https://github.com/containers/skopeo/blob/main/install.md))
- [Podman](https://podman.io/getting-started/installation) (including [Skopeo](https://github.com/containers/skopeo/blob/main/install.md))
--- ---
# Verifying Signed Images ## Verifying Signed Images
All Seerr container images published to GitHub Container Registry (GHCR) are cryptographically signed using [Sigstore Cosign](https://docs.sigstore.dev/quickstart/quickstart-cosign/).
This ensures that the images you pull are authentic, tamper-proof, and built by the official Seerr release pipeline.
Each image also includes a CycloneDX SBOM (Software Bill of Materials) attestation, generated with [Trivy](https://aquasecurity.github.io/trivy/), providing transparency about all dependencies included in the image.
---
### Image Locations ### Image Locations
@@ -227,17 +220,6 @@ This confirms that the image was:
--- ---
### Troubleshooting
| Issue | Likely Cause | Suggested Fix |
|-------|---------------|----------------|
| `no matching signatures` | Incorrect digest or tag | Retrieve the digest again using Docker or Skopeo |
| `certificate identity does not match expected` | Workflow reference changed | Ensure your `--certificate-identity` matches this documentation |
| `cosign: command not found` | Cosign not installed | Install Cosign from the official release |
| `certificate expired` | Old release | Verify a newer tag or digest |
---
### Example: Full Verification Flow ### Example: Full Verification Flow
<Tabs groupId="verify-examples"> <Tabs groupId="verify-examples">
@@ -269,6 +251,127 @@ cosign verify ghcr.io/seerr-team/seerr@"$DIGEST" \
</TabItem> </TabItem>
</Tabs> </Tabs>
## Verifying Signed Helm charts
### Helm Chart Locations
Official Seerr helm charts are available from:
- GitHub Container Registry (GHCR): `ghcr.io/seerr-team/seerr/seerr-chart/seerr-chart:<tag>`
You can view all available tags on the [Seerr Releases page](https://github.com/seerr-team/seerr/pkgs/container/seerr%2Fseerr-chart).
---
### Verifying a Specific Release Tag
Each tagged release (for example `3.0.0`) is immutable and cryptographically signed.
Verification should always be performed using the image digest (SHA256).
#### Retrieve the Helm Chart Digest
<Tabs groupId="verify-methods">
<TabItem value="docker" label="Docker">
```bash
docker buildx imagetools inspect ghcr.io/seerr-team/seerr/seerr-chart:3.0.0 --format '{{json .Manifest.Digest}}' | tr -d '"'
```
</TabItem>
<TabItem value="podman" label="Podman / Skopeo">
```bash
skopeo inspect docker://ghcr.io/seerr-team/seerr/seerr-chart:3.0.0 --format '{{.Digest}}'
```
</TabItem>
</Tabs>
Example output:
```
sha256:abcd1234...
```
---
#### Verify the Helm Chart Signature
```bash
cosign verify ghcr.io/seerr-team/seerr/seerr-chart@sha256:abcd1234... \
--certificate-identity "https://github.com/seerr-team/seerr/.github/workflows/helm.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
```
:::info Successful Verification Example
Verification for `ghcr.io/seerr-team/seerr/seerr-chart@sha256:abcd1234...`
The following checks were performed:
- Cosign claims validated
- Signatures verified against the transparency log
- Certificate issued by Fulcio to the expected workflow identity
:::
---
### Expected Certificate Identity
The expected certificate identity for all signed Seerr images is:
```
https://github.com/seerr-team/seerr/.github/workflows/helm.yml@refs/heads/main
```
This confirms that the image was:
- Built by the official Seerr Release workflow
- Produced from the seerr-team/seerr repository
- Signed using GitHubs OIDC identity via Sigstore Fulcio
---
### Example: Full Verification Flow
<Tabs groupId="verify-examples">
<TabItem value="docker" label="Docker">
```bash
DIGEST=$(docker buildx imagetools inspect ghcr.io/seerr-team/seerr/seerr-chart:3.0.0 --format '{{json .Manifest.Digest}}' | tr -d '"')
cosign verify ghcr.io/seerr-team/seerr/seerr-chart@"$DIGEST" \
--certificate-identity-regexp "https://github.com/seerr-team/seerr/.github/workflows/helm.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
cosign verify-attestation ghcr.io/seerr-team/seerr/seerr-chart@"$DIGEST" \
--type cyclonedx \
--certificate-identity-regexp "https://github.com/seerr-team/seerr/.github/workflows/helm.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
```
</TabItem>
<TabItem value="podman" label="Podman / Skopeo">
```bash
DIGEST=$(skopeo inspect docker://ghcr.io/seerr-team/seerr/seerr-chart:3.0.0 --format '{{.Digest}}')
cosign verify ghcr.io/seerr-team/seerr/seerr-chart@"$DIGEST" \
--certificate-identity-regexp "https://github.com/seerr-team/seerr/.github/workflows/helm.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
```
</TabItem>
</Tabs>
---
## Troubleshooting
| Issue | Likely Cause | Suggested Fix |
|-------|---------------|----------------|
| `no matching signatures` | Incorrect digest or tag | Retrieve the digest again using Docker or Skopeo |
| `certificate identity does not match expected` | Workflow reference changed | Ensure your `--certificate-identity` matches this documentation |
| `cosign: command not found` | Cosign not installed | Install Cosign from the official release |
| `certificate expired` | Old release | Verify a newer tag or digest |
--- ---
## Further Reading ## Further Reading