Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6123f469f6
|
||
|
|
7d0c1b9c22
|
No files matched your search
@@ -0,0 +1,249 @@
|
||||
name: Publish Helm Chart with patched Application Version
|
||||
|
||||
# Called through "uses:" only, never triggered here. The thin wrappers in the
|
||||
# calling repository carry their own "workflow_dispatch" and "on: push", so the
|
||||
# workflow_dispatch block this file used to have is gone: a dispatch in THIS
|
||||
# repository would run against this repository's files, where there is no chart.
|
||||
#
|
||||
# The comments below are kept from the copy this file replaces, in English as
|
||||
# they were written. Each of them records something that was paid for once -
|
||||
# none of it is restating what the YAML already says.
|
||||
on:
|
||||
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"
|
||||
# Deliberately required and without a default, following the house rule
|
||||
# of this repository: an owner segment is never guessed. The caller
|
||||
# passes "${{ github.repository_owner }}", where the value is measured
|
||||
# (jlab-cluster, run 282 of validate-charts.yml) - inside a workflow
|
||||
# called from another repository it is documented to stay the caller's
|
||||
# owner, but that is not measured on this Gitea version, and a published
|
||||
# chart landing under the wrong owner is not worth relying on a context
|
||||
# semantic for. As an input the value is evaluated in the caller, where
|
||||
# it is known to be right.
|
||||
#
|
||||
# type: string, like every input here. On this Gitea version (measured on
|
||||
# 1.27.0, issue #4) a boolean workflow_call input does not reach the
|
||||
# called workflow at all - neither its default nor an explicitly passed
|
||||
# value - while the run stays green. Every input of this block is
|
||||
# therefore a string, even one that is a switch in meaning.
|
||||
registry-owner:
|
||||
required: true
|
||||
type: string
|
||||
description: >-
|
||||
Owner segment of the chart registry URL, i.e. the <owner> in
|
||||
<base>/<owner>/helm/api/charts. Deliberately has no default: an owner
|
||||
segment is never guessed. Callers pass github.repository_owner, which
|
||||
resolves in the calling repository, where the value is measured.
|
||||
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
|
||||
# The Connect host is a variable of the CALLING repository, not a value
|
||||
# wired into this file: a shared building block must not carry one
|
||||
# tenant's infrastructure address. The price of that is a prerequisite
|
||||
# that can be forgotten, and an unset variable arrives as an empty
|
||||
# string. An empty connect-host does not fail here - it fails two steps
|
||||
# later, inside the loading action, against a URL nobody can see. So the
|
||||
# guard comes first and names the setting, the place to set it and the
|
||||
# shape of the value.
|
||||
- name: "Check the calling repository's prerequisites"
|
||||
env:
|
||||
OP_CONNECT_HOST: ${{ vars.OP_CONNECT_HOST }}
|
||||
run: |-
|
||||
if [ -z "$OP_CONNECT_HOST" ]; then
|
||||
echo "::error::the variable OP_CONNECT_HOST is not set in the calling repository (Settings -> Actions -> Variables). It must hold the address of the 1Password Connect server, e.g. https://op-connect.example.com. This workflow deliberately does not hardcode a host."
|
||||
exit 1
|
||||
fi
|
||||
- name: "Configure OP-Connect"
|
||||
uses: 1password/load-secrets-action/configure@v5
|
||||
with:
|
||||
connect-host: ${{ vars.OP_CONNECT_HOST }}
|
||||
# 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.
|
||||
#
|
||||
# The script belongs to the CALLING repository and that is unchanged by
|
||||
# the move: the checkout above clones the repository the run belongs to -
|
||||
# the caller - so ${{ github.workspace }} is the caller's tree and the
|
||||
# script is the caller's copy. It is therefore a prerequisite of every
|
||||
# caller, the same way OP_CONNECT_HOST is, and the guard below says so
|
||||
# rather than letting the shell report a missing file.
|
||||
- name: "Fetch chart dependencies"
|
||||
env:
|
||||
HELM_DEPENDENCIES_SCRIPT: ${{ github.workspace }}/ci/helm-dependencies.sh
|
||||
CHART_NAME: ${{ inputs.chart-name }}
|
||||
run: |-
|
||||
test -x "$HELM_DEPENDENCIES_SCRIPT" || {
|
||||
echo "::error::${HELM_DEPENDENCIES_SCRIPT} is missing or not executable - ci/helm-dependencies.sh is a script of the calling repository and this workflow runs it before helm package"
|
||||
exit 1
|
||||
}
|
||||
"$HELM_DEPENDENCIES_SCRIPT" "$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 Chart Registry Credentials"
|
||||
id: load-registry-creds
|
||||
uses: 1password/load-secrets-action@v5
|
||||
env:
|
||||
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
|
||||
REGISTRY_PASSWORD: op://GitHub-Actions/jlab-registry-access/password
|
||||
REGISTRY_USERNAME: op://GitHub-Actions/jlab-registry-access/username
|
||||
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
|
||||
# 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>/<owner>/helm/api/charts, authenticated per request.
|
||||
#
|
||||
# That URL is composed, not stored (jlab-cluster/helm-charts#63).
|
||||
# charts-url holds only the base up to "/api/packages"; the owner segment
|
||||
# comes from the registry-owner input, which the caller fills with
|
||||
# github.repository_owner - 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 the
|
||||
# calling repository, and a second owner on the same instance needs no
|
||||
# second setting. 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 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
|
||||
# shell variables - never interpolated into the command line. load-secrets-
|
||||
# action v5 no longer exports the loaded values as environment variables
|
||||
# for later steps the way v4 did; only the step outputs carry them. Every
|
||||
# "${{ env.* }}" 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. The ":?" guards fail
|
||||
# loudly and name the op:// field that is missing, and quoting keeps an
|
||||
# empty value from silently shifting arguments.
|
||||
#
|
||||
# 2) The credentials never reach the process list. "curl --user $U:$P"
|
||||
# would put them in argv; "curl --config -" reads its options from stdin
|
||||
# instead, so nothing sensitive appears in any argument. The price of that
|
||||
# config file is its value syntax: an unquoted value ends at the first
|
||||
# space (measured on curl 8.5.0 - "has space" arrived as "has"), so the
|
||||
# step refuses whitespace in the credentials up front rather than letting
|
||||
# a truncated password come back as a puzzling 401. A Gitea access token
|
||||
# is 40 hex characters, so this only ever fires on a mis-entered item.
|
||||
#
|
||||
# 3) "--fail-with-body" is what makes a failed upload fail the job. Unlike
|
||||
# "helm push", curl exits 0 on HTTP 401/403/409 unless asked not to - the
|
||||
# publish would go green while nothing was uploaded. Measured against Gitea
|
||||
# 1.24.7: a 401 without the flag is exit 0, with the flag exit 22, and the
|
||||
# response body is still printed so the log names the cause.
|
||||
#
|
||||
# The file name is built from the chart name and the version read above
|
||||
# instead of a "<chart>-*.tgz" glob: --upload-file takes exactly one file,
|
||||
# and a glob matching two turns the second into curl's URL argument, while
|
||||
# a glob matching none is passed through literally. Both fail obscurely.
|
||||
# "helm package" keeps the "+" of a "1.4.0+up1.9.2" version verbatim in the
|
||||
# file name, so "<name>-<version>.tgz" is an exact match (measured).
|
||||
- name: "Upload Helm Chart to the Chart Registry"
|
||||
env:
|
||||
REGISTRY_PASSWORD: ${{ steps.load-registry-creds.outputs.REGISTRY_PASSWORD }}
|
||||
REGISTRY_USERNAME: ${{ steps.load-registry-creds.outputs.REGISTRY_USERNAME }}
|
||||
REGISTRY_CHARTS_URL: ${{ steps.load-registry-creds.outputs.REGISTRY_CHARTS_URL }}
|
||||
REGISTRY_OWNER: ${{ inputs.registry-owner }}
|
||||
CHART_NAME: ${{ inputs.chart-name }}
|
||||
CHART_VERSION: ${{ steps.get-chart-version.outputs.CHART_VERSION }}
|
||||
run: |-
|
||||
: "${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_PASSWORD:?empty - check op://GitHub-Actions/jlab-registry-access/password}"
|
||||
: "${REGISTRY_OWNER:?empty - the registry-owner input did not arrive; the caller passes it from github.repository_owner, do not hardcode an owner}"
|
||||
: "${CHART_NAME:?empty - the chart-name input did not arrive}"
|
||||
: "${CHART_VERSION:?empty - could not read version: from ${CHART_NAME}/Chart.yaml}"
|
||||
# 1Password masks the full URL, but a curl connection error prints only
|
||||
# "<host>:<port>", which that mask would not cover. Mask the host too.
|
||||
CHARTS_HOST="${REGISTRY_CHARTS_URL#*://}"
|
||||
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 registry-owner input 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
|
||||
*[[: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"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
CHART_PACKAGE="${CHART_NAME}-${CHART_VERSION}.tgz"
|
||||
test -f "$CHART_PACKAGE" || {
|
||||
echo "::error::${CHART_PACKAGE} does not exist - helm package produced: $(ls -1 ./*.tgz 2>&1)"
|
||||
exit 1
|
||||
}
|
||||
echo "uploading ${CHART_PACKAGE}"
|
||||
printf 'user = %s:%s\nurl = %s\n' \
|
||||
"$REGISTRY_USERNAME" "$REGISTRY_PASSWORD" "$CHARTS_API_URL" |
|
||||
curl --config - \
|
||||
--fail-with-body \
|
||||
--silent --show-error \
|
||||
--request POST \
|
||||
--upload-file "$CHART_PACKAGE" \
|
||||
--write-out '\nHTTP %{http_code}\n'
|
||||
working-directory: ${{ inputs.chart-location }}
|
||||
@@ -4,6 +4,12 @@ Geteilte CI/CD-Bausteine für die Gitea-Instanz `git.jk-dev.de`. Die Workflows
|
||||
hier werden nicht selbst ausgelöst, sondern von anderen Repositories über
|
||||
`uses:` aufgerufen (`on: workflow_call`).
|
||||
|
||||
Diese Datei ist die **Kurzreferenz für Aufrufer**: was ein Baustein tut, seine
|
||||
Eingaben, ein Aufrufbeispiel, die Voraussetzungen. Wer einen Aufruf schreibt,
|
||||
braucht nur das. Die Langform — warum die Dinge so sind und was dafür gemessen
|
||||
wurde — steht im [Wiki](https://git.jk-dev.de/workflow-util/pipelines/wiki),
|
||||
eine Seite je Baustein und eine für die Eigenheiten dieser Forge.
|
||||
|
||||
## Bausteine
|
||||
|
||||
### `.gitea/workflows/docker-build-push.yml`
|
||||
@@ -148,6 +154,121 @@ Die eigentlichen Zugänge für Checkout und Registry liegen in 1Password
|
||||
(`GitHub-Actions/jlab-git-checkout-token`, `GitHub-Actions/jlab-registry-access`)
|
||||
und müssen in Gitea nicht hinterlegt werden.
|
||||
|
||||
### `.gitea/workflows/publish-helm-chart.yml`
|
||||
|
||||
Packt ein Helm-Chart und lädt es in eine **klassische, indexbasierte**
|
||||
Helm-Registry. Das ist kein OCI-`helm push`: Es gibt keine Registry-Sitzung,
|
||||
sondern ein einzelnes HTTP-`POST` des gepackten `.tgz`, je Anfrage
|
||||
authentifiziert. Läuft auf `ubuntu-latest`.
|
||||
|
||||
Die Ziel-URL wird zusammengesetzt, nicht gespeichert:
|
||||
|
||||
```
|
||||
<charts-url>/<registry-owner>/helm/api/charts
|
||||
```
|
||||
|
||||
`<charts-url>` ist die Basis bis `/api/packages` und liegt in 1Password
|
||||
(`GitHub-Actions/jlab-registry-access`, Feld `charts-url`), `<registry-owner>`
|
||||
ist die Pflichteingabe. Damit steht im aufrufenden Repository kein Wert, der auf
|
||||
die Registry zeigt — nicht einmal ein Pfadsegment —, und ein zweiter Eigentümer
|
||||
auf derselben Instanz braucht keine zweite Einstellung.
|
||||
|
||||
#### Eingaben
|
||||
|
||||
| Eingabe | Pflicht | Vorgabe | Bedeutung |
|
||||
| ---------------- | ------- | ------- | --------- |
|
||||
| `chart-name` | ja | — | Name des Charts, zugleich der Name seines Verzeichnisses unterhalb von `chart-location` |
|
||||
| `registry-owner` | ja | — | Eigentümer-Segment der Ziel-URL, also der `<owner>` in `<basis>/<owner>/helm/api/charts` |
|
||||
| `chart-location` | nein | `.` | Verzeichnis, in dem das Chart-Verzeichnis liegt |
|
||||
|
||||
Dass `registry-owner` Pflicht ist und keine Vorgabe hat, folgt derselben
|
||||
Hausregel wie `image-owner` oben: Ein Eigentümer-Segment wird nie geraten.
|
||||
Aufrufer übergeben `${{ github.repository_owner }}`; der Wert wird damit im
|
||||
**aufrufenden** Repository ausgewertet, wo er gemessen ist (`jlab-cluster`, Lauf
|
||||
282 von `validate-charts.yml`). Dass der Kontext auch in einem von fremd
|
||||
aufgerufenen Workflow der des Aufrufers bleibt, ist dokumentiert, auf dieser
|
||||
Gitea-Fassung aber nicht gemessen — und ein Chart, das unter dem falschen
|
||||
Eigentümer veröffentlicht wird, ist nichts, wofür man sich auf eine
|
||||
Kontext-Semantik verlassen müsste. Als Eingabe hängt der Baustein an keiner.
|
||||
|
||||
Alle Eingaben sind `type: string`, auch dort, wo künftig ein Schalter
|
||||
hinzukommt: Boolesche `workflow_call`-Eingaben werden auf dieser Gitea-Version
|
||||
nicht durchgereicht — bei grünem Lauf. Siehe
|
||||
[Issue #4](https://git.jk-dev.de/workflow-util/pipelines/issues/4) und die
|
||||
Begründung im Abschnitt zu `push` oben.
|
||||
|
||||
#### Ausgaben
|
||||
|
||||
| Ausgabe | Bedeutung |
|
||||
| --------------- | --------- |
|
||||
| `chart-version` | Die veröffentlichte Chart-Version, gelesen aus `version:` in `Chart.yaml` |
|
||||
|
||||
#### Aufrufbeispiel
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
publish-outline:
|
||||
name: Publish Outline Helm Chart
|
||||
uses: workflow-util/pipelines/.gitea/workflows/publish-helm-chart.yml@v3
|
||||
with:
|
||||
chart-name: 'outline'
|
||||
registry-owner: ${{ github.repository_owner }}
|
||||
secrets: inherit
|
||||
```
|
||||
|
||||
`secrets: inherit` ist notwendig — der Workflow braucht `OP_CONNECT_TOKEN` aus
|
||||
dem aufrufenden Repository.
|
||||
|
||||
Der Baustein löst sich nicht selbst aus. Wann veröffentlicht wird, entscheidet
|
||||
die Hülle im aufrufenden Repository über ihr eigenes `on:` — üblicherweise ein
|
||||
`push` auf `main`, eingeschränkt auf das Chart-Verzeichnis, dazu ein
|
||||
`workflow_dispatch` für den Fall, dass von Hand nachgeschoben werden muss.
|
||||
|
||||
#### Form des Tresor-Felds `charts-url`
|
||||
|
||||
Das Feld enthält **nur** die Basis:
|
||||
|
||||
```
|
||||
https://<host>/api/packages # so
|
||||
https://<host>/api/packages/<owner>/helm/api/charts # nicht so
|
||||
```
|
||||
|
||||
Drei Wächter weisen eine andere Form namentlich zurück: wenn der Wert auf
|
||||
`/api/charts` endet, wenn er das Segment `/helm` enthält, und wenn er nicht auf
|
||||
`/api/packages` endet. Der Grund ist die Lesbarkeit des Fehlers — 1Password
|
||||
maskiert die URL im Protokoll, ein 404 gegen eine maskierte Adresse wäre nicht zu
|
||||
deuten. Der Eigentümer wird aus demselben Grund geprüft: Ein leerer Wert ergäbe
|
||||
`<basis>//helm/api/charts`, also wieder einen nackten 404.
|
||||
|
||||
#### Voraussetzungen im aufrufenden Repository
|
||||
|
||||
| Name | Art | Zweck |
|
||||
| ------------------------- | ----------------- | ----- |
|
||||
| `OP_CONNECT_HOST` | Variable | Adresse des 1Password-Connect-Servers im Cluster, etwa `https://op-connect.example.com` |
|
||||
| `OP_CONNECT_TOKEN` | Secret | Zugang zu diesem Connect-Server, kommt über `secrets: inherit` |
|
||||
| `ci/helm-dependencies.sh` | Datei im Repository | Ausführbares Skript, das die deklarierten Abhängigkeiten eines Charts nach `charts/` holt |
|
||||
|
||||
`OP_CONNECT_HOST` ist bewusst eine Variable des aufrufenden Repositories und
|
||||
kein fest verdrahteter Wert: Ein geteilter Baustein trägt nicht die
|
||||
Infrastruktur-Adresse eines einzelnen Mandanten. Der Preis dafür ist eine
|
||||
Voraussetzung, die man vergessen kann, und eine nicht gesetzte Variable kommt als
|
||||
leere Zeichenkette an. Deshalb prüft der erste Schritt sie und benennt beim
|
||||
Abbruch die Einstellung samt Ort — statt zwei Schritte später an einer leeren
|
||||
Adresse zu scheitern.
|
||||
|
||||
`ci/helm-dependencies.sh` ist ein Skript des **aufrufenden** Repositories, und
|
||||
das bleibt nach dem Umzug hierher so: Der Checkout holt das Repository, zu dem
|
||||
der Lauf gehört — das des Aufrufers —, also ist `${{ github.workspace }}` dessen
|
||||
Arbeitsverzeichnis. Der Aufruf steht vor `helm package`, weil `helm package` mit
|
||||
`found in Chart.yaml, but missing in charts/ directory` abbricht, solange eine
|
||||
deklarierte Abhängigkeit nicht geholt ist. Für ein Chart ohne `dependencies:`
|
||||
tut das Skript nichts, vorhanden und ausführbar muss es trotzdem sein; fehlt es,
|
||||
sagt der Lauf genau das.
|
||||
|
||||
Der Registry-Zugang selbst liegt in 1Password
|
||||
(`GitHub-Actions/jlab-registry-access`: `username`, `password`, `charts-url`) und
|
||||
muss in Gitea nicht hinterlegt werden.
|
||||
|
||||
## Auf einen Tag zeigen, nicht auf `@main`
|
||||
|
||||
Aufrufe bitte immer an einen Tag binden:
|
||||
@@ -169,3 +290,19 @@ bestehende Aufrufe, wird der Major-Anteil erhöht.
|
||||
Aufrufe, die den Wert als YAML-Boolean übergeben. `v1` bleibt unverändert
|
||||
stehen; wer umstellt, wechselt auf `@v2` und schreibt `push: 'true'` bzw.
|
||||
`push: 'false'`.
|
||||
|
||||
### Die Nummer bezeichnet den Zustand des Repositories
|
||||
|
||||
Tags gelten hier **repository-weit**, nicht je Datei. `v3` ist der Stand, ab dem
|
||||
es `publish-helm-chart.yml` gibt; unter `v1` und `v2` existiert die Datei nicht,
|
||||
ein Aufruf auf `@v2` findet sie also nicht.
|
||||
|
||||
Das hat eine Folge, die man kennen muss, weil sie sonst als Fehler gelesen wird:
|
||||
Wer künftig `docker-build-push.yml` ändert, erhöht auf `v4` — und ein Aufruf von
|
||||
`publish-helm-chart.yml@v3` bleibt gültig und richtig, obwohl sich an dieser
|
||||
Datei nichts geändert hat. Die Nummer ist keine Dateiversion. Umgekehrt zwingt
|
||||
eine brechende Änderung an einem Baustein die Aufrufer der anderen zu nichts:
|
||||
Sie bleiben auf ihrem Tag stehen, bis sie bewusst nachziehen.
|
||||
|
||||
`v3` selbst ist additiv. Der neue Baustein ändert an `docker-build-push.yml`
|
||||
nichts, bestehende Aufrufer auf `@v2` bleiben unberührt.
|
||||
Reference in new issue
Block a user