Compare commits

..

10 Commits

Author SHA1 Message Date
niko cc7f4f0976 ohjeita helm build-push (#28)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 21s
CI Main / Check existing artifact (push) Successful in 18s
acc-tests Cucumber test report
CI Main / Cucumber tests (push) Successful in 1m16s
ci-docker-build-push Docker build & push 0.2.15 OK
CI Main / Build & Push Docker (push) Successful in 54s
unit-tests Bats test report
CI Main / Bats tests (push) Successful in 2m4s
CI Main / Report Summary (push) Successful in 5s
CI Main / Move provider version tag (push) Successful in 15s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #28
2026-06-19 10:13:24 +03:00
niko 6621b3f336 Fix/helm build node (#27)
CI Git-Pages Main / Check existing artifact (push) Successful in 20s
CI Git-Pages Main / Load git-pages.gitea-env.conf to pipeline env (push) Successful in 22s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 23s
CI Main / Check existing artifact (push) Successful in 21s
acc-tests Cucumber test report
CI Main / Cucumber tests (push) Successful in 52s
CI Git-Pages Main / Report Summary (push) Successful in 4s
ci-helm-build-push Helm chart 0.1.0
CI Git-Pages Main / Build & Push Helm chart (push) Successful in 43s
unit-tests Bats test report
CI Main / Bats tests (push) Successful in 1m51s
ci-docker-build-push Docker build & push 0.2.14 OK
CI Main / Build & Push Docker (push) Successful in 31s
CI Main / Report Summary (push) Successful in 3s
CI Main / Move provider version tag (push) Successful in 11s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #27
2026-06-19 08:48:56 +03:00
niko c0012ba6fa Feature/helm chart (#26)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 23s
CI Git-Pages Main / Load git-pages.gitea-env.conf to pipeline env (push) Successful in 22s
CI Main / Check existing artifact (push) Successful in 22s
CI Git-Pages Main / Check existing artifact (push) Successful in 23s
CI Git-Pages Main / Report Summary (push) Successful in 6s
CI Main / Cucumber tests (push) Successful in 1m13s
CI Main / Bats tests (push) Successful in 2m1s
CI Feature / Load example-gitea-env.conf to pipeline env (push) Successful in 27s
CI Git-Pages Main / Build & Push Helm chart (push) Failing after 21s
ci-docker-build-push Docker build & push 0.2.13 OK
CI Main / Build & Push Docker (push) Successful in 53s
CI Main / Report Summary (push) Successful in 6s
CI Main / Move provider version tag (push) Successful in 16s
acc-tests Cucumber test report
CI Feature / Cucumber tests (push) Successful in 1m17s
unit-tests Bats test report
CI Feature / Bats tests (push) Successful in 1m59s
CI Feature / Report Summary (push) Successful in 5s
riippuvuudet haetaan main haarasta, ei etene ennen kuin on main haarassa nämä muutokset

---------

Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #26
2026-06-19 08:35:52 +03:00
niko 908aee9f8b job status parametrin käyttö (#25)
CI Main / Cucumber tests (push) Successful in 1m11s
CI Main / Bats tests (push) Successful in 1m41s
acc-tests Cucumber test report
CI Feature / Cucumber tests (push) Successful in 58s
unit-tests Bats test report
CI Feature / Bats tests (push) Successful in 1m27s
CI Feature / Report Summary (push) Successful in 4s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 25s
CI Main / Check existing artifact (push) Successful in 14s
ci-docker-build-push Docker build & push 0.2.12 OK
CI Main / Build & Push Docker (push) Successful in 44s
CI Main / Report Summary (push) Successful in 7s
CI Main / Move provider version tag (push) Successful in 17s
CI Feature / Load example-gitea-env.conf to pipeline env (push) Successful in 22s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #25
2026-06-19 02:56:37 +03:00
niko ad4c2cd570 Fix/ci reports fail param (#24)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 23s
CI Main / Check existing artifact (push) Successful in 14s
CI Main / Cucumber tests (push) Successful in 1m11s
unit-tests Bats test report
CI Main / Bats tests (push) Successful in 1m25s
ci-docker-build-push Docker build & push 0.2.11 OK
CI Main / Report Summary (push) Successful in 7s
CI Main / Move provider version tag (push) Successful in 14s
acc-tests Cucumber test report
CI Main / Build & Push Docker (push) Successful in 32s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #24
2026-06-18 16:19:45 +03:00
niko 1e10633e60 Fix/uusi consumer kontti taktiikka (#23)
acc-tests Cucumber test report
CI Main / Cucumber tests (push) Successful in 49s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 17s
CI Main / Check existing artifact (push) Successful in 12s
CI Main / Build & Push Docker (push) Successful in 35s
CI Main / Move provider version tag (push) Successful in 13s
unit-tests Bats test report
CI Main / Bats tests (push) Successful in 1m19s
ci-docker-build-push Docker build & push 0.2.10 OK
CI Main / Report Summary (push) Successful in 6s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #23
2026-06-18 13:54:38 +03:00
niko 737b2fc3f2 Feature/yksinkertaistetaan raportointi logiikkaa (#22)
unit-tests Bats test report
CI Main / Bats tests (push) Successful in 1m21s
ci-docker-build-push Docker build & push 0.2.9 OK
CI Main / Build & Push Docker (push) Successful in 40s
CI Main / Report Summary (push) Successful in 5s
CI Main / Move provider version tag (push) Successful in 12s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 18s
CI Main / Check existing artifact (push) Successful in 12s
acc-tests Cucumber test report
CI Main / Cucumber tests (push) Successful in 2m3s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #22
2026-06-18 12:55:02 +03:00
niko 65d385f9b9 skill päivitetty (#21)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 17s
CI Main / Check existing artifact (push) Successful in 13s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 1m35s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 54s
ci-docker-build-push Docker build & push 0.2.8 OK
CI Main / Build & Push Docker (push) Successful in 40s
CI Main / Report Summary (push) Successful in 6s
CI Main / Move provider version tag (push) Successful in 13s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #21
2026-06-17 17:02:44 +03:00
niko 2e5a4dca3a Fix/ci container manual start (#20)
CI Main / Bats tests (push) Successful in 1m34s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 51s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 21s
CI Main / Check existing artifact (push) Successful in 13s
unit-tests Link to Bats reports
ci-docker-build-push Docker build & push 0.2.7 OK
CI Main / Build & Push Docker (push) Successful in 41s
CI Main / Report Summary (push) Successful in 6s
CI Main / Move provider version tag (push) Successful in 12s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #20
2026-06-17 14:19:05 +03:00
niko 29fde14445 Fix/fragile test (#19)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 17s
CI Main / Check existing artifact (push) Successful in 12s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 2m41s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 1m44s
CI Main / Move provider version tag (push) Successful in 12s
ci-docker-build-push Docker build & push 0.2.6 OK
CI Main / Build & Push Docker (push) Successful in 38s
CI Main / Report Summary (push) Successful in 5s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #19
2026-06-17 07:41:01 +03:00
34 changed files with 1626 additions and 474 deletions
+20 -28
View File
@@ -1,37 +1,29 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
WORKSPACE_VOLUME="${1:-}" REPORT_DIR="${1:-}"
REPORT_DIR="${2:-}" COVERAGE_DIR="${REPORT_DIR}/coverage"
[ -n "$WORKSPACE_VOLUME" ] || { echo "ERROR: workspace volume name required" >&2; exit 1; }
[ -n "$REPORT_DIR" ] || { echo "ERROR: report directory required" >&2; exit 1; } [ -n "$REPORT_DIR" ] || { echo "ERROR: report directory required" >&2; exit 1; }
HAS_COVERAGE=false if [ -d coverage ]; then
COVERAGE_SRC="" mkdir -p "$COVERAGE_DIR"
if docker run --rm -v "$WORKSPACE_VOLUME":/data alpine sh -c '[ -d /data/coverage ] && ls -A /data/coverage | grep -q .' 2>/dev/null; then cp -a coverage/. "$COVERAGE_DIR/"
COVERAGE_SRC="/data/coverage"
fi fi
if [ -n "$COVERAGE_SRC" ]; then if [ -d "$COVERAGE_DIR" ] && [ ! -f "$COVERAGE_DIR/index.html" ]; then
mkdir -p "$REPORT_DIR/coverage" SHA8="${GITHUB_SHA:0:8}"
docker run --rm -v "$WORKSPACE_VOLUME":/data alpine tar c -C "$COVERAGE_SRC" . | tar x -C "$REPORT_DIR/coverage" {
HAS_COVERAGE=true echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
echo "<title>Coverage report ${SHA8}</title>"
echo '<style>body{font-family:sans-serif;margin:2em}h1{color:#1e293b}ul{list-style:none;padding:0}li{margin:.5em 0}a{color:#2563eb}</style>'
echo "</head><body><h1>Coverage report <code>${SHA8}</code></h1><ul>"
while IFS= read -r -d '' f; do
base=$(basename "$f")
name="${base%.*}"
name="${name//-/ }"
echo "<li><a href=\"${base}\">${name^}</a></li>"
done < <(find "$COVERAGE_DIR" -maxdepth 1 -type f \( -name '*.html' -o -name '*.txt' \) ! -name index.html -print0 2>/dev/null || true)
echo '</ul></body></html>'
} > "$COVERAGE_DIR/index.html"
fi fi
cat > "$REPORT_DIR/index.html" << EOF
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">
<title>Bats report ${GITHUB_SHA:0:8}</title>
<style>body{font-family:sans-serif;margin:2em;max-width:960px}
h1{color:#1e293b}a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}
</style></head><body>
<h1>Bats report <code>${GITHUB_SHA:0:8}</code></h1>
<ul>
<li><a href="test-report.html">Test results</a></li>
EOF
if [ "$HAS_COVERAGE" = true ]; then
echo '<li><a href="coverage/index.html">Coverage report</a></li>' >> "$REPORT_DIR/index.html"
fi
echo '</ul></body></html>' >> "$REPORT_DIR/index.html"
+9 -46
View File
@@ -27,54 +27,17 @@ jobs:
version: ${{ steps.set-outputs.outputs.version }} version: ${{ steps.set-outputs.outputs.version }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Check existing artifact and calculate version - name: Check existing artifact and calculate version
run: | env:
if [ -n "${VERSION_FILE}" ]; then SERVER_URL: ${{ gitea.server_url }}
if echo "${VERSION_FILE}" | grep -q '\.json$'; then REPO: ${{ github.repository }}
RAW_VERSION=$(jq -r '.version' "${VERSION_FILE}") SHA: ${{ github.sha }}
else run: bash .ci/scripts/check-version.sh
RAW_VERSION=$(cat "${VERSION_FILE}" | tr -d '[:space:]')
fi
elif [ -f VERSION ]; then
RAW_VERSION=$(cat VERSION | tr -d '[:space:]')
elif [ -f package.json ]; then
RAW_VERSION=$(jq -r '.version' package.json)
elif [ -f pom.xml ]; then
RAW_VERSION=$(grep -oP '<version>\K[^<]+' pom.xml | head -1)
else
echo "ERROR: No VERSION file, package.json, or pom.xml found" >&2
exit 1
fi
BASE_VERSION=$(echo "$RAW_VERSION" | cut -d'.' -f1-2)
echo "gitea-ci-library - Tunnistettu Major.Minor versio: $BASE_VERSION"
TAGS_JSON=$(curl -s -f -H "Authorization: token $GITEA_TOKEN" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/tags")
TAG=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX}" '
if type == "array" then
.[] | select(.commit.sha == "${{ github.sha }}" and (.name | startswith($prefix))) | .name
else empty end' | head -1)
mkdir -p /tmp/build-ctx
if [ -n "$TAG" ]; then
echo "ARTIFACT_EXISTS=true" > /tmp/build-ctx/build.env
echo "NEXT_VERSION=$TAG" >> /tmp/build-ctx/build.env
echo "gitea-ci-library - Artefakti löytyi jo tagilla: $TAG."
else
echo "ARTIFACT_EXISTS=false" > /tmp/build-ctx/build.env
HIGHEST_PATCH=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX}" --arg bv "${GIT_TAG_PREFIX}${BASE_VERSION}." '
if type == "array" then .[] | .name | select(startswith($bv)) | sub($bv; "") | tonumber else empty end' | sort -rn | head -1)
if [ -z "$HIGHEST_PATCH" ]; then NEXT_PATCH=0; else NEXT_PATCH=$((HIGHEST_PATCH + 1)); fi
FULL_VERSION="${BASE_VERSION}.${NEXT_PATCH}"
echo "NEXT_VERSION=$FULL_VERSION" >> /tmp/build-ctx/build.env
echo "gitea-ci-library - Uusi vapaa versio: $FULL_VERSION"
fi
- name: Set job outputs - name: Set job outputs
id: set-outputs id: set-outputs
+9 -9
View File
@@ -12,31 +12,32 @@ on:
required: true required: true
type: string type: string
tag: tag:
required: true required: false
type: string type: string
default: 'latest'
secrets: secrets:
DOCKER_USERNAME: DOCKER_USERNAME:
required: false required: false
DOCKER_PASSWORD: DOCKER_PASSWORD:
required: true required: true
env:
DOCKER_REGISTRY: ${{ fromJson(inputs.env_json).DOCKER_REGISTRY || '' }}
IMAGE_NAME: ${{ inputs.image_name }}
TAG: ${{ inputs.tag }}
DOCKERFILE: ${{ inputs.dockerfile_path }}
jobs: jobs:
build-push: build-push:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build and push CI container - name: Build and push container
env: env:
DOCKER_REGISTRY: ${{ fromJson(inputs.env_json).DOCKER_REGISTRY || '' }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: | run: |
REGISTRY="${DOCKER_REGISTRY:?DOCKER_REGISTRY not set in conf}"
DOCKERFILE="${{ inputs.dockerfile_path }}"
IMAGE_NAME="${{ inputs.image_name }}"
TAG="${{ inputs.tag }}"
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ) NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker build \ docker build \
--label "git.commit=${{ github.sha }}" \ --label "git.commit=${{ github.sha }}" \
@@ -45,7 +46,6 @@ jobs:
-f "${DOCKERFILE}" \ -f "${DOCKERFILE}" \
-t "${IMAGE_NAME}:${TAG}" . -t "${IMAGE_NAME}:${TAG}" .
REGISTRY="${DOCKER_REGISTRY:?DOCKER_REGISTRY not set in env.conf}"
REGISTRY_HOST="${REGISTRY%%/*}" REGISTRY_HOST="${REGISTRY%%/*}"
FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}" FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
+14 -27
View File
@@ -6,8 +6,9 @@ on:
required: true required: true
type: string type: string
bats-image: bats-image:
required: true required: false
type: string type: string
default: gitea.app.keskikuja.site/niko/ci-bats:git
secrets: secrets:
GITEA_TOKEN: GITEA_TOKEN:
required: true required: true
@@ -23,6 +24,8 @@ env:
jobs: jobs:
bats: bats:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: ${{ inputs.bats-image }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -31,34 +34,18 @@ jobs:
path: .ci path: .ci
- name: Run bats tests - name: Run bats tests
id: bats-tests
shell: bash
run: | run: |
docker volume create bats-workspace mkdir -p reports/bats
tar c . | docker run --rm -i -v bats-workspace:/data alpine tar x -C /data bashcov -- bats tests/ > reports/bats/results.txt 2>&1
mkdir -p "reports/${GITHUB_SHA:0:8}/bats"
set +e
docker run --rm \
-v bats-workspace:/data \
--entrypoint bash ${{ inputs.bats-image }} \
-c 'cd /data && bashcov -- bats tests/' \
> "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1
BATS_EXIT=$?
bash .ci/.gitea/scripts/bats-coverage.sh bats-workspace "reports/${GITHUB_SHA:0:8}/bats"
docker volume rm bats-workspace > /dev/null 2>&1
bash .ci/.gitea/scripts/bats-report.sh "reports/${GITHUB_SHA:0:8}/bats"
echo "BATS_EXIT=${BATS_EXIT}" >> "${GITHUB_ENV}"
exit ${BATS_EXIT}
- name: Publish bats reports - name: Post-process coverage
if: always() if: always()
run: bash .ci/scripts/publish-git-pages.sh bats run: bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
- name: Report status - name: Post-process test report
if: always() if: always()
run: | run: bash .ci/.gitea/scripts/bats-report.sh reports/bats
if [ "${BATS_EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "Link to Bats reports" unit-tests bats - name: Report
else if: always()
bash .ci/scripts/report-status.sh failure "Link to Bats reports" unit-tests bats run: bash .ci/scripts/ci-report.sh "Bats test report" unit-tests bats ${{ job.status }}
fi
+28 -8
View File
@@ -1,21 +1,41 @@
name: Build CI Bats Container (Manual) name: CI Container Build Bats
on: workflow_dispatch on:
workflow_dispatch:
inputs:
config_path:
required: true
type: string
default: '.gitea/workflows/example-gitea-env.conf'
description: 'Polku .gitea-env.conf-tiedostoon'
dockerfile_path:
required: true
type: string
default: 'Dockerfile.ci-bats'
description: 'Polku Dockerfileen'
image_name:
required: true
type: string
default: 'ci-bats'
description: 'Kontin nimi ilman registry-polkua'
tag:
required: true
type: string
default: 'latest'
description: 'Image-tägi'
jobs: jobs:
load-config: load-config:
name: Load config
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit secrets: inherit
with: with:
config_path: .gitea/workflows/example-gitea-env.conf config_path: ${{ inputs.config_path }}
build-push: build-push:
name: Build & Push
needs: [load-config] needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@main uses: niko/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@main
secrets: inherit secrets: inherit
with: with:
env_json: ${{ needs.load-config.outputs.env_json }} env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: Dockerfile.ci-bats dockerfile_path: ${{ inputs.dockerfile_path }}
image_name: ci-bats image_name: ${{ inputs.image_name }}
tag: latest tag: ${{ inputs.tag }}
+28 -8
View File
@@ -1,21 +1,41 @@
name: Build CI Cucumber Container (Manual) name: CI Container Build Cucumber
on: workflow_dispatch on:
workflow_dispatch:
inputs:
config_path:
required: true
type: string
default: '.gitea/workflows/example-gitea-env.conf'
description: 'Polku .gitea-env.conf-tiedostoon'
dockerfile_path:
required: true
type: string
default: 'Dockerfile.ci-cucumber'
description: 'Polku Dockerfileen'
image_name:
required: true
type: string
default: 'ci-cucumber'
description: 'Kontin nimi ilman registry-polkua'
tag:
required: true
type: string
default: 'latest'
description: 'Image-tägi'
jobs: jobs:
load-config: load-config:
name: Load config
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit secrets: inherit
with: with:
config_path: .gitea/workflows/example-gitea-env.conf config_path: ${{ inputs.config_path }}
build-push: build-push:
name: Build & Push
needs: [load-config] needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@main uses: niko/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@main
secrets: inherit secrets: inherit
with: with:
env_json: ${{ needs.load-config.outputs.env_json }} env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: Dockerfile.ci-cucumber dockerfile_path: ${{ inputs.dockerfile_path }}
image_name: ci-cucumber image_name: ${{ inputs.image_name }}
tag: latest tag: ${{ inputs.tag }}
+7 -28
View File
@@ -6,8 +6,9 @@ on:
required: true required: true
type: string type: string
cucumber-node-image: cucumber-node-image:
required: true required: false
type: string type: string
default: gitea.app.keskikuja.site/niko/ci-cucumber:latest
secrets: secrets:
GITEA_TOKEN: GITEA_TOKEN:
required: true required: true
@@ -33,36 +34,14 @@ jobs:
path: .ci path: .ci
- name: Run cucumber tests - name: Run cucumber tests
id: cucumber-tests
shell: bash shell: bash
run: | run: |
mkdir -p "reports/${GITHUB_SHA:0:8}/cucumber" mkdir -p reports/cucumber
set +e
npx cucumber-js \ npx cucumber-js \
--format json:"reports/${GITHUB_SHA:0:8}/cucumber/report.json" \ --format json:reports/cucumber/results.json \
--format html:"reports/${GITHUB_SHA:0:8}/cucumber/index.html" 2>&1 --format html:reports/cucumber/test-report.html 2>&1
CUCUMBER_EXIT=$?
echo "CUCUMBER_EXIT=${CUCUMBER_EXIT}" >> "${GITHUB_ENV}"
exit ${CUCUMBER_EXIT}
- name: Publish cucumber reports - name: Report
if: always()
run: bash .ci/scripts/publish-git-pages.sh cucumber
- name: Report status
if: always() if: always()
shell: bash shell: bash
run: | run: bash .ci/scripts/ci-report.sh "Cucumber test report" acc-tests cucumber ${{ job.status }}
if [ "${CUCUMBER_EXIT}" = "0" ]; then
if [ -f "reports/${GITHUB_SHA:0:8}/cucumber/index.html" ]; then
bash .ci/scripts/report-status.sh success "Link to Cucumber reports" acc-tests cucumber
else
bash .ci/scripts/report-status.sh success "Link to Cucumber reports" acc-tests
fi
else
if [ -f "reports/${GITHUB_SHA:0:8}/cucumber/index.html" ]; then
bash .ci/scripts/report-status.sh failure "Link to Cucumber reports" acc-tests cucumber
else
bash .ci/scripts/report-status.sh failure "Link to Cucumber reports" acc-tests
fi
fi
-2
View File
@@ -20,7 +20,6 @@ jobs:
secrets: inherit secrets: inherit
with: with:
env_json: ${{ needs.load-config.outputs.env_json }} env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: gitea.app.keskikuja.site/niko/ci-bats:latest
cucumber: cucumber:
name: Cucumber tests name: Cucumber tests
@@ -29,7 +28,6 @@ jobs:
secrets: inherit secrets: inherit
with: with:
env_json: ${{ needs.load-config.outputs.env_json }} env_json: ${{ needs.load-config.outputs.env_json }}
cucumber-node-image: gitea.app.keskikuja.site/niko/ci-cucumber:latest
report-summary: report-summary:
name: Report Summary name: Report Summary
-2
View File
@@ -29,7 +29,6 @@ jobs:
secrets: inherit secrets: inherit
with: with:
env_json: ${{ needs.load-config.outputs.env_json }} env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: gitea.app.keskikuja.site/niko/ci-bats:latest
cucumber: cucumber:
name: Cucumber tests name: Cucumber tests
@@ -39,7 +38,6 @@ jobs:
secrets: inherit secrets: inherit
with: with:
env_json: ${{ needs.load-config.outputs.env_json }} env_json: ${{ needs.load-config.outputs.env_json }}
cucumber-node-image: gitea.app.keskikuja.site/niko/ci-cucumber:latest
build-push: build-push:
name: Build & Push Docker name: Build & Push Docker
+47
View File
@@ -0,0 +1,47 @@
name: CI Git-Pages Main
on:
push:
branches:
- main
- fix/helm-build-node
paths:
- git-pages/**
- .gitea/workflows/helm-build-push.yml
- .gitea/workflows/git-pages.*
workflow_dispatch:
jobs:
load-config:
name: Load git-pages.gitea-env.conf to pipeline env
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit
with:
config_path: .gitea/workflows/git-pages.gitea-env.conf
check-version:
name: Check existing artifact
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/check-version.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
helm-push:
name: Build & Push Helm chart
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: niko/gitea-ci-library/.gitea/workflows/helm-build-push.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
version: ${{ needs.check-version.outputs.version }}
chart_path: git-pages
report-summary:
name: Report Summary
needs: [load-config, helm-push]
if: always()
uses: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: ""
@@ -0,0 +1,5 @@
GITEA_API_URL=https://gitea.app.keskikuja.site
HELM_REGISTRY=gitea.app.keskikuja.site/niko
HELM_UI_URL=https://gitea.app.keskikuja.site/niko/-/packages/container
GIT_TAG_PREFIX=git-pages/
VERSION_FILE=git-pages/Chart.yaml
+103
View File
@@ -0,0 +1,103 @@
name: Helm Build & Push
on:
workflow_call:
inputs:
env_json:
required: true
type: string
version:
required: true
type: string
chart_path:
required: false
type: string
default: '.'
secrets:
GITEA_TOKEN:
required: true
HELM_USER:
required: false
HELM_PASSWORD:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
HELM_REGISTRY: ${{ fromJson(inputs.env_json).HELM_REGISTRY || '' }}
HELM_UI_URL: ${{ fromJson(inputs.env_json).HELM_UI_URL || '' }}
GIT_TAG_PREFIX: ${{ fromJson(inputs.env_json).GIT_TAG_PREFIX || '' }}
CHART_PATH: ${{ inputs.chart_path }}
VERSION: ${{ inputs.version }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-push:
runs-on: ubuntu-latest
container:
image: alpine/helm:3.19.0
steps:
- name: Install Node.js for actions/checkout
# COMPROMISE: Requires internet access.
# Does NOT work in air-gapped environments.
# Replace with a custom image (e.g., extending alpine/helm + nodejs) if needed.
run: apk add --no-cache nodejs
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Package Helm chart
run: |
helm package "${CHART_PATH}" \
--version "${VERSION}" \
--app-version "${VERSION}" \
--destination /tmp/helm-packages
- name: Push to OCI registry
env:
HELM_USER: ${{ secrets.HELM_USER || github.actor }}
HELM_PASSWORD: ${{ secrets.HELM_PASSWORD }}
run: |
REGISTRY="${HELM_REGISTRY:?HELM_REGISTRY not set in env.conf}"
echo "$HELM_PASSWORD" | helm registry login "${REGISTRY}" \
-u "$HELM_USER" \
--password-stdin
helm push /tmp/helm-packages/*.tgz "oci://${REGISTRY}"
helm registry logout "${REGISTRY}"
- name: Report status with UI link
if: success() && env.HELM_UI_URL != ''
run: |
CHART_NAME=$(grep '^name:' "${CHART_PATH}/Chart.yaml" | awk '{print $2}')
UI_URL="${HELM_UI_URL}/${CHART_NAME}/${VERSION}"
bash .ci/scripts/report-status.sh success "Helm chart ${VERSION}" ci-helm-build-push "" "$UI_URL"
tag-commit:
runs-on: ubuntu-latest
needs: [build-push]
steps:
- uses: actions/checkout@v4
- name: Create git tag
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
SERVER_URL: ${{ gitea.server_url }}
RUN_NUMBER: ${{ github.run_number }}
SHA: ${{ github.sha }}
run: |
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"$SERVER_URL/api/v1/repos/${{ github.repository }}/tags" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${GIT_TAG_PREFIX}${VERSION}\", \"message\": \"Build #$RUN_NUMBER\", \"target\": \"$SHA\"}")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "409" ]; then
exit 0
else
exit 1
fi
+1
View File
@@ -7,3 +7,4 @@ tmp/
coverage/ coverage/
.DS_Store .DS_Store
reports/ reports/
.vscode/
+2 -2
View File
@@ -1,3 +1,3 @@
FROM bats/bats:latest FROM bats/bats:1.11.0
RUN apk add --no-cache lsof python3 jq curl ruby && \ RUN apk add --no-cache lsof python3 jq curl ruby nodejs git && \
gem install bashcov -v 3.3.0 gem install bashcov -v 3.3.0
+2 -1
View File
@@ -123,8 +123,8 @@ Hae token Giteasta:
```bash ```bash
GITEA_URL="https://<gitea-server-url>" GITEA_URL="https://<gitea-server-url>"
GITEA_ACTIONS_TOKEN="<registration-token>"
GITEA_ACTIONS_NAMESPACE="gitea-actions" GITEA_ACTIONS_NAMESPACE="gitea-actions"
GITEA_ACTIONS_TOKEN="<registration-token>"
``` ```
### 3. Tee secret vain init install yhteydessä ### 3. Tee secret vain init install yhteydessä
@@ -158,6 +158,7 @@ helm upgrade --install act-runner gitea/actions \
--set giteaRootURL="$GITEA_URL" \ --set giteaRootURL="$GITEA_URL" \
--set existingSecret=act-runner-token \ --set existingSecret=act-runner-token \
--set existingSecretKey=token \ --set existingSecretKey=token \
--set statefulset.replicas=3 \
--set statefulset.runner.tag=1.0.8 \ --set statefulset.runner.tag=1.0.8 \
--set statefulset.dind.tag=29.5.2-dind \ --set statefulset.dind.tag=29.5.2-dind \
--set-string 'statefulset.runner.config=log: --set-string 'statefulset.runner.config=log:
+28 -6
View File
@@ -1,6 +1,6 @@
# AI Context: Gitea Actions CI -kirjasto # AI Context: Gitea Actions CI -kirjasto
**Updated**: 2026-06-15 (siivottu, provider/consumer-erottelu valmis) **Updated**: 2026-06-19 (provider/consumer dual role, Project Skills -aktivointi)
## Project Overview ## Project Overview
Gitea Actions reusable workflow -kirjasto mikropalveluiden build-, testaus-, Gitea Actions reusable workflow -kirjasto mikropalveluiden build-, testaus-,
@@ -11,8 +11,8 @@ käyttävät kirjastoa `uses:`-direktiivillä.
## Monorepo: kaksi erillistä kokonaisuutta ## Monorepo: kaksi erillistä kokonaisuutta
### 1. Juuri (`gitea-ci-library`) ### 1. Juuri (`gitea-ci-library`)
Provider-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio. Provider- ja consumer-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio,
Consumer kutsuu provider-workflowta `uses:`-direktiivillä. ja consumer-esimerkit (dogfood). Consumer kutsuu provider-workflowta `uses:`-direktiivillä.
### 2. `git-pages/` — oma kokonaisuus ### 2. `git-pages/` — oma kokonaisuus
Helm-chartti Codeberg git-pagesille. Täysin itsenäinen — oma dokumentaatio, Helm-chartti Codeberg git-pagesille. Täysin itsenäinen — oma dokumentaatio,
@@ -26,22 +26,25 @@ kuuluu `git-pages/docs/`-alle, ei juuren `docs/`-kansioon.
| `.gitea/workflows/config-provider.yml` | Provider: lataa + validoi config-tiedoston, tuottaa `env_json` | | `.gitea/workflows/config-provider.yml` | Provider: lataa + validoi config-tiedoston, tuottaa `env_json` |
| `.gitea/workflows/check-version.yml` | Provider: tarkistaa onko commitille jo artifact, laskee version | | `.gitea/workflows/check-version.yml` | Provider: tarkistaa onko commitille jo artifact, laskee version |
| `.gitea/workflows/docker-build-push.yml` | Provider: buildaa + puskea Docker-imagen, tagittaa commitin | | `.gitea/workflows/docker-build-push.yml` | Provider: buildaa + puskea Docker-imagen, tagittaa commitin |
| `.gitea/workflows/ci-container-build-push.yml` | Provider: buildaa + puskea CI-työkalukontin |
| `.gitea/workflows/example-*` | **Consumer-esimerkki**: tämän repon oma CI (dogfood) | | `.gitea/workflows/example-*` | **Consumer-esimerkki**: tämän repon oma CI (dogfood) |
| `scripts/` | Provider-skriptit: `report-status.sh`, `publish-git-pages.sh`, `ci-validate.sh` | | `scripts/` | Provider-skriptit: `report-status.sh`, `publish-git-pages.sh`, `ci-validate.sh` |
| `.gitea/scripts/` | **Consumer-skriptit**: `bats-coverage.sh`, `bats-report.sh` | | `.gitea/scripts/` | **Consumer-skriptit**: `bats-coverage.sh`, `bats-report.sh` |
| `docs/` | Arkkitehtuuri, ADRt (00040008) | | `docs/` | Arkkitehtuuri, ADRt (00040008) |
| `skills/consumer-pipelines/` | Consumer-pipeline-standardit — AI:n pakottavat säännöt consumer-CI:lle | | `skills/consumer-pipelines/` | Consumer-pipeline-standardit (ks. Project Skills). Koskee vain consumer-puolta |
| `skills/ci-container-build/` | CI-kontin build-workflow'n template (ks. Project Skills) |
| `docs/adr/` | Architecture Decision Records | | `docs/adr/` | Architecture Decision Records |
| `git-pages/` | Raporttien hostaus (Helm-chartti) | | `git-pages/` | Raporttien hostaus (Helm-chartti) |
| `tests/` | Bats-testit skripteille | | `tests/` | Bats-testit skripteille |
### Provider workflowt (4 kpl) ### Provider workflowt (5 kpl)
| Workflow | Input | Output | Kuvaus | | Workflow | Input | Output | Kuvaus |
|---|---|---|---| |---|---|---|---|
| `config-provider.yml` | `config_path` | `env_json`, `config_path` | Validoi ja jäsentää `.conf` → JSON. Sama kutsu hoitaa validoinnin. | | `config-provider.yml` | `config_path` | `env_json`, `config_path` | Validoi ja jäsentää `.conf` → JSON. Sama kutsu hoitaa validoinnin. |
| `check-version.yml` | `env_json` | `artifact_exists`, `version` | Tarkistaa git-tagit ja `package.json`:n, laskee seuraavan version. Vain main-haarassa. | | `check-version.yml` | `env_json` | `artifact_exists`, `version` | Tarkistaa git-tagit ja `package.json`:n, laskee seuraavan version. Vain main-haarassa. |
| `docker-build-push.yml` | `env_json`, `version` | — | Buildaa Docker-imagen, puskea rekisteriin, tagittaa commitin. | | `docker-build-push.yml` | `env_json`, `version` | — | Buildaa Docker-imagen, puskea rekisteriin, tagittaa commitin. |
| `ci-container-build-push.yml` | `env_json`, `dockerfile_path`, `image_name`, `tag` | — | Buildaa CI-työkalukontin, puskea rekisteriin. Ei versiointia eikä git-tägäystä. |
| `report-summary.yml` | `env_json`, `suites` | — | Generoi `GITHUB_STEP_SUMMARY`-taulukon raporttilinkeillä (Gitea 1.27+) | | `report-summary.yml` | `env_json`, `suites` | — | Generoi `GITHUB_STEP_SUMMARY`-taulukon raporttilinkeillä (Gitea 1.27+) |
### Example-tiedostot (consumer-referenssi) ### Example-tiedostot (consumer-referenssi)
@@ -54,9 +57,28 @@ kuuluu `git-pages/docs/`-alle, ei juuren `docs/`-kansioon.
| `example-cucumber-tests.yml` | workflow_call | Hyväksymätestit Cucumberilla, raportit git-pagesiin, status linkillä | | `example-cucumber-tests.yml` | workflow_call | Hyväksymätestit Cucumberilla, raportit git-pagesiin, status linkillä |
| `example-gitea-env.conf` | — | KEY=VALUE config tälle repolle | | `example-gitea-env.conf` | — | KEY=VALUE config tälle repolle |
## Provider & Consumer Dual Role
Tämä repo on **yhtä aikaa sekä provider että consumer**. Eri puolilla on eri säännöt:
- **Provider-puoli**: `.gitea/workflows/*.yml` (pl. `example-*`), `scripts/` — reusable workflowt joita muut projektit kutsuvat. Saa käyttää `docker run` -komentoja (esim. `docker-build-push.yml`). Consumer-pipeline-standardit (`skills/consumer-pipelines/`) eivät koske provideria.
- **Consumer-puoli**: `.gitea/workflows/example-*`, `.gitea/scripts/` — tämän repon oma CI (dogfood), toimii consumer-esimerkkinä. Käyttää `@main`-refiä provider-viittauksissa (sama repo). Noudattaa `skills/consumer-pipelines/`-sääntöjä.
- **Ulkoiset consumerit** käyttävät `@v1`-tagia provider-viittauksissa.
## Project Skills (skills/)
Tämä projekti sisältää omia `.ai/skills/`-järjestelmästä riippumattomia skillejä `skills/`-kansiossa. Jokainen alihakemisto sisältää `SKILL.md`:n jossa on `activation-gate`-kenttä.
**Sääntö:** Uuden tehtävän alussa skannaa `skills/*/SKILL.md` ja arvioi jokaisen `activation-gate` annettua tehtävää vasten. Jos gate matchaa, lataa skill aktiiviseksi ohjeeksi ennen toimenpiteitä.
| Skill | Gate | Kuvaus |
|---|---|---|
| `skills/consumer-pipelines/` | Consumer-pipeline-muutokset | Consumer-pipeline-standardit: reitittimen puhtaus, exit-koodi, konttipolitiikka, raportointi, nimeäminen. Koskee vain consumer-puolta. |
| `skills/ci-container-build/` | CI-kontin build | CI-kontin build-workflown template ja Dockerfile-ohjeet |
## Key Technical Decisions ## Key Technical Decisions
- **Provider & Consumer -malli**: `example-*`-tiedostot ovat consumer-esimerkkejä, provider-workflowt reusableja. ADR 0005. - **Provider & Consumer -malli**: Tämä repo on sekä provider että consumer. Provider-workflowt reusableja muille, `example-*`-tiedostot tämän repon oma consumer-CI (dogfood). ADR 0005.
- **Vain Gitea, vain reusable workflowt**: ei custom actioneita, ei multi-platform - **Vain Gitea, vain reusable workflowt**: ei custom actioneita, ei multi-platform
- **Commit-status API vain raporttilinkeille**: Tool-jobit luottavat natiiviin. Test-jobit käyttävät API:a koska se on ainoa tapa upottaa raporttilinkki. ADR 0004, 0007. - **Commit-status API vain raporttilinkeille**: Tool-jobit luottavat natiiviin. Test-jobit käyttävät API:a koska se on ainoa tapa upottaa raporttilinkki. ADR 0004, 0007.
- **Exit-koodi on ainoa onnistumisen mittari**: Ei pipeä, ei tiedostoheuristiikkaa. ADR 0008. - **Exit-koodi on ainoa onnistumisen mittari**: Ei pipeä, ei tiedostoheuristiikkaa. ADR 0008.
+1 -1
View File
@@ -38,4 +38,4 @@ suoraan Gitea UI:ssa.
| **Multi-Git-platform** | Vain Gitea — yksi alusta kunnolla (periaate 10) | | **Multi-Git-platform** | Vain Gitea — yksi alusta kunnolla (periaate 10) |
| **Custom actionit** | Reusable workflow on kevyempi ja natiivimpi (periaate 2) | | **Custom actionit** | Reusable workflow on kevyempi ja natiivimpi (periaate 2) |
| **Ulkoinen orkestraattori** | Gitean `needs` + `if` hoitaa ohjauksen | | **Ulkoinen orkestraattori** | Gitean `needs` + `if` hoitaa ohjauksen |
| **Artifactory/Nexus** | Gitea Packages riittää MVP:ssä | | **Artifactory/Nexus** | Build & push toimii Docker-standardilla. UI-tason linkitys (`report-summary`) vaatii Nexus/Artifactory-spesifin URL-rakenteen — ei vielä toteutettu, toteutetaan tarvittaessa |
+64 -2
View File
@@ -63,15 +63,77 @@ checkout → laske versio package.json + git-tageista → output
**Trigger:** `workflow_call` **Trigger:** `workflow_call`
**Inputs:** `env_json`, `version` **Inputs:**
| Parametri | Pakollinen | Kuvaus |
|-----------|------------|--------|
| `env_json` | Kyllä | Konffi `gitea-env.conf`:stä |
| `version` | Kyllä | Version string (check-version output) |
**`env_json`-avaimet:**
| Avain | Pakollinen | Kuvaus |
|-------|------------|--------|
| `DOCKER_REGISTRY` | Kyllä | Registry (esim. `gitea.app.keskikuja.site/niko`) |
| `DOCKER_IMAGE_NAME` | Kyllä | Kuvan nimi ilman registry-polkua |
| `DOCKER_UI_URL` | Ei | Registry UI -linkki raportointia varten |
| `DOCKERFILE` | Ei | Dockerfile-polku, oletus `Dockerfile` |
| `GITEA_API_URL` | Kyllä | Gitean API-URL |
| `GIT_TAG_PREFIX` | Ei | Tag-prefix (esim. `docker/`) |
**Secrets:** `GITEA_TOKEN`, `DOCKER_USERNAME`, `DOCKER_PASSWORD` **Secrets:** `GITEA_TOKEN`, `DOCKER_USERNAME`, `DOCKER_PASSWORD`
**Steppi-kaavio:** **Steppi-kaavio:**
``` ```
build-push (build + push samassa jobissa, ei levyn kautta) → tag-commit build-push (build + push, labelit: commit+date) → tag-commit (git-tagin luonti)
``` ```
**Huomio:** Ei käytä `container:`-direktiiviä — ajaa suoraan runnerilla,
joten `actions/checkout` toimii ilman node-asennuksia.
---
### `helm-build-push.yml` — Helm chart build & push
**Trigger:** `workflow_call`
**Inputs:**
| Parametri | Pakollinen | Kuvaus |
|-----------|------------|--------|
| `env_json` | Kyllä | Konffi `gitea-env.conf`:stä |
| `version` | Kyllä | Version string (check-version output) |
| `chart_path` | Ei | Polku Chart.yaml-hakemistoon, oletus `.` |
**`env_json`-avaimet:**
| Avain | Pakollinen | Kuvaus |
|-------|------------|--------|
| `HELM_REGISTRY` | Kyllä | OCI-registry (esim. `gitea.app.keskikuja.site/niko`) |
| `HELM_UI_URL` | Ei | Registry UI -linkki raportointia varten |
| `GITEA_API_URL` | Kyllä | Gitean API-URL |
| `GIT_TAG_PREFIX` | Ei | Tag-prefix (esim. `helm/`) |
**Secrets:** `GITEA_TOKEN`, `HELM_USER`, `HELM_PASSWORD`
**Steppi-kaavio:**
```
build-push (helm package → helm push OCI) → tag-commit (git-tagin luonti)
```
**Steppien kuvaus `build-push`-jobissa:**
1. **Node.js-asennus**`apk add --no-cache nodejs` (vaaditaan `actions/checkout`-actionia varten)
2. **Checkout** — sovellusrepo ja gitea-ci-library `.ci/`-polkuun
3. **Package**`helm package` versiolla `$VERSION`
4. **Push OCI**`helm push` registryyn autentikoinnilla
5. **Report status** — commit-status + UI-linkki
**Kompromissi:** Kontti `alpine/helm` ei sisällä node.js:ää, mutta
`actions/checkout@v4` on JavaScript-action ja vaatii sen. Siksi nodejs
asennetaan lennossa ennen checkouttia. Tämä vaatii internet-yhteyden
eikä toimi air gap -ympäristössä. Korvaa tarvittaessa custom-kontilla
(jossa helm + nodejs, ks. `skills/ci-container-build/SKILL.md`).
--- ---
## Consumer-esimerkki (`example-*`) ## Consumer-esimerkki (`example-*`)
+2 -2
View File
@@ -68,8 +68,8 @@ open "https://gitea.app.keskikuja.site/${REPO_OWNER}/${REPO_NAME}/settings/actio
> 💡 **Monelle repoille:** Toista vaiheet 34, tai katso [automatisointi](docs/secrets.md#automatisointi-useamman-repon-salaisuuden-lis%C3%A4%C3%A4miseen). > 💡 **Monelle repoille:** Toista vaiheet 34, tai katso [automatisointi](docs/secrets.md#automatisointi-useamman-repon-salaisuuden-lis%C3%A4%C3%A4miseen).
--- ---
## Käyttöönotto ## Käyttöönotto
### 1. Secretit ### 1. Secretit
+113
View File
@@ -0,0 +1,113 @@
# Helm Registry Setup (OCI)
Pipeline paketoi Helm chartin OCI-artefaktiksi ja pushee sen OCI-rekisteriin.
---
## 1. Konfiguroi `gitea-env.conf`
```
# HELM_REGISTRY on muotoa: registry.example.com/org
#
# host+org: registry.example.com/org
#
# Pipeline rakentaa OCI-refin: oci://${HELM_REGISTRY}/<chart-name>:${VERSION}
# (chart-name tulee Chart.yaml:n name-kentästä)
HELM_REGISTRY=gitea.app.keskikuja.site/niko # PAKOLLINEN — tyhjä ei käy
HELM_UI_URL= # valinnainen — tarkista Giteasta kontin oma UI-osoite, workflow liittää perään /chart-name/VERSION
GIT_TAG_PREFIX=git-pages/ # valinnainen — monorepo-tägäys
VERSION_FILE=git-pages/Chart.yaml # valinnainen — jos Chart.yaml ei rootissa
```
| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `HELM_REGISTRY` | **kyllä** | Registry host + owner (esim. `gitea.app.site/niko`). **Tyhjä pysäyttää workflow'n.** |
| `HELM_UI_URL` | ei | Base-URL OCI-paketin UI-sivulle (ilman chart-nimeä ja versiota). Osoite riippuu onko paketti linkitetty repoon vai ei — tarkista Giteasta. Workflow liittää perään `/chart-name/VERSION`. Jos tyhjä, commit-statusia ei erikseen aseteta. |
| `GIT_TAG_PREFIX` | ei | Etuliite git-tägille. Pakollinen monorepossa, jotta tagit eivät sekoitu muihin komponentteihin. |
| `VERSION_FILE` | ei | Polku version lähteeseen (Chart.yaml, package.json, VERSION). Oletus: juuren `Chart.yaml`. |
**OCI-ref = `oci://${HELM_REGISTRY}/<chart-name>:${VERSION}`**
Esim. `oci://gitea.app.keskikuja.site/niko/git-pages:1.2.3`
Chartin nimi (`<chart-name>`) määräytyy `Chart.yaml`-tiedoston `name`-kentästä.
---
## 2. Luo PAT (Personal Access Token) Giteassa
**Gitea → oma profiili (oikea yläkulma) → Settings → Applications → Manage Access Tokens → Generate New Token**
Valitse scope:
| Scope | Pääsy |
|---|---|
| `package` | **Read and Write** |
> Tämä token toimii salasanana `helm registry login` -komennossa. Muut scopet (kuten `repository`) eivät riitä — konttirekisteri vaatii nimenomaan `package`-scopen.
Tokenin arvo näytetään **vain kerran** luomisen yhteydessä. Kopioi se talteen.
---
## 3. Tallenna PAT repositoryn Secretsiin
Nämä ovat kaksi eri paikkaa:
- **Access Tokenit** (User Settings) = missä luot tokenin
- **Repository Secrets** (Repository Settings) = minne talletat sen workflow'n käyttöön
**Repository → Settings → Actions → Secrets → Add new secret**
| Secret | Arvo |
|---|---|
| `HELM_PASSWORD` | Edellisessä vaiheessa luotu PAT |
`HELM_USER`-secretiä **ei tarvita**. Workflow käyttää automaattisesti `${{ github.actor }}` (workflowin käynnistäjä).
Jos registry vaatii eri käyttäjätunnuksen kuin `github.actor` (esim. Harbor, Artifactory), lisää myös:
| Secret | Arvo |
|---|---|
| `HELM_USER` | Registryn käyttäjätunnus |
---
## 4. Tarkistuslista ennen ajoa
- [ ] `HELM_REGISTRY` asetettu `gitea-env.conf`issa
- [ ] (tarvittaessa) `HELM_UI_URL` asetettu — ilman tätä commit-statusia ei erikseen aseteta
- [ ] PAT luotu Giteassa scopella `package` Read and Write
- [ ] `HELM_PASSWORD`-secret tallennettu repositoryn Secretsiin (se PAT)
- [ ] (tarvittaessa) `HELM_USER`-secret — oletus `github.actor`
---
## 5. Esimerkkejä eri polkurakenteista
### 5a. Hosti + org — Gitea user-taso
```
HELM_REGISTRY=gitea.app.keskikuja.site/niko
```
- OCI-ref: `oci://gitea.app.keskikuja.site/niko/git-pages:1.2.3`
- Paketti käyttäjän `niko` alla. Linkitys repoon tehdään Gitean UI:sta: paketin sivulta (Package → Settings) → linkitä repositoryyn.
- `HELM_PASSWORD` = Gitea PAT scopella `package`
### 5b. Hosti + org — Harbor
```
HELM_REGISTRY=harbor.example.com/projekti
```
- `HELM_USER` = Harbor-käyttäjä
- `HELM_PASSWORD` = Harbor-token
### 5c. Artifactory
```
HELM_REGISTRY=artifactory.example.com/helm-local
```
- `HELM_USER` = service account
- `HELM_PASSWORD` = API-token
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -e
RAW_VERSION=""
if [ -n "${VERSION_FILE-}" ] && [ -f "${VERSION_FILE-}" ]; then
RAW_VERSION=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "${VERSION_FILE}")
if [ -z "${RAW_VERSION}" ]; then
if echo "${VERSION_FILE}" | grep -q -E '\.json$'; then
RAW_VERSION=$(jq -r '.version' "${VERSION_FILE}")
else
RAW_VERSION=$(cat "${VERSION_FILE}" | tr -d '[:space:]')
fi
fi
fi
if [ -z "${RAW_VERSION}" ]; then
if [ -f VERSION ]; then
RAW_VERSION=$(cat VERSION | tr -d '[:space:]')
elif [ -f package.json ]; then
RAW_VERSION=$(jq -r '.version' package.json)
elif [ -f pom.xml ]; then
RAW_VERSION=$(grep -oP '<version>\K[^<]+' pom.xml | head -1)
elif [ -f Chart.yaml ]; then
RAW_VERSION=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' Chart.yaml)
else
echo "ERROR: No version source found (VERSION_FILE, VERSION, package.json, pom.xml, Chart.yaml)" >&2
exit 1
fi
fi
BASE_VERSION=$(echo "$RAW_VERSION" | cut -d'.' -f1-2)
echo "gitea-ci-library - Tunnistettu Major.Minor versio: $BASE_VERSION"
TAGS_JSON=$(curl -s -f -H "Authorization: token ${GITEA_TOKEN}" \
"${SERVER_URL}/api/v1/repos/${REPO}/tags")
TAG=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX-}" --arg sha "${SHA}" '
if type == "array" then
.[] | select(.commit.sha == $sha and (.name | startswith($prefix))) | .name
else empty end' | head -1)
mkdir -p /tmp/build-ctx
if [ -n "$TAG" ]; then
echo "ARTIFACT_EXISTS=true" > /tmp/build-ctx/build.env
echo "NEXT_VERSION=$TAG" >> /tmp/build-ctx/build.env
echo "gitea-ci-library - Artefakti löytyi jo tagilla: $TAG."
else
echo "ARTIFACT_EXISTS=false" > /tmp/build-ctx/build.env
HIGHEST_PATCH=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX-}" --arg bv "${GIT_TAG_PREFIX-}${BASE_VERSION}." '
if type == "array" then .[] | .name | select(startswith($bv)) | sub($bv; "") | tonumber else empty end' | sort -rn | head -1)
if [ -z "$HIGHEST_PATCH" ]; then NEXT_PATCH=0; else NEXT_PATCH=$((HIGHEST_PATCH + 1)); fi
FULL_VERSION="${BASE_VERSION}.${NEXT_PATCH}"
echo "NEXT_VERSION=$FULL_VERSION" >> /tmp/build-ctx/build.env
echo "gitea-ci-library - Uusi vapaa versio: $FULL_VERSION"
fi
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
set -euo pipefail
DESCRIPTION="${1:-}"
CONTEXT="${2:-}"
SUITE="${3:-}"
STATUS="${4:-success}"
[ -n "$DESCRIPTION" ] || { echo "ERROR: description argument required" >&2; exit 1; }
[ -n "$CONTEXT" ] || { echo "ERROR: context argument required" >&2; exit 1; }
[ -n "$SUITE" ] || { echo "ERROR: suite argument required" >&2; exit 1; }
REPORT_DIR="reports/${SUITE}"
if [ ! -d "$REPORT_DIR" ]; then
echo "ERROR: $REPORT_DIR not found" >&2
bash .ci/scripts/report-status.sh failure "$DESCRIPTION" "$CONTEXT"
exit 1
fi
FILES=()
while IFS= read -r -d '' f; do
FILES+=("$(basename "$f")")
done < <(find "$REPORT_DIR" -maxdepth 1 -type f ! -name index.html -print0 2>/dev/null || true)
SUBDIRS=()
while IFS= read -r -d '' d; do
name="${d#$REPORT_DIR/}"
[ -f "$d/index.html" ] && SUBDIRS+=("$name")
done < <(find "$REPORT_DIR" -maxdepth 1 -type d ! -name . -print0 2>/dev/null || true)
TOTAL=$(( ${#FILES[@]} + ${#SUBDIRS[@]} ))
if [ "$TOTAL" -eq 0 ]; then
echo "ERROR: no reportable items in $REPORT_DIR" >&2
bash .ci/scripts/report-status.sh failure "$DESCRIPTION" "$CONTEXT"
exit 1
fi
SHA8="${GITHUB_SHA:0:8}"
humanize() {
local name="$1"
name="${name%.*}"
name="${name//-/ }"
name="${name//_/ }"
echo "${name^}"
}
generate_index() {
local html
html='<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
html+="<title>$DESCRIPTION</title>"
html+='<style>body{font-family:sans-serif;margin:2em;max-width:960px}h1{color:#1e293b}ul{list-style:none;padding:0}li{margin:.5em 0;padding:.5em;background:#f8fafc;border-radius:6px}a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}</style>'
html+="</head><body><h1>$DESCRIPTION</h1><ul>"
for f in "${FILES[@]}"; do
html+="<li><a href=\"$f\">$(humanize "$f")</a></li>"
done
for d in "${SUBDIRS[@]}"; do
html+="<li><a href=\"$d/index.html\">${d^}</a></li>"
done
html+='</ul></body></html>'
printf '%s' "$html" > "$REPORT_DIR/index.html"
}
STAGED="reports/${SHA8}/${SUITE}"
mkdir -p "$STAGED"
if [ "$TOTAL" -eq 1 ]; then
cp -a "$REPORT_DIR/." "$STAGED/"
bash .ci/scripts/publish-git-pages.sh "$SUITE"
if [ ${#FILES[@]} -eq 1 ]; then
ENTRY="${FILES[0]}"
else
ENTRY="${SUBDIRS[0]}/index.html"
fi
URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${SHA8}/${SUITE}/${ENTRY}"
bash .ci/scripts/report-status.sh "$STATUS" "$DESCRIPTION" "$CONTEXT" "" "$URL"
else
generate_index
cp -a "$REPORT_DIR/." "$STAGED/"
bash .ci/scripts/publish-git-pages.sh "$SUITE"
bash .ci/scripts/report-status.sh "$STATUS" "$DESCRIPTION" "$CONTEXT" "$SUITE"
fi
rm -rf "$STAGED"
+36 -1
View File
@@ -33,7 +33,42 @@ else
fi fi
mkdir -p "$TARGET" mkdir -p "$TARGET"
cp -a "$REPORT_DIR/." "$TARGET/" cp -a "$REPORT_DIR/." "$TARGET/"
cat > "$WORK/${OWNER}/${REPO}/reports/${SHA8}/.meta" <<EOF if [ ! -f "$TARGET/index.html" ]; then
items=()
while IFS= read -r -d '' f; do
items+=("$(basename "$f")")
done < <(find "$TARGET" -maxdepth 1 -type f ! -name index.html -print0 2>/dev/null || true)
while IFS= read -r -d '' d; do
name=$(basename "$d")
[ -f "$d/index.html" ] && items+=("$name")
done < <(find "$TARGET" -maxdepth 1 -type d ! -name . -print0 2>/dev/null || true)
if [ ${#items[@]} -gt 1 ]; then
{
echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
echo "<title>Test report ${SHA8}</title>"
echo '<style>body{font-family:sans-serif;margin:2em;max-width:960px}'
echo 'h1{color:#1e293b}ul{list-style:none;padding:0}'
echo 'li{margin:.5em 0;padding:.5em;background:#f8fafc;border-radius:6px}'
echo 'a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}'
echo '</style></head><body>'
echo "<h1>Test report <code>${SHA8}</code></h1><ul>"
for item in "${items[@]}"; do
label="${item%.*}"
label="${label//-/ }"
label="${label//_/ }"
if [ -f "$TARGET/$item" ]; then
echo "<li><a href=\"$item\">${label^}</a></li>"
else
echo "<li><a href=\"$item/index.html\">${label^}</a></li>"
fi
done
echo '</ul></body></html>'
} > "$TARGET/index.html"
fi
fi
cat > "$TARGET/.meta" <<EOF
{"branch":"${GITHUB_REF_NAME:-}","sha":"${GITHUB_SHA}","published_at":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"} {"branch":"${GITHUB_REF_NAME:-}","sha":"${GITHUB_SHA}","published_at":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
EOF EOF
find "$WORK/$OWNER" \( -type f -o -type l \) -print | sed "s|^${WORK}/||" | tar -cf "$TAR" -C "$WORK" -T - find "$WORK/$OWNER" \( -type f -o -type l \) -print | sed "s|^${WORK}/||" | tar -cf "$TAR" -C "$WORK" -T -
+149
View File
@@ -0,0 +1,149 @@
---
name: ci-container-build
description: |
Creating or modifying CI container build workflows. Activates when consumer
needs to build a custom CI container image (multi-tool image for tests,
linting, validation) that is pushed to a container registry for use in
other pipeline jobs.
activation-gate: |
User mentions CI container, custom CI image, ci-container-build, Dockerfile
for CI tools, multi-tool container, or needs to build/push a container that
other CI jobs will use as their runtime environment.
category: ci
impact: high
---
# CI Container Build — Template
Template jolla consumer luo oman CI-kontin build-workflown.
Kontti sisältää useamman työkalun yhdistelmän (esim. helm + kubeconform + xsltproc),
jota muut jobit käyttävät ajonaikaisena ympäristönä.
## Rakenne
Vain `workflow_dispatch`**ei automaattista buildausta koskaan**. Kontti buildataan
manuaalisesti Gitea Actions UI:sta. Tiedoston ilmestyessä main-haaraan workflow näkyy
välittömästi Actions-tabissa ajettavana.
Build-prosessi lataa ensin `config-provider.yml`:llä `DOCKER_REGISTRY`:n
conf-tiedostosta, sitten buildaa ja puskaa kontin.
Kun kontti on pushattu registryyn, se on muiden pipeline-jobien käytettävissä
`latest`-tägillä — rebuild = käyttöönotto. Mitään versioviittauksia ei tarvitse
päivittää.
## Nimeäminen
CI-kontin build-workflow noudattaa samaa nimeämiskonventiota kuin muutkin
tiedostot `.gitea/workflows/`-kansiossa:
```
<komponentti>.ci-feature.yml ← feature-haaran reititin
<komponentti>.ci-main.yml ← main-haaran reititin
<komponentti>.<testityyppi>.yml ← yksittäinen testi tai operaatio
<komponentti>.ci-container-build-<kontti>.yml ← CI-kontin build-workflow
<komponentti>.gitea-env.conf ← komponenttikohtainen konfiguraatio
```
Single repossa `<komponentti>` jätetään pois — tiedostot ovat suoraan `ci-feature.yml`,
`ci-main.yml`, `<testityyppi>.yml`, `ci-container-build-<kontti>.yml`.
Monorepossa prefiksi pitää komponentin tiedostot yhdessä: `ls <komponentti>.*` löytää kaikki
kerralla. **Olemassaolevia prefiksejä ei saa poistaa.**
Esimerkkejä:
```
.gitea/workflows/chart.ci-container-build-helm.yml
.gitea/workflows/api.ci-container-build-node.yml
.gitea/workflows/ci-container-build-bats.yml ← single repo
```
## Template
> **Korvaa kaikki `__SUURAAKKOSET__`-placeholderit projektin todellisilla arvoilla.**
> Ainoastaan `${{ ... }}`-syntaksilla merkityt Gitea Actions -muuttujat ovat ajonaikaisia
> eikä niitä korvata.
Luo `.gitea/workflows/__KOMPONENTTI__.ci-container-build-__KONTTI__.yml`
(single repo: `ci-container-build-__KONTTI__.yml`):
```yaml
name: CI Container Build & Push
on:
workflow_dispatch:
inputs:
config_path:
required: true
type: string
description: 'Polku .gitea-env.conf-tiedostoon (esim. .gitea/workflows/chart.gitea-env.conf)'
dockerfile_path:
required: true
type: string
description: 'Polku Dockerfileen (esim. ci-helm.Dockerfile)'
image_name:
required: true
type: string
description: 'Kontin nimi ilman registry-polkua (esim. ci-helm)'
tag:
required: true
type: string
default: 'latest'
description: 'Image-tägi'
jobs:
load-config:
uses: __OWNER__/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: ${{ inputs.config_path }}
build-push:
needs: [load-config]
uses: __OWNER__/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: ${{ inputs.dockerfile_path }}
image_name: ${{ inputs.image_name }}
tag: ${{ inputs.tag }}
```
### Käyttö
**Gitea Actions UI:sta** (heti kun tiedosto on main-haarassa):
```
Gitea → Actions → CI Container Build & Push → Run workflow
config_path: .gitea/workflows/__KOMPONENTTI__.gitea-env.conf
dockerfile_path: ci-__TYÖKALU__.Dockerfile
image_name: ci-__TYÖKALU__
tag: latest
```
### Dockerfile
Dockerfile yhdistää tarvitut työkalut yhteen konttiin. Molemmat tavat kelpaavat:
```dockerfile
# Tapa A: COPY --from toisesta imagesta
FROM __BASE_IMAGE__:__VERSION__
COPY --from=__SOURCE_IMAGE__:__VERSION__ /path/to/binary /usr/local/bin/
RUN apk add --no-cache __PAKETIT__ # Ei koskaan git:iä — kloonaus kuuluu pipelinelle
# Tapa B: curl-lataus (normaali Dockerfilessa)
FROM __BASE_IMAGE__:__VERSION__
RUN apk add --no-cache curl __PAKETIT__ && \
curl -fsSL __URL__/__BINARY__.tar.gz | tar xz -C /usr/local/bin && \
apk del curl
```
`COPY --from` on kevyempi (ei curl-asennusta). `curl` on selkeämpi kun binääri
tulee suoraan GitHub Releasesista tai vastaavasta.
## Mitä EI kannata tehdä
- Älä lisää `workflow_call`-triggariä — CI-konttia ei koskaan buildata automaattisesti
- Älä poista `<komponentti>.`-prefiksiä olemassaolevista tiedostoista — ne kuuluvat monorepo-nimeämiskonventioon
- Älä sisällytä CI-konttiin mitään sovelluskoodia — vain työkalut
- Älä koskaan asenna `git`:iä CI-konttiin — repon kloonaus ja checkout ovat Gitea Actionsin natiiveja operaatioita, eivät kontin vastuulla. Git paisuttaa konttia turhaan ja luo harhan että kontti hallitsee repoa
+480
View File
@@ -0,0 +1,480 @@
# Consumer Pipelines — Reference
Mallipohjat, esimerkit ja konfiguraatiot. Katso säännöt `SKILL.md`:stä.
## Reititin — täydellinen esimerkki
```yaml
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
<test-1>:
needs: [load-config]
uses: ./.gitea/workflows/<component>.<test-1>.yml
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
<test-2>:
needs: [load-config]
uses: ./.gitea/workflows/<component>.<test-2>.yml
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
report-summary:
needs: [load-config, <test-1>, <test-2>]
if: always()
uses: <owner>/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: <suite-1> <suite-2>
```
## CI-kontin build — parametroitu workflow
CI-kontin build on `workflow_dispatch`-triggeröity job, joka näkyy Gitea Actionsissa kuten Jenkinsin
parametroitu job — käyttäjä antaa inputit UI:sta ennen ajoa.
```yaml
name: CI Container Build <työkalu>
on:
workflow_dispatch:
inputs:
config_path:
required: true
type: string
default: '.gitea/workflows/<komponentti>.gitea-env.conf'
description: 'Polku .gitea-env.conf-tiedostoon'
dockerfile_path:
required: true
type: string
default: '<komponentti>/Dockerfile.ci-<työkalu>'
description: 'Polku Dockerfileen'
image_name:
required: true
type: string
default: 'ci-<työkalu>'
description: 'Kontin nimi ilman registry-polkua'
tag:
required: true
type: string
default: 'latest'
description: 'Image-tägi'
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: ${{ inputs.config_path }}
build-push:
needs: [load-config]
uses: <owner>/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: ${{ inputs.dockerfile_path }}
image_name: ${{ inputs.image_name }}
tag: ${{ inputs.tag }}
```
### CI-kontin ajaminen testijobissa
**Ainoa sallittu tapa** consumer-puolella on `container:`-direktiivi. `docker run` komennolla
kontin käynnistäminen stepin sisällä on anti-pattern. `container:`-direktiivillä kaikki stepit
ajetaan samassa kontissa — tiedostot ovat suoraan filesystemillä eikä erillistä
volyyminhallintaa tarvita.
```yaml
jobs:
<työkalu>:
runs-on: ubuntu-latest
container:
image: ${{ inputs.<image-name> }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: <owner>/gitea-ci-library
path: .ci
- name: Run <työkalu>
shell: bash
run: |
mkdir -p "reports/<suite>"
<komento> > "reports/<suite>/results.txt" 2>&1
- name: Post-process reports
if: always()
run: |
<mahdollinen_raporttien_jälkikäsittely>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
```
**Usean runnerin cache-ongelma:** Jos eri kerroilla käynnistyy eri runnereita,
niillä voi olla eri versio `latest`-imagen digesteistä. Ratkaisuja:
- Rebuildaa kontti ja aja `docker pull <image>` manuaalisesti kaikilla runnereilla
- Käytä versioitua tagia (`v2`, `v3`, ...) ja päivitä workflow'n default buildauksen jälkeen
**Mallit:**
- `example-cucumber-tests.yml` — ei post-processia
- `example-bats-tests.yml` — post-process coverage + report
## Raporttitasot — tarkat YAML-mallit
### Taso 1: Ei jälkikäsittelyä
```yaml
- name: Run tests
shell: bash
run: |
mkdir -p "reports/<suite>"
<testikomento>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
```
### Taso 2: Jälkikäsittely tarvitaan
```yaml
- name: Run tests
shell: bash
run: |
mkdir -p "reports/<suite>"
<testikomento> > "reports/<suite>/results.txt" 2>&1
- name: Post-process coverage
if: always()
run: <siirrä coverage-data reports/<suite>/coverage/-hakemistoon>
- name: Post-process test report
if: always()
run: <HTML-generointi raa'asta outputista>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
```
### Väärin vs oikein — yksi asia per step
```yaml
# VÄÄRIN — helm template fail → kubeconform jää ajamatta, report jää tekemättä
- name: Run tests
run: |
helm template ... > /tmp/manifests.yaml
kubeconform ... > results.txt 2>&1
# OIKEIN — erilliset stepit
- name: Helm template
run: helm template platform-helm/ -f values.yaml > /tmp/manifests.yaml 2>&1
- name: Kubeconform
if: success()
run: |
mkdir -p reports/kubeconform
kubeconform ... > reports/kubeconform/results.txt 2>&1
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "Helm kubeconform" helm-test kubeconform ${{ job.status }}
```
### Väärin vs oikein — post-process
```yaml
# VÄÄRIN — jos coverage epäonnistuu, report jää generoimatta
- name: Post-process reports
run: |
bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
bash .ci/.gitea/scripts/bats-report.sh reports/bats
# OIKEIN — erilliset stepit if: always()
- name: Post-process coverage
if: always()
run: bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
- name: Post-process test report
if: always()
run: bash .ci/.gitea/scripts/bats-report.sh reports/bats
```
## Raportin julkaisukelpoisuus
`ci-report.sh` päättää onko raportti julkaisukelpoinen skannaamalla `reports/<suite>/`-hakemistoa.
### Mitä skannataan
| Mitä | Sääntö |
|---|---|
| **Tiedostot (FILES)** | Kaikki `reports/<suite>/`-juuressa olevat tiedostot paitsi `index.html` |
| **Alihakemistot (SUBDIRS)** | Vain ne, joissa on `index.html` |
### Julkaisukelpoisuus
| Tila | Seuraus |
|---|---|
| `FILES + SUBDIRS = 0` | **Failure**`ci-report.sh` palauttaa virheen, raporttia ei julkaista |
| `FILES + SUBDIRS = 1` | Suora linkki itemiin — ei generoi index-sivua |
| `FILES + SUBDIRS > 1` | Generoi `reports/<suite>/index.html`-sivun, linkit kaikkiin itemeihin |
### Hakemistorakenne
```
reports/<suite>/
├── results.txt ← testin stdout (skannataan FILES)
├── test-report.html ← generoitu HTML (skannataan FILES)
└── <mikä tahansa>/ ← alihakemisto (skannataan SUBDIRS)
└── index.html ← VAIN jos tämä on olemassa
```
### Esimerkki: coverage-näkymä
```
reports/<suite>/coverage/index.html ← on olemassa
```
Coverage-dataa ei siirretä automaattisesti. Testin tai post-process-stepin pitää
siirtää coverage `reports/<suite>/coverage/`-hakemistoon ja varmistaa että `index.html` on mukana.
**Provider vastuulla:** `ci-report.sh` (provider-skripti) hoitaa sekä hakemistorakenteen
skannauksen, `index.html`-generoinnin että julkaisun git-pagesiin. Consumer tuottaa
vain raakatiedostot `reports/<suite>/`-hakemistoon — `ci-report.sh` päättää
julkaisukelpoisuuden ja generoi tarvittavan navigaation.
## Debug-ohje: raportti ei näy
### 1. Aja lokaalisti samalla komennolla kuin CI
```bash
mkdir -p reports/bats
bashcov -- bats tests/ > reports/bats/results.txt 2>&1
echo "exit: $?"
ls -la reports/bats/
```
### 2. Lisää `echo "DEBUG: ..." >&2` ennen ja jälkeen kriittisen operaation
```bash
echo "DEBUG: coverage exists? $([ -d coverage ] && echo YES || echo NO)" >&2
echo "DEBUG: target/index.html exists? $([ -f reports/suite/coverage/index.html ] && echo YES || echo NO)" >&2
```
### 3. Tarkista kutsuparametrit
Yleisin virhe: skripti odottaa `$1` = X, mutta kutsuja antaa `$1` = Y ja `$2` = X.
### 4. Tarkista tiedostopolut
1. Onko lähdetiedosto olemassa ennen kopiointia?
2. Onko kohde olemassa kopioinnin jälkeen?
3. Onko `index.html` subdirissä (vaaditaan `ci-report.sh`:lle)?
### 5. Poista debug-echot kun ongelma on korjattu
### 6. Älä kokeile — debuggaa
Kokeilu = arvaus. Debuggaus = lisää echo, aja, lue logi, eristä ongelma. Vasta sitten korjaa.
## Konfiguraatiotiedosto (.gitea-env.conf)
Tiedosto on `key=value`-muotoinen (kuten `.env`). Kommentit ja tyhjät rivit sallittuja.
### Single repo
```ini
# .gitea/workflows/gitea-env.conf
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
```
### Docker-artifaktin buildaavat projektit
```ini
DOCKER_REGISTRY=gitea.example.com/myorg
DOCKER_IMAGE_NAME=my-service
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
#DOCKERFILE=Dockerfile.platform # valinnainen, oletus Dockerfile
```
`DOCKER_UI_URL` ei sisällä image-nimeä — se on puhdas container-registryn osoite.
Image-nimi lisätään automaattisesti URL:iin `docker-build-push.yml`:ssä.
### Helm-artifaktin buildaavat projektit
```ini
HELM_REGISTRY=gitea.example.com/myorg
GIT_TAG_PREFIX=git-pages/
VERSION_FILE=git-pages/Chart.yaml
```
| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `HELM_REGISTRY` | **kyllä** | Registry host + owner, esim. `gitea.example.com/myorg`. **Tyhjä pysäyttää workflow'n.** |
| `GIT_TAG_PREFIX` | ei | Etuliite git-tägille. Pakollinen monorepossa. |
| `VERSION_FILE` | ei | Polku version lähteeseen. Oletus: juuren `Chart.yaml`. |
### Salaisuudet (Gitea Settings → Secrets)
| Secret | Pakollinen |
|---|---|
| `GITEA_TOKEN` | Aina (Gitean sisäinen, automaattisesti saatavilla) |
| `GIT_PAGES_PUBLISH_TOKEN` | Aina |
| `DOCKER_USERNAME` | Vain jos buildaat kontteja |
| `DOCKER_PASSWORD` | Vain jos buildaat kontteja |
| `HELM_USER` | Vain jos pushaat Helm chartin OCI-rekisteriin (oletus `github.actor`) |
| `HELM_PASSWORD` | Vain jos pushaat Helm chartin OCI-rekisteriin |
## Monorepo
Monorepossa yhdessä repossa asuu useampi julkaistava komponentti. Jokaiselle komponentille
oma conf-tiedosto `.gitea/workflows/<komponentti>.gitea-env.conf`.
### Suositus: komponentit omiin juurihakemistoihin
On suositeltavaa sijoittaa jokaisen komponentin koko lähdekoodi omaan juuritason
hakemistoonsa (`api/`, `frontend/`, `shared/`). Tämä helpottaa `paths:`-filtteröintiä,
pitää komponentit selkeästi erillään, ja tekee repossa navigoinnista suoraviivaista.
### Ongelmat ja ratkaisut
| Ongelma | Ratkaisu |
|---|---|
| Monta komponenttia, yksi repo — mikä triggeröi? | `paths:`-filtteri: `push: { paths: ['<komponentti>/**'] }` |
| Jokaisella komponentilla oma versio | `VERSION_FILE=<komponentti>/package.json` confissa |
| Git-tägit sekaisin ellei nimiavaruutta | `GIT_TAG_PREFIX=<komponentti>/` confissa → tägi `<komponentti>/1.2.3` |
| Eri julkaisutahdit | Riippumattomat CI-triggerit, omat versiopolut |
### Komponenttikohtainen conf
```ini
# .gitea/workflows/<komponentti>.gitea-env.conf
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
DOCKER_REGISTRY=gitea.example.com/myorg
DOCKER_IMAGE_NAME=<image-nimi>
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
GIT_TAG_PREFIX=<komponentti>/
# Jompikumpi — JSON (.version-kenttä) tai plain text:
VERSION_FILE=<komponentti>/package.json
#VERSION_FILE=<komponentti>/VERSION
```
### Monorepo reititin
```yaml
name: CI <Komponentti> Main
on:
push:
branches:
- main
paths:
- '<komponentti>/**'
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: .gitea/workflows/<komponentti>.gitea-env.conf
check-version:
needs: [load-config]
uses: <owner>/gitea-ci-library/.gitea/workflows/check-version.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
<testit>:
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: ./.gitea/workflows/<komponentti>.<testi>.yml
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
build-push:
needs: [load-config, check-version, <testit>]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: <owner>/gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
version: ${{ needs.check-version.outputs.version }}
report-summary:
name: Report Summary
needs: [load-config, build-push]
if: always()
uses: <owner>/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: '<suite-1> <suite-2>'
```
### Version elinkaari per komponentti
`GIT_TAG_PREFIX` takaa että eri komponenttien versiohistoria pysyy erillään.
Git-tägi `<komponentti>/0.2.3` ei sekoitu toisen komponentin tägeihin.
`check-version.yml` suodattaa ja laskee seuraavan patchin vain kyseisen
komponentin etuliitteellä. Idempotenttius toimii komponenttikohtaisesti:
jos commitilla on jo tägi, pipeline skipataan `if: artifact_exists != 'true'`.
### Mitä EI kannata tehdä monorepossa
- Älä aja kaikkia komponentteja samasta triggeristä — `paths:` pitää CI:t erillisinä
- Älä käytä samaa versionhallintatiedostoa usealle komponentille
- Älä anna monorepo-parametreja pipeline-overrideina — kaikki kuuluu conf-tiedostoon
## Versionhallinta
`check-version.yml` lukee version automaattisesti prioriteettijärjestyksessä:
| # | Lähde | Formaatti |
|---|---|---|
| 1 | `VERSION_FILE` confissa | Määritelty polku |
| 2 | `VERSION`-tiedosto (root) | Plain text |
| 3 | `package.json` (root) | `.version`-kenttä |
| 4 | `pom.xml` (root) | `<version>`-elementti |
`major.minor` otetaan tästä. Patch lasketaan automaattisesti git-tageista.
Esim. `VERSION` = `0.2`, tagit = `0.2.0`, `0.2.1` → seuraava `0.2.2`.
## Branch protection (PR-gate)
Gitean Settings → Branches → Add Rule:
- **Branch:** `main`
- **Enable Require Status Checks:** päälle
- **Status checks:** valitse testijobien nimet
## Provider-rajapinnat — referenssi
### Workflowt
| Workflow | Käyttötarkoitus |
|---|---|
| `config-provider.yml` | Lataa + validoi `.conf`, tuottaa `env_json` |
| `check-version.yml` | Tarkistaa onko commit buildattu, laskee version |
| `docker-build-push.yml` | Buildaa + puskea Docker-imagen, tagittaa commitin |
| `helm-build-push.yml` | Paketoi + puskea Helm chartin OCI-rekisteriin, tagittaa commitin |
| `report-summary.yml` | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä (Gitea 1.27+) |
### Skriptit (kutsutaan `.ci/scripts/`-polun kautta)
| Skripti | Käyttötarkoitus |
|---|---|
| `ci-report.sh` | Yhdistetty raportointi: julkaisee git-pagesiin ja asettaa commit-statuksen. Korvaa erilliset `publish-git-pages.sh` + `report-status.sh` -kutsut. Käyttö: `bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}` |
| `report-status.sh` | POSTaa commit-statuksen linkillä (kutsutaan `ci-report.sh`:n sisältä) |
| `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin (kutsutaan `ci-report.sh`:n sisältä) |
| `ci-validate.sh` | Validoi `.conf`-tiedoston (kutsutaan `config-provider.yml`:stä) |
+139 -299
View File
@@ -19,6 +19,8 @@ impact: high
Säännöt joilla consumer-projektit rakentavat CI-pipelinejä `gitea-ci-library`-kirjaston päälle. Säännöt joilla consumer-projektit rakentavat CI-pipelinejä `gitea-ci-library`-kirjaston päälle.
Nämä eivät ole provider-kirjaston sääntöjä — ne kuvaavat miten consumerin kuuluu käyttää kirjastoa oikein. Nämä eivät ole provider-kirjaston sääntöjä — ne kuvaavat miten consumerin kuuluu käyttää kirjastoa oikein.
Katso tarkat mallipohjat ja esimerkit `REFERENCE.md`:stä.
## 1. Reitittimen puhtaus ## 1. Reitittimen puhtaus
Reitittimet (`ci-feature.yml`, `ci-main.yml`) eivät sisällä `run:`-steppejä. Ne koostuvat vain: Reitittimet (`ci-feature.yml`, `ci-main.yml`) eivät sisällä `run:`-steppejä. Ne koostuvat vain:
@@ -36,36 +38,7 @@ with:
Jokainen job vastaa yhtä loogista testiä tai operaatiota. Reititin on orkestraattori — kaikki suorittava Jokainen job vastaa yhtä loogista testiä tai operaatiota. Reititin on orkestraattori — kaikki suorittava
logiikka on omassa `workflow_call`-tiedostossaan. logiikka on omassa `workflow_call`-tiedostossaan.
**Esimerkki:** Katso täydellinen esimerkki `REFERENCE.md`:stä.
```yaml
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
<test-1>:
needs: [load-config]
uses: ./.gitea/workflows/<component>.<test-1>.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
<test-2>:
needs: [load-config]
uses: ./.gitea/workflows/<component>.<test-2>.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
report-summary:
needs: [load-config, <test-1>, <test-2>]
if: always()
uses: <owner>/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: <suite-1> <suite-2>
```
## 2. Yksi asia per tiedosto ## 2. Yksi asia per tiedosto
@@ -79,316 +52,108 @@ Ei monoliittista `ci-tests.yml`. Jokainen testityyppi tai operaatio on oma `work
## 3. Exit-koodin käsittely ## 3. Exit-koodin käsittely
Jokainen testi kaappaa komentonsa exit-koodin eksplisiittisesti: `set -e` on oletuksena käytössä Gitea Actions -stepeissä — ensimmäinen feilaava komento pysäyttää stepin
ja exit-koodi välittyy natiivisti. Ylimääräistä `EXIT=$?` + `echo >> GITHUB_ENV` -käärettä ei tarvita.
```yaml ```yaml
- name: Run tests - name: Run tests
shell: bash shell: bash
run: | run: |
<testikomento> > results.txt 2>&1 <testikomento> > results.txt 2>&1
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
``` ```
**Miksi ei pipeä (`| tee`):** **Miksi ei pipeä (`| tee`):** `|` syö exit-koodin. Käytä redirectiä `>`.
```bash **Yksi asia per step:** Älä koskaan niputa useaa komentoa samaan `run:`-blockiin. `bash -e` pysäyttää
# VÄÄRIN — pipe syö exit-koodin koko stepin ensimmäisellä failaavalla komennolla, ja loput jäävät ajamatta. Sama pätee post-process-steppeihin.
<komento> | tee results.txt
# OIKEIN — redirect tiedostoon
<komento> > results.txt 2>&1
```
Ilman `EXIT=$?` + `exit ${EXIT}` komento voi feilata mutta job menee läpi vihreänä — `container:`-modessa
shellin käyttäytyminen vaihtelee.
## 4. Konttipolitiikka ## 4. Konttipolitiikka
1. **Julkiset registry-kontit kiinteällä versiolla**`alpine/helm:3.19.0`, `node:22`, `maven:3.9-eclipse-temurin-21`. 1. **Julkiset registry-kontit kiinteällä versiolla**`alpine/helm:3.19.0`, `node:22`, `maven:3.9-eclipse-temurin-21`.
Toistettavuus ja turvallisuus eivät saa riippua ulkoisesta `latest`:sta Toistettavuus ja turvallisuus eivät saa riippua ulkoisesta `latest`:sta.
2. **Projektin omat CI-kontit `latest`-tägillä** — buildattu `ci-container-build-push.yml`:llä. 2. **Projektin omat CI-kontit `latest`-tägillä** — buildattu `ci-container-build-<kontti>.yml`:llä.
Kontin build-pipeline päivittää `latest`:n automaattisesti. Rebuild = käyttöönotto Kontin build-pipeline päivittää `latest`:n automaattisesti. Rebuild = käyttöönotto
kaikissa pipelineissa ilman versioviittauksien päivittelyä. kaikissa pipelineissa ilman versioviittauksien päivittelyä.
`latest` on näille paras käytäntö, ei kompromissi
3. **Ei koskaan `curl`-latauksia CI-ajon sisällä** — työkalujen asennus CI-stepeissä hidastaa, 3. **Ei koskaan `curl`-latauksia CI-ajon sisällä** — työkalujen asennus CI-stepeissä hidastaa,
epäluotettavaa, ja vaikeuttaa toistettavuutta epäluotettavaa, ja vaikeuttaa toistettavuutta.
4. **Konttikuva hallitaan workflow'ssa, ei kutsujassa** — jos workflow vaatii tietyn
konttikuvan, se määritellään oletuksena (`default:`) workflow'n inputissa.
Kutsujan ei tarvitse tietää eikä välittää image-nimeä ellei halua ylikirjoittaa.
Pre-buildattu kontti rakennetaan minimaalisella Dockerfilella, joka kopioi tarvitut binäärit CI-kontin build-workflow'n template: `skills/ci-container-build/SKILL.md`.
perusimagesta ja asentaa vain välttämättömät paketit.
### 4.1 CI-kontin ajaminen jobissa
Ainoa sallittu tapa on `container:`-direktiivi. `docker run` komennolla kontin
käynnistäminen stepin sisällä on anti-pattern.
Katso CI-kontin template `REFERENCE.md`:stä.
**Huomio `actions/checkout@v4`:stä:** `container:`-direktiivillä kaikki stepit
ajetaan kontin *sisällä* — myös `actions/checkout@v4`. Se on JavaScript-action
joka vaatii sekä `nodejs` että `git`. Varmista että CI-kontin Dockerfilessä on molemmat.
## 5. Raporttitasot ## 5. Raporttitasot
Testi tuottaa raportin `reports/${GITHUB_SHA:0:8}/<suite>/`-hakemistoon. `publish-git-pages.sh` julkaisee sen, Testi tuottaa raportin `reports/<suite>/`-hakemistoon. Yksi `ci-report.sh`-kutsu hoitaa se
`report-status.sh` linkittää commit-statusin siihen. Molemmat `if: always()`. julkaisun että commit-statuksen.
### Taso 1: Pelkkä teksti ### Taso 1: Ei jälkisittelyä
Kun testi tuottaa vain stdout/stderr — tallennetaan `results.txt`: Kun testi tuottaa raportit suoraan (kuten `pytest --html` tai `cucumber-js --format html`):
- testi kirjoittaa `reports/<suite>/`-hakemistoon
- `ci-report.sh` julkaisee ja asettaa commit-statuksen
```yaml ### Taso 2: Jälkikäsittely tarvitaan
- name: Run tests
shell: bash
run: |
mkdir -p "reports/${GITHUB_SHA:0:8}/<suite>"
<testikomento> > "reports/${GITHUB_SHA:0:8}/<suite>/results.txt" 2>&1
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
- name: Publish reports Kun testi tuottaa raakadataa (stdout, coverage-tiedostot) joka pitää muuntaa tai siirtää
if: always() `reports/<suite>/`-hakemistoon. **Jokainen operaatio omassa stepissään** `if: always()`.
shell: bash
run: bash .ci/scripts/publish-git-pages.sh <suite>
- name: Report status Tarkat YAML-mallit molemmista tasoista: `REFERENCE.md`.
if: always()
shell: bash
run: |
if [ "${EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "<kuvaus>" <context> <suite>
else
bash .ci/scripts/report-status.sh failure "<kuvaus>" <context> <suite>
fi
```
### Taso 2: HTML-raportti **Subdir-sääntö:** Alihakemisto näkyy indexissä VAIN jos se sisältää `index.html`:n.
Kun testi tuottaa strukturoitua dataa (JUnit XML, coverage, tms.) — generoidaan HTML ja `index.html`:
```
reports/<sha8>/<suite>/
├── index.html ← generoitu: linkit alla oleviin
├── results.txt ← testin stdout
├── junit.xml ← testin JUnit XML -output
└── junit.html ← generoitu HTML (xsltproc, tms.)
```
`index.html` linkittää kaikkiin raporttitiedostoihin. Selain avaa sen ja navigoi sieltä
yksittäisiin raportteihin.
## 6. Nimeäminen ## 6. Nimeäminen
Tiedostonimet `.gitea/workflows/`-kansiossa noudattavat yhtenäistä rakennetta, jotta Tiedostonimet `.gitea/workflows/`-kansiossa noudattavat yhtenäistä rakennetta:
tiedostot löytyvät nopeasti ja niiden rooli on selvillä:
``` ```
<komponentti>.ci-feature.yml ← feature-haaran reititin <komponentti>.ci-feature.yml ← feature-haaran reititin
<komponentti>.ci-main.yml ← main-haaran reititin <komponentti>.ci-main.yml ← main-haaran reititin
<komponentti>.<testityyppi>.yml ← yksittäinen testi tai operaatio <komponentti>.<testityyppi>.yml ← yksittäinen testi tai operaatio
<komponentti>.gitea-env.conf ← komponenttikohtainen konfiguraatio <komponentti>.ci-container-build-<kontti>.yml ← CI-kontin build-workflow
<komponentti>.gitea-env.conf ← komponenttikohtainen konfiguraatio
``` ```
Single repossa `<komponentti>` jätetään pois — tiedostot ovat suoraan `ci-feature.yml`, Single repossa `<komponentti>` jätetään pois.
`ci-main.yml`, `<testityyppi>.yml`. Monorepossa prefiksi pitää komponentin tiedostot yhdessä.
Monorepossa prefiksi pitää komponentin tiedostot yhdessä: `ls <komponentti>.*` löytää kaikki
kerralla.
## 7. Artifact-kuri ## 7. Artifact-kuri
Gitea Actionsin `upload-artifact` jättää pysyvän tiedoston. Artifakteja ei käytetä Gitea Actionsin `upload-artifact` jättää pysyvän tiedoston. Artifakteja ei käytetä
workflow_call:ien väliseen datan siirtoon ellei se ole teknisesti välttämätöntä. `workflow_call`:ien väliseen datan siirtoon ellei se ole teknisesti välttämätöntä.
**Ensisijainen ratkaisu:** jokainen testi tuottaa tarvitsemansa datan itse. Ei **Ensisijainen ratkaisu:** jokainen testi tuottaa tarvitsemansa datan itse. Ei
`upload-artifact` + `download-artifact` -riippuvuuksia. `upload-artifact` + `download-artifact` -riippuvuuksia.
```yaml ## 8. Report-Summary — pakollinen jokaisen pipelinen lopuksi
# OIKEIN — molemmat testit tuottavat oman datansa
- name: Prepare data
run: <komento> > /tmp/data
- name: Validate data
run: <validointikomento> /tmp/data
```
**Miksi:** Jokaisen reitittimen (oli se `ci-main.yml`, `ci-feature.yml` tai mikä tahansa) viimeinen job on `report-summary`.
- Testit pysyvät itsenäisinä — yhden testin fail ei estä muita
- Ei "artifact expired" -virheitä myöhemmin
- Ei pysyviä artifakteja siivoamatta
--- **Säännöt:**
- `needs:` sisältää **kaikki edeltävät jobit** — summary odottaa että kaikki on valmis (onnistui tai ei)
- `if: always()` — ajetaan aina, vaikka pipeline olisi keskeytetty tai joku jobi failannut
- `suites:` on välilyönnein eroteltu lista suiten nimistä (esim. `bats cucumber`). Tyhjä merkkijono sallittu jos testisuiteja ei ole.
- Provider (`report-summary.yml`) hoitaa summaryn logiikan — reititin vain kutsuu
## Konfiguraatiotiedosto (.gitea-env.conf) **Miksi aina:**
- Gitea 1.27+ näyttää `GITHUB_STEP_SUMMARY`:n Actions UI:ssa. Ilman summarya pipeline näyttää epätäydelliseltä.
- Summaryyn voidaan myöhemmin lisätä muutakin kuin testilinkkejä (build-artefaktit, deploy-tiedot).
- Yhtenäinen rakenne jokaisessa pipeline-parissa vähentää kysymyksiä.
Tiedosto on `key=value`-muotoinen (kuten `.env`). Kommentit ja tyhjät rivit sallittuja. YAML-malli: `REFERENCE.md`.
### Single repo ## 9. ADR-yhteenveto — consumerin kannalta oleelliset säännöt
```ini
# .gitea/workflows/gitea-env.conf
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
```
### Docker-artifaktin buildaavat projektit
```ini
DOCKER_REGISTRY=gitea.example.com/myorg
DOCKER_IMAGE_NAME=my-service
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
#DOCKERFILE=Dockerfile.platform # valinnainen, oletus Dockerfile
```
`DOCKER_UI_URL` ei sisällä image-nimeä — se on puhdas container-registryn osoite.
Image-nimi lisätään automaattisesti URL:iin `docker-build-push.yml`:ssä.
### Salaisuudet (Gitea Settings → Secrets)
| Secret | Pakollinen |
|---|---|
| `GITEA_TOKEN` | Aina (Gitean sisäinen, automaattisesti saatavilla) |
| `GIT_PAGES_PUBLISH_TOKEN` | Aina |
| `DOCKER_USERNAME` | Vain jos buildaat kontteja |
| `DOCKER_PASSWORD` | Vain jos buildaat kontteja |
---
## Monorepo
Monorepossa yhdessä repossa asuu useampi julkaistava komponentti. Jokaiselle komponentille
oma conf-tiedosto `.gitea/workflows/<komponentti>.gitea-env.conf`, jossa on kaikki
komponenttikohtainen tieto.
### Suositus: komponentit omiin juurihakemistoihin
On suositeltavaa sijoittaa jokaisen komponentin koko lähdekoodi omaan juuritason
hakemistoonsa (`api/`, `frontend/`, `shared/`). Tämä helpottaa `paths:`-filtteröintiä,
pitää komponentit selkeästi erillään, ja tekee repossa navigoinnista suoraviivaista.
Tämä on kuitenkin vain suositus — ei pakottava sääntö.
### Ongelmat ja ratkaisut
| Ongelma | Ratkaisu |
|---|---|
| Monta komponenttia, yksi repo — mikä triggeröi? | `paths:`-filtteri: `push: { paths: ['<komponentti>/**'] }` |
| Jokaisella komponentilla oma versio | `VERSION_FILE=<komponentti>/package.json` confissa |
| Git-tägit sekaisin ellei nimiavaruutta | `GIT_TAG_PREFIX=<komponentti>/` confissa → tägi `<komponentti>/1.2.3` |
| Eri julkaisutahdit | Riippumattomat CI-triggerit, omat versiopolut |
### Komponenttikohtainen conf
```ini
# .gitea/workflows/<komponentti>.gitea-env.conf
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
DOCKER_REGISTRY=gitea.example.com/myorg
DOCKER_IMAGE_NAME=<image-nimi>
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
GIT_TAG_PREFIX=<komponentti>/
# Jompikumpi — JSON (.version-kenttä) tai plain text:
VERSION_FILE=<komponentti>/package.json
#VERSION_FILE=<komponentti>/VERSION
```
### Monorepo reititin
```yaml
name: CI <Komponentti> Main
on:
push:
branches:
- main
paths:
- '<komponentti>/**'
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: .gitea/workflows/<komponentti>.gitea-env.conf
check-version:
needs: [load-config]
uses: <owner>/gitea-ci-library/.gitea/workflows/check-version.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
<testit>:
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: ./.gitea/workflows/<komponentti>.<testi>.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
build-push:
needs: [load-config, check-version, <testit>]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: <owner>/gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
version: ${{ needs.check-version.outputs.version }}
```
### Version elinkaari per komponentti
`GIT_TAG_PREFIX` takaa että eri komponenttien versiohistoria pysyy erillään.
Git-tägi `<komponentti>/0.2.3` ei sekoitu toisen komponentin tägeihin.
`check-version.yml` suodattaa ja laskee seuraavan patchin vain kyseisen
komponentin etuliitteellä. Idempotenttius toimii komponenttikohtaisesti:
jos commitilla on jo tägi, pipeline skipataan `if: artifact_exists != 'true'`.
### Mitä EI kannata tehdä monorepossa
- Älä aja kaikkia komponentteja samasta triggeristä — `paths:` pitää CI:t erillisinä
- Älä käytä samaa versionhallintatiedostoa usealle komponentille
- Älä anna monorepo-parametreja pipeline-overrideina — kaikki kuuluu conf-tiedostoon
---
## Versionhallinta
`check-version.yml` lukee version automaattisesti prioriteettijärjestyksessä:
| # | Lähde | Formaatti |
|---|---|---|
| 1 | `VERSION_FILE` confissa | Määritelty polku |
| 2 | `VERSION`-tiedosto (root) | Plain text |
| 3 | `package.json` (root) | `.version`-kenttä |
| 4 | `pom.xml` (root) | `<version>`-elementti |
`major.minor` otetaan tästä. Patch lasketaan automaattisesti git-tageista.
Esim. `VERSION` = `0.2`, tagit = `0.2.0`, `0.2.1` → seuraava `0.2.2`.
---
## Branch protection (PR-gate)
Gitean Settings → Branches → Add Rule:
- **Branch:** `main`
- **Enable Require Status Checks:** päälle
- **Status checks:** valitse testijobien nimet
---
## Provider-rajapinnat — referenssi
### Workflowt
| Workflow | Käyttötarkoitus |
|---|---|
| `config-provider.yml` | Lataa + validoi `.conf`, tuottaa `env_json` |
| `check-version.yml` | Tarkistaa onko commit buildattu, laskee version |
| `docker-build-push.yml` | Buildaa + puskea Docker-imagen, tagittaa commitin |
| `report-summary.yml` | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä (Gitea 1.27+) |
### Skriptit (kutsutaan `.ci/scripts/`-polun kautta)
| Skripti | Käyttötarkoitus |
|---|---|
| `report-status.sh` | POSTaa commit-statuksen linkillä |
| `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin |
| `ci-validate.sh` | Validoi `.conf`-tiedoston (kutsutaan `config-provider.yml`:stä) |
---
## ADR-yhteenveto — consumerin kannalta oleelliset säännöt
### Reititin ei sisällä suorittavaa koodia (ADR 0010) ### Reititin ei sisällä suorittavaa koodia (ADR 0010)
@@ -405,16 +170,91 @@ Ei kahta eri komentoa samassa workflow'ssa.
Kaikki provider-viittaukset käyttävät `@v1`-tagia. `@main` on vain providerin oman repon Kaikki provider-viittaukset käyttävät `@v1`-tagia. `@main` on vain providerin oman repon
sisäiseen dogfood-käyttöön. Breaking changet kielletty — `v1`-rajapinta on pysyvä. sisäiseen dogfood-käyttöön. Breaking changet kielletty — `v1`-rajapinta on pysyvä.
### Paikalliset `uses:` eivät käytä refiä
Gitea act runner v1.0.8 muodostaa paikallisista `uses: ./.gitea/workflows/*.yml@main`-viittauksista
epävalidin git-refin `main@<sha>`.
Paikallisista `uses:`-direktiiveistä EI koskaan käytetä `@main`- tai muuta ref-päätettä:
- `uses: ./.gitea/workflows/chart.helm-lint.yml` ← oikein
- `uses: ./.gitea/workflows/chart.helm-lint.yml@main` ← väärin
Ilman refiä runner käyttää workflow'ta triggeröivästä commitista.
### Exit-koodi on ainoa onnistumisen mittari (ADR 0008) ### Exit-koodi on ainoa onnistumisen mittari (ADR 0008)
Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä (`> file 2>&1`). Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä (`> file 2>&1`).
### Commit-status vain raporttilinkille (ADR 0007)
`report-status.sh`-skriptiä käytetään VAIN kun on raportti linkitettäväksi.
Tool-jobit (build, deploy) luottavat Gitean natiiviin job-statukseen.
### Providerin checkout ei kuulu consumerille ### Providerin checkout ei kuulu consumerille
Providerin scriptit haetaan `actions/checkout`-stepillä `.ci/`-polkuun. Providerin scriptit haetaan `actions/checkout`-stepillä `.ci/`-polkuun.
Consumer ei kopioi eikä muokkaa providerin tiedostoja. Consumer ei kopioi eikä muokkaa providerin tiedostoja.
## 10. Build & Push -providerit
### `docker-build-push.yml` — Docker image build & push
Buildaa ja pushee Docker-imagen OCI-registryyn. Ajaa suoraan runnerilla
(ei `container:`-direktiiviä), joten `actions/checkout` toimii natiivisti.
**`env_json`-avaimet (pakolliset):**
```yaml
DOCKER_REGISTRY: gitea.app.keskikuja.site/niko
DOCKER_IMAGE_NAME: my-app
```
**Käyttö reitittimessä:**
```yaml
docker-build-push:
uses: OWNER/gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1
needs: [check-version]
if: needs.check-version.outputs.artifact_exists == 'false'
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
version: ${{ needs.check-version.outputs.version }}
```
Tarkka input/secret-lista: `docs/workflows.md`.
### `helm-build-push.yml` — Helm chart build & push
Pakkaa ja pushee Helm-chartin OCI-registryyn. Käyttää `alpine/helm`-konttia.
**`env_json`-avaimet (pakolliset):**
```yaml
HELM_REGISTRY: gitea.app.keskikuja.site/niko
```
**Käyttö reitittimessä:**
```yaml
helm-build-push:
uses: OWNER/gitea-ci-library/.gitea/workflows/helm-build-push.yml@v1
needs: [check-version]
if: needs.check-version.outputs.artifact_exists == 'false'
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
version: ${{ needs.check-version.outputs.version }}
# chart_path: '.' # oletus, vaihda jos Chart.yaml on alihakemistossa
```
**Node.js-kompromissi:** `actions/checkout@v4` on JavaScript-action.
Kontissa `alpine/helm` ei ole node.js:ää, joten se asennetaan lennossa
`apk add --no-cache nodejs` ennen checkouttia.
- Vaatii internet-yhteyden
- Ei toimi air gap -ympäristössä
- Korvaa tarvittaessa custom-kontilla (helm + nodejs):
rakenna `ci-container-build`-skillillä ja päivitä workflow'n
`container: image:` osoittamaan omaan konttiin
**Yksittäisten Helm-UI-linkkien raportointi:** `HELM_UI_URL` on
tarkoitettu yleiselle registry UI:lle — provider muodostaa linkin
`${HELM_UI_URL}/${CHART_NAME}/${VERSION}` automaattisesti.
Tarkka input/secret-lista: `docs/workflows.md`.
+167
View File
@@ -0,0 +1,167 @@
#!/usr/bin/env bats
source "$BATS_TEST_DIRNAME/helpers/mock-api.sh"
setup() {
export GITEA_TOKEN=test-token
export GIT_TAG_PREFIX=""
export SERVER_URL="http://localhost:18080"
export REPO="niko/test"
export SHA="abc123"
rm -rf /tmp/build-ctx
}
teardown() {
mock_stop 2>/dev/null || true
rm -rf /tmp/build-ctx
}
@test "VERSION_FILE=Chart.yaml extracts version from YAML" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/Chart.yaml"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "false" ]
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "VERSION_FILE=VERSION extracts version from plain text" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "false" ]
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "VERSION_FILE=package.json extracts version from JSON" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/package.json"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "false" ]
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "VERSION_FILE=subdir/Chart.yaml extracts version from monorepo" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/subdir/Chart.yaml"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "false" ]
[ "$NEXT_VERSION" = "0.4.0" ]
}
@test "no VERSION_FILE, root VERSION found" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
WORKDIR=$(mktemp -d)
cp "$BATS_TEST_DIRNAME/fixtures/check-version/VERSION" "$WORKDIR/VERSION"
SCRIPT="$PWD/scripts/check-version.sh"
run bash -c "cd '$WORKDIR' && exec bash '$SCRIPT'"
rm -rf "$WORKDIR"
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "no VERSION_FILE, root Chart.yaml found" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
WORKDIR=$(mktemp -d)
cp "$BATS_TEST_DIRNAME/fixtures/check-version/Chart.yaml" "$WORKDIR/Chart.yaml"
SCRIPT="$PWD/scripts/check-version.sh"
run bash -c "cd '$WORKDIR' && exec bash '$SCRIPT'"
rm -rf "$WORKDIR"
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "tag exists for commit sets ARTIFACT_EXISTS=true" {
mock_set_sequence '[{"code": 200, "body": [{"name": "0.3.0", "commit": {"sha": "abc123"}}]}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "true" ]
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "tag with prefix filters correctly" {
mock_set_sequence '[{"code": 200, "body": [{"name": "git-pages/0.3.0", "commit": {"sha": "abc123"}}, {"name": "docker/0.3.0", "commit": {"sha": "abc123"}}]}]'
mock_start
export GIT_TAG_PREFIX="git-pages/"
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "true" ]
[ "$NEXT_VERSION" = "git-pages/0.3.0" ]
}
@test "no tag, new version calculated" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "false" ]
[ "$NEXT_VERSION" = "0.3.0" ]
}
@test "highest patch calculated correctly" {
mock_set_sequence '[{"code": 200, "body": [{"name": "0.3.0", "commit": {"sha": "def456"}}, {"name": "0.3.1", "commit": {"sha": "def456"}}]}]'
mock_start
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
run bash scripts/check-version.sh
[ "$status" -eq 0 ]
source /tmp/build-ctx/build.env
[ "$ARTIFACT_EXISTS" = "false" ]
[ "$NEXT_VERSION" = "0.3.2" ]
}
@test "no version source exits with error" {
mock_set_sequence '[{"code": 200, "body": []}]'
mock_start
WORKDIR=$(mktemp -d)
SCRIPT="$PWD/scripts/check-version.sh"
run bash -c "cd '$WORKDIR' && exec bash '$SCRIPT'"
rm -rf "$WORKDIR"
[ "$status" -eq 1 ]
[[ "$output" == *"ERROR"* ]]
}
@@ -6,7 +6,7 @@ const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..');
const MOCK_SCRIPT = path.join(PROJECT_ROOT, 'tests', 'helpers', 'mock-api.sh'); const MOCK_SCRIPT = path.join(PROJECT_ROOT, 'tests', 'helpers', 'mock-api.sh');
Before({ tags: '@mock' }, function () { Before({ tags: '@mock' }, function () {
const out = execSync(`bash -c 'source "${MOCK_SCRIPT}" && mock_start && sleep 0.3 && curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://localhost:18080/api/v1/repos/health/check'`, { const out = execSync(`bash -c 'source "${MOCK_SCRIPT}" && mock_start && sleep 1 && curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://localhost:18080/api/v1/repos/health/check'`, {
cwd: PROJECT_ROOT, cwd: PROJECT_ROOT,
encoding: 'utf-8', encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'], stdio: ['pipe', 'pipe', 'pipe'],
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v2
name: test-chart
description: Test chart for version extraction
type: application
version: 0.3.0
appVersion: "1.0.0"
+1
View File
@@ -0,0 +1 @@
0.3.0
+1
View File
@@ -0,0 +1 @@
{"version": "0.3.0"}
+1
View File
@@ -0,0 +1 @@
<project><version>0.3.0</version></project>
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v2
name: subdir-chart
description: Chart in subdirectory for monorepo testing
type: application
version: 0.4.0
appVersion: "1.0.0"
+9 -1
View File
@@ -24,6 +24,14 @@ _wait_port_free() {
done done
} }
_wait_port_ready() {
local i=0
while ! lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 5 ]; do
sleep 0.2
i=$((i + 1))
done
}
mock_set_sequence() { mock_set_sequence() {
MOCK_SEQUENCE_FILE=$(mktemp) MOCK_SEQUENCE_FILE=$(mktemp)
echo "$1" | jq -c '.' > "$MOCK_SEQUENCE_FILE" echo "$1" | jq -c '.' > "$MOCK_SEQUENCE_FILE"
@@ -55,7 +63,7 @@ mock_start() {
nohup python3 "$(dirname "${BASH_SOURCE[0]}")/mock-server.py" "$MOCK_PORT" "$MOCK_CONFIG_FILE" "$MOCK_REQUEST_FILE" \ nohup python3 "$(dirname "${BASH_SOURCE[0]}")/mock-server.py" "$MOCK_PORT" "$MOCK_CONFIG_FILE" "$MOCK_REQUEST_FILE" \
</dev/null >/dev/null 2>&1 & </dev/null >/dev/null 2>&1 &
MOCK_PID=$! MOCK_PID=$!
sleep 0.5 _wait_port_ready
} }
mock_stop() { mock_stop() {