Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
facd2a7675
|
||
|
|
f3ad3a9844 | ||
|
|
b614b53602
|
||
|
|
a12983fbea
|
||
|
|
61e28a1459
|
||
|
|
56bed94064
|
||
|
|
5c513291a8
|
No files matched your search
@@ -66,18 +66,18 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Configure OP-Connect"
|
- name: "Configure OP-Connect"
|
||||||
uses: 1password/load-secrets-action/configure@v5
|
uses: 1password/load-secrets-action/configure@70062d7a876d3eb6334754fa26efd2fbd90c32f2 # v5
|
||||||
with:
|
with:
|
||||||
connect-host: https://op-connect.jk-dev.de
|
connect-host: https://op-connect.jk-dev.de
|
||||||
- name: "Load GitHub Credentials"
|
- name: "Load GitHub Credentials"
|
||||||
id: load-jk-cluster-bot-creds
|
id: load-jk-cluster-bot-creds
|
||||||
uses: 1password/load-secrets-action@v5
|
uses: 1password/load-secrets-action@70062d7a876d3eb6334754fa26efd2fbd90c32f2 # v5
|
||||||
env:
|
env:
|
||||||
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
JK_CLUSTER_BOT_TOKEN: op://GitHub-Actions/github-helm-charts-push-token/credential
|
JK_CLUSTER_BOT_TOKEN: op://GitHub-Actions/github-helm-charts-push-token/credential
|
||||||
JK_CLUSTER_USERNAME: op://GitHub-Actions/github-helm-charts-push-token/username
|
JK_CLUSTER_USERNAME: op://GitHub-Actions/github-helm-charts-push-token/username
|
||||||
JK_CLUSTER_EMAIL: op://GitHub-Actions/github-helm-charts-push-token/email
|
JK_CLUSTER_EMAIL: op://GitHub-Actions/github-helm-charts-push-token/email
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
token: ${{ env.JK_CLUSTER_BOT_TOKEN }}
|
token: ${{ env.JK_CLUSTER_BOT_TOKEN }}
|
||||||
- name: "Get Chart Version"
|
- name: "Get Chart Version"
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
count: ${{ steps.select.outputs.count }}
|
count: ${{ steps.select.outputs.count }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- name: "Select the charts to test"
|
- name: "Select the charts to test"
|
||||||
id: select
|
id: select
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -131,9 +131,9 @@ jobs:
|
|||||||
RUNNER_LABEL: ${{ github.server_url == 'https://github.com' && 'ubuntu-latest' || 'ubuntu-docker' }}
|
RUNNER_LABEL: ${{ github.server_url == 'https://github.com' && 'ubuntu-latest' || 'ubuntu-docker' }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- name: "Install Helm"
|
- name: "Install Helm"
|
||||||
uses: azure/setup-helm@v5
|
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5
|
||||||
- name: "Install kubectl"
|
- name: "Install kubectl"
|
||||||
run: |
|
run: |
|
||||||
# kubectl and the cluster are a pair: the client talks to the server
|
# kubectl and the cluster are a pair: the client talks to the server
|
||||||
@@ -205,13 +205,13 @@ jobs:
|
|||||||
# touches the VPN key.
|
# touches the VPN key.
|
||||||
- name: "Configure OP-Connect"
|
- name: "Configure OP-Connect"
|
||||||
if: matrix.chart == 'freesailarr'
|
if: matrix.chart == 'freesailarr'
|
||||||
uses: 1password/load-secrets-action/configure@v5
|
uses: 1password/load-secrets-action/configure@70062d7a876d3eb6334754fa26efd2fbd90c32f2 # v5
|
||||||
with:
|
with:
|
||||||
connect-host: https://op-connect.jk-dev.de
|
connect-host: https://op-connect.jk-dev.de
|
||||||
- name: "Load freesailarr VPN credentials"
|
- name: "Load freesailarr VPN credentials"
|
||||||
id: load-freesailarr-vpn
|
id: load-freesailarr-vpn
|
||||||
if: matrix.chart == 'freesailarr'
|
if: matrix.chart == 'freesailarr'
|
||||||
uses: 1password/load-secrets-action@v5
|
uses: 1password/load-secrets-action@70062d7a876d3eb6334754fa26efd2fbd90c32f2 # v5
|
||||||
env:
|
env:
|
||||||
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
FREESAILARR_WG_KEY: op://GitHub-Actions/freesailarr-mullvad-test-config/credential
|
FREESAILARR_WG_KEY: op://GitHub-Actions/freesailarr-mullvad-test-config/credential
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- name: "Install Helm"
|
- name: "Install Helm"
|
||||||
uses: azure/setup-helm@v5
|
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5
|
||||||
- name: "Configure OP-Connect"
|
- name: "Configure OP-Connect"
|
||||||
uses: 1password/load-secrets-action/configure@v5
|
uses: 1password/load-secrets-action/configure@70062d7a876d3eb6334754fa26efd2fbd90c32f2 # v5
|
||||||
with:
|
with:
|
||||||
connect-host: https://op-connect.jk-dev.de
|
connect-host: https://op-connect.jk-dev.de
|
||||||
# helm package exits 1 with "found in Chart.yaml, but missing in charts/
|
# helm package exits 1 with "found in Chart.yaml, but missing in charts/
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
working-directory: ${{ inputs.chart-location }}
|
working-directory: ${{ inputs.chart-location }}
|
||||||
- name: "Load Chart Registry Credentials"
|
- name: "Load Chart Registry Credentials"
|
||||||
id: load-registry-creds
|
id: load-registry-creds
|
||||||
uses: 1password/load-secrets-action@v5
|
uses: 1password/load-secrets-action@70062d7a876d3eb6334754fa26efd2fbd90c32f2 # v5
|
||||||
env:
|
env:
|
||||||
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||||
REGISTRY_PASSWORD: op://GitHub-Actions/jlab-registry-access/password
|
REGISTRY_PASSWORD: op://GitHub-Actions/jlab-registry-access/password
|
||||||
@@ -74,9 +74,28 @@ jobs:
|
|||||||
REGISTRY_CHARTS_URL: op://GitHub-Actions/jlab-registry-access/charts-url
|
REGISTRY_CHARTS_URL: op://GitHub-Actions/jlab-registry-access/charts-url
|
||||||
# The target is a classic, index-based Helm registry, not an OCI one, so
|
# The target is a classic, index-based Helm registry, not an OCI one, so
|
||||||
# there is no "helm registry login" session and no "helm push": the upload
|
# there is no "helm registry login" session and no "helm push": the upload
|
||||||
# is a single HTTP POST of the packaged .tgz to <charts-url>/api/charts,
|
# is a single HTTP POST of the packaged .tgz to
|
||||||
# authenticated per request. Three properties of this step are load-bearing
|
# <charts-url>/<owner>/helm/api/charts, authenticated per request.
|
||||||
# and each of them was paid for once already.
|
#
|
||||||
|
# That URL is composed, not stored (#63). charts-url holds only the base
|
||||||
|
# up to "/api/packages"; the owner segment comes from
|
||||||
|
# github.repository_owner - which Gitea does populate: measured as
|
||||||
|
# "jlab-cluster" in run 282 of validate-charts.yml, on a temporary step
|
||||||
|
# that was removed again. So no value pointing at the registry - not even
|
||||||
|
# a path segment - lives in this repository, and a second owner on the same
|
||||||
|
# instance needs no second setting. The mirror on github.com has Actions
|
||||||
|
# switched off, so the publish never runs there; if it ever did, the
|
||||||
|
# derived owner would be one the instance does not know and the upload
|
||||||
|
# would 404 rather than land somewhere wrong. Two guards make the switch
|
||||||
|
# from the old shape loud instead of puzzling: charts-url is rejected
|
||||||
|
# unless it ends in "/api/packages" (an old full URL still carrying
|
||||||
|
# "/helm", "/api/charts" or the owner is named explicitly), and the derived
|
||||||
|
# owner is rejected when empty or when it holds characters that are not a
|
||||||
|
# path segment - an empty one would build "<base>//helm/api/charts", i.e.
|
||||||
|
# another bare 404 against a masked URL.
|
||||||
|
#
|
||||||
|
# Three further properties of this step are load-bearing and each of them
|
||||||
|
# was paid for once already.
|
||||||
#
|
#
|
||||||
# 1) The values are mapped into the step's own env block and read as quoted
|
# 1) The values are mapped into the step's own env block and read as quoted
|
||||||
# shell variables - never interpolated into the command line. load-secrets-
|
# shell variables - never interpolated into the command line. load-secrets-
|
||||||
@@ -114,18 +133,49 @@ jobs:
|
|||||||
REGISTRY_PASSWORD: ${{ steps.load-registry-creds.outputs.REGISTRY_PASSWORD }}
|
REGISTRY_PASSWORD: ${{ steps.load-registry-creds.outputs.REGISTRY_PASSWORD }}
|
||||||
REGISTRY_USERNAME: ${{ steps.load-registry-creds.outputs.REGISTRY_USERNAME }}
|
REGISTRY_USERNAME: ${{ steps.load-registry-creds.outputs.REGISTRY_USERNAME }}
|
||||||
REGISTRY_CHARTS_URL: ${{ steps.load-registry-creds.outputs.REGISTRY_CHARTS_URL }}
|
REGISTRY_CHARTS_URL: ${{ steps.load-registry-creds.outputs.REGISTRY_CHARTS_URL }}
|
||||||
|
REGISTRY_OWNER: ${{ github.repository_owner }}
|
||||||
CHART_NAME: ${{ inputs.chart-name }}
|
CHART_NAME: ${{ inputs.chart-name }}
|
||||||
CHART_VERSION: ${{ steps.get-chart-version.outputs.CHART_VERSION }}
|
CHART_VERSION: ${{ steps.get-chart-version.outputs.CHART_VERSION }}
|
||||||
run: |-
|
run: |-
|
||||||
: "${REGISTRY_CHARTS_URL:?empty - check op://GitHub-Actions/jlab-registry-access/charts-url}"
|
: "${REGISTRY_CHARTS_URL:?empty - check op://GitHub-Actions/jlab-registry-access/charts-url}"
|
||||||
: "${REGISTRY_USERNAME:?empty - check op://GitHub-Actions/jlab-registry-access/username}"
|
: "${REGISTRY_USERNAME:?empty - check op://GitHub-Actions/jlab-registry-access/username}"
|
||||||
: "${REGISTRY_PASSWORD:?empty - check op://GitHub-Actions/jlab-registry-access/password}"
|
: "${REGISTRY_PASSWORD:?empty - check op://GitHub-Actions/jlab-registry-access/password}"
|
||||||
|
: "${REGISTRY_OWNER:?empty - github.repository_owner is not populated on this forge; do not hardcode an owner, report it}"
|
||||||
: "${CHART_NAME:?empty - the chart-name input did not arrive}"
|
: "${CHART_NAME:?empty - the chart-name input did not arrive}"
|
||||||
: "${CHART_VERSION:?empty - could not read version: from ${CHART_NAME}/Chart.yaml}"
|
: "${CHART_VERSION:?empty - could not read version: from ${CHART_NAME}/Chart.yaml}"
|
||||||
# 1Password masks the full URL, but a curl connection error prints only
|
# 1Password masks the full URL, but a curl connection error prints only
|
||||||
# "<host>:<port>", which that mask would not cover. Mask the host too.
|
# "<host>:<port>", which that mask would not cover. Mask the host too.
|
||||||
CHARTS_HOST="${REGISTRY_CHARTS_URL#*://}"
|
CHARTS_HOST="${REGISTRY_CHARTS_URL#*://}"
|
||||||
echo "::add-mask::${CHARTS_HOST%%/*}"
|
echo "::add-mask::${CHARTS_HOST%%/*}"
|
||||||
|
# charts-url is the base only; this workflow appends the owner, the
|
||||||
|
# package type and the endpoint. Reject the old, full shape by name so
|
||||||
|
# the failure says what to change in the vault item instead of 404ing
|
||||||
|
# against a URL the log has masked.
|
||||||
|
CHARTS_BASE="${REGISTRY_CHARTS_URL%/}"
|
||||||
|
case "$CHARTS_BASE" in
|
||||||
|
*/api/charts)
|
||||||
|
echo "::error::op://GitHub-Actions/jlab-registry-access/charts-url still ends in /api/charts - it must hold only the base up to /api/packages (https://<host>/api/packages); this workflow appends /<owner>/helm/api/charts itself"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*/helm|*/helm/*)
|
||||||
|
echo "::error::op://GitHub-Actions/jlab-registry-access/charts-url still contains the /helm path segment - it must hold only the base up to /api/packages (https://<host>/api/packages); this workflow appends /<owner>/helm/api/charts itself"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*/api/packages) ;;
|
||||||
|
*)
|
||||||
|
echo "::error::op://GitHub-Actions/jlab-registry-access/charts-url must end in /api/packages (https://<host>/api/packages) - no owner, no package type, no endpoint; this workflow appends /<owner>/helm/api/charts itself"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
# An empty owner would build "<base>//helm/api/charts", a stray "/" or
|
||||||
|
# "?" would build something else entirely - both are bare 404s.
|
||||||
|
case "$REGISTRY_OWNER" in
|
||||||
|
*[!A-Za-z0-9._-]*)
|
||||||
|
echo "::error::the derived repository owner is not usable as a URL path segment: '${REGISTRY_OWNER}'"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
CHARTS_API_URL="${CHARTS_BASE}/${REGISTRY_OWNER}/helm/api/charts"
|
||||||
case "${REGISTRY_USERNAME}${REGISTRY_PASSWORD}" in
|
case "${REGISTRY_USERNAME}${REGISTRY_PASSWORD}" in
|
||||||
*[[:space:]]*)
|
*[[:space:]]*)
|
||||||
echo "::error::username/password in op://GitHub-Actions/jlab-registry-access must not contain whitespace - curl --config truncates an unquoted value at the first space"
|
echo "::error::username/password in op://GitHub-Actions/jlab-registry-access must not contain whitespace - curl --config truncates an unquoted value at the first space"
|
||||||
@@ -139,7 +189,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
echo "uploading ${CHART_PACKAGE}"
|
echo "uploading ${CHART_PACKAGE}"
|
||||||
printf 'user = %s:%s\nurl = %s\n' \
|
printf 'user = %s:%s\nurl = %s\n' \
|
||||||
"$REGISTRY_USERNAME" "$REGISTRY_PASSWORD" "${REGISTRY_CHARTS_URL%/}/api/charts" |
|
"$REGISTRY_USERNAME" "$REGISTRY_PASSWORD" "$CHARTS_API_URL" |
|
||||||
curl --config - \
|
curl --config - \
|
||||||
--fail-with-body \
|
--fail-with-body \
|
||||||
--silent --show-error \
|
--silent --show-error \
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
any-install: ${{ steps.detect.outputs.any-install }}
|
any-install: ${{ steps.detect.outputs.any-install }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: "Detect changed charts"
|
- name: "Detect changed charts"
|
||||||
@@ -116,11 +116,11 @@ jobs:
|
|||||||
chart: ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }}
|
chart: ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }}
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: "Install Helm"
|
- name: "Install Helm"
|
||||||
uses: azure/setup-helm@v5
|
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5
|
||||||
# Both steps below refuse to run while a chart declares a dependency that
|
# Both steps below refuse to run while a chart declares a dependency that
|
||||||
# is not in its charts/ directory - lint with "[WARNING] chart directory
|
# is not in its charts/ directory - lint with "[WARNING] chart directory
|
||||||
# is missing these dependencies", package with "found in Chart.yaml, but
|
# is missing these dependencies", package with "found in Chart.yaml, but
|
||||||
@@ -167,9 +167,9 @@ jobs:
|
|||||||
# build-chart, so it adds no wall-clock time to the pipeline.
|
# build-chart, so it adds no wall-clock time to the pipeline.
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- name: "Install Helm"
|
- name: "Install Helm"
|
||||||
uses: azure/setup-helm@v5
|
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5
|
||||||
- name: "Render every chart with bare integer quantities"
|
- name: "Render every chart with bare integer quantities"
|
||||||
run: ci/run-quantity-render-test.sh
|
run: ci/run-quantity-render-test.sh
|
||||||
|
|
||||||
@@ -197,9 +197,9 @@ jobs:
|
|||||||
TRIVY_CONFIG_MODE: report
|
TRIVY_CONFIG_MODE: report
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- name: "Install Helm"
|
- name: "Install Helm"
|
||||||
uses: azure/setup-helm@v5
|
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5
|
||||||
# A plain release download rather than aquasecurity/setup-trivy, and the
|
# A plain release download rather than aquasecurity/setup-trivy, and the
|
||||||
# reason is measured on the Gitea probe PR of #225, not assumed. That
|
# reason is measured on the Gitea probe PR of #225, not assumed. That
|
||||||
# action runs actions/checkout against aquasecurity/trivy on github.com
|
# action runs actions/checkout against aquasecurity/trivy on github.com
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Additional rules for all commits, PRs, issues, and comments:
|
|||||||
|
|
||||||
- Do **not** add a `Co-Authored-By` trailer and do **not** append "Generated with Claude Code" footers.
|
- Do **not** add a `Co-Authored-By` trailer and do **not** append "Generated with Claude Code" footers.
|
||||||
- **Commits must be signed**, and in the Coder workspace they are: signing runs through its own SSH signer (`gpg.format = ssh`, `gpg.ssh.program = coder-git-ssh-sign`, `commit.gpgsign = true`), so no 1Password step is involved. Verify before opening the PR — `git log --format='%G?' -1` must print `G` — and if signing fails, stop and report rather than pushing unsigned work, because branch protection rejects it at merge time.
|
- **Commits must be signed**, and in the Coder workspace they are: signing runs through its own SSH signer (`gpg.format = ssh`, `gpg.ssh.program = coder-git-ssh-sign`, `commit.gpgsign = true`), so no 1Password step is involved. Verify before opening the PR — `git log --format='%G?' -1` must print `G` — and if signing fails, stop and report rather than pushing unsigned work, because branch protection rejects it at merge time.
|
||||||
|
- **A PR that does not finish its issue writes `Part of #n`, never `Fixes #n`.** The keyword closes the issue on merge, so a `Fixes` on a PR that leaves work behind marks an open task as done — and reopening by hand is the only repair, which only happens if someone remembers to look. This is not hypothetical: Gitea PR #48 carried `Fixes #40` while `rss-bridge` had been deliberately left out of the sweep, and the merge closed Gitea #40 with one occurrence still open. Step 8 above is the other half of the same rule: the auto-close it verifies is only the right outcome when the keyword was the right one.
|
||||||
|
|
||||||
## The wiki
|
## The wiki
|
||||||
|
|
||||||
@@ -28,6 +29,8 @@ The knowledge base is the Gitea wiki at `https://git.jk-dev.de/jlab-cluster/helm
|
|||||||
git clone ssh://git@git.jk-dev.de:30022/jlab-cluster/helm-charts.wiki.git
|
git clone ssh://git@git.jk-dev.de:30022/jlab-cluster/helm-charts.wiki.git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Its default branch is `main`, not `master`.
|
||||||
|
|
||||||
Flat layout, one Markdown file per page, page name = file name, links are relative page names without the extension (`[CI/CD](CI-CD)`). A new page must also be listed in `_Sidebar.md`, or it is reachable only by search.
|
Flat layout, one Markdown file per page, page name = file name, links are relative page names without the extension (`[CI/CD](CI-CD)`). A new page must also be listed in `_Sidebar.md`, or it is reachable only by search.
|
||||||
|
|
||||||
**There is no PR flow here, so fetch and rebase before every push and never force-push** — two agents have already edited the same page minutes apart, and a force push deletes the other edit silently, with nothing left to report it.
|
**There is no PR flow here, so fetch and rebase before every push and never force-push** — two agents have already edited the same page minutes apart, and a force push deletes the other edit silently, with nothing left to report it.
|
||||||
@@ -55,6 +58,7 @@ Everything runs on the Gitea instance JLab-Git: issues, pull requests, CI and Re
|
|||||||
|
|
||||||
- **`tea`, not `gh`.** Work happens in a **Coder workspace, and it has no `op`** — that is the normal case here, not a broken environment, so do not wait for the 1Password CLI or propose installing it. Call `tea` directly; it carries the workspace's stored login and opens PRs and issues under the **bot account**, which is accepted. Say so in the report when something is authored that way. (`op plugin run -- tea`, the older instruction, applies only where `op` actually exists.)
|
- **`tea`, not `gh`.** Work happens in a **Coder workspace, and it has no `op`** — that is the normal case here, not a broken environment, so do not wait for the 1Password CLI or propose installing it. Call `tea` directly; it carries the workspace's stored login and opens PRs and issues under the **bot account**, which is accepted. Say so in the report when something is authored that way. (`op plugin run -- tea`, the older instruction, applies only where `op` actually exists.)
|
||||||
- **The remote is `origin`, and it points at Gitea** (`ssh://git@git.jk-dev.de:30022/jlab-cluster/helm-charts.git`). The checkout has exactly that one, and so does every worktree cut from it, so a bare `git push` goes to the leading copy; the github.com mirror is fed by Gitea itself and is never pushed to by hand. A clone elsewhere may carry the mirror as a second remote named `github` — wherever more than one exists, say which one you mean.
|
- **The remote is `origin`, and it points at Gitea** (`ssh://git@git.jk-dev.de:30022/jlab-cluster/helm-charts.git`). The checkout has exactly that one, and so does every worktree cut from it, so a bare `git push` goes to the leading copy; the github.com mirror is fed by Gitea itself and is never pushed to by hand. A clone elsewhere may carry the mirror as a second remote named `github` — wherever more than one exists, say which one you mean.
|
||||||
|
- **Cut every worktree from a freshly fetched `origin/main`.** `git fetch origin main` first, then branch from `origin/main` — never from the local `main` ref, and never assume the shared checkout is current: both can be many merges behind. The staleness is invisible where it matters most, and it outlives the fix: the project-instructions block a subagent receives is a **snapshot** of the spawning session's working directory, taken when **that session started** — not a live read. Fetching mid-session does not refresh it, so the file on disk can be correct while every agent started from then on is still handed the superseded rules. Measured on 2026-09-27: an agent whose own worktree was identical to `origin/main` (`git rev-list --left-right --count origin/main...HEAD` returned `0 0`) received a block from before Gitea #47 and reported a contradiction in this file that two PRs had already fixed, quoting the stale copy as binding. Two consequences, and only these two help: the spawning session's directory must be current **before** the session starts, and an agent checks a rule against the **file in its own worktree** plus a diff against `origin/main` — never against `main`, and never against its own context block, because the first consequence cannot be enforced from inside an agent and the second can.
|
||||||
- **Branch protection with a signature requirement is active**, and signatures verify on Gitea. A commit that is not signed does not reach `main` — check before opening a PR, do not discover it at merge time.
|
- **Branch protection with a signature requirement is active**, and signatures verify on Gitea. A commit that is not signed does not reach `main` — check before opening a PR, do not discover it at merge time.
|
||||||
- **No merge queue.** Gitea has none, and its place is taken by **auto-merge on green checks**: the PR is armed once and merges itself when the required check passes **and the required review exists** — both are active on `main`, so an armed PR without a review sits there instead of merging. Rule 7 above is unchanged — the user merges, and an agent never merges its own PR or arms auto-merge on it.
|
- **No merge queue.** Gitea has none, and its place is taken by **auto-merge on green checks**: the PR is armed once and merges itself when the required check passes **and the required review exists** — both are active on `main`, so an armed PR without a review sits there instead of merging. Rule 7 above is unchanged — the user merges, and an agent never merges its own PR or arms auto-merge on it.
|
||||||
- **A required check per chart is not possible.** Gitea runs a whole matrix as one job under one status context, so the install-test reports once for all charts. What is required on `main` is `validate-charts`, as that single context; per chart is the part that cannot be had. Relevant whenever branch protection is touched.
|
- **A required check per chart is not possible.** Gitea runs a whole matrix as one job under one status context, so the install-test reports once for all charts. What is required on `main` is `validate-charts`, as that single context; per chart is the part that cannot be had. Relevant whenever branch protection is touched.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Every chart has a wiki page of its own — `https://git.jk-dev.de/jlab-cluster/h
|
|||||||
## CI/CD
|
## CI/CD
|
||||||
|
|
||||||
- `publish-<chart>.yml` — one thin workflow per chart; triggers on pushes to `main` touching `<chart>/**` (or manually) and calls the reusable workflow below. **A push to a chart directory on `main` publishes that chart**, so the chart version must be bumped in the same change.
|
- `publish-<chart>.yml` — one thin workflow per chart; triggers on pushes to `main` touching `<chart>/**` (or manually) and calls the reusable workflow below. **A push to a chart directory on `main` publishes that chart**, so the chart version must be bumped in the same change.
|
||||||
- `publish-helm-chart.yml` — reusable workflow: `helm package`, then a single HTTP `POST` of the packaged `.tgz` to the private chart registry. The target is a **classic, index-based** Helm registry (Gitea's `/api/charts` endpoint), not an OCI one: `helm push` speaks only OCI and is not used, there is no `helm registry login` session, and every request carries its own Basic auth. Credentials **and the target URL** come from 1Password Connect (`OP_CONNECT_TOKEN` secret, item `op://GitHub-Actions/jlab-registry-access/` with `username`, `password`, `charts-url`); the workflow appends `/api/charts` to `charts-url`. No registry address ever appears in this repository. The loaded values are read via **`steps.<id>.outputs.*`**, mapped into the step's own `env:` block and used as quoted shell variables — never interpolated into the command line. `load-secrets-action` **v5 no longer exports them as environment variables** for later steps the way v4 did, so after the v5 bump every `${{ env.* }}` reference resolved to an empty string while the loading step still logged `Populating variable` and stayed green. Each value is additionally guarded with `:?`, because an empty one used to collapse into a missing argument rather than an error. Three properties of the upload step are load-bearing:
|
- `publish-helm-chart.yml` — reusable workflow: `helm package`, then a single HTTP `POST` of the packaged `.tgz` to the private chart registry. The target is a **classic, index-based** Helm registry (Gitea's `/api/charts` endpoint), not an OCI one: `helm push` speaks only OCI and is not used, there is no `helm registry login` session, and every request carries its own Basic auth. Credentials **and the target URL** come from 1Password Connect (`OP_CONNECT_TOKEN` secret, item `op://GitHub-Actions/jlab-registry-access/` with `username`, `password`, `charts-url`). `charts-url` holds only the **base up to `/api/packages`**; the workflow composes the target as `<charts-url>/<owner>/helm/api/charts`, and the owner comes from **`github.repository_owner`** (#63) — so the 26 thin callers stay as they are and a second owner on the same instance needs no second setting. **No registry address ever appears in this repository**, and since #63 not even the owner path segment: it is derived from the repository rather than stored. Both halves of that composition are guarded, because the only other signal would be a 404 against a URL the log has masked: `charts-url` is rejected unless it ends in `/api/packages` (an old, full URL still carrying `/helm`, `/api/charts` or the owner is named explicitly in the error), and the derived owner is rejected when empty or when it is not a usable path segment — an empty one would build `<base>//helm/api/charts`. The loaded values are read via **`steps.<id>.outputs.*`**, mapped into the step's own `env:` block and used as quoted shell variables — never interpolated into the command line. `load-secrets-action` **v5 no longer exports them as environment variables** for later steps the way v4 did, so after the v5 bump every `${{ env.* }}` reference resolved to an empty string while the loading step still logged `Populating variable` and stayed green. Each value is additionally guarded with `:?`, because an empty one used to collapse into a missing argument rather than an error. Three properties of the upload step are load-bearing:
|
||||||
- **Credentials stay out of the process list.** `curl --user "$U:$P"` would put them in `argv`; the step uses `curl --config -` and pipes the options in on stdin instead.
|
- **Credentials stay out of the process list.** `curl --user "$U:$P"` would put them in `argv`; the step uses `curl --config -` and pipes the options in on stdin instead.
|
||||||
- **A failed upload fails the job.** Unlike `helm push`, `curl` exits **0** on HTTP 401/403/409 — a publish would go green with nothing uploaded. `--fail-with-body` turns those into a non-zero exit and still prints the response body, so the log names the cause.
|
- **A failed upload fails the job.** Unlike `helm push`, `curl` exits **0** on HTTP 401/403/409 — a publish would go green with nothing uploaded. `--fail-with-body` turns those into a non-zero exit and still prints the response body, so the log names the cause.
|
||||||
- **The file name is built, not globbed.** `<chart>-<version>.tgz` is assembled from the chart name and the version already read from `Chart.yaml`; `--upload-file` takes exactly one file, and a glob matching two silently turns the second into curl's URL argument. `helm package` keeps the `+` of a `1.4.0+up1.9.2` version verbatim in the file name, so the built name is an exact match.
|
- **The file name is built, not globbed.** `<chart>-<version>.tgz` is assembled from the chart name and the version already read from `Chart.yaml`; `--upload-file` takes exactly one file, and a glob matching two silently turns the second into curl's URL argument. `helm package` keeps the `+` of a `1.4.0+up1.9.2` version verbatim in the file name, so the built name is an exact match.
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ description: A Helm chart to deploy urlaubsverwaltung on Kubernetes
|
|||||||
icon: https://raw.githubusercontent.com/urlaubsverwaltung/urlaubsverwaltung/main/src/main/resources/static/favicons/apple-touch-icon.png
|
icon: https://raw.githubusercontent.com/urlaubsverwaltung/urlaubsverwaltung/main/src/main/resources/static/favicons/apple-touch-icon.png
|
||||||
type: application
|
type: application
|
||||||
|
|
||||||
version: 10.0.1+up6.13.1
|
version: 10.0.2+up6.14.0
|
||||||
appVersion: 6.13.1
|
appVersion: 6.14.0
|
||||||
Reference in new issue
Block a user