Files
helm-charts/.github/workflows/publish-helm-chart.yml
T
jklein 6b0d8c9232 ytdlp-web-player: new chart with an optional oauth2-proxy subchart (1.0.0+upv1.2.0)
Deploys matszwe02/ytdlp_web_player, a Flask app that fetches videos with
yt-dlp, transcodes them with ffmpeg and serves them as HLS. It ships no
authentication of any kind, so the chart carries a preconfigured oauth2-proxy
as an optional subchart, in the data path in front of the app.

This is the first chart in the repo with a dependency, so the rule "charts
have no dependencies" falls here. It is replaced in CLAUDE.md and README.md by
a bar: a dependency has to be optional and off by default, upstream's chart is
consumed as it comes (documented where it falls short of our conventions,
never patched), and the price is written down - above all that a subchart is
invisible to everything downstream watching Helm release versions, so a new
proxy release only reaches a cluster when this chart is republished.

CI learns the dependency in five places, not the three the plan expected:
helm lint, package, template and install all exit 1 while charts/ is unfilled,
so ci/helm-dependencies.sh runs before lint and package in validate-charts.yml,
before package in publish-helm-chart.yml, and inside run-install-test.sh,
run-trivy-config.sh and run-quantity-render-test.sh - the last of which renders
every chart on every PR and would have gone red first.

Notable chart decisions, each measured rather than assumed:

- The image has no USER and runs as root by default, but not by necessity: it
  runs under an arbitrary uid with readOnlyRootFilesystem and the full
  hardening baseline. Only the pinned uid is a deviation, and it is documented.
- Two emptyDirs, each justified separately. /data holds the video cache
  (DATA_PATH). /tmp is NOT mounted - measured across metadata extraction, HLS
  transcoding, sprite generation and audio extraction, the image never writes
  there. What it does write is $HOME/.cache, so /cache is mounted with HOME and
  XDG_CACHE_HOME pointed at it; without it yt-dlp re-downloads its JavaScript
  challenge-solver from github.com on every extraction.
- The proxy is wired through config.configFile, the only value the subchart
  passes through tpl - config.upstreams is emitted with toJson and would carry
  a literal {{ .Release.Name }} into the manifest.
- Rendering aborts when this chart's Ingress and the proxy's claim the same
  host: ingress-nginx rejects the second one and a Fleet bundle then sits in
  ErrApplied until forceSyncGeneration is bumped.

Fixes #214
2026-08-30 17:34:49 +02:00

116 lines
5.5 KiB
YAML

name: Publish Helm Chart with patched Application Version
on:
workflow_dispatch:
inputs:
chart-location:
required: false
type: string
default: "."
description: "location of the helm chart directory"
chart-name:
required: true
type: string
description: "name of the helm chart"
workflow_call:
inputs:
chart-location:
required: false
type: string
default: "."
description: "location of the helm chart directory"
chart-name:
required: true
type: string
description: "name of the helm chart"
outputs:
chart-version:
description: "the version of the published helm chart"
value: ${{ jobs.publish-helm-chart.outputs.chart-version }}
secrets:
OP_CONNECT_TOKEN:
required: true
jobs:
publish-helm-chart:
outputs:
chart-version: ${{ steps.get-chart-version.outputs.CHART_VERSION }}
runs-on: ubuntu-latest
steps:
- name: "Checkout Repository"
uses: actions/checkout@v7
- name: "Install Helm"
uses: azure/setup-helm@v5
- name: "Configure OP-Connect"
uses: 1password/load-secrets-action/configure@v5
with:
connect-host: https://op-connect.jk-dev.de
# helm package exits 1 with "found in Chart.yaml, but missing in charts/
# directory" while a declared dependency has not been fetched, so this
# comes first. It is a no-op for every chart without a dependencies:
# block; ci/helm-dependencies.sh explains why it resolves from Chart.yaml
# instead of a committed Chart.lock. Note the consequence for the
# published artifact: the subchart .tgz is packaged INTO ours, so the
# version pinned in Chart.yaml is the version operators get, and nothing
# downstream ever sees the dependency again.
- name: "Fetch chart dependencies"
run: ${{ github.workspace }}/ci/helm-dependencies.sh "${{ inputs.chart-name }}"
working-directory: ${{ inputs.chart-location }}
- name: "Package Helm Chart"
run: helm package ${{ inputs.chart-name }}
working-directory: ${{ inputs.chart-location }}
- name: "Get Helm Chart Version"
id: get-chart-version
run: |-
CHART_VERSION=$(grep '^version:' ${{ inputs.chart-name}}/Chart.yaml | awk '{print $2}')
echo $CHART_VERSION
echo "CHART_VERSION=$CHART_VERSION" >> $GITHUB_OUTPUT
working-directory: ${{ inputs.chart-location }}
- name: "Load Helm Registry Credentials"
id: load-jk-cr-creds
uses: 1password/load-secrets-action@v5
env:
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
JK_CR_TOKEN: op://GitHub-Actions/jk-registry-access/password
JK_CR_USERNAME: op://GitHub-Actions/jk-registry-access/username
JK_CR_CHARTS_LOCATION: op://GitHub-Actions/jk-registry-access/charts-location
JK_CR_HELM_REGISTRY: op://GitHub-Actions/jk-registry-access/helm-registry
# The values are mapped into each step's own env block and read as quoted
# shell variables - never interpolated into the command line.
#
# Two separate failures made this necessary. First, load-secrets-action v5
# (bumped in #140) no longer exports the loaded values as environment
# variables for later steps the way v4 did; only the step outputs carry
# them. Every "${{ env.JK_CR_* }}" reference therefore resolved to an empty
# string from that bump on, with nothing reporting an error - the loading
# step logs "Populating variable" and stays green either way.
#
# Second, an empty value collapsed into a missing argument instead of an
# error: with an empty username, "-u" swallowed the following
# "--password-stdin", helm stopped reading the password from stdin,
# prompted interactively and died on the runner with "inappropriate ioctl
# for device" - a terminal error for what was really an unset variable.
#
# The ":?" guards fail loudly and name the value that is missing, quoting
# keeps an empty one from shifting arguments, and the token no longer
# appears in the process list.
- name: "Login to JK-Registry"
env:
JK_CR_TOKEN: ${{ steps.load-jk-cr-creds.outputs.JK_CR_TOKEN }}
JK_CR_USERNAME: ${{ steps.load-jk-cr-creds.outputs.JK_CR_USERNAME }}
JK_CR_HELM_REGISTRY: ${{ steps.load-jk-cr-creds.outputs.JK_CR_HELM_REGISTRY }}
run: |-
: "${JK_CR_HELM_REGISTRY:?empty - check op://GitHub-Actions/jk-registry-access/helm-registry}"
: "${JK_CR_USERNAME:?empty - check op://GitHub-Actions/jk-registry-access/username}"
: "${JK_CR_TOKEN:?empty - check op://GitHub-Actions/jk-registry-access/password}"
printf '%s' "$JK_CR_TOKEN" | helm registry login "$JK_CR_HELM_REGISTRY" -u "$JK_CR_USERNAME" --password-stdin
- name: "Push Helm Chart to JK-Container Registry"
env:
JK_CR_CHARTS_LOCATION: ${{ steps.load-jk-cr-creds.outputs.JK_CR_CHARTS_LOCATION }}
run: |-
: "${JK_CR_CHARTS_LOCATION:?empty - check op://GitHub-Actions/jk-registry-access/charts-location}"
helm push "${{ inputs.chart-name }}"-*.tgz "$JK_CR_CHARTS_LOCATION"
working-directory: ${{ inputs.chart-location }}
- name: "Logout from JK-Registry"
env:
JK_CR_HELM_REGISTRY: ${{ steps.load-jk-cr-creds.outputs.JK_CR_HELM_REGISTRY }}
run: helm registry logout "$JK_CR_HELM_REGISTRY"