Compare commits

...

5 Commits

Author SHA1 Message Date
niko 8622b6fc4e Feature/ci container (#15)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 19s
CI Main / Check existing artifact (push) Successful in 14s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 1m43s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 1m9s
ci-docker-build-push Docker build & push 0.2.4 OK
CI Main / Build & Push Docker (push) Successful in 37s
CI Main / Report Summary (push) Successful in 5s
CI Main / Move provider version tag (push) Successful in 13s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #15
2026-06-16 09:13:36 +03:00
niko dd9ad9e2c8 Feature/ci container (#14)
ci-docker-build-push Docker build & push 0.2.3 OK
CI Main / Build & Push Docker (push) Successful in 36s
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 21s
CI Main / Check existing artifact (push) Successful in 14s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 1m46s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 1m12s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #14
2026-06-16 07:16:47 +03:00
niko ffc0734b65 tägitys käyttöön consumer viittauksessa provider tiedostoihin (#13)
CI Main / Check existing artifact (push) Successful in 12s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 1m44s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 1m31s
CI Main / Build & Push Docker (push) Successful in 54s
CI Main / Report Summary (push) Successful in 13s
CI Main / Move provider version tag (push) Successful in 13s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 19s
ci-docker-build-push Docker build & push 0.2.2 OK
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #13
2026-06-16 06:07:40 +03:00
niko 14a411e340 consumer project käyttöönotossa tulleitea muutoksia (#12)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 18s
CI Main / Check existing artifact (push) Successful in 12s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 1m41s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 1m7s
ci-docker-build-push Docker build & push 0.2.1 OK
CI Main / Build & Push Docker (push) Successful in 34s
CI Main / Report Summary (push) Successful in 4s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #12
2026-06-16 04:48:05 +03:00
niko f7b2353eb9 Feature/docker kuntoon (#11)
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 25s
CI Main / Check existing artifact (push) Successful in 22s
unit-tests Link to Bats reports
CI Main / Bats tests (push) Successful in 1m47s
acc-tests Link to Cucumber reports
CI Main / Cucumber tests (push) Successful in 1m4s
ci-docker-build-push Docker build & push 0.2.0 OK
CI Main / Build & Push Docker (push) Successful in 35s
CI Main / Report Summary (push) Successful in 4s
Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #11
2026-06-15 17:22:04 +03:00
44 changed files with 2142 additions and 1427 deletions
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SHA8="${GITHUB_SHA:0:8}"
REPORTS_DIR="reports/${SHA8}"
mkdir -p "${REPORTS_DIR}"
BATS_PASS=$(grep -c 'ok' "${REPORTS_DIR}/bats/results.txt" 2>/dev/null || echo 0)
BATS_FAIL=$(grep -c 'not ok' "${REPORTS_DIR}/bats/results.txt" 2>/dev/null || echo 0)
CUCUMBER_PASS=$(jq '.summary.passed // 0' "${REPORTS_DIR}/cucumber/report.json" 2>/dev/null || echo 0)
CUCUMBER_FAIL=$(jq '.summary.failed // 0' "${REPORTS_DIR}/cucumber/report.json" 2>/dev/null || echo 0)
{
echo "<!DOCTYPE html><html><head><meta charset='utf-8'>"
echo "<title>CI report ${SHA8}</title>"
echo "<style>body{font-family:sans-serif;margin:2em}a{color:#2563eb}table{border-collapse:collapse}"
echo "th,td{border:1px solid #ccc;padding:8px;text-align:left}"
echo ".pass{color:#059669}.fail{color:#dc2626}</style></head><body>"
echo "<h1>CI report <code>${SHA8}</code></h1>"
echo "<p>Commit: ${GITHUB_SHA}<br>Branch: ${GITHUB_REF_NAME}<br>Run: ${GITHUB_RUN_ID}</p>"
echo "<table><tr><th>Suite</th><th>Passed</th><th>Failed</th><th>Report</th></tr>"
echo "<tr><td>Bats</td><td class='pass'>${BATS_PASS}</td><td class='fail'>${BATS_FAIL}</td>"
echo "<td><a href='bats/results.txt'>results.txt</a>"
echo " | <a href='bats/junit.xml'>junit.xml</a></td></tr>"
echo "<tr><td>Cucumber</td><td class='pass'>${CUCUMBER_PASS}</td><td class='fail'>${CUCUMBER_FAIL}</td>"
echo "<td><a href='cucumber/index.html'>report</a>"
echo " | <a href='cucumber/report.json'>json</a></td></tr>"
echo "</table></body></html>"
} > "${REPORTS_DIR}/index.html"
-124
View File
@@ -1,124 +0,0 @@
name: Build & Publish Artifact
on:
workflow_call:
inputs:
env_json:
required: true
type: string
bats-image:
required: true
type: string
cucumber-node-image:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
GIT_PAGES_PUBLISH_TOKEN:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs:
check:
runs-on: ubuntu-latest
outputs:
artifact_exists: ${{ steps.check.outputs.artifact_exists }}
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Check existing artifact
id: check
run: |
VERSION=$(jq -r '.version' package.json)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
TAG=$(curl -s "$GITEA_API_URL/api/v1/repos/$GITHUB_REPOSITORY/tags" | \
jq -r '.[] | select(.commit.sha == "'"$GITHUB_SHA"'") | .name' | head -1)
if [ -n "$TAG" ]; then
echo "artifact_exists=true" >> "$GITHUB_OUTPUT"
echo "Commit already tagged as $TAG, skipping build"
else
echo "artifact_exists=false" >> "$GITHUB_OUTPUT"
fi
quality-gate:
needs: [check]
if: needs.check.outputs.artifact_exists == 'false'
uses: niko/gitea-ci-library/.gitea/workflows/quality-gate.yml@main
secrets: inherit
with:
env_json: ${{ inputs.env_json }}
bats-image: ${{ inputs.bats-image }}
cucumber-node-image: ${{ inputs.cucumber-node-image }}
build:
needs: [check, quality-gate]
if: needs.check.outputs.artifact_exists == 'false'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build container
run: |
docker build -t "minimal:${{ needs.check.outputs.version }}" .
mkdir -p /tmp/image
docker save "minimal:${{ needs.check.outputs.version }}" -o /tmp/image/artifact.tar
- name: Save Docker image for next job
uses: actions/upload-artifact@v4
with:
name: docker-image
path: /tmp/image/artifact.tar
push:
needs: [check, build]
runs-on: ubuntu-latest
steps:
- name: Load saved Docker image
uses: actions/download-artifact@v4
with:
name: docker-image
path: /tmp/image
- name: Push to Gitea Packages
run: |
VERSION="${{ needs.check.outputs.version }}"
docker load -i /tmp/image/artifact.tar
REGISTRY=$(echo "$GITEA_API_URL" | sed 's|https://||')
IMAGE="$REGISTRY/niko/gitea-ci-library/minimal:$VERSION"
docker tag "minimal:$VERSION" "$IMAGE"
docker login "$REGISTRY" -u niko -p "$GITEA_TOKEN"
docker push "$IMAGE"
docker logout "$REGISTRY"
tag-commit:
needs: [check, push]
runs-on: ubuntu-latest
steps:
- name: Create git tag
run: |
VERSION="${{ needs.check.outputs.version }}"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"$GITEA_API_URL/api/v1/repos/$GITHUB_REPOSITORY/tags" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"$VERSION\",
\"message\": \"Build #$GITHUB_RUN_NUMBER\",
\"target\": \"$GITHUB_SHA\"
}")
if [ "$HTTP_CODE" = "201" ]; then
echo "Tag $VERSION created"
elif [ "$HTTP_CODE" = "409" ]; then
echo "Tag $VERSION already exists (parallel build won), skipping"
else
echo "Failed to create tag: HTTP $HTTP_CODE"
exit 1
fi
+73
View File
@@ -0,0 +1,73 @@
name: Check Existing Artifact
on:
workflow_call:
inputs:
env_json:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
outputs:
artifact_exists:
value: ${{ jobs.check.outputs.artifact_exists }}
version:
value: ${{ jobs.check.outputs.version }}
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
jobs:
check:
runs-on: ubuntu-latest
outputs:
artifact_exists: ${{ steps.set-outputs.outputs.artifact_exists }}
version: ${{ steps.set-outputs.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Check existing artifact and calculate version
run: |
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)
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 'if type == "array" then .[] | select(.commit.sha == "${{ github.sha }}") | .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 bv "$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
id: set-outputs
run: |
source /tmp/build-ctx/build.env
echo "artifact_exists=$ARTIFACT_EXISTS" >> "$GITHUB_OUTPUT"
echo "version=$NEXT_VERSION" >> "$GITHUB_OUTPUT"
@@ -0,0 +1,57 @@
name: CI Container Build & Push
on:
workflow_call:
inputs:
env_json:
required: true
type: string
dockerfile_path:
required: true
type: string
image_name:
required: true
type: string
tag:
required: true
type: string
secrets:
DOCKER_USERNAME:
required: false
DOCKER_PASSWORD:
required: true
env:
DOCKER_REGISTRY: ${{ fromJson(inputs.env_json).DOCKER_REGISTRY || '' }}
IMAGE_NAME: ${{ inputs.image_name }}
TAG: ${{ inputs.tag }}
DOCKERFILE: ${{ inputs.dockerfile_path }}
jobs:
build-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push CI container
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker build \
--label "git.commit=${{ github.sha }}" \
--label "git.commitBy=${{ github.actor }}" \
--label "build.date=${NOW}" \
-f "${DOCKERFILE}" \
-t "${IMAGE_NAME}:${TAG}" .
REGISTRY="${DOCKER_REGISTRY:?DOCKER_REGISTRY not set in env.conf}"
REGISTRY_HOST="${REGISTRY%%/*}"
FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
echo "Pushing ${FULL_IMAGE} ..."
docker tag "${IMAGE_NAME}:${TAG}" "$FULL_IMAGE"
echo "$DOCKER_PASSWORD" | docker login "$REGISTRY_HOST" -u "$DOCKER_USERNAME" --password-stdin
docker push "$FULL_IMAGE"
docker logout "$REGISTRY_HOST"
-34
View File
@@ -1,34 +0,0 @@
name: CI
on:
push:
branches: ["**"]
workflow_dispatch:
jobs:
load-config:
name: Load gitea-env.conf to pipeline env
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
with:
config_path: .gitea/workflows/gitea-env.conf
feature:
name: Quality Gate
if: github.ref != 'refs/heads/main'
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/quality-gate.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: bats/bats:latest
cucumber-node-image: node:22
main:
name: Build & Push Artifact
if: github.ref == 'refs/heads/main'
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/build_publish-artifact.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: bats/bats:latest
cucumber-node-image: node:22
+24 -1
View File
@@ -1,21 +1,41 @@
name: Config Provider Library name: Config Provider
on: on:
workflow_call: workflow_call:
inputs: inputs:
config_path: config_path:
required: true required: true
type: string type: string
secrets:
GITEA_TOKEN:
required: true
GIT_PAGES_PUBLISH_TOKEN:
required: true
outputs: outputs:
env_json: env_json:
value: ${{ jobs.parse-config.outputs.json_data }} value: ${{ jobs.parse-config.outputs.json_data }}
config_path:
value: ${{ jobs.parse-config.outputs.config_path }}
env:
CI_CONF_FILE: ${{ inputs.config_path }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs: jobs:
parse-config: parse-config:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
json_data: ${{ steps.convert.outputs.JSON_OUT }} json_data: ${{ steps.convert.outputs.JSON_OUT }}
config_path: ${{ steps.set-path.outputs.CONFIG_PATH }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Validate CI config
run: bash .ci/scripts/ci-validate.sh
- id: convert - id: convert
run: | run: |
@@ -29,3 +49,6 @@ jobs:
CLEAN_JSON=$(echo "$JSON_STRING" | jq -c .) CLEAN_JSON=$(echo "$JSON_STRING" | jq -c .)
echo "JSON_OUT=$CLEAN_JSON" >> "$GITHUB_OUTPUT" echo "JSON_OUT=$CLEAN_JSON" >> "$GITHUB_OUTPUT"
- id: set-path
run: echo "CONFIG_PATH=${{ inputs.config_path }}" >> "$GITHUB_OUTPUT"
+103
View File
@@ -0,0 +1,103 @@
name: Docker Build & Push
on:
workflow_call:
inputs:
env_json:
required: true
type: string
version:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
DOCKER_USERNAME:
required: false
DOCKER_PASSWORD:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
DOCKER_REGISTRY: ${{ fromJson(inputs.env_json).DOCKER_REGISTRY || '' }}
DOCKER_IMAGE_NAME: ${{ fromJson(inputs.env_json).DOCKER_IMAGE_NAME || '' }}
DOCKER_UI_URL: ${{ fromJson(inputs.env_json).DOCKER_UI_URL || '' }}
DOCKERFILE: ${{ fromJson(inputs.env_json).DOCKERFILE || 'Dockerfile' }}
VERSION: ${{ inputs.version }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Build and push container
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker build \
--label "git.commit=${{ github.sha }}" \
--label "git.commitBy=${{ github.actor }}" \
--label "build.date=${NOW}" \
-f "${DOCKERFILE}" \
-t "${DOCKER_IMAGE_NAME}:${VERSION}" .
REGISTRY="${DOCKER_REGISTRY:?DOCKER_REGISTRY not set in env.conf}"
IMAGE="${DOCKER_IMAGE_NAME:?DOCKER_IMAGE_NAME not set in env.conf}"
REGISTRY_HOST="${REGISTRY%%/*}"
FULL_IMAGE="${REGISTRY}/${IMAGE}:${VERSION}"
echo "Pushing ${FULL_IMAGE} ..."
docker tag "${DOCKER_IMAGE_NAME}:${VERSION}" "$FULL_IMAGE"
echo "$DOCKER_PASSWORD" | docker login "$REGISTRY_HOST" -u "$DOCKER_USERNAME" --password-stdin
docker push "$FULL_IMAGE"
docker logout "$REGISTRY_HOST"
- name: Report status SUCCESS
if: success()
run: |
CONTAINER_URL=""
if [ -n "${DOCKER_UI_URL:-}" ] && [ -n "${VERSION:-}" ]; then
CONTAINER_URL="${DOCKER_UI_URL}/${VERSION}"
fi
bash .ci/scripts/report-status.sh success "Docker build & push ${VERSION} OK" ci-docker-build-push "" "$CONTAINER_URL"
- name: Report status FAILURE
if: failure()
run: bash .ci/scripts/report-status.sh failure "Docker build & push ${VERSION} FAILED" ci-docker-build-push
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\": \"${VERSION}\", \"message\": \"Build #$RUN_NUMBER\", \"target\": \"$SHA\"}")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "409" ]; then
exit 0
else
exit 1
fi
+64
View File
@@ -0,0 +1,64 @@
name: Bats Tests
on:
workflow_call:
inputs:
env_json:
required: true
type: string
bats-image:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
GIT_PAGES_PUBLISH_TOKEN:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs:
bats:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Run bats tests
id: bats-tests
shell: bash
run: |
docker volume create bats-workspace
tar c . | docker run --rm -i -v bats-workspace:/data alpine tar x -C /data
mkdir -p "reports/${GITHUB_SHA:0:8}/bats"
set +e
docker run --rm \
-v bats-workspace:/data \
--entrypoint bash ${{ inputs.bats-image }} \
-c 'apk add -q lsof python3 jq curl ruby && cd /data && gem install bashcov -v 3.3.0 2>&1 | tail -1 && 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
if: always()
run: bash .ci/scripts/publish-git-pages.sh bats
- name: Report status
if: always()
run: |
if [ "${BATS_EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "Link to Bats reports" unit-tests bats
else
bash .ci/scripts/report-status.sh failure "Link to Bats reports" unit-tests bats
fi
@@ -0,0 +1,21 @@
name: Build CI Bats Container (Manual)
on: workflow_dispatch
jobs:
load-config:
name: Load config
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit
with:
config_path: .gitea/workflows/example-gitea-env.conf
build-push:
name: Build & Push
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: Dockerfile.ci-bats
image_name: ci-bats
tag: latest
@@ -0,0 +1,21 @@
name: Build CI Cucumber Container (Manual)
on: workflow_dispatch
jobs:
load-config:
name: Load config
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit
with:
config_path: .gitea/workflows/example-gitea-env.conf
build-push:
name: Build & Push
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: Dockerfile.ci-cucumber
image_name: ci-cucumber
tag: latest
@@ -0,0 +1,70 @@
name: Cucumber Tests
on:
workflow_call:
inputs:
env_json:
required: true
type: string
cucumber-node-image:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
GIT_PAGES_PUBLISH_TOKEN:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs:
cucumber:
runs-on: ubuntu-latest
container:
image: ${{ inputs.cucumber-node-image }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Run cucumber tests
id: cucumber-tests
shell: bash
run: |
apt-get update -qq && apt-get install -y -qq --no-install-recommends lsof jq
npm install @cucumber/cucumber > /dev/null 2>&1
mkdir -p "reports/${GITHUB_SHA:0:8}/cucumber"
set +e
npx cucumber-js \
--format json:"reports/${GITHUB_SHA:0:8}/cucumber/report.json" \
--format html:"reports/${GITHUB_SHA:0:8}/cucumber/index.html" 2>&1
CUCUMBER_EXIT=$?
echo "CUCUMBER_EXIT=${CUCUMBER_EXIT}" >> "${GITHUB_ENV}"
exit ${CUCUMBER_EXIT}
- name: Publish cucumber reports
if: always()
run: bash .ci/scripts/publish-git-pages.sh cucumber
- name: Report status
if: always()
shell: bash
run: |
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
+41
View File
@@ -0,0 +1,41 @@
name: CI Feature
on:
push:
branches-ignore:
- main
workflow_dispatch:
jobs:
load-config:
name: Load example-gitea-env.conf to pipeline env
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit
with:
config_path: .gitea/workflows/example-gitea-env.conf
bats:
name: Bats tests
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/example-bats-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: bats/bats:latest
cucumber:
name: Cucumber tests
needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/example-cucumber-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
cucumber-node-image: node:22
report-summary:
name: Report Summary
needs: [load-config, bats, cucumber]
if: always()
uses: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: bats cucumber
+6
View File
@@ -0,0 +1,6 @@
GITEA_API_URL=https://gitea.app.keskikuja.site
GIT_PAGES_URL=https://ci-reports.helm-dev.keskikuja.site
DOCKER_REGISTRY=gitea.app.keskikuja.site/niko
DOCKER_IMAGE_NAME=gitea-ci-library-test-image
DOCKER_UI_URL=https://gitea.app.keskikuja.site/niko/-/packages/container/gitea-ci-library-test-image
#DOCKERFILE=Dockerfile.platform
+68
View File
@@ -0,0 +1,68 @@
name: CI Main
on:
push:
branches:
- main
workflow_dispatch:
jobs:
load-config:
name: Load example-gitea-env.conf to pipeline env
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
secrets: inherit
with:
config_path: .gitea/workflows/example-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 }}
bats:
name: Bats tests
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: niko/gitea-ci-library/.gitea/workflows/example-bats-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: bats/bats:latest
cucumber:
name: Cucumber tests
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: niko/gitea-ci-library/.gitea/workflows/example-cucumber-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
cucumber-node-image: node:22
build-push:
name: Build & Push Docker
needs: [load-config, check-version, bats, cucumber]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: niko/gitea-ci-library/.gitea/workflows/docker-build-push.yml@main
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: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: bats cucumber
tag-maintenance:
name: Move provider version tag
needs: [build-push]
if: success()
uses: niko/gitea-ci-library/.gitea/workflows/tag-maintenance.yml@main
secrets: inherit
+11
View File
@@ -0,0 +1,11 @@
name: Hello World (Manual)
on:
workflow_dispatch:
push:
# branches: [ main ] # Tai [ feature/ci-container ]
branches: [feature/ci-container]
jobs:
greet:
runs-on: ubuntu-latest
steps:
- run: echo "Hello"
-2
View File
@@ -1,2 +0,0 @@
GITEA_API_URL=https://gitea.app.keskikuja.site
GIT_PAGES_URL=https://ci-reports.helm-dev.keskikuja.site
-133
View File
@@ -1,133 +0,0 @@
name: Quality Gate
on:
workflow_call:
inputs:
env_json:
required: true
type: string
bats-image:
required: true
type: string
cucumber-node-image:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
GIT_PAGES_PUBLISH_TOKEN:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Validate CI config
run: bash .ci/scripts/ci-validate.sh
bats:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Run bats tests
id: bats-tests
shell: bash
run: |
docker volume create bats-workspace
tar c . | docker run --rm -i -v bats-workspace:/data alpine tar x -C /data
mkdir -p "reports/${GITHUB_SHA:0:8}/bats"
set +e
docker run --rm \
-v bats-workspace:/data \
--entrypoint bash ${{ inputs.bats-image }} \
-c 'apk add -q lsof python3 jq curl ruby && cd /data && gem install bashcov -v 3.3.0 2>&1 | tail -1 && 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
if: always()
run: bash .ci/scripts/publish-git-pages.sh bats
- name: Set bats commit status
if: always()
run: |
if [ "${BATS_EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "Bats tests" ci-bats bats
else
bash .ci/scripts/report-status.sh failure "Bats tests FAILED" ci-bats bats
fi
cucumber:
runs-on: ubuntu-latest
container:
image: ${{ inputs.cucumber-node-image }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Run cucumber tests
id: cucumber-tests
shell: bash
run: |
apt-get update -qq && apt-get install -y -qq --no-install-recommends lsof jq
npm install @cucumber/cucumber > /dev/null 2>&1
mkdir -p "reports/${GITHUB_SHA:0:8}/cucumber"
set +e
npx cucumber-js \
--format json:"reports/${GITHUB_SHA:0:8}/cucumber/report.json" \
--format html:"reports/${GITHUB_SHA:0:8}/cucumber/index.html" 2>&1
CUCUMBER_EXIT=$?
STATE="success"
[ "${CUCUMBER_EXIT}" != "0" ] && STATE="failure"
if [ -f "reports/${GITHUB_SHA:0:8}/cucumber/index.html" ]; then
bash .ci/scripts/report-status.sh "${STATE}" "Cucumber tests" ci-cucumber cucumber
else
bash .ci/scripts/report-status.sh "${STATE}" "Cucumber tests" ci-cucumber
fi
exit ${CUCUMBER_EXIT}
- name: Publish cucumber reports
if: always()
run: bash .ci/scripts/publish-git-pages.sh cucumber
build:
runs-on: ubuntu-latest
needs: [bats, cucumber]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: niko/gitea-ci-library
path: .ci
- name: Generate report index
run: bash .ci/.gitea/scripts/generate-report-index.sh
- name: Set build commit status
run: bash .ci/scripts/report-status.sh success "Build complete" ci-build
+34
View File
@@ -0,0 +1,34 @@
name: Report Summary
on:
workflow_call:
inputs:
env_json:
required: true
type: string
suites:
required: true
type: string
description: Space-separated suite names published to git-pages
env:
GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
jobs:
summary:
runs-on: ubuntu-latest
steps:
- name: Generate report links
shell: bash
run: |
SHA8="${GITHUB_SHA:0:8}"
BASE="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${SHA8}"
{
echo "## Test Reports"
echo ""
echo "| Suite | Report |"
echo "|-------|--------|"
for suite in ${{ inputs.suites }}; do
echo "| ${suite} | [View report](${BASE}/${suite}/) |"
done
} >> "${GITHUB_STEP_SUMMARY}"
+38
View File
@@ -0,0 +1,38 @@
name: Tag Maintenance
on:
workflow_call:
secrets:
GITEA_TOKEN:
required: true
jobs:
move-tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read current provider version
id: version
run: echo "TAG=$(cat CURRENT_PROVIDER_VERSION | tr -d '[:space:]')" >> "$GITHUB_OUTPUT"
- name: Move tag to commit
run: |
TAG="${{ steps.version.outputs.TAG }}"
SHA="${{ github.sha }}"
curl -sf -X DELETE \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ gitea.server_url }}/api/v1/repos/${{ github.repository }}/tags/${TAG}" || true
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ gitea.server_url }}/api/v1/repos/${{ github.repository }}/tags" \
-d "{\"tag_name\": \"${TAG}\", \"message\": \"Release ${TAG}\", \"target\": \"${SHA}\"}")
if [ "$HTTP_CODE" = "201" ]; then
echo "${TAG} tag moved to ${SHA}"
else
echo "ERROR: failed to move ${TAG} tag (HTTP $HTTP_CODE)" >&2
exit 1
fi
+1
View File
@@ -0,0 +1 @@
v1
+3
View File
@@ -0,0 +1,3 @@
FROM bats/bats:latest
RUN apk add --no-cache lsof python3 jq curl ruby && \
gem install bashcov -v 3.3.0
+6
View File
@@ -0,0 +1,6 @@
FROM node:22
RUN apt-get update -qq && \
apt-get install -y -qq --no-install-recommends lsof jq && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
npm install -g @cucumber/cucumber
+31 -7
View File
@@ -2,6 +2,8 @@
Reusable workflow -kirjasto Gitea Actionsille. Lisätietoja: [docs/](docs/) Reusable workflow -kirjasto Gitea Actionsille. Lisätietoja: [docs/](docs/)
**Consumer-käyttöönotto:** [docs/consumer-guide.md](docs/consumer-guide.md) — vaiheittainen ohje uuden projektin liittämiseen
## Provider-binding — miten consumer löytää providerin ## Provider-binding — miten consumer löytää providerin
Consumer kutsuu provideria `uses:`-viittauksella. Ei discoveryä — polku kovakoodataan consumerin Consumer kutsuu provideria `uses:`-viittauksella. Ei discoveryä — polku kovakoodataan consumerin
@@ -29,11 +31,11 @@ Polku löytyy repostasi:
git remote get-url origin git remote get-url origin
# → ssh://git@gitea.app.keskikuja.site:30009/niko/gitea-ci-library.git # → ssh://git@gitea.app.keskikuja.site:30009/niko/gitea-ci-library.git
# owner = niko, repo = gitea-ci-library # owner = niko, repo = gitea-ci-library
``` ```
Consumerin `ci.yml`: Consumerin `ci.yml`:
```yaml ```yaml
jobs: jobs:
call-engine: call-engine:
uses: niko/gitea-ci-library/.gitea/workflows/build-feature.yml@v1 uses: niko/gitea-ci-library/.gitea/workflows/build-feature.yml@v1
@@ -102,34 +104,56 @@ Act runner suorittaa Gitea Actions workflowt. **IaC-lähde:** alla oleva Helm-sn
klusterin totuus — muutokset vain snippetiin, sitten `helm upgrade --install` (ei käsin muokattuja klusterin totuus — muutokset vain snippetiin, sitten `helm upgrade --install` (ei käsin muokattuja
arvoja klusterissa). arvoja klusterissa).
> HUOM! Gitea ei ole vielä kunnolla stabiilissa tilassa, ja chart default dind sekä runner versiot ovat tätä tehdessä olleet bugiset. Niistä on olemassa uudemmat versiot, mutta eivät ole chartissa. Tätyy seurata ja päivittää tarpeen tulle.
Asennus Kubernetes-klusteriin Helm chartilla: Asennus Kubernetes-klusteriin Helm chartilla:
### 1. Rekisteröintitoken ### 1. Rekisteröi token
Hae token Giteasta: Hae token Giteasta:
- **Organization-taso:** Org → Settings → Actions → Runners → Create new runner - **Organization-taso:** Org → Settings → Actions → Runners → Create new runner
- **Globaali (site admin):** Site Admin → Actions → Runners → Create new runner - **Globaali (site admin):** Site Admin → Actions → Runners → Create new runner
### 2. Asenna runner ### 2. variables
```bash ```bash
GITEA_URL="https://<gitea-server-url>" GITEA_URL="https://<gitea-server-url>"
GITEA_ACTIONS_TOKEN="<registration-token>" GITEA_ACTIONS_TOKEN="<registration-token>"
GITEA_ACTIONS_NAMESPACE="gitea-actions" GITEA_ACTIONS_NAMESPACE="gitea-actions"
```
helm repo add gitea https://dl.gitea.com/charts ### 3. Tee secret vain init install yhteydessä
helm repo update
```bash
kubectl create secret generic act-runner-token \ kubectl create secret generic act-runner-token \
--from-literal=token="$GITEA_ACTIONS_TOKEN" \ --from-literal=token="$GITEA_ACTIONS_TOKEN" \
--namespace "$GITEA_ACTIONS_NAMESPACE" \ --namespace "$GITEA_ACTIONS_NAMESPACE" \
--dry-run=client -o yaml | kubectl apply -f - --dry-run=client -o yaml | kubectl apply -f -
```
### 4. Helm install / upgrade
Menee samalla komennolla.
> Muista asettaa variables ennen ajoa.
Päivittää olemassa olevan installaation, käyttää olemassa olevaa secret
ja sitä kautta Gitea ei tarvitse päivityksessä mitään temppuja.
Päivityksen jälkeen muista tappaa pod (käynnistyy automaattisesti uudelleen), että lataa varmasti kaikki uudesta. Sillä ConfigMap tms eivät lataudu
mikäli pod jatkaa ajamista.
```bash
helm repo add gitea https://dl.gitea.com/charts
helm repo update
helm upgrade --install act-runner gitea/actions \ helm upgrade --install act-runner gitea/actions \
--set enabled=true \ --set enabled=true \
--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.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:
level: info level: info
+8 -9
View File
@@ -6,8 +6,8 @@ Provider-repossa (`gitea-ci-library`) kansioiden omistajuus on seuraava:
| Kansio / Tiedosto | Omistaja | Tyyppi | | Kansio / Tiedosto | Omistaja | Tyyppi |
|-------------------|----------|--------| |-------------------|----------|--------|
| `.gitea/workflows/` | Sekoitettu | Providerin reusable workflowt + consumerin pipeline | | `.gitea/workflows/` | Sekoitettu | Providerin reusable workflowt + consumerin example-pipeline |
| `.gitea/workflows/gitea-env.conf` | Consumer | KEY=VALUE config | | `.gitea/workflows/example-gitea-env.conf` | Consumer | KEY=VALUE config |
| `.gitea/scripts/` | Consumer | Consumer-skriptit | | `.gitea/scripts/` | Consumer | Consumer-skriptit |
| `scripts/` | Provider | Providerin sisäiset työkalut | | `scripts/` | Provider | Providerin sisäiset työkalut |
@@ -30,12 +30,12 @@ uses: org/repo/scripts/workflow.yml@branch
``` ```
Tästä syystä providerin reusable workflowt (`config-provider.yml`, Tästä syystä providerin reusable workflowt (`config-provider.yml`,
`build-feature.yml`) ovat samassa `.gitea/workflows/`-kansiossa consumerin `check-version.yml`, `docker-build-push.yml`) ovat samassa `.gitea/workflows/`-kansiossa
pipeline-tiedostojen (`ci.yml`) kanssa. consumerin esimerkkipipeline-tiedostojen (`example-*`) kanssa.
Erottelu on nimessä ja dokumentaatiossa, ei kansiorakenteessa: Erottelu on nimessä ja dokumentaatiossa, ei kansiorakenteessa:
- `config-provider.yml`, `build-feature.yml` — providerin tarjoamia - `config-provider.yml`, `check-version.yml`, `docker-build-push.yml` — providerin tarjoamia
- `ci.yml` — consumerin omistamia - `example-feature.yml`, `example-main.yml`, `example-*.yml` — consumer-esimerkkejä
## Providerin `scripts/` (juuressa) ## Providerin `scripts/` (juuressa)
@@ -52,7 +52,7 @@ Consumerin omat skriptit, osana consumerin pipeline-logiikkaa.
Kutsutaan consumerin workflowista ilman tupla checkouttia: Kutsutaan consumerin workflowista ilman tupla checkouttia:
`.gitea/scripts/bats-report.sh`. `.gitea/scripts/bats-report.sh`.
## Consumerin `.gitea/workflows/gitea-env.conf` ## Consumerin `.gitea/workflows/example-gitea-env.conf`
Consumerin konfiguraatiotiedosto. Providerin `config-provider.yml` Consumerin konfiguraatiotiedosto. Providerin `config-provider.yml`
lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön. lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön.
@@ -61,8 +61,7 @@ lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön.
- Provider voi muuttaa `scripts/` ja `config-provider.yml` sisältöä - Provider voi muuttaa `scripts/` ja `config-provider.yml` sisältöä
ilman consumerin hyväksyntää (versiovaihdon yhteydessä) ilman consumerin hyväksyntää (versiovaihdon yhteydessä)
- Consumer voi muuttaa `.gitea/workflows/ci.yml`, - Consumer voi muuttaa `example-*.yml` ja `.gitea/scripts/` sisältöä
`.gitea/workflows/build-feature.yml` ja `.gitea/scripts/` sisältöä
ilman providerin muutoksia ilman providerin muutoksia
- Providerin workflowt käyttävät `.ci/scripts/...` -polkua (tupla checkout) - Providerin workflowt käyttävät `.ci/scripts/...` -polkua (tupla checkout)
- Consumerin workflowt käyttävät `.gitea/scripts/...` -polkua (natiivi checkout) - Consumerin workflowt käyttävät `.gitea/scripts/...` -polkua (natiivi checkout)
+85
View File
@@ -0,0 +1,85 @@
# 7. Statusraportoinnin pattern
## Päätös
Gitea Actionsin natiivi job-status on ensisijainen. Commit-status API:a
(`report-status.sh`) käytetään **vain** kun työvaihe tuottaa ulkoisen linkin
(testiraportti, Docker registry), jota natiivistaatus ei tue.
### Tool-jobit (validate, check-version, tag-commit)
Ei API-kutsuja. Luotetaan Gitean omaan job-statukseen.
```yaml
- uses: actions/checkout@v4
- name: Do work
run: do-something
```
### Test-jobit (bats, cucumber)
API:a käytetään raporttilinkin upottamiseksi commit-näkymään.
```
testit → publish (always) → status (always, exit-koodin mukaan)
```
```yaml
- name: Run tests
shell: bash
run: |
run-tests
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
- name: Publish reports
if: always()
run: bash .ci/scripts/publish-git-pages.sh bats
- name: Report status
if: always()
shell: bash
run: |
if [ "${EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "Link to Bats reports" unit-tests bats
else
bash .ci/scripts/report-status.sh failure "Link to Bats reports" unit-tests bats
fi
```
### Build & push -jobit (docker-build-push)
API:a käytetään Docker registry -linkin upottamiseksi.
```
build → push → SUCCESS (registry-linkillä) / FAILURE
```
## Periaatteet
1. Gitea Actionsin natiivi job-status on ensisijainen — myös PENDING/Running-tila
tulee natiivisti. API:a käytetään vain custom-linkin tarpeeseen (ADR 0004).
2. `run`-komennon on nostettava virhe ylös — oli kyse tool-callista tai
testivirheestä (ADR 0008).
3. Test-jobit käyttävät `if: always()` publish- ja status-stepeissä — raportti
julkaistaan ja status asetetaan aina, riippumatta testin lopputuloksesta.
4. Testiraportit julkaistaan myös virhetilanteessa, mikäli ne ovat syntyneet.
5. Commit-statuksen duplikaatio natiivijob-statuksen kanssa hyväksytään
testijobeille — se on ainoa mekanismi upottaa raporttilinkki commit-näkymään.
6. Tool-jobit eivät käytä API:a lainkaan — ne luottavat Gitean natiiviin
job-statukseen.
## Tausta
Aiemmin commit-status API:a käytettiin jokaisessa työvaiheessa, myös niissä
joilla ei ollut raporttia linkitettäväksi (validate, check-version, tag-commit).
Tämä tuotti duplikaatiota: Gitea näytti sekä natiivin `CI Main / Validate CI config
Successful` että API-statuksen `ci-validate CI config valid`. Kehittäjälle
molemmat kertoivat saman asian.
Käytännön pakko kuitenkin pakottaa API:n käyttöön testijobeissa: ilman
raporttilinkkiä kukaan ei löydä testituloksia. Gitean natiivi job-status
linkittää aina jobin lokiin — ei ulkoiseen raporttiin. Tämä on paras
saatavilla oleva kompromissi.
+73
View File
@@ -0,0 +1,73 @@
# 8. Exit code — ainoa onnistumisen mittari
## Päätös
Jokaisen `run`-stepin on nostettava virheellinen exit-koodi ylös sellaisenaan.
Exit-koodia ei saa "syödä" missään tilanteessa. Onnistumisen ja epäonnistumisen
päättely tapahtuu **ainoastaan** exit-koodin perusteella — ei tiedostojen
olemassaolon, stdout-tulosteen tai minkään muun heuristiikan perusteella.
## Periaatteet
1. Exit-koodi on ainoa totuus. `0` = onnistui, kaikki muut = epäonnistui.
2. Exit-koodia ei saa syödä. Pipe (`|`) viimeisenä komentona `tee`:hen syö
exit-koodin — `docker run … | tee file` palauttaa aina 0.
3. Data transfer -pipet ovat sallittuja (`tar c . | docker run … tar x`),
koska niiden exit-koodilla ei ole semanttista merkitystä.
4. Testien tai työkalujen ajaminen ei saa päättyä pipeen.
5. `set -o pipefail` ei ole riittävä suojaus — PIPESTATUS resetoituu herkästi.
## Sallitut patternit
```yaml
# Oikein: suora ajo, exit koodi $?:iin
- name: Do work
run: |
some-command
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
# Oikein: stdout talteen ilman pipeä
- name: Do work
run: |
some-command > results.txt 2>&1
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
# Oikein: docker run ilman pipeä
- name: Run in container
run: |
docker run --rm image command > output.txt 2>&1
EXIT=$?
exit ${EXIT}
```
## Kielletyt patternit
```yaml
# Väärin: pipe syö exit-koodin
- run: docker run … | tee results.txt
# Väärin: pipe syö exit-koodin
- run: tar … | docker … | tee file
# Väärin: onnistumisen päättely tiedoston olemassaolosta
- run: |
some-command || true
[ -f success.txt ] && exit 0 || exit 1
```
## Tausta
Gitea Actionsissa `run`-stepin tila määräytyy viimeisen komennon exit-koodista.
Pipe (`|`) asettaa `$?`:ksi viimeisen komennon tuloksen — jos viimeinen komento
on `tee`, tulos on aina 0 riippumatta siitä mitä aiemmat komennot palauttivat.
Tämä on aiheuttanut tuotannossa tilanteita, joissa testit feilasivat mutta jobi
näytti vihreää, koska `tee` söi exit-koodin. Virhe havaittiin vasta kun raportteja
alettiin lukea manuaalisesti — commit-status valehteli.
Ratkaisu on yksiselitteinen: exit-koodi talteen `$?`-muuttujaan ennen kuin mikään
muu komento ehtii muuttaa sitä, ja stepin viimeinen komento on aina `exit ${EXIT}`.
@@ -0,0 +1,53 @@
# 9. Breaking changes kielletty
## Päätös
Providerin `v1`-tagin osoittamaa rajapintaa ei koskaan rikota.
Consumerin `uses:`-kutsut säilyvät yhteensopivina — uusi versiotagi
(`v2`, `v3`) luodaan VAIN jos taaksepäin yhteensopimaton muutos on
pakottava. Käytännössä: `v1` on pysyvä, ja sitä ylläpidetään
eteenpäin.
## Rajapinnan määritelmä
Providerin rajapinta = `config-provider.yml` ja `check-version.yml`
workflow_call-inputit ja -outputit:
- Inputtien nimet, tyypit ja required-arvot eivät muutu
- Outputtien nimet eivät katoa
- Secret-nimet eivät muutu
- Workflow-tiedoston nimi ja polku eivät muutu
Sisäinen toteutus (scriptit, checkout-logiikka, build-vaiheet) voi
muuttua vapaasti — consumer ei ole niistä riippuvainen.
## Versiotagin siirto
Tagi `v1` siirretään automaattisesti uusimpaan onnistuneeseen
main-commitin CI-ajoon (`tag-maintenance.yml`). Tagin nimi luetaan
tiedostosta `CURRENT_PROVIDER_VERSION`.
Jos breaking change joskus tulee pakottavaksi:
1. Päivitä `CURRENT_PROVIDER_VERSION``v2`
2. Luo uusi `v2`-tagi manuaalisesti (osoittaa uuteen rajapintaan)
3. `tag-maintenance.yml` alkaa ylläpitää `v2`:ta eteenpäin
4. `v1`-tagia **ei poisteta** — se jää osoittamaan viimeistä
v1-yhteensopivaa committia. `v1`:tä käyttävät consumerit
jatkavat toimintaansa ilman muutoksia.
5. Uudet consumerit ottavat käyttöön `@v2`:n.
Aktiivisesti ylläpidetään vain yhtä tagia (`CURRENT_PROVIDER_VERSION`).
Vanhat tagit säilyvät paikoillaan taaksepäin yhteensopivuutta varten.
## Perustelu
Yhden aktiivisen tagin ylläpito on yksinkertaisempaa kuin usean
rinnakkaisen version aktiivinen hallinta. Homelab-ympäristössä
consumerit ovat saman ylläpitäjän hallinnassa — eri tiimien
rinnakkaisia aktiiviversioita ei tarvita.
Breaking changen sattuessa vanha tagi säilyy — vanhat consumerit
eivät hajoa. Uusi tagi otetaan käyttöön uusissa consumereissa.
Breaking changen kielto pakottaa suunnittelemaan rajapinnat
huolellisesti etukäteen.
@@ -0,0 +1,43 @@
# 10. Pipeline-reititin — ei komentoja
## Päätös
CI-pipelinetiedostot (`ci-main.yml`, `ci-feature.yml`) ovat puhtaita
**reitittimiä**. Ne eivät saa sisältää `run:`-komentoja, inline-skriptejä
tai varsinaista build-/test-logiikkaa. Niiden ainoa sallittu sisältö on:
- `uses:` — viittaus reusable workflow'hun
- `needs:` — riippuvuus toisen jobin valmistumiseen
- `if:` — ehdollinen suoritus
- `secrets: inherit` — salaisuuksien välitys
## Mikä ei kuulu reitittimeen
- `run:`-komennot
- Inline-skriptit (shell, Python, tms.)
- `actions/checkout` (paitsi providerin sisäinen infrastruktuuri)
- Build-työkalut, testikomennot, Docker-komennot
- Raporttien generointi
## Missä logiikka on
| Kerros | Tiedosto | Sisältö |
|---|---|---|
| Reititin | `ci-main.yml`, `ci-feature.yml` | Vain `uses:`-kutsut |
| Workflow_call | `ci-unit-tests.yml`, `ci-acc-tests.yml`, … | Varsinainen testi-/build-logiikka |
| Provider | `config-provider.yml`, `check-version.yml`, … | Jaettu infrastruktuuri |
| Skriptit | `scripts/` | Apufunktiot (status, publish, validointi) |
## Perustelu
Reitittimen ja toteutuksen erottelu:
- Reititin kertoo **mitä** ajetaan ja **missä järjestyksessä** — luettavissa
yhdellä silmäyksellä ilman teknistä taustaa
- Toteutus (workflow_call) kertoo **miten** — tekninen henkilö voi keskittyä
yhteen tiedostoon kerrallaan
- Providerin reusable workflow't eivät koskaan sisällä inline-logiikkaa —
skriptit ovat omissa tiedostoissaan
Tämä on sama periaate kuin web-sovelluksissa: reititin ei sisällä
business-logiikkaa, vain HTTP-verbit ja polut.
+40 -62
View File
@@ -1,6 +1,6 @@
# AI Context: Gitea Actions CI -kirjasto # AI Context: Gitea Actions CI -kirjasto
**Updated**: 2026-06-12 (POC-vaihe, suunniteltu uudelleenkirjoitus) **Updated**: 2026-06-15 (siivottu, provider/consumer-erottelu valmis)
## Project Overview ## Project Overview
Gitea Actions reusable workflow -kirjasto mikropalveluiden build-, testaus-, Gitea Actions reusable workflow -kirjasto mikropalveluiden build-, testaus-,
@@ -8,90 +8,68 @@ raportointi-, deployment- ja test flow -prosessien orkestrointiin. Korvaa
`ci-jenkins-library`:n Gitea-natiivilla toteutuksella. Mikropalvelut `ci-jenkins-library`:n Gitea-natiivilla toteutuksella. Mikropalvelut
käyttävät kirjastoa `uses:`-direktiivillä. käyttävät kirjastoa `uses:`-direktiivillä.
POC on valmis: raporttien julkaisu git-pagesiin ja commit-status linkillä
toimii.
## Monorepo: kaksi erillistä kokonaisuutta ## Monorepo: kaksi erillistä kokonaisuutta
Tämä repo on käytännössä monorepo, jossa on kaksi itsenäistä osaa:
### 1. Juuri (`gitea-ci-library`) ### 1. Juuri (`gitea-ci-library`)
Provider-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio. Provider-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio.
Consumer kutsuu `build-feature.yml`-workflowa `uses:`-direktiivillä. 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,
omat tekniset valinnat, oma design-rationale. Kohdeltava kuten se olisi jo omat tekniset valinnat, oma design-rationale. Kaikki git-pages-spesifi tieto
oma reponsa: kaikki git-pages-spesifi tieto kuuluu `git-pages/docs/`- alle, kuuluu `git-pages/docs/`-alle, ei juuren `docs/`-kansioon.
ei juuren `docs/`-kansioon.
### Rajapinta juuren ja git-pagesin välillä
Ohut ja yksiselitteinen:
```
scripts/publish-git-pages.sh <report-dir>
→ PATCH tar osoitteeseen GIT_PAGES_URL
→ palauttaa BASE URL:n
git-pages tarjoaa:
- HTTP endpoint (GET/PATCH/PUT)
- retention (automaattinen)
- TLS, BasicAuth (Traefik)
```
Juuri ei tiedä git-pagesin sisäisestä toiminnasta (storage v2, .index,
blob-arkkitehtuuri). Git-pages ei tiedä workflowista, scripteistä tai
provider-logiikasta.
## Architecture (POC-tila)
- **Provider & Consumer -malli**: `build-feature.yml` on lukittu rajapinta.
ADR 0005.
- **Raporttien hostaus**: git-pages Helm-chartilla (`git-pages/`), `GIT_PAGES_URL` määrittää perusosoitteen.
- **Retention**: sidecar samassa podissa, HTTP API localhost:3000,
Gitea API branch-check.
- **Commit-status**: Gitea Actions näyttää automaattisesti. API vain
custom-linkkiin. ADR 0004.
- **Julkaisu**: `publish-git-pages.sh` → PATCH tar git-pagesiin.
## Repository Structure ## Repository Structure
| Path | Purpose | | Path | Purpose |
|---|---| |---|---|
| `.gitea/workflows/` | Reusable workflowt (`build-feature.yml`, `config-provider.yml`) | | `.gitea/workflows/config-provider.yml` | Provider: lataa + validoi config-tiedoston, tuottaa `env_json` |
| `scripts/` | `publish-git-pages.sh`, `report-status.sh`, `dispatch-workflow.sh` | | `.gitea/workflows/check-version.yml` | Provider: tarkistaa onko commitille jo artifact, laskee version |
| **`git-pages/`** | **Oma kokonaisuus: Helm-chartti + docs + retention** | | `.gitea/workflows/docker-build-push.yml` | Provider: buildaa + puskea Docker-imagen, tagittaa commitin |
| `docs/` | Root-tason arkkitehtuuri, ADRt (00010005) | | `.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` |
| `.gitea/scripts/` | **Consumer-skriptit**: `bats-coverage.sh`, `bats-report.sh` |
| `docs/` | Arkkitehtuuri, ADRt (00040008) |
| `docs/adr/` | Architecture Decision Records | | `docs/adr/` | Architecture Decision Records |
| `git-pages/` | Raporttien hostaus (Helm-chartti) |
| `tests/` | Bats-testit skripteille | | `tests/` | Bats-testit skripteille |
| `.gitea/workflows/ci.yml` | Dogfood — kutsuu `build-feature.yml`:a |
**Tarkemmat git-pages-asiat:** `git-pages/docs/` (implementation-notes, ### Provider workflowt (4 kpl)
architecture, design-rationale, secrets, tech-stack).
| Workflow | Input | Output | Kuvaus |
|---|---|---|---|
| `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. |
| `docker-build-push.yml` | `env_json`, `version` | — | Buildaa Docker-imagen, puskea rekisteriin, tagittaa commitin. |
| `report-summary.yml` | `env_json`, `suites` | — | Generoi `GITHUB_STEP_SUMMARY`-taulukon raporttilinkeillä (Gitea 1.27+) |
### Example-tiedostot (consumer-referenssi)
| Tiedosto | Laukaisin | Flow |
|---|---|---|
| `example-feature.yml` | push [ei main] | load-config → bats + cucumber → report-summary |
| `example-main.yml` | push [main] | load-config → check-version → bats + cucumber → report-summary → docker-build-push |
| `example-bats-tests.yml` | workflow_call | Unit-testit Batsilla, 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 |
## Key Technical Decisions ## Key Technical Decisions
- **Provider & Consumer**: `build-feature.yml` lukittu rajapinta, muu koodi
vapaasti muutettavissa
- **Vain Gitea, vain reusable workflowt**: ei custom actioneita, ei
multi-platform
- **Raportit git-pagesissa**: HTML selailtavissa, retention automaattinen
- **Git-pages omana kokonaisuutena**: voi erottaa omaksi repokseen
tulevaisuudessa
## Tech Stack (POC) - **Provider & Consumer -malli**: `example-*`-tiedostot ovat consumer-esimerkkejä, provider-workflowt reusableja. ADR 0005.
- **Runtime:** Bash, curl, jq, python3 (retention whiteout) - **Vain Gitea, vain reusable workflowt**: ei custom actioneita, ei multi-platform
- **Alusta:** Gitea Actions, Gitea act runner - **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.
- **Hostaus:** git-pages 0.9.1 (Codeberg), Traefik, cert-manager - **Exit-koodi on ainoa onnistumisen mittari**: Ei pipeä, ei tiedostoheuristiikkaa. ADR 0008.
- **Integraatiot:** Gitea REST API, Gitea Packages - **Raportit git-pagesissa**: HTML selailtavissa, retention automaattinen
- **GITHUB_STEP_SUMMARY**: Summary-näkymä raporttilinkeille Gitea 1.27:ssä (forward-compat)
## Common Commands ## Common Commands
- Helm-asennus: `helm upgrade --install git-pages ./git-pages -n <ns> -f <values>` - Helm-asennus: `helm upgrade --install git-pages ./git-pages -n <ns> -f <values>`
- Julkaisu: `bash scripts/publish-git-pages.sh <report-dir>` - Julkaisu: `bash scripts/publish-git-pages.sh <report-dir>`
- Status: `bash scripts/report-status.sh <state> <desc> <url> <context>` - Status: `bash scripts/report-status.sh <state> <desc> <context> [suite] [url]`
## What NOT to Do ## What NOT to Do
- Älä lisää tukea muille Git-alustoille - Älä lisää tukea muille Git-alustoille
- Älä lisää Docker custom actioneita ilman pakottavaa syytä - Älä lisää Docker custom actioneita ilman pakottavaa syytä
- Älä kirjoita git-pages-spesifiä tietoa juuren `docs/`-kansioon - Älä kirjoita git-pages-spesifiä tietoa juuren `docs/`-kansioon
kuuluu `git-pages/docs/`-alle - Älä käytä commit-status API:a jollei ole raporttia linkitettäväksi (ADR 0007)
- Älä POSTaa commit-status APIin jokaiselle vaiheelle — natiivi riittää - Älä käytä pipeä `run`-komennon viimeisenä — se syö exit-koodin (ADR 0008)
+37 -17
View File
@@ -1,7 +1,6 @@
# Architecture — Gitea Actions CI -kirjasto # Architecture — Gitea Actions CI -kirjasto
> ⚠️ POC-vaihe. Tämä dokumentti kuvaa suunniteltua arkkitehtuuria. > Normatiivinen lähde: ADR 0004, 0005, 0006, 0007, 0008.
> Normatiivinen lähde: ADR 0004, ADR 0005, `docs/design-rationale.md`.
--- ---
@@ -18,31 +17,52 @@ Kirjasto on Gitea-spesifi. Raportit hallinnoidaan git-pages Helm-chartilla
| Rooli | Kuvaus | | Rooli | Kuvaus |
|-------|--------| |-------|--------|
| **Provider** | `gitea-ci-library` — tarjoaa `build-feature.yml` (lukittu rajapinta) sekä scriptit | | **Provider** | `gitea-ci-library` — tarjoaa reusable workflowt (`config-provider.yml`, `check-version.yml`, `docker-build-push.yml`) ja scriptit |
| **Consumer** | Mikropalveluprojekti — kutsuu `uses:`-direktiivillä, omistaa pipeline-logiikan | | **Consumer** | Mikropalveluprojekti — kutsuu `uses:`-direktiivillä, omistaa pipeline-logiikan. Tämän repon oma toteutus: `example-*`-tiedostot |
Tarkemmin: ADR 0005. Tarkemmin: ADR 0005.
## Komponentit (POC) ## Komponentit
| Komponentti | Tila | | Komponentti | Tyyppi | Kuvaus |
|-------------|------| |---|---|---|
| `build-feature.yml` | Toimii. Ainoa reusable workflow. | | `config-provider.yml` | Provider | Lataa + validoi `.conf`-tiedoston, tuottaa `env_json` |
| `publish-git-pages.sh` | Toimii. PATCH tar git-pagesiin. | | `check-version.yml` | Provider | Tarkistaa git-tagit, laskee version, palauttaa `artifact_exists` + `version` |
| `report-status.sh` | Toimii. POSTaa commit-status (vain custom-linkkiin). | | `docker-build-push.yml` | Provider | Buildaa Docker-imagen, puskea rekisteriin, tagittaa commitin |
| `dispatch-workflow.sh` | Toimii. Dispatchee workflown ja pollaa valmistumista. | | `example-feature.yml` | Consumer | Feature-haaran CI: load-config → bats + cucumber → summary |
| `git-pages/` | Helm-chartti raporttien hostaukseen. Oma kokonaisuus, tarkemmin: `git-pages/docs/`. | | `example-main.yml` | Consumer | Main-haaran CI: load-config → check-version → bats + cucumber → summary → docker |
| `example-bats-tests.yml` | Consumer | Unit-testit Batsilla |
| `example-cucumber-tests.yml` | Consumer | Hyväksymätestit Cucumberilla |
| `report-summary.yml` | Provider | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä (Gitea 1.27+) |
| `publish-git-pages.sh` | Provider-skripti | PATCH tar git-pagesiin |
| `report-status.sh` | Provider-skripti | POSTaa commit-status (vain custom-linkkiin) |
| `ci-validate.sh` | Provider-skripti | Validoi `.conf`-tiedoston ja tarkistaa secretit |
| `dispatch-workflow.sh` | Provider-skripti | Dispatchee workflown ja pollaa valmistumista |
| `git-pages/` | Infra | Helm-chartti raporttien hostaukseen. Oma kokonaisuus |
## Statusraportointi
| Job-tyyppi | Mekanismi | Syy |
|---|---|---|
| Tool-jobit | Vain Gitea natiivi job-status | Ei raporttia linkitettäväksi |
| Test-jobit | Commit-status API linkillä | Ainoa tapa upottaa raporttilinkki commit-näkymään |
| Docker-build-push | Commit-status API linkillä | Linkki Docker registryyn |
Tarkemmin: ADR 0004, 0007.
## Ulkoiset palvelut ## Ulkoiset palvelut
| Palvelu | Rooli | | Palvelu | Rooli |
|---------|-------| |---|---|
| **Gitea REST API** | Commit-status, workflow-dispatch, run-pollaus | | Gitea REST API | Commit-status (vain custom-linkit), git-tagit |
| **Gitea Packages** | Docker-imagen säilytys | | Gitea Packages | Docker-imagen säilytys |
| **git-pages** | Raporttien hostaus | | git-pages | Raporttien hostaus |
## Arkkitehtuuriset rajoitteet ## Arkkitehtuuriset rajoitteet
- `build-feature.yml` on ainoa consumerin kutsuma rajapinta (ADR 0005) - Provider-workflowt ovat reusableja (`workflow_call`), consumer omistaa orkestroinnin
- Gitea Actionsin natiivi commit-status on ensisijainen (ADR 0004) - Gitea Actionsin natiivi commit-status on ensisijainen (ADR 0004)
- API:a käytetään vain custom-linkkeihin (ADR 0007)
- Exit-koodi on ainoa onnistumisen mittari — ei pipeä (ADR 0008)
- Raportit ovat julkisia URL:lla (osoite tunnettava) - Raportit ovat julkisia URL:lla (osoite tunnettava)
- Consumer-skriptit `.gitea/scripts/`-alla, provider-skriptit `scripts/`-alla (ADR 0006)
+2 -1
View File
@@ -1,4 +1,5 @@
**⚠️ STATUS: ALERT DRAFT** — Ei ole validoitu. Voi sisältää virheellisiä tai puutteellisia käytäntöjä. **⚠️ STATUS: OSITTAIN VANHENTUNUT** — Statusraportointi (7) ja exit-koodit (8)
on formalisoitu ADR:iin 0007 ja 0008. Loput osiot validioitu POC-ajossa.
# CI Pipeline Practices # CI Pipeline Practices
+109 -254
View File
@@ -1,269 +1,124 @@
# Konfiguraatiomalli — `ci-flow-values.yaml` # Konfiguraatiomalli — `gitea-env.conf`
> ⚠️ **POC-vaihe.** Tämä dokumentti on peritty Jenkins-versiosta ja sisältää > Consumer määrittelee ympäristökohtaiset arvot KEY=VALUE-muotoisessa
> Docker-spesifistä legacyä (isContainerBuild, Docker-labelit). POC osoitti, > `.conf`-tiedostossa. Providerin `config-provider.yml` validoi tiedoston
> että provider on agnostinen consumerin build-ekosysteemille. > ja muuntaa sen JSON:ksi (`env_json`), jota kaikki downstream-workflowt
> > käyttävät.
> Uusi ajattelu: `isContainerBuild()` → `isArtifactBuild()`. Provider ei
> tiedä eikä tarvitse tietää, buildaako consumer kontin, JARin, npm-paketin
> vai mitään muuta. Dokumentti odottaa uudelleenkirjoitusta.
>
> Normatiivinen lähde: `docs/design-rationale.md`, ADR 0005.
--- ---
## Miksi redesign ## Tiedoston sijainti ja nimi
Jenkins-version `ci-flow-values.yaml` oli rakennettu Jenkinsin ympäristömuuttuja- ja credential-mallin ympärille. Gitea Actionsissa konfiguraatio luetaan suoraan YAML:sta workflow'n sisällä — ei tarvita env-muuttujien kautta kierrättämistä. Lisäksi: Consumerin repossa: `.gitea/workflows/gitea-env.conf` (nimi vapaasti
valittavissa — `config-provider.yml`:n `config_path`-input määrittää polun).
- `creditentials`-viittaukset korvautuvat Gitea org secrets/variables -mekanismilla Esimerkki (tämän repon dogfood):
- `test-flow`-syntaksi yksinkertaistuu (ei enää JSON-serialisointia env-muuttujiin) ```
- Docker/NPM-rekisterit MVP:ssä vain Gitea Packages — factory/adapter-pattern valmiina laajennukselle .gitea/workflows/example-gitea-env.conf
- `sonarqube`-konfiguraatio pysyy samankaltaisena ```
---
## Skeema ## Skeema
```
KEY=VALUE
```
Yksi `KEY=VALUE` per rivi. Kommentit `#`-merkillä. Tyhjät rivit ohitetaan.
### Pakolliset avaimet
| Avain | Kuvaus | Esimerkki |
|---|---|---|
| `GITEA_API_URL` | Gitea-instanssin URL | `https://gitea.example.com` |
| `GIT_PAGES_URL` | Raporttihostauksen URL | `https://reports.example.com` |
### Docker-spesifit avaimet (vain jos käytetään `docker-build-push.yml`)
| Avain | Pakollinen | Kuvaus |
|---|---|---|
| `DOCKER_REGISTRY` | Kyllä | Rekisterin host/path, esim. `gitea.example.com/org` |
| `DOCKER_IMAGE_NAME` | Kyllä | Kontin nimi ilman registry-prefixiä |
| `DOCKER_UI_URL` | Ei | Linkki kontin UI-näkymään registryssä |
| `DOCKERFILE` | Ei | Dockerfile-nimi, oletus `Dockerfile` |
### Esimerkki
```ini
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
DOCKER_REGISTRY=gitea.example.com/myorg
DOCKER_IMAGE_NAME=temperature-store
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container/temperature-store
#DOCKERFILE=Dockerfile.platform
```
---
## Salaisuudet
Salaisuudet eivät ole `.conf`-tiedostossa. Ne määritellään Gitean
organization/repository secrets -mekanismissa ja välitetään workflowlle
`secrets: inherit` -direktiivillä.
| Secret | Pakollinen | Käyttäjä |
|---|---|---|
| `GITEA_TOKEN` | Kyllä | `report-status.sh`, `check-version.yml`, `docker-build-push.yml` |
| `GIT_PAGES_PUBLISH_TOKEN` | Kyllä | `publish-git-pages.sh`, `config-provider.yml` (validointi) |
| `DOCKER_USERNAME` | Ei | `docker-build-push.yml` (oletus: `github.actor`, ei pakollinen kaikissa registryissä) |
| `DOCKER_PASSWORD` | Kyllä | `docker-build-push.yml` |
---
## `config-provider.yml` — lataus ja validointi
Provider-workflow joka lukee `.conf`-tiedoston, validoi sen ja palauttaa
JSON-muotoisen `env_json`:n.
**Input:** `config_path` (polku `.conf`-tiedostoon)
**Output:** `env_json` (JSON-string), `config_path` (sama polku takaisin)
**Validointi:**
- `.conf`-tiedosto on olemassa
- Jokaisella `KEY=VALUE`-rivillä on arvo (ei tyhjää)
- URL-tyyppiset avaimet alkavat `http://` tai `https://`
- Pakolliset secretit (`GITEA_TOKEN`, `GIT_PAGES_PUBLISH_TOKEN`) on asetettu
Kutsu:
```yaml ```yaml
# ci-flow-values.yaml — projektin juuressa load-config:
# uses: org/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
# Pakolliset osiot: docker (jos master-branch buildaa kontin), test-flow (jos ketjutetaan)
# Vapaaehtoiset: sonarqube, deployment
docker:
registry: gitea # gitea | artifactory | nexus (MVP: vain gitea)
imageName: temperature-store # kontin nimi, pakollinen
sonarqube:
url: https://sonar.example.com
projectKey: temperature-store
deployment:
jobName: deploy # deploy-workflown nimi (vakio)
projectFolder: microservices # polku Helm-repossa
fileName: values-{.environment}.yaml
property: container.version
test-flow:
- deploy: development # 1. deploy development-ympäristöön
wait: true # odota deployn valmistumista
- test:
name: "integration fast"
environment: integration
repo: tests/integration # testi-repo (owner/repo)
workflow: test.yml # workflow-tiedosto testi-repossa
ref: main # branch
tags: "@temperature and not @slow"
- deploy: staging
wait: true
- test:
name: e2e
environment: staging
repo: tests/e2e
workflow: test.yml
ref: main
tags: "@e2e and not @slow"
```
### Kenttäkuvaukset
#### `docker`
| Kenttä | Pakollinen | Kuvaus |
|--------|------------|--------|
| `registry` | Ei (oletus `gitea`) | Rekisterityyppi. MVP: `gitea`. Factory/adapter-pattern avaa `artifactory`, `nexus` myöhemmin |
| `imageName` | Kyllä | Kontin nimi. Lopullinen tagi: `{gitea_host}/{owner}/{imageName}:{version}.{run_number}` |
#### `sonarqube`
| Kenttä | Pakollinen | Kuvaus |
|--------|------------|--------|
| `url` | Kyllä | SonarQube-palvelimen URL |
| `projectKey` | Kyllä | SonarQube-projektin avain |
SonarQube-token tulee Gitea org secretsista (`SONAR_TOKEN`). Ei `creditentials`-viittausta.
#### `deployment`
| Kenttä | Pakollinen | Kuvaus |
|--------|------------|--------|
| `jobName` | Ei (oletus `deploy`) | Deploy-workflown tiedostonimi ilman `.yml`-päätettä |
| `projectFolder` | Kyllä | Polku mikropalvelun kansioon Helm-repossa |
| `fileName` | Kyllä | YAML-tiedoston nimi. `{.environment}` korvataan ympäristön nimellä |
| `property` | Kyllä | Päivitettävä avain (piste-eroteltu polku, esim. `container.version`) |
Deploy-token (kirjoitusoikeus Helm-repoon) tulee Gitea org secretsista (`DEPLOY_TOKEN`).
#### `test-flow`
Array testi-steppejä. Jokainen steppi on joko `deploy` tai `test`.
**`deploy`-steppi:**
| Kenttä | Pakollinen | Kuvaus |
|--------|------------|--------|
| `deploy` | Kyllä | Ympäristön nimi (esim. `development`, `staging`) |
| `wait` | Ei (oletus `true`) | Odotetaanko deployn valmistumista ennen seuraavaa steppiä |
**`test`-steppi:**
| Kenttä | Pakollinen | Kuvaus |
|--------|------------|--------|
| `name` | Kyllä | Testivaiheen nimi (näkyy statusviestissä) |
| `environment` | Kyllä | Ympäristö jota vasten testataan |
| `repo` | Kyllä | Testi-repo muodossa `owner/repo` |
| `workflow` | Kyllä | Workflow-tiedosto testi-repossa |
| `ref` | Kyllä | Branch (esim. `main`) |
| `tags` | Ei | Cucumber-tagit (esim. `"@smoke and not @slow"`) |
| `versionApiUrl` | Ei | URL deployed-version tarkistukseen |
| `versionCheckScript` | Ei | Polku version check -skriptiin (repossa tai kontissa) |
---
## `isArtifactBuild()`-mekanismi (POC: suunniteltu, ei toteutettu)
> **Jenkins-legacy:** Jenkins-versiossa tämä oli `isContainerBuild()`. POC
> osoitti, että provider ei tiedä eikä tarvitse tietää, buildaako consumer
> kontin, JARin vai npm-paketin. Siksi `isContainerBuild()` →
> `isArtifactBuild()`.
**Ongelma:** Samaa committia vasten voidaan ajaa master-workflow useita
kertoja. On mieletöntä buildata artifakti uudestaan, koska commit on jo
tagätty versiolla ja artifakti on olemassa rekisterissä. Uudelleenbuildaus
aiheuttaa versiokonflikteja ja tuhlaa CI-aikaa.
**Ratkaisu:** Workflow'n alussa tarkistetaan, onko tälle commitille jo
olemassa versiotagi.
```bash
# is-container-built.sh
TAG=$(git tag --points-at HEAD | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
if [ -n "$TAG" ]; then
echo "container_already_built=true" >> $GITHUB_ENV
echo "container_version=$TAG" >> $GITHUB_ENV
else
echo "container_already_built=false" >> $GITHUB_ENV
fi
```
Workflow käyttää tätä ehtona:
```yaml
jobs:
build-container:
if: env.container_already_built != 'true'
steps:
- run: docker build ...
test-flow:
if: always()
needs: [build-container]
steps:
- run: dispatch-workflow.sh ...
```
**Mitä `isContainerBuild() == true` tarkoittaa käytännössä:**
- Kontti on jo buildattu ja pushattu rekisteriin
- Commit on tagätty versiolla (esim. `1.2.3.42`)
- Build-steppi skipataan → siirrytään suoraan test flow'hun
- Sama versio deployataan ja testataan — ei uutta konttia
**Miksi tämä on välttämätöntä:**
- Estää versiokonfliktit: `1.2.3.42` ei voi olla kahdesti
- Säästää CI-aikaa: artifaktin buildaus on hitain vaihe
- Pitää commitin ja artifaktin välisen suhteen yksiselitteisenä: `git tag` kertoo suoraan mikä versio vastaa tätä committia
---
## Version check (Fibonacci-backoff)
Ennen kuin testit ajetaan, pitää varmistua että haluttu konttiversio on oikeasti deployattu ympäristöön. Muuten testataan väärää versiota.
**Malli:** Skripti, joka pollaa deployatun version API:a Fibonacci-backoffilla:
```bash
#!/bin/bash
# check-version.sh <version_api_url> <expected_version>
# Palauttaa 0 jos versiot täsmäävät, 1 muuten.
URL=$1
EXPECTED=$2
MAX_RETRIES=10
# Fibonacci-sekvenssi: 1 2 3 5 8 13 21 34 55 89
FIB=(1 2 3 5 8 13 21 34 55 89)
for i in $(seq 1 $MAX_RETRIES); do
ACTUAL=$(curl -s "$URL" | jq -r '.version // empty')
if [ "$ACTUAL" = "$EXPECTED" ]; then
echo "Version match: $ACTUAL"
exit 0
fi
echo "Attempt $i/$MAX_RETRIES: $ACTUAL != $EXPECTED, waiting ${FIB[$i-1]}s..."
sleep ${FIB[$i-1]}
done
echo "Version mismatch after $MAX_RETRIES attempts"
exit 1
```
**Miksi Fibonacci:** Uusi deploy käynnistyy nopeasti (ensimmäiset pollaukset tiheään). Jos kontin pullaus tai podin käynnistys kestää, pollausväli kasvaa — ei turhaan kuormiteta API:a. Maksimiaika: ~231 sekuntia (summa 1..89).
Version check -skripti joko:
- Asuu testi-repossa (projektin oma toteutus) → `versionCheckScript`-kenttä
- Tai käyttää geneeristä API:a → `versionApiUrl`-kenttä, skripti on osa kirjastoa
---
## `doNotDowngrade`
Jenkinsin deploy-jobissa oli `doNotDowngrade`-parametri, joka esti vanhemman version deployaamisen uudemman päälle. Gitea Actions -versiossa:
- **Ei MVP:ssä.** Deploy tekee sen mitä käsketään. `doNotDowngrade` on lisäturva, joka voidaan lisätä deploy-workflow'hun myöhemmin.
- **Mekanismi:** Ennen YAML:n muokkausta tarkistetaan nykyinen versio. Jos `new < current`, skipataan ja raportoidaan.
- **Toteutus:** Yksi `if`-ehto deploy-workflow'n alussa, ei vaadi muutoksia muualle.
---
## UX-esimerkki: projektin `.gitea/workflows/ci.yml`
Näin mikropalvelun kehittäjä käyttää kirjastoa:
```yaml
# .gitea/workflows/ci.yml — projektin juuressa
name: CI
on:
push:
branches: ["**"]
workflow_dispatch:
jobs:
feature:
if: github.ref != 'refs/heads/master'
uses: org/gitea-ci-library/.gitea/workflows/ci-feature.yml@v1
secrets: inherit secrets: inherit
with: with:
config-file: ci-flow-values.yaml config_path: .gitea/workflows/gitea-env.conf
maven-image: maven:3.9-eclipse-temurin-21
master:
if: github.ref == 'refs/heads/master'
uses: org/gitea-ci-library/.gitea/workflows/ci-master.yml@v1
secrets: inherit
with:
config-file: ci-flow-values.yaml
maven-image: maven:3.9-eclipse-temurin-21
docker-image: docker:26-dind
``` ```
Kehittäjä määrittelee: ---
- Millä kontilla buildataan (`maven-image` — koska kirjasto ei tiedä projektin Java-versiota)
- Mistä konfiguraatio luetaan (`config-file`)
- Millä docker-versiolla kontit rakennetaan (`docker-image`)
Kaikki Git-, raportointi-, SonarQube- ja deploy-konfiguraatio on `ci-flow-values.yaml`:ssa. ## `check-version.yml` — version päättely
Provider-workflow joka etsii version prioriteettijärjestyksessä:
1. `VERSION`-tiedosto (plain text, esim. `0.2`)
2. `package.json``.version`-kenttä (Node.js)
3. `pom.xml``<version>`-elementti (Maven)
Hakee git-tagit Gitea API:sta ja laskee seuraavan vapaan patch-version.
**Output:** `artifact_exists` (true/false), `version` (string)
**Idempotentti:** Jos commitilla on jo versiotagi, `artifact_exists=true` ja
build-vaiheet skipataan. Samaa committia ei buildata kahdesti.
---
## `env_json`-propagointi
```
gitea-env.conf → config-provider.yml → env_json (JSON-string)
ci.yml with: env_json → kaikki downstream-workflowt
```
Jokainen provider-workflow purkaa tarvitsemansa arvot `fromJson(inputs.env_json).KEY`:lla.
Consumerin ei tarvitse tietää mitä avaimia kukin provider käyttää.
+446
View File
@@ -0,0 +1,446 @@
# Consumer Guide — Kirjaston käyttöönotto
> Anna tämä dokumentti AI:lle kun haluat ottaa `gitea-ci-library`:n käyttöön
> uudessa projektissa tai muokata olemassa olevia pipelineja.
---
## Rakenneperiaate
**Pipeline-tiedostot (`ci-feature.yml`, `ci-main.yml`) eivät saa sisältää
varsinaista logiikkaa.** Ne ovat puhtaita reitittimiä: pelkkiä `uses:`-kutsuja
`if`- ja `needs`-ehdoilla. Kaikki testien ajaminen, buildaus ja raportointi
kuuluu omiin `workflow_call`-tiedostoihinsa.
```
ci-unit-tests.yml ← testien ajaminen (varsinainen logiikka)
ci-acc-tests.yml ← hyväksymätestit (varsinainen logiikka)
ci-feature.yml ← reititin: load-config → test-workflow't → summary
ci-main.yml ← reititin: load-config → check-version → testit → build → summary
```
Provider tarjoaa 3 reusable workflow'ta ja joukon skriptejä.
Consumer omistaa orkestroinnin: mitä palikoita käytetään, missä järjestyksessä,
millä branch-ehdoilla. Consumer ei kopioi providerin koodia — se viittaa
`uses:`-direktiivillä.
---
## Vaihe 1: Konfiguraatiotiedosto
Luo `.gitea/workflows/gitea-env.conf`:
```ini
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
```
Jos buildaat Docker-kontteja, lisää:
```ini
DOCKER_REGISTRY=gitea.example.com/myorg
DOCKER_IMAGE_NAME=my-service
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container/my-service
#DOCKERFILE=Dockerfile.platform # valinnainen, oletus Dockerfile
```
Salaisuudet määritellään Gitean Settings → Secrets -näkymässä:
| Secret | Pakollinen |
|---|---|
| `GITEA_TOKEN` | Aina |
| `GIT_PAGES_PUBLISH_TOKEN` | Aina |
| `DOCKER_USERNAME` | Vain jos buildaat kontteja (ei pakollinen kaikissa registryissä) |
| `DOCKER_PASSWORD` | Vain jos buildaat kontteja |
---
## Vaihe 2: Test-workflow't (varsinainen logiikka)
Jokainen testityyppi omaan `workflow_call`-tiedostoonsa. Tässä esimerkki
Maven-yksikkötesteistä. Luo `.gitea/workflows/ci-unit-tests.yml`:
```yaml
name: Unit Tests
on:
workflow_call:
inputs:
env_json:
required: true
type: string
secrets:
GITEA_TOKEN:
required: true
GIT_PAGES_PUBLISH_TOKEN:
required: true
env:
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs:
test:
runs-on: ubuntu-latest
container: maven:3.9-eclipse-temurin-21
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: org/gitea-ci-library
path: .ci
- name: Run tests
shell: bash
run: |
mvn test
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
- name: Publish reports
if: always()
run: bash .ci/scripts/publish-git-pages.sh junit
- name: Report status
if: always()
shell: bash
run: |
if [ "${EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "Link to JUnit reports" unit-tests junit
else
bash .ci/scripts/report-status.sh failure "Link to JUnit reports" unit-tests junit
fi
```
Hyväksymätesteille vastaava tiedosto `ci-acc-tests.yml` (Cucumber, Playwright
tms.), jossa oma `container:`, oma testikomento ja oma `suite`-nimi.
**Tärkeää:** `mvn test` korvataan omalla testikomennolla. `container:` ja
`publish-git-pages.sh`-suite ovat projektikohtaisia. Muu runko pysyy samana.
---
## Vaihe 3: Feature-haaran CI (puhdas reititin)
Luo `.gitea/workflows/ci-feature.yml`. **Ei sisällä yhtään `run:`-steppiä**
— pelkkiä `uses:`-kutsuja:
```yaml
name: CI Feature
on:
push:
branches-ignore:
- main
workflow_dispatch:
jobs:
load-config:
uses: org/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: .gitea/workflows/gitea-env.conf
unit-tests:
needs: [load-config]
uses: ./.gitea/workflows/ci-unit-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
acc-tests:
needs: [load-config]
uses: ./.gitea/workflows/ci-acc-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
report-summary:
needs: [load-config, unit-tests, acc-tests]
if: always()
uses: org/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: junit cucumber
```
---
## Vaihe 4: Main-haaran CI (puhdas reititin)
Luo `.gitea/workflows/ci-main.yml`. **Ei sisällä yhtään `run:`-steppiä**:
```yaml
name: CI Main
on:
push:
branches:
- main
workflow_dispatch:
jobs:
load-config:
uses: org/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: .gitea/workflows/gitea-env.conf
check-version:
needs: [load-config]
uses: org/gitea-ci-library/.gitea/workflows/check-version.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
unit-tests:
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: ./.gitea/workflows/ci-unit-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
acc-tests:
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: ./.gitea/workflows/ci-acc-tests.yml@main
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
build-push:
needs: [load-config, check-version, unit-tests, acc-tests]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: org/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:
needs: [load-config, unit-tests, acc-tests]
if: always()
uses: org/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: junit cucumber
```
Mihin kiinnittää huomiota:
- `check-version` on **idempotentti** — jos commitilla on jo tagi, kaikki
sen jälkeiset jobit skipataan (`if: artifact_exists != 'true'`)
- `needs`-ketju takaa järjestyksen ja virheiden propagointin
- Artifakti voi olla **mitä tahansa**`docker-build-push.yml` on yksi
esimerkki. Voit korvata sen Maven-deploylla, npm-publishilla, tai millä
tahansa omalla build-workflow'lla. Rajapinta on `version`-input.
---
## Versionhallinta
`check-version.yml` lukee version automaattisesti prioriteettijärjestyksessä:
| # | Lähde | Formaatti | Esimerkki |
|---|---|---|---|
| 1 | `VERSION`-tiedosto | Plain text | `0.2` |
| 2 | `package.json` | `.version` | `"version": "0.2.0"` |
| 3 | `pom.xml` | `<version>` | `<version>0.2.0</version>` |
`major.minor` otetaan tästä. Patch (kolmas numero) lasketaan automaattisesti
git-tageista. Esim. jos `VERSION` on `0.2` ja tagit ovat `0.2.0`, `0.2.1`,
niin seuraava on `0.2.2`.
---
## Testien lisääminen — oma työkalu
Kopioi `ci-unit-tests.yml`:n rakenne uudelle testityypille ja muuta:
- `container:` — oma testikonttisi
- Testikomento — oma testityökalusi (`npm test`, `pytest`, `go test`, ...)
- `publish-git-pages.sh <suite>` — oma suite-nimi
- `report-status.sh ... <context> <suite>` — oma uniikki konteksti
Lisää uusi jobi reititintiedostoihin (`ci-feature.yml`, `ci-main.yml`)
samalla `uses:`-kaavalla.
Testijobit ajetaan rinnakkain — ne kaikki `needs: [load-config]` ilman
keskinäisiä riippuvuuksia.
### Tärkeimmät säännöt
1. **Exit-koodi aina ylös:**
```bash
run-tests
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
```
2. **Ei pipeä testikomennon perään.** `command | tee file` syö exit-koodin.
Käytä `command > file 2>&1` jos haluat logit talteen.
3. **Status vain jos on raportti.** Testijobit käyttävät commit-status API:a
raporttilinkin takia. Tool-jobit luottavat Gitean natiiviin job-statukseen.
4. **`if: always()`** publish- ja status-stepeissä — raportti julkaistaan
ja status asetetaan vaikka testit feilaisivat.
### Raporttien generointi
`publish-git-pages.sh <suite>` odottaa hakemiston `reports/${SHA8}/<suite>/`
olevan olemassa. Sen sisältö sellaisenaan julkaistaan git-pagesiin.
`report-status.sh` linkittää statuksen suoraan tähän hakemistoon — selain
avaa sieltä `index.html`:n.
Test-workflow'n vastuulla on tuottaa raportit oikeaan polkuun. Kaksi
tyypillistä patternia:
**Pattern 1: Yksi raporttitiedosto (Cucumber)**
Testityökalu tuottaa suoraan HTML-raportin. Yksinkertaisin tapaus:
```bash
mkdir -p "reports/${GITHUB_SHA:0:8}/cucumber"
npx cucumber-js \
--format html:"reports/${GITHUB_SHA:0:8}/cucumber/index.html"
```
**Pattern 2: Monta raporttitiedostoa (Bats + coverage)**
Eri työkalut tuottavat eri tiedostoja. Generoi `index.html` joka linkittää
ne yhteen:
```
reports/${SHA8}/bats/
├── index.html ← generoitu: linkit alla oleviin
├── results.txt ← bats-testien stdout
├── coverage/ ← bashcov-coverage HTML
│ └── index.html
└── ...
```
```bash
mkdir -p "reports/${GITHUB_SHA:0:8}/bats"
# Aja testit → results.txt
bats tests/ > "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1
# Generoi coverage → coverage-hakemisto
bashcov -- bats tests/
# Generoi index.html joka linkittää kaikkiin raportteihin
cat > "reports/${GITHUB_SHA:0:8}/bats/index.html" <<'EOF'
<!DOCTYPE html>
<html><body>
<h1>Bats Test Reports</h1>
<ul>
<li><a href="results.txt">Test results (raw)</a></li>
<li><a href="coverage/index.html">Code coverage</a></li>
</ul>
</body></html>
EOF
```
Yhteistä molemmille: `publish-git-pages.sh <suite>`-kutsun jälkeen raportit
ovat julkisesti selailtavissa. `report-status.sh`-kutsu `suite`-parametrilla
linkittää commit-statuksen suoraan `index.html`:ään.
Jos testit feilasivat, raportti generoidaan silti — se kertoo MITKÄ testit
feilasivat. Siksi publish- ja status-stepit käyttävät `if: always()`.
---
## Branch protection (PR-gate)
Gitean Settings → Branches → Add Rule:
- **Branch:** `main`
- **Enable Require Status Checks:** päälle
- **Status checks:** valitse `unit-tests`, `acc-tests`
---
## Raporttien koonti (Gitea 1.27+)
Kun Gitea päivittyy versioon 1.27, `GITHUB_STEP_SUMMARY`-tuki mahdollistaa
raporttilinkkien koontinäkymän suoraan Gitea UI:ssa. `report-summary`-jobi
on mukana molemmissa reititinesimerkeissä yllä — forward-compatibeli, ei
hajota vanhemmilla versioilla.
---
## 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 |
| `report-summary.yml` | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä |
### 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
Nämä säännöt on formalisoitu [docs/adr/](docs/adr/)-hakemistossa. Tässä tiivistelmä
consumer-näkökulmasta:
### Reititin ei sisällä suorittavaa koodia (ADR 0010)
`ci-feature.yml` ja `ci-main.yml` ovat **puhtaita reitittimiä**:
- **Vain** `uses:`, `needs:` ja `if:` sallittu
- **Ei** `run:`-komentoja, ei inline-skriptejä, ei `actions/checkout`
Kaikki suorittava koodi on omissa `workflow_call`-tiedostoissaan:
- `ci-unit-tests.yml` — testikomento, publish, status
- `ci-acc-tests.yml` — testikomento, publish, status
- Provider-workflowt (`config-provider.yml`, `check-version.yml`, …)
### Yksi steppi = yksi workflow_call-tiedosto
Jokainen pipeline-steppi (testityyppi, build, deploy) on oma tiedostonsa.
Ei kahta eri komentoa samassa workflow'ssa. Tämä pitää reitittimet ohuina
ja steppitiedostot itsenäisinä — testattavissa erikseen.
### Provider-versio on `@v1` (ADR 0009)
Kaikki `org/gitea-ci-library/…`-viittaukset käyttävät `@v1`-tagia:
```yaml
uses: org/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
```
`@main` on vain providerin oman repon sisäiseen dogfood-käyttöön.
Breaking changet on kielletty — `v1`-rajapinta on pysyvä.
### Exit-koodi on ainoa onnistumisen mittari (ADR 0008)
Älä käytä pipeä (`|`) komennon perässä — se syö exit-koodin.
Käytä redirectiä (`> file 2>&1`) jos haluat logit talteen.
### 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 scriptit haetaan `actions/checkout`-stepillä. Consumer käyttää
providerin määrittelemää polkua (`.ci/scripts/`). Consumer ei kopioi eikä
muokkaa providerin tiedostoja.
### Testattavuus
Jokainen `workflow_call`-tiedosto on testattavissa itsenäisesti — consumer
voi ajaa `ci-unit-tests.yml`:n paikallisesti act:lla tai Gitean
`workflow_dispatch`:llä ilman koko pipelineä.
+160 -107
View File
@@ -1,150 +1,195 @@
# Design Rationale — Gitea Actions CI -kirjasto # Design Rationale — Gitea Actions CI -kirjasto
> Miksi kirjasto on rakennettu näin. Arvot, periaatteet ja reunaehdot, joiden varaan arkkitehtuuri nojaa. > Miksi kirjasto on rakennettu näin. Arvot, periaatteet ja reunaehdot, joiden
> varaan arkkitehtuuri nojaa.
> >
> Tämä dokumentti on **normatiivinen** — arkkitehtuurin on noudatettava näitä periaatteita. Jos ehdotettu muutos on ristiriidassa rationalen kanssa, rationalen on muututtava ensin. > Tämä dokumentti on **normatiivinen** — arkkitehtuurin on noudatettava näitä
> periaatteita. Jos ehdotettu muutos on ristiriidassa rationalen kanssa,
> rationalen on muututtava ensin.
--- ---
## Miksi tämä projekti on olemassa ## Miksi tämä projekti on olemassa
Organisaatiolla on tuotannossa Jenkins-pohjainen CI-järjestelmä (`ci-jenkins-library`, 53 lähdetiedostoa, 21 Cucumber-featurea), joka on osoittautunut toimivaksi vuosien ajan. Se integroi Git-commitit, testiraportoinnin, Docker-buildit, deploymentin ja test flow'n yhtenäiseksi putkeksi, jossa jokainen vaihe raportoi tilansa suoraan Git-committiin. Mikropalveluarkkitehtuurissa jokainen palvelu tarvitsee CI-putken: testit,
laatutarkistukset, buildin, kontituksen ja julkaisun. Ilman jaettua
kirjastoa jokainen tiimi kopioi saman YAML-boilerplaten, tekee omat
virheensä ja ylläpitää omaa versiotaan. Ajan myötä putket ajautuvat erilleen
— toisessa on `shell: bash`, toisessa ei; toinen käyttää `set -o pipefail`,
toinen kadottaa exit-koodin `tee`:hen.
Jenkins on kuitenkin raskas ylläpitää Kubernetesissa, ja organisaatio on siirtymässä Giteaan. Tavoitteena on **sama toiminnallisuus, pienemmällä ylläpitotaakalla**, hyödyntäen Gitea Actionsin natiiveja ominaisuuksia. Tämä kirjasto on se mitä kopioidaan. Se tarjoaa valmiit, testatut,
dokumentoidut rakennuspalikat joista jokainen tiimi kokoaa oman putkensa.
Kirjasto ei ole Jenkins-migraatiotyökalu. Se on Gitea Actions -natiivi Palikat ovat Gitea Actionsin `uses:`-direktiivillä kutsuttavia reusable
uudelleensuunnittelu, joka säilyttää Jenkins-version todistetut patternit workflow'ta — ei asennusta, ei runtime-riippuvuutta, ei versiopäivityksiä
mutta hylkää ne osat, jotka olivat sidottuja Jenkinsin arkkitehtuuriin. projekteihin.
Gitea Actionsin ja Gitean natiiveja ominaisuuksia hyödynnetään aina kun
mahdollista — uutta kilpailevaa toteutusta ei kirjoiteta, jos toimiva
ratkaisu on jo olemassa.
--- ---
## Suunnitteluperiaatteet ## Suunnitteluperiaatteet
### 1. Hyödynnä natiivia ### 1. Palikka-arkkitehtuuri: pieniä, vaihdettavia, yhden vastuun workflow'ta
Gitea Actionsin ja Gitean natiiveja ominaisuuksia käytetään aina kun ne Jokainen provider-workflow tekee yhden asian:
riittävät. Uutta kilpailevaa toteutusta ei kirjoiteta, jos toimiva ratkaisu
on jo olemassa.
**Miksi:** Oma toteutus on aina ylläpidettävä, testattava ja | Workflow | Vastuu |
dokumentoitava. Natiivi ominaisuus tulee ilmaiseksi, kehittyy alustan |---|---|
mukana ja on käyttäjälle tuttu. | `config-provider.yml` | Lataa ja validoi konfiguraatio |
| `check-version.yml` | Tarkistaa onko commit buildattu, laskee version |
| `docker-build-push.yml` | Buildaa, puskea ja tagittaa kontin |
**Esimerkkejä:** Mikään workflow ei kutsu toista provider-workflowta. Consumer
- Gitea Actions näyttää jobien statuksen automaattisesti — omaa — siis mikropalvelun oma pipeline-tiedosto — on ainoa paikka joka
commit-status API -kutsua ei tarvita jokaiselle vaiheelle tietää mitä palikoita tarvitaan ja missä järjestyksessä.
- Gitea organization secrets/variables korvaa erillisen credential-hallinnan
- Reusable workflow -mekanismi korvaa custom action -runtimen
### 2. Git-commit on universaali statusnäkymä **Miksi:** Tämä on sama periaate kuin Unix-putkissa tai mikropalveluissa:
pieniä, itsenäisiä komponentteja jotka tekevät yhden asian hyvin.
Consumer voi vaihtaa yhden palikan toiseen — esimerkiksi Docker-buildin
tilalle Maven-paketoinnin — ilman että muut palikat muuttuvat.
Ratkaisu ei ole se että kaikki ajetaan, vaan se että jokainen tiimi
valitsee mitä tarvitsee. Monoliittinen "kaikki yhdessä" -workflow
pakottaisi jokaisen tiimin ajamaan tarpeettomia vaiheita.
Buildin jokainen vaihe raportoi tilansa Git-committiin. Kehittäjä näkee yhdellä silmäyksellä, missä vaiheessa build on — ei tarvitse navigoida CI-järjestelmän UI:hun. ### 2. Gitea ensin — hyödynnä alustaa, älä taistele sitä vastaan
**Miksi:** Jenkins-versio osoitti, että commit-statusviestit poistavat tarpeen CI-dashboardille. Kehittäjä työskentelee Gitissä, joten status kuuluu Gitiin. Statusviestien `url`-kenttä linkittää suoraan raportteihin — Cucumber-tulokset, SonarQube-tulokset, Docker-rekisteri — ilman että URL tarvitsee etsiä erikseen. Gitea Actions tarjoaa kolme asiaa ilmaiseksi:
**Mitä tarkoittaa käytännössä:** Gitea Actions näyttää jobien tilan 1. **Jobien visuaalinen status** — jokainen jobi näkyy automaattisesti
(checkmark/risti/spinner) commit-näkymässä automaattisesti. API:a commit-näkymässä checkmarkilla, spinnerillä tai ristillä.
(`/api/v1/repos/{owner}/{repo}/statuses/{sha}`) käytetään vain 2. **Cross-job riippuvuudet**`needs` hoitaa virheiden propagointin:
custom-raporttilinkin välittämiseen. ADR 0004. jos edeltävä jobi feilaa, riippuvat jobit skipataan.
3. **Reusable workflow -jakelu**`uses: org/repo/.gitea/workflows/file.yml@v1`
on natiivisti versioitu, skopattu ja välimuistitettu.
### 3. Reusable workflow — ei omaa runtimea Kirjasto käyttää näitä kaikkia. Ei omaa tilakonetta, ei custom
action -runtimea, ei ulkoista orkestraattoria.
Kirjasto jaetaan Gitea Actionsin reusable workflow -mekanismilla. Ei Docker-pohjaisia custom actioneita, ei erillistä ajonaikaista palvelinta. **Esimerkki:** Tool-jobit eivät kutsu commit-status API:a lainkaan.
Gitean oma job-status riittää — `success`/`failure`/`running` näkyy
automaattisesti. API:a käytetään vain kun tarvitaan **custom-linkki**
(testiraporttiin tai Docker registryyn), jota natiivistaatus ei tarjoa.
Tämä linjaus on dokumentoitu ADR 0004 ja 0007:ssä.
**Miksi:** Reusable workflow on Gitea Actionsin natiivein tapa jakaa CI-logiikkaa. Se on kevein (ei ylimääräistä runtimea), läpinäkyvin (workflow-tiedosto on luettavissa sellaisenaan) ja teknisesti kestävin (Gitea huolehtii versioinnista ja jakelusta). Custom actionit otetaan käyttöön vain jos reusable workflow'n rajat tulevat vastaan. ### 3. Status näkyy siellä missä työ tehdään — Git-commitissa
**Mitä tämä ei ole:** Tämä ei ole monorepo-työkalu, joka asennetaan projekteihin. Tämä on joukko `.gitea/workflows/`-tiedostoja, joihin mikropalvelut viittaavat `uses:`-direktiivillä. Kehittäjä työskentelee Gitissä. `git log`, `git blame`, PR-näkymä —
nämä ovat päivittäiset työkalut. CI-statuksen kuuluu näkyä siellä,
ei erillisessä dashboardissa.
### 4. Konfiguraatio kuuluu repoon Gitea Actionsin natiivi job-status tekee tämän automaattisesti:
jokainen commit näyttää välittömästi mitkä jobit on ajettu ja millä
tuloksella. Testiraportteihin pääsee yhdellä klikkauksella commitin
status-kuvakkeesta — koska `report-status.sh` asettaa `target_url`:n
osoittamaan suoraan HTML-raporttiin git-pagesissa.
Projektikohtainen konfiguraatio (`ci-flow-values.yaml`-tyyppinen tiedosto) asuu mikropalvelun omassa repossa. Reusable workflow lukee sen, ei toisinpäin. Tämä ei ole kosmeettinen yksityiskohta. Se on devops-käytännön
ydin: palautesilmukka on lyhin mahdollinen. Commit → build → status
näkyy samassa näkymässä jossa kehittäjä jo on.
**Miksi:** Mikropalvelun kehittäjä omistaa buildinsa. Hän tietää mitä Dockefileä käytetään, mitä SonarQube-projektia, mitä testi-steppejä tarvitaan. Jos konfiguraatio hajautetaan useaan repoon, muutokset vaativat koordinaatiota, ja yhden totuuden lähteen periaate rikkoutuu. ### 4. Exit-koodi on ainoa totuus
**Poikkeus:** Infra-tason asetukset (git-pages host, Gitea-instanssin URL) CI-putken jokaisen `run`-stepin onnistuminen määräytyy **vain ja
ovat organisaatiotasolla Gitean organization secrets/variables ainoastaan** exit-koodin perusteella. Ei tiedoston olemassaolon, ei
-mekanismissa. Ne eivät ole repokohtaisia. stdout-tulosteen, ei arvauksen. `0` = ok, kaikki muu = ei ok.
### 5. Deterministinen testigraafi, vaiheittainen suoritus Tämä kuulostaa itsestään selvältä, mutta YAML-pipelineissa se rikkoutuu
helposti. Pipe (`|`) `tee`:hen syö exit-koodin. Tiedoston olemassaolon
tarkistus (`[ -f results.xml ]`) ei kerro testien läpimenosta.
Test flow on tunnettu ennen buildin alkua, ja testit ajetaan yksi kerrallaan. Jos steppi epäonnistuu, koko flow pysähtyy. **Käytännössä:** Jokainen `run`-steppi ottaa exit-koodin talteen
`$?`-muuttujaan ennen kuin mikään muu komento ehtii muuttaa sitä,
ja stepin viimeinen rivi on `exit ${EXIT}`. Pipeä ei käytetä
työvaiheen viimeisenä komentona. Ks. ADR 0008.
**Miksi:** Rinnakkainen suoritus aiheuttaa resurssikilpailua (erityisesti suorituskykytestit) ja piilottaa virheitä. Kun integraatiotesti epäonnistuu, e2e-testien ajaminen on turhaa — konttia ei viedä tuotantoon, eikä kukaan lue niitä tuloksia. Vaiheittainen suoritus on deterministinen, debuggattava ja säästää CI-minuutteja. ### 5. Pienin mahdollinen pinta-ala
**Miten:** Orkestroiva workflow käyttää Gitea REST API:a workflow-dispatchiin ja pollaa ajettavan workflow'n tilaa synkronisesti (`GET /api/v1/repos/{owner}/{repo}/actions/runs/{id}`). Tämä vastaa Jenkinsin `buildJob()`-kutsun semantiikkaa, mutta toteutetaan curl + pollaus -silmukalla. Jokainen ylimääräinen riippuvuus on ylimääräinen vikaantumispiste.
Kirjaston ainoat riippuvuudet:
### 6. Raporttien hallinta erillisellä palvelulla - Gitea Actions (alusta)
- `bash`, `curl`, `jq` (ubuntu-latest runnerissa valmiina)
- Docker (runnerissa valmiina)
- git-pages (raporttien hostaus, erillinen palvelu)
Raporttien selailtavuudesta ja elinkaaresta vastaa erillinen palvelu, joka Ei Pythonia, ei Node.js:ää ajonaikaisesti (testit omissa konteissaan).
asennetaan git-pages Helm-chartilla. Raportit ovat julkisia URL:lla Ei tietokantaa. Ei ulkoista tilanhallintaa. Kirjasto on joukko
(osoite tunnettava). URL linkitetään Git-committiin. YAML-tiedostoja ja shell-skriptejä — samat työkalut jotka jokainen
devops-ihminen jo osaa.
**Miksi:** Jenkins-versiossa linkki Cucumber-raporttiin oli kriittinen ### 6. Konfiguraatio repoon, salaisuudet Giteaan
feature. Gitea Actionsin artifact-järjestelmä ei tue HTML-selailtavuutta
(vain ZIP-lataus). Erillinen palvelu mahdollistaa hallitun retention ja
pääsyn ilman CI-alustan rajoitteita.
### 7. Yksi CI-alusta, yksi integraatiopiste Projektikohtainen konfiguraatio (`.gitea/workflows/gitea-env.conf`)
asuu mikropalvelun omassa repossa. Kehittäjä omistaa sen — hän tietää
mikä on Docker-imagen nimi, mihin registryyn puskea, mikä on
testiympäristön URL.
Kirjasto tukee vain Giteaa. Ei GitLab-, BitBucket- tai GitHub-abstraktioita. Salaisuudet (tokenit, salasanat) elävät Gitean secrets-mekanismissa,
eivät repon tiedostoissa. `secrets: inherit` välittää ne providerin
workflow'hun ilman että consumerin tarvitsee tietää mitä salaisuuksia
mikäkin provider tarvitsee.
**Miksi:** Jenkins-versio tuki neljää Git-alustaa, koska Jenkins itsessään ei tarjonnut commit-statusraportointia. Gitea Actionsissa tilanne on päinvastainen — Gitea on sekä CI-että Git-alusta. Multi-platform-tuesta tulisi pelkkää ylimääräistä abstraktiota ilman konkreettista tarvetta. Poikkeus: infra-tason asetukset (`GIT_PAGES_URL`, `GITEA_API_URL`)
ovat Gitean organization secrets/variables -mekanismissa. Ne eivät
ole repokohtaisia.
**Mitä tarkoittaa tulevaisuudessa:** Jos toinen alusta tulee ajankohtaiseksi, Gitea-versiota käytetään joko pohjana redesignille tai mallina erilliselle toteutukselle. Rajapintoja ei suunnitella etukäteen alustariippumattomiksi — se on ennenaikaista optimointia. ### 7. Consumer omistaa orkestroinnin, provider tarjoaa palikat
### 8. Cross-repo commit traceability Tämä on kirjaston tärkein arkkitehtuurinen päätös (ADR 0005).
Kun build-ketju ylittää reporajat (mikropalvelu → deployment → integraatiotestit → e2e-testit), jokainen vaihe raportoi kahteen suuntaan: omaan committiinsa ja takaisin root-committiin, josta ketju käynnistyi. Provider (`gitea-ci-library`) ei tiedä mitä testejä ajetaan, missä
järjestyksessä, tai millä branchilla. Se tarjoaa kolme reusable
workflow'ta ja joukon skriptejä.
**Miksi:** Kehittäjän ei pidä arvailla mikä versio on missäkin ympäristössä. Kun mikropalvelun commitista näkee koko ketjun — buildattu, deployattu stagingiin, integraatiotestit ajettu, e2e hyväksytty — virheenjäljitys on suora polku commitista ympäristöön. Vastaavasti Helm-repon commit kertoo mikä konttiversio sinne deployattiin ja kenen mikropalvelu-commitista se tuli. Tämä on Jenkins-version **eniten arvoa tuottanut ominaisuus**. Consumer (mikropalvelun `example-feature.yml` / `example-main.yml`)
päättää:
- Mitkä palikat kutsutaan
- Missä järjestyksessä (`needs`)
- Millä branch-ehdoilla (`if`)
- Mitkä testikontit käytetään (input-parametrit)
**Mekanismi:** Tämä on tarkoituksellinen vallanjako. Provider ei voi tietää jokaisen
tiimin tarpeita — eikä sen pidäkään. Consumer ei voi muuttaa providerin
sisäistä toteutusta — eikä sen pidäkään. Rajapinta on `workflow_call` ja
se on molemmille osapuolille selvä.
```mermaid ### 8. Branch-kohtainen reititys, ei yhtä kaikille
%%{init: {'theme': 'base'}}%%
sequenceDiagram
participant MR as Mikropalvelu-repo
participant HR as Helm-repo
participant TR as Testi-repo
participant GA as Gitea API
Note over MR: commit abc123 Eri brancheilla on eri tavoite:
Note over MR: build kontti v1.2.3
MR->>GA: POST dispatch deploy-workflow - **Feature-haara:** Onko koodi laadukasta? → testit, validointi
GA->>HR: workflow käyntiin - **Main-haara:** Onko tästä versiosta jo artifakti? Jos ei →
Note over HR: commit def456 testit + build + push + tag. Jos on → ei tehdä mitään (tai
Note over HR: container.version = 1.2.3 jatketaan klusteritesteihin).
HR->>GA: POST status "deployed by abc123" Tämä logiikka elää consumerin pipeline-tiedostossa, ei providerissa.
GA->>MR: Status: deployed to staging Se on puhdasta `if`-ehtoa ja `needs`-ketjutusta — ei skriptausta,
Note right of MR: URL → def456 ei monimutkaisia ehtoja providerin sisällä.
HR->>GA: POST status "from abc123" ### 9. Raportit erillisellä palvelulla, linkit commitissa
GA->>HR: Status: from abc123
Note right of HR: URL → abc123
MR->>GA: POST dispatch integraatiotestit Gitea Actionsin artifact-järjestelmä on binääriarkisto — ZIP-lataus,
GA->>TR: workflow käyntiin ei HTML-selailtavuutta. Testiraportit (Cucumber HTML, Bats-coverage)
Note over TR: commit ghi789 on voitava avata selaimessa yhdellä klikkauksella.
TR->>GA: POST status "integration OK" Ratkaisu: git-pages Helm-chartti, joka tarjoaa staattista
GA->>MR: Status: integration OK tiedostohostingia HTTP:llä. `publish-git-pages.sh` vie raportit
Note right of MR: URL → ghi789 sinne; `report-status.sh` linkittää commit-statuksen suoraan
raporttiin. Retention hoitaa git-pagesin sidecar automaattisesti.
TR->>GA: POST status "tested v1.2.3" Tulevaisuudessa `GITHUB_STEP_SUMMARY` (Gitea 1.27+) tarjoaa
GA->>HR: Status: tested v1.2.3 vaihtoehtoisen kanavan: jobin Summary-välilehdelle renderöityvä
Note right of HR: URL → def456 Markdown-taulukko kaikista raporttilinkeistä.
TR->>GA: POST status "tested abc123" ### 10. Vain Gitea — ei monialustatukea ilman tarvetta
GA->>MR: Status: tested abc123 (root)
Note right of MR: URL → abc123
```
**Mitä tarkoittaa käytännössä:** Jokaisella workflow'lla on kaksi build-referenssiä: `current` (oma commit) ja `root` (mikropalvelun commit, josta ketju alkoi). Molempiin POSTataan statusviestit. Root-build kulkee workflow-dispatchin `inputs`-parametrina koko ketjun läpi. Deployment-job raportoi sekä Helm-repon committiin ("from abc123") että mikropalvelun committiin ("deployed to staging → def456"). Testi-job raportoi omaan committiinsa, mikropalvelun committiin ja Helm-repon committiin. Yhden alustan tukeminen kunnolla on vaikeampaa kuin kolmen tukeminen
huonosti. Gitea Actionsin `uses:`-mekanismi, `needs`-semantiikka,
`secrets: inherit`, `gitea`-konteksti — nämä ovat alustakohtaisia
ominaisuuksia joita abstraktiokerros vain haittaisi.
Jos toinen alusta tulee ajankohtaiseksi, sille kirjoitetaan oma
toteutus. Siihen asti yksi alusta riittää. Ennenaikainen yleistys
on devopsissa yhtä haitallista kuin ohjelmistosuunnittelussa.
--- ---
@@ -152,23 +197,31 @@ sequenceDiagram
### Mitä kirjasto EI tee ### Mitä kirjasto EI tee
- **Ei ulkoista orkestraattoria.** Test flow -ketjutus perustuu Gitean REST APIin ja workflowhin itseensä. Ei erillistä palvelinta, joka hallinnoi tilaa. - **Ei ulkoista orkestraattoria.** Pipeline-ohjaus on Gitea Actionsin
- **Ei Jenkins-migraatiota.** Vanhaa Jenkinsfileä ei voi ajaa Gitea Actionsissa. Tämä on uusi kirjasto uudella konfiguraatioformaatilla. `needs`-ketjuissa ja consumerin `if`-ehdoissa.
- **Ei reaaliaikaista build-seurantaa.** Commit-statusviestit ovat pollattavia, eivät push-pohjaisia. Gitean UI hoitaa reaaliaikaisuuden. - **Ei custom actioneita.** Reusable workflow on kevyempi, versioitu
- **Ei multi-repo-monorepo-konfiguraatiota.** Jokainen mikropalvelu omistaa oman `ci-flow-values.yaml`:nsa. Jaettua konfiguraatiota ei ole projektitasolla. ja jaeltu Gitean oman mekanismin kautta.
- **Ei asennusta projekteihin.** Consumer viittaa `uses:`-direktiivillä
suoraan tämän repon workflow-tiedostoihin. Ei npm-pakettia, ei
git-submodulea, ei kopioitavia tiedostoja.
- **Ei runtime-riippuvuuksia.** Provider-skriptit käyttävät vain
työkaluja jotka ovat Gitea Actionsin `ubuntu-latest` runnerissa
valmiina: `bash`, `curl`, `jq`.
- **Ei monorepo-konfiguraatiota.** Jokainen mikropalvelu omistaa
oman pipeline-tiedostonsa ja konfiguraationsa.
--- ---
## Mitä tietoisesti hylättiin ## Mitä tietoisesti hylättiin
## Mitä tietoisesti hylättiin
| Hylätty | Syy | | Hylätty | Syy |
|---------|-----| |---|---|
| Multi-Git-platform-tuki (GitLab, BitBucket) | Vain Gitea on relevantti. Abstraktointi ilman tarvetta on turhaa kompleksisuutta | | Monoliittinen "kaikki yhdessä" -workflow | Pakottaa kaikille samat vaiheet. Palikka-arkkitehtuuri antaa jokaiselle tiimille vain mitä se tarvitsee |
| Gitea Packages raporttien hostingiin | Ei tue HTML-selailtavuutta — vain binääriartefaktien lataus | | Oma orkestraattoripalvelin | Ylimääräinen ylläpidettävä. Gitean `needs` ja `if` riittävät |
| Gitea Releases raporttien hostingiin | Saastuttaa release-historian. Satoja CI-raportteja oikeiden julkaisujen seassa | | Docker-pohjaiset custom actionit | Tuovat riippuvuuden Docker-rekisteriin. Reusable workflow on natiivimpi |
| Gitea Pages + reports-branch | Race condition rinnakkaisten buildien pushissa samaan branchiin | | Commit-status API jokaiselle vaiheelle | Duplikointia — Gitea näyttää job-statuksen automaattisesti. API vain custom-linkeille |
| Ulkoinen orkestraattoripalvelin | Ylimääräinen ylläpidettävä. Gitean oma API riittää | | `tee`-putki debug-näkyvyyteen | Syö exit-koodin. stdout ohjataan tiedostoon `>` ilman pipeä |
| Docker-pohjaiset custom actionit | Tuovat riippuvuuden Docker-rekisteriin ja monimutkaistavat jakelua. Otetaan käyttöön vain pakon edessä | | Multi-Git-platform-tuki | Ennenaikaista optimointia ilman tarvetta |
| `repository_dispatch` (webhook) test flow -ketjutukseen | Lisää konfiguraatiota vastaanottaviin repoihin. Suora REST API -kutsu on eksplisiittisempi ja debuggattavampi | | Gitea Packages raporttien hostingiin | Ei HTML-selailtavuutta — vain binäärilataus |
| Gitea Pages + reports-branch | Race condition rinnakkaisten pushien kanssa |
| `repository_dispatch` ketjutukseen | Lisää konfiguraatiota vastaanottaviin repoihin. Suora API-kutsu eksplisiittisempi |
+57 -100
View File
@@ -1,8 +1,6 @@
# Vaatimukset — Gitea Actions CI -kirjasto # Vaatimukset — Gitea Actions CI -kirjasto
> Funktionaaliset vaatimukset käyttäjän näkökulmasta. Muoto: käyttötapaukset (use cases). > Funktionaaliset vaatimukset käyttäjän näkökulmasta. Muoto: käyttötapaukset (use cases).
>
> Linkittyy: [design-rationale.md](design-rationale.md), [architecture.md](architecture.md), [report-hosting.md](report-hosting.md)
--- ---
@@ -13,130 +11,89 @@
**Main success:** **Main success:**
- Kehittäjä avaa commitin Giteassa - Kehittäjä avaa commitin Giteassa
- Näkee statusviestit: "Building...", "Unit tests OK", "Docker build OK", "Docker pushed" - Näkee job-statukset automaattisesti: spinner (käynnissä), checkmark (ok), risti (feilasi)
- Jokainen statusviesti on klikattavissa → vie buildin sivuun tai raporttiin - Testijobit näyttävät statuksen linkillä: "unit-tests Link to Bats reports", "acc-tests Link to Cucumber reports"
- Epäonnistunut steppi näkyy punaisella — kehittäjä klikkaa ja näkee mikä meni vikaan - Klikkaamalla testistatusta kehittäjä pääsee suoraan HTML-raporttiin git-pagesissa
- Docker-build näyttää linkin konttirekisteriin
**Poikkeukset:** **Poikkeukset:**
- Statusviesti puuttuu (workflow kaatui ennen raportointia) → commitissa näkyy timeout/error - Statusviesti puuttuu (workflow kaatui ennen raportointia) → commitissa näkyy timeout/error
- Useampi workflow samalle commitille → statukset erottuvat `key`-arvolla - Useampi workflow samalle commitille → statukset erottuvat `context`-avaimella
--- ---
## UC2: Kehittäjä lukee testiraportteja selaimessa ## UC2: Kehittäjä lukee testiraportteja selaimessa
**Actor:** Kehittäjä **Actor:** Kehittäjä
**Precondition:** Build on valmistunut, raportit pushattu Minioon **Precondition:** Build on valmistunut, raportit julkaistu git-pagesiin
**Main success:** **Main success:**
- Kehittäjä klikkaa commitin statusviestin URL:ää ("Unit tests OK" → URL) - Kehittäjä klikkaa commitin status-kuvaketta (esim. "unit-tests")
- Selain avautuu, OIDC-kirjautuminen (Gitea-tunnuksilla) - Selain avautuu suoraan HTML-raporttiin git-pagesissa
- Cucumber-raportti renderöityy HTML:nä selaimessa - Bats-raportissa näkyy: testitulokset, code coverage
- Raportissa näkyy: mitkä testit menivät läpi, mitkä epäonnistuivat, stack tracet - Cucumber-raportissa näkyy: mitkä testit menivät läpi, mitkä epäonnistuivat, stack tracet
- Yläreunassa linkki "← Back to build" → palaa buildin raportti-indeksiin
**Poikkeukset:** **Poikkeukset:**
- Raporttia ei ole (testit skipattiin, workflow kaatui ennen pushausta) → 404 - Raporttia ei ole (testit skipattiin, workflow kaatui ennen julkaisua) → 404
- OIDC-sessio vanhentunut → uudelleenohjaus kirjautumiseen
--- ---
## UC3: Kehittäjä selaa projektin build-historiaa ## UC3: Kehittäjä vertailee kahden buildin raportteja
**Actor:** Kehittäjä
**Precondition:** Projektilla on vähintään yksi build
**Main success:**
- Kehittäjä menee `{MINIO_BASE}/{repo_slug}/index.html`
- Näkee listan kaikista buildeista aikajärjestyksessä (uusin ensin)
- Jokaisella buildilla: commitin 8-merkkinen hash, päivämäärä, branch, status (✅/❌)
- Klikkaa buildia → siirtyy `{commit_short}/index.html` — buildin raporttilistaukseen
- Buildin sivulla: lista kaikista raporteista (Cucumber, JaCoCo, Maven Site) linkkeinä
- "← Back to builds" → palaa projektin build-indeksiin
**Poikkeukset:**
- Projekti poistettu / siivottu retention policyn mukaan → 404
- Indeksitiedosto puuttuu (ensimmäinen build kesken) → 404, generoituu seuraavalla pushauksella
---
## UC4: Kehittäjä jäljittää kontin koko ketjun commitista
**Actor:** Kehittäjä
**Precondition:** Mikropalvelun commitista on ajettu vähintään deployment
**Main success:**
- Kehittäjä avaa mikropalvelun commitin abc123
- Näkee statusviestit: "Build OK", "Deployed to staging → def456", "Integration tests OK → ghi789"
- Klikkaa "Deployed to staging → def456" → siirtyy Helm-repon committiin def456
- Helm-repon commitissa näkyy: "from abc123", "tested v1.2.3", "tested abc123"
- Klikkaa "tested abc123" → palaa mikropalvelun committiin
- Koko ketju on navigoitavissa edestakaisin commit-statuslinkkien kautta
**Poikkeukset:**
- Välivaiheen commit siivottu → statusviesti jää, mutta linkki vie 404:ään
- Deploytty versio ei vastaa odotettua → statusviestissä näkyy ristiriita
---
## UC5: Kehittäjä näkee deployatun version ympäristössä
**Actor:** Kehittäjä
**Precondition:** Deployment on suoritettu, Helm-repon commit tehty
**Main success:**
- Kehittäjä avaa Helm-repon commitin def456
- Näkee: "container.version = 1.2.3", "Deployed by abc123"
- Tietää heti mikä konttiversio on missäkin ympäristössä
- Voi verrata mikropalvelun uusimpaan commitin — onko ympäristö ajan tasalla?
**Poikkeukset:**
- `doNotDowngrade` esti deploymentin → statusviesti "Skipped: newer version already deployed"
---
## UC6: Testi-insinööri näkee mitä konttia testattiin
**Actor:** Testi-insinööri
**Precondition:** Integraatio- tai e2e-testit on ajettu
**Main success:**
- Avaa testi-repon commitin ghi789
- Näkee: "Tested v1.2.3" (mikä kontti), "Tested abc123" (mikä mikropalvelun commit)
- Klikkaa testiraporttiin → näkee tulokset
- Näkee myös mitkä tagit olivat käytössä (`@smoke and not @slow`)
- Voi todentaa että testattiin oikeaa versiota
**Poikkeukset:**
- Version check epäonnistui (haluttu versio ei ollut ympäristössä) → status: "Version mismatch"
- Testit keskeytyivät timeoutiin → status: timeout, osittaiset tulokset raportissa
---
## UC7: Kehittäjä vertailee kahden buildin raportteja
**Actor:** Kehittäjä **Actor:** Kehittäjä
**Precondition:** Projektilla on vähintään kaksi buildia **Precondition:** Projektilla on vähintään kaksi buildia
**Main success:** **Main success:**
- Kehittäjä avaa projektin build-indeksin `{MINIO_BASE}/{repo}/index.html` - Kehittäjä avaa kaksi buildia Gitean Actions-näkymässä eri välilehtiin
- Näkee viimeisimmät buildit vierekkäin
- Avaa kaksi buildia eri välilehtiin
- Voi verrata Cucumber-tuloksia: "build #42 vs #41 — mikä testi meni rikki?" - Voi verrata Cucumber-tuloksia: "build #42 vs #41 — mikä testi meni rikki?"
**Poikkeukset:** ---
- Vanha build siivottu → ei näy indeksissä
## UC4: Kehittäjä jäljittää kontin koko ketjun commitista (tuleva)
**Actor:** Kehittäjä
**Precondition:** Mikropalvelun commitista on ajettu deployment ja klusteritestit
**Main success:**
- Kehittäjä avaa mikropalvelun commitin
- Näkee statusviestit: "Build OK", "Deployed to staging", "Integration tests OK"
- Klikkaamalla statusta siirtyy toisen repon committiin
- Koko ketju on navigoitavissa edestakaisin commit-statuslinkkien kautta
---
## UC5: Kehittäjä näkee deployatun version ympäristössä (tuleva)
**Actor:** Kehittäjä
**Precondition:** Deployment on suoritettu
**Main success:**
- Avaa Helm-repon commitin
- Näkee suoraan mikä konttiversio on deployattu
- Voi verrata mikropalvelun uusimpaan commitiin — onko ympäristö ajan tasalla?
---
## UC6: Kehittäjä saa Gitea 1.27+ Summary-näkymän raporttilinkeistä (tuleva)
**Actor:** Kehittäjä
**Precondition:** Gitea 1.27+ ja päivitetty runner
**Main success:**
- Avaa workflow'n Gitea Actionsissa
- "Report Summary" -jobin Summary-välilehdellä näkyy taulukko linkeillä kaikkiin raportteihin
- Yhdellä silmäyksellä näkee mitä testejä ajettiin ja pääsee klikkaamalla raportteihin
--- ---
## Ei-toiminnalliset vaatimukset ## Ei-toiminnalliset vaatimukset
| Vaatimus | Toteutus | | Vaatimus | Toteutus |
|----------|----------| |---|---|
| Raportit selailtavissa HTML:nä | MinIO static web hosting | | Raportit selailtavissa HTML:nä | git-pages static hosting |
| Linkki commitista suoraan raporttiin | Statusviestin `url`-kenttä | | Linkki commitista suoraan raporttiin | Commit-status API:n `target_url` |
| Build-indeksi per projekti | Generoitu `index.html` Minioon | | Raporttien pysyvyys | git-pages retention sidecar |
| Navigaatio raporttien välillä | "Back to build" / "Back to builds" — linkit indeksisivuilla | | Virheiden propagointi | Gitea Actions `needs`-ketju |
| Cross-repo-navigaatio | Statusviestit linkittävät repoja ristiin | | Pipeline-pysäytys virhetilanteessa | `needs` automaattinen skip |
| Raporttien pysyvyys | ConfigMap-pohjainen retention policy | | Exit-koodi ainoa totuus | ADR 0008 |
| Autentikointi | OIDC (Traefik middleware, Gitea OAuth2) | | Statusraportointi vain raporttilinkeille | ADR 0007 |
+1 -11
View File
@@ -18,7 +18,7 @@ Runnerilla on yksi vastuu: **suorittaa workflow-steppejä**. Kaikki runtime-ymp
## Kontit ja palvelut ## Kontit ja palvelut
Jokainen job voi määritellä käyttämänsä kontit. Tämä vastaa Jenkinsin pod template -konseptia, mutta on yksinkertaisempi: Jokainen job voi määritellä käyttämänsä kontit. Eri jobeilla voi olla eri kontti:
### `container:` — ajonaikainen ympäristö ### `container:` — ajonaikainen ympäristö
@@ -99,13 +99,3 @@ Eri labelit mahdollistavat erikoistuneet runnerit (ARM, GPU, Windows), mutta MVP
|------|-------|-------|--------------| |------|-------|-------|--------------|
| **Global** | Kaikki organisaatiot ja repot | Token-vuoto → hyökkääjä voi ajaa koodia missä tahansa | Jaettu infra, keskitetty hallinta | | **Global** | Kaikki organisaatiot ja repot | Token-vuoto → hyökkääjä voi ajaa koodia missä tahansa | Jaettu infra, keskitetty hallinta |
| **Organization** | Yhden organisaation repot | Rajoittuu yhteen orgiin | Per organisaatio, eristetty — **suositeltu** | | **Organization** | Yhden organisaation repot | Rajoittuu yhteen orgiin | Per organisaatio, eristetty — **suositeltu** |
## Jenkins-vertailu
| Jenkins | Gitea Actions |
|---------|--------------|
| Pod template (YAML) määrittelee kontit | `container:` + `services:` per job |
| Jokaiselle jobille oma pod | Jokaiselle jobille omat konttimääritykset |
| DinD sidecar-podissa | `services: docker:dind` samassa jobissa |
| Agentti = erillinen JVM-prosessi | Runner = kevyt Go-binääri tai K8s-pod |
| Labelit Jenkins-nodessa | Labelit runner-rekisteröinnissä |
+89 -145
View File
@@ -1,197 +1,141 @@
# Jaetut skriptit # Jaetut skriptit
> ⚠️ **POC-vaihe.** Osa kuvatuista skripteistä (push-reports.sh, tag-commit.sh) > Provider-skriptit asuvat `scripts/`-hakemistossa. Consumer-skriptit
> on suunniteltu mutta ei toteutettu. Toteutetut: `publish-git-pages.sh`, > asuvat `.gitea/scripts/`-hakemistossa. ADR 0006.
> `report-status.sh`, `dispatch-workflow.sh` (POC-taso).
>
> Uudelleenkirjoitus odottaa: skriptien määrä ja rajapinnat voivat muuttua.
Skriptit asuvat `gitea-ci-library/scripts/`-hakemistossa.
--- ---
## `report-status.sh` ## `report-status.sh`
POSTaa build-statuksen Gitea-commitin REST APIin. POSTaa commit-statuksen Gitea REST APIin. Käytetään **vain** kun tarvitaan
custom-linkki (testiraportti, Docker registry). Tool-jobit luottavat
Gitean natiiviin job-statukseen. ADR 0007.
### Rajapinta ### Rajapinta
```bash ```bash
report-status.sh <state> <description> <url> [key] [root_commit] [root_repo] report-status.sh <state> <description> <context> [suite] [custom_url]
``` ```
| Parametri | Pakollinen | Kuvaus | | Parametri | Pakollinen | Kuvaus |
|-----------|------------|--------| |---|---|---|
| `state` | Kyllä | `pending`, `success`, `failure`, `error` | | `state` | Kyllä | `pending`, `success`, `failure` |
| `description` | Kyllä | Ihmisluettava kuvaus (esim. "Unit tests passed") | | `description` | Kyllä | Ihmisluettava kuvaus |
| `url` | Kyllä | Linkki buildiin tai raporttiin | | `context` | Kyllä | Uniikki avain (`unit-tests`, `acc-tests`, `ci-docker-build-push`) |
| `key` | Ei | Uniikki avain. Oletus: `commit-{sha_short}` | | `suite` | Ei | Julkaistun raportin suite-nimi → linkki git-pagesiin |
| `root_commit` | Ei | Root-buildin commit-hash (cross-repo-raportointia varten) | | `custom_url` | Ei | Oma URL (ohittaa oletus-URL:n generoinnin) |
| `root_repo` | Ei | Root-buildin repo (cross-repo-raportointia varten) |
### Kutsuesimerkkejä ### Kutsuesimerkkejä
```bash ```bash
# Buildin aloitus # Testijobi, linkki git-pages-raporttiin
report-status.sh pending "Building..." "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" report-status.sh success "Link to Bats reports" unit-tests bats
# Testivaihe valmis, linkki raporttiin # Docker build, custom URL registryyn
report-status.sh success "Unit tests OK" "$MINIO_BASE_URL/$GITHUB_REPOSITORY/${GITHUB_SHA::8}/cucumber/overview-features.html" "unit-test" report-status.sh success "Docker build & push 1.2.0 OK" ci-docker-build-push "" \
"https://gitea.example.com/org/-/packages/container/app/1.2.0"
# Deployment valmis, cross-repo: raportoi takaisin mikropalvelun committiin
report-status.sh success "Deployed to staging" "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/commit/$GITHUB_SHA" "deploy-staging" "$ROOT_COMMIT" "$ROOT_REPO"
``` ```
### URL-generointi
- Jos `suite` annettu → URL: `${GIT_PAGES_URL}/${repo}/reports/${sha8}/${suite}/`
- Jos `custom_url` annettu → käytetään sellaisenaan
- Muuten → URL: `${GITEA_API_URL}/${repo}/actions/runs/${run_id}` (Gitea Actions -loki)
### Gitea API -kutsu ### Gitea API -kutsu
```bash ```bash
curl -X POST "$GITEA_API_URL/api/v1/repos/$REPO/statuses/$COMMIT" \ curl -X POST "$GITEA_API_URL/api/v1/repos/$REPO/statuses/$COMMIT" \
-H "Authorization: token $GITEA_TOKEN" \ -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{ -d "{\"state\":\"$STATE\",\"target_url\":\"$URL\",\"description\":\"$DESCRIPTION\",\"context\":\"$CONTEXT\"}"
\"state\": \"$STATE\",
\"target_url\": \"$URL\",
\"description\": \"$DESCRIPTION\",
\"context\": \"$KEY\"
}"
``` ```
Status-arvot mapataan Gitean skeemaan: `pending` (INPROGRESS), `success` (SUCCESS), `failure` (FAILURE), `error` (STOPPED). ---
## `publish-git-pages.sh`
Julkaisee raporttihakemiston git-pages-palveluun PATCH-tar:na.
### Rajapinta
```bash
publish-git-pages.sh <suite>
```
| Parametri | Pakollinen | Kuvaus |
|---|---|---|
| `suite` | Kyllä | Raporttihakemiston nimi (`bats`, `cucumber`, `junit`, ...) |
### Toiminta
1. Lukee raportit hakemistosta `reports/${SHA8}/${suite}/`
2. Pakkaa tar:ksi ja PATCHaa git-pagesiin BasicAuthilla
3. Tulostaa raportin base-URL:n stdoutiin
### Vaaditut env-muuttujat
| Muuttuja | Lähde |
|---|---|
| `GITEA_API_URL` | `env_json` → workflow `env:` |
| `GIT_PAGES_URL` | `env_json` → workflow `env:` |
| `GIT_PAGES_PUBLISH_TOKEN` | Gitea secret → `env:` |
| `GITHUB_REPOSITORY` | Automaattinen |
| `GITHUB_SHA` | Automaattinen |
---
## `ci-validate.sh`
Validoi `.conf`-tiedoston ja tarkistaa että pakolliset secretit on asetettu.
Kutsutaan `config-provider.yml`:stä osana konfiguraation latausta.
### Rajapinta
```bash
ci-validate.sh
```
Lukee tiedoston polun `CI_CONF_FILE`-env-muuttujasta (oletus: `.gitea/workflows/gitea-env.conf`).
### Validointisäännöt
- `.conf`-tiedosto on olemassa
- Jokaisella `KEY=VALUE`-rivillä on arvo (ei tyhjää)
- URL-tyyppiset avaimet alkavat `http://` tai `https://`
- `GITEA_TOKEN` on asetettu
- `GIT_PAGES_PUBLISH_TOKEN` on asetettu
--- ---
## `dispatch-workflow.sh` ## `dispatch-workflow.sh`
Dispatchaa workflow'n toisessa repossa ja pollaa sen valmistumista synkronisesti. Dispatchaa workflow'n toisessa repossa ja pollaa sen valmistumista synkronisesti.
Käytetään GitOps-deploymentissa ja klusteritestien ketjutuksessa (tuleva).
### Rajapinta ### Rajapinta
```bash ```bash
dispatch-workflow.sh <target_repo> <workflow_file> <ref> <inputs_json> <gitea_api_url> <gitea_token> [timeout_minutes] dispatch-workflow.sh <target_repo> <workflow_file> <ref> <inputs_json> [timeout_minutes]
``` ```
| Parametri | Pakollinen | Kuvaus |
|-----------|------------|--------|
| `target_repo` | Kyllä | `owner/repo` |
| `workflow_file` | Kyllä | Workflow-tiedoston nimi (esim. `test.yml`) |
| `ref` | Kyllä | Branch |
| `inputs_json` | Kyllä | JSON-objekti input-parametreina |
| `gitea_api_url` | Kyllä | Gitean API-URL (esim. `https://gitea.example.com`) |
| `gitea_token` | Kyllä | Gitea API -token |
| `timeout_minutes` | Ei | Oletus: 360 (6 tuntia) |
### Toiminta ### Toiminta
1. **Dispatch:** `POST /api/v1/repos/{target_repo}/actions/workflows/{workflow_file}/dispatches` 1. **Dispatch:** `POST /api/v1/repos/{target_repo}/actions/workflows/{workflow_file}/dispatches`
2. **Etsi run:** `GET /api/v1/repos/{target_repo}/actions/runs?status=running` → etsi uusin (aikaleimasta) 2. **Poll:** `GET /api/v1/repos/{target_repo}/actions/runs` → odota valmistumista
3. **Poll:** `GET /api/v1/repos/{target_repo}/actions/runs/{run_id}` 10s välein 3. **Palauta:** `conclusion` (`success`/`failure`/`timeout`)
4. **Lopeta:** Kun `status == "completed"` → palauta `conclusion` (`success`/`failure`/`cancelled`)
5. **Timeout:** Jos kestää yli `timeout_minutes` → palauta `timeout`
### Kutsuesimerkki
```bash
dispatch-workflow.sh "tests/integration" "test.yml" "main" \
'{"version":"1.2.3","tags":"@smoke","root_commit":"abc123","root_repo":"services/temperature-store"}' \
"https://gitea.example.com" "gtp_abc123"
```
---
## `push-reports.sh` (vanhentunut — korvattu `publish-git-pages.sh`:lla)
Puskaa raporttihakemiston git-pagesiin.
### Rajapinta
```bash
push-reports.sh <report_type> <source_dir> [index_title]
```
| Parametri | Pakollinen | Kuvaus |
|-----------|------------|--------|
| `report_type` | Kyllä | Raportin tyyppi (`cucumber`, `jacoco`, `junit`, `site`) |
| `source_dir` | Kyllä | Paikallinen hakemisto, jossa raporttitiedostot |
| `index_title` | Ei | Näkyvä nimi indeksisivulla (esim. "Cucumber Reports") |
### Toiminta
1. Kopioi raportit: `mc cp --recursive {source_dir} minio/reports/{repo}/{commit_short}/{report_type}/`
2. Päivitä `/reports/{repo}/{commit_short}/index.html` — lisää linkki tähän raporttiin
3. Päivitä `/reports/{repo}/index.html` — varmista että tämä build on listalla
4. Palauta URL: `{MINIO_BASE_URL}/{repo}/{commit_short}/{report_type}/index.html`
### Indeksisivut
**Projektin build-indeksi** (`/reports/{repo}/index.html`):
- Lista buildeista aikajärjestyksessä (uusin ensin)
- Jokainen rivi: commit hash (linkki), päivämäärä, status (✅/❌), branch
**Buildin raportti-indeksi** (`/reports/{repo}/{commit_short}/index.html`):
- Lista raporteista linkkeinä
- Linkki "← Back to builds" → projektin build-indeksiin
Molemmat generoidaan uudestaan jokaisen pushauksen yhteydessä. Staattinen HTML, ei vaadi palvelinpuolen logiikkaa.
### Kutsuesimerkki
```bash
push-reports.sh cucumber target/cucumber-report "Cucumber Reports"
# → https://reports.example.com/temperature-store/abc12345/cucumber/overview-features.html
push-reports.sh jacoco target/jacoco-report "JaCoCo Coverage"
# → https://reports.example.com/temperature-store/abc12345/jacoco/index.html
```
---
## `tag-commit.sh`
Tagittaa commitin versiolla Gitea REST API:n kautta.
### Rajapinta
```bash
tag-commit.sh <version>
```
### Toiminta
```bash
curl -X POST "$GITEA_API_URL/api/v1/repos/$GITHUB_REPOSITORY/tags" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"$VERSION\",
\"message\": \"Build #$GITHUB_RUN_NUMBER\",
\"target\": \"$GITHUB_SHA\"
}"
```
### Kutsu
```bash
tag-commit.sh "1.2.3.$GITHUB_RUN_NUMBER"
```
Tagataan vain onnistuneen buildin ja pushin jälkeen. Tämän jälkeen `isContainerBuilt()` palauttaa `true` samalle commitille.
--- ---
## Muuttujat, joita skriptit olettavat ## Muuttujat, joita skriptit olettavat
Skriptit lukevat nämä Gitea Actionsin ympäristömuuttujat:
| Muuttuja | Lähde | Käyttäjä | | Muuttuja | Lähde | Käyttäjä |
|----------|-------|----------| |---|---|---|
| `GITEA_API_URL` | Org variable | `report-status.sh` | | `GITEA_API_URL` | `env_json` | `report-status.sh`, `ci-validate.sh` |
| `GITEA_TOKEN` | Org secret | `report-status.sh`, `tag-commit.sh` | | `GIT_PAGES_URL` | `env_json` | `publish-git-pages.sh`, `report-status.sh` |
| `MINIO_BASE_URL` | Org variable | `push-reports.sh` | | `GITEA_TOKEN` | Gitea secret | `report-status.sh`, `check-version.yml`, `docker-build-push.yml` |
| `MINIO_ACCESS_KEY` | Org secret | `push-reports.sh` | | `GIT_PAGES_PUBLISH_TOKEN` | Gitea secret | `publish-git-pages.sh` |
| `MINIO_SECRET_KEY` | Org secret | `push-reports.sh` |
| `GITHUB_REPOSITORY` | Automaattinen | Kaikki skriptit | | `GITHUB_REPOSITORY` | Automaattinen | Kaikki skriptit |
| `GITHUB_SHA` | Automaattinen | Kaikki skriptit | | `GITHUB_SHA` | Automaattinen | Kaikki skriptit |
| `GITHUB_SERVER_URL` | Automaattinen | `report-status.sh` | | `GITHUB_RUN_ID` | Automaattinen | `report-status.sh` |
| `GITHUB_RUN_ID` | Automaattinen | `report-status.sh`, `tag-commit.sh` | | `GITHUB_RUN_NUMBER` | Automaattinen | `docker-build-push.yml` (tag-commit) |
| `GITHUB_RUN_NUMBER` | Automaattinen | `tag-commit.sh` |
| `GITHUB_ACTOR` | Automaattinen | Docker-labelit |
+11 -9
View File
@@ -1,7 +1,6 @@
# Tech Stack — Gitea Actions CI -kirjasto # Tech Stack — Gitea Actions CI -kirjasto
> ⚠️ POC-vaihe. Osa teknologiavalinnoista voi muuttua uudelleenkirjoituksen > Katso myös `git-pages/docs/tech-stack.md`.
> myötä. Katso myös `git-pages/docs/tech-stack.md`.
--- ---
@@ -21,19 +20,22 @@ Raportit hostataan git-pages-palvelulla (`git-pages/`-Helm-chartti).
Julkaisu: `scripts/publish-git-pages.sh` → PATCH tar. Tarkemmat Julkaisu: `scripts/publish-git-pages.sh` → PATCH tar. Tarkemmat
teknologiavalinnat: `git-pages/docs/tech-stack.md`. teknologiavalinnat: `git-pages/docs/tech-stack.md`.
Tulevaisuus: `GITHUB_STEP_SUMMARY` (Gitea 1.27+) tarjoaa Summary-näkymän
suoraan Gitea UI:ssa.
## Tuetut ulkoiset palvelut ## Tuetut ulkoiset palvelut
| Palvelu | Rajapinta | Käyttötarkoitus | | Palvelu | Rajapinta | Käyttötarkoitus |
|---|---|---| |---|---|---|
| **Gitea REST API** | `/api/v1/` | Commit-status, workflow-dispatch, branch-listaus (retention) | | **Gitea REST API** | `/api/v1/` | Commit-status, git-tagit, workflow-dispatch |
| **git-pages** | HTTP | Raporttien hostaus | | **git-pages** | HTTP (PATCH tar) | Raporttien hostaus |
| **Gitea Packages** | Container registry API | Docker-imagen push | | **Gitea Packages** | Container registry API | Docker-imagen push |
## Mitä EI tueta (verrattuna Jenkins-versioon) ## Mitä EI tueta
| Teknologia | Syy | | Teknologia | Syy |
|---|---| |---|---|
| **MinIO** | Korvattu git-pagesilla | | **Multi-Git-platform** | Vain Gitea — yksi alusta kunnolla (periaate 10) |
| **Multi-Git-platform** | Vain Gitea | | **Custom actionit** | Reusable workflow on kevyempi ja natiivimpi (periaate 2) |
| **Jenkins** (shared library, plugins) | Gitea Actions korvaa | | **Ulkoinen orkestraattori** | Gitean `needs` + `if` hoitaa ohjauksen |
| **Artifactory/Nexus** | MVP:ssä ei, factory/adapter-pattern valmiina | | **Artifactory/Nexus** | Gitea Packages riittää MVP:ssä |
+72 -336
View File
@@ -1,390 +1,126 @@
# Reusable workflowt # Reusable workflowt
> ⚠️ **POC-vaihe.** Toteutettu: `quality-gate.yml`. Suunnitteilla: > Provider-workflowt tarjoavat ydintoiminnallisuuden. Consumer kokoaa ne
> `ci-master.yml`, `deploy.yml`, `test.yml`. > haluamakseen pipelineksi. Esimerkkitoteutus: `example-*`-tiedostot.
--- ---
## Yhteiset konventiot ## Yhteiset konventiot
Kaikki workflowt: Kaikki workflowt:
- Käyttävät `concurrency:`-ryhmää estämään saman branchin rinnakkaiset ajot (vastaa Jenkins `disableConcurrentBuilds()`) - Käyttävät `concurrency:`-ryhmää estämään saman branchin rinnakkaiset ajot
- Lukevat konfiguraation `ci-flow-values.yaml`:sta - Provider-workflowt lukevat konfiguraation inputtina (`env_json`)
- Raportoivat jokaisen vaiheen Gitea-commitin statukseen `report-status.sh`:lla - Statusraportointi: tool-jobit natiivilla, test-jobit API:lla raporttilinkin takia (ADR 0007)
- Käyttävät projektilta saatuja `with:`-parametreja konttien määrittelyyn (kirjasto ei pakota konttiversioita) - Exit-koodi aina ylös, ei pipeä (ADR 0008)
--- ---
## `quality-gate.yml` — Merge-portti ## Provider-workflowt
**Trigger:** `workflow_call`consumer kutsuu `uses:`-direktiivillä ### `config-provider.yml` — Konfiguraation lataus ja validointi
**Rooli:** Laatuportti, joka ajetaan branch protection -sääntönä ennen PR:n **Trigger:** `workflow_call`
sulkemista mainiin. Pipeline on ajettava (`run > 1`) eikä yhtään jobia
saa failata.
**Provider-Consumer-malli (ADR 0005):** Provider tarjoaa orkestroinnin **Inputs:**
(validointi, raporttien julkaisu, commit-status). Consumer omistaa
pipeline-stepit — valitsee testityökalunsa, mahdolliset laatu- ja
tietoturva-analyy sit sekä niiden järjestyksen. Alla oleva esimerkki
kuvaa tyypillistä Java-mikropalvelua Mavenilla; consumer korvaa nämä
omalla tekniikkapinollaan.
### Inputs (providerin rajapinta) | Parametri | Pakollinen | Kuvaus |
|-----------|------------|--------|
| `config_path` | Kyllä | Polku `.conf`-tiedostoon |
| Parametri | Pakollinen | Tyyppi | Kuvaus | **Secrets:**
|-----------|------------|--------|--------|
| `env_json` | Kyllä | string | JSON-muotoiset ympäristömuuttujat (`GITEA_API_URL`, `GIT_PAGES_URL`) |
| `*` | — | — | Consumer lisää omat parametrinsa (`maven-image`, `docker-image`, jne.) |
### Secrets
| Secret | Pakollinen | Kuvaus | | Secret | Pakollinen | Kuvaus |
|--------|------------|--------| |--------|------------|--------|
| `GITEA_TOKEN` | Kyllä | Gitea API-kutsuihin (commit-status) | | `GITEA_TOKEN` | Kyllä | Validointia varten |
| `GIT_PAGES_PUBLISH_TOKEN` | Kyllä | Raporttien julkaisuun git-pagesiin | | `GIT_PAGES_PUBLISH_TOKEN` | Kyllä | Validointia varten |
### Steppi-kaavio (Java-esimerkki) **Outputs:**
```mermaid | Output | Kuvaus |
%%{init: {'theme': 'base', 'flowchart': {'arrowheadScale': 2}}}%% |--------|--------|
flowchart TD | `env_json` | JSON-muotoiset ympäristömuuttujat |
VAL["validate | `config_path` | Sama polku takaisin (DRY downstream-käyttöön) |
provider: tarkista
CI-konfiguraatio"] --> TEST["test
consumer: mvn test
→ testiraportit + coverage"]
VAL --> AI_SCAN["ai-scan \[optional\] **Steppi-kaavio:**
consumer: tietoturva- ```
tai laatu-skannaus"] checkout → validate CI config → parse conf to JSON
TEST --> SONAR["sonarqube \[optional\]
consumer: mvn sonar:sonar
→ laatupoikkeamat"]
TEST --> PUB["publish-reports
provider: vie raportit
git-pagesiin"]
SONAR --> PUB
AI_SCAN --> PUB
PUB --> STATUS["commit-status
provider: aseta status
linkillä raporttiin"]
FAIL("fail") -. "if: always()" .-> PUB
style VAL fill:#2563eb,color:#ffffff
style TEST fill:#059669,color:#ffffff
style SONAR fill:#7c3aed,color:#ffffff
style AI_SCAN fill:#7c3aed,color:#ffffff
style PUB fill:#0891b2,color:#ffffff
style STATUS fill:#f59e0b,color:#111827
style FAIL fill:#dc2626,color:#ffffff
linkStyle default stroke:#9ca3af,stroke-width:3px
``` ```
Consumerin omat stepit (test, sonarqube, ai-scan) ovat esimerkki. ### `check-version.yml` — Version ja artifactin tarkistus
Vastaava rakenne toimii millä tahansa kielellä tai työkalulla.
### Optionaaliset laatu- ja tietoturvaskannaukset **Trigger:** `workflow_call` — käytetään vain main-haarassa
Consumer voi lisätä pipelineen omia skannaussteppejä testien rinnalle. **Inputs:** `env_json`
Nämä ajetaan rinnakkain `validate`-vaiheen jälkeen ja syöttävät
raporttinsa providerin `publish-reports`-palveluun. Jokainen skannaus
on oma Gitea Actions -jobinsa.
```mermaid **Outputs:** `artifact_exists` (true/false), `version` (string)
%%{init: {'theme': 'base', 'flowchart': {'arrowheadScale': 2}}}%%
flowchart LR
VAL["validate"] --> SAST["sast
semgrep / codeql"]
VAL --> SCA["sca
snyk / owasp dc"]
VAL --> SECRETS["secret-scan
gitleaks"]
VAL --> LICENSE["license
fossa / scancode"]
VAL --> AI_REVIEW["ai-review
code quality"]
SAST --> PUB **Steppi-kaavio:**
SCA --> PUB ```
SECRETS --> PUB checkout → laske versio package.json + git-tageista → output
LICENSE --> PUB
AI_REVIEW --> PUB
PUB["publish-reports + commit-status"]
style VAL fill:#2563eb,color:#ffffff
style SAST fill:#7c3aed,color:#ffffff
style SCA fill:#7c3aed,color:#ffffff
style SECRETS fill:#7c3aed,color:#ffffff
style LICENSE fill:#7c3aed,color:#ffffff
style AI_REVIEW fill:#7c3aed,color:#ffffff
style PUB fill:#0891b2,color:#ffffff
linkStyle default stroke:#9ca3af,stroke-width:3px
``` ```
| Kategoria | Esimerkki | Kuvaus | ### `docker-build-push.yml` — Docker build & push
|-----------|-----------|--------|
| **SAST** | Semgrep, CodeQL | Staattinen analyysi — bugit ja haavoittuvuudet koodista |
| **SCA** | Snyk, OWASP Dependency-Check | Riippuvuuksien tunnetut haavoittuvuudet |
| **Secret scan** | Gitleaks, TruffleHog | API-avaimet, tokenit ja salasanat repossa |
| **Lisenssit** | FOSSA, ScanCode | Riippuvuuksien lisenssien yhteensopivuus |
| **AI review** | — | Automaattinen koodikatselmointi |
### Error handling **Trigger:** `workflow_call`
Providerin julkaisu- ja status-stepit käyttävät `if: always()`-ehtoa, **Inputs:** `env_json`, `version`
jotta raportit ja commit-status päivittyvät myös failaavista ajoista.
Consumerin omat stepit voivat vapaasti päättää `continue-on-error`- tai
`if: failure()`-logiikastaan. Provider ei määrittele virheidenkäsittelyä
consumerin pipelineen.
### Merge-portti **Secrets:** `GITEA_TOKEN`, `DOCKER_USERNAME`, `DOCKER_PASSWORD`
Branch protection -säännössä Giteassa vaaditaan ennen PR:n sulkemista: **Steppi-kaavio:**
- **Pipeline on ajettu** (`run > 1`, ei "never run" -tila)
- **Kaikki commit-statukset vihreitä** — validate, testit, laatuportit
- Jos joku steppi failaa, status asettuu `failure`-tilaan ja PR:n
sulkeminen estyy
### Optionaalinen PR-ympäristö (preview app)
Consumer voi halutessaan buildata kontin ja deployata sen väliaikaiseen
PR-ympäristöön. Tämä on optionaalinen continuation-haara, joka
aktivoituu ehdolla:
- PR:ssä on tietty label (esim. `preview`)
- Commit message sisältää triggerisanan (esim. `[preview]`)
**Elinkaari:**
```mermaid
%%{init: {'theme': 'base', 'flowchart': {'arrowheadScale': 2}}}%%
flowchart LR
QG["quality-gate
testit + skannaukset
ok"] --> BUILD["build-container
tag: pr-42"]
BUILD --> DEPLOY["deploy-pr-env
väliaikainen ympäristö"]
DEPLOY --> STATUS["commit-status
linkki PR-ympäristöön"]
PR_CLOSE["PR merged / closed"] --> CLEANUP["cleanup-pr-env
tuhoa ympäristö"]
style QG fill:#059669,color:#ffffff
style BUILD fill:#0891b2,color:#ffffff
style DEPLOY fill:#7c3aed,color:#ffffff
style STATUS fill:#f59e0b,color:#111827
style PR_CLOSE fill:#dc2626,color:#ffffff
style CLEANUP fill:#dc2626,color:#ffffff
linkStyle default stroke:#9ca3af,stroke-width:3px
``` ```
build-push (build + push samassa jobissa, ei levyn kautta) → tag-commit
1. Quality-gate läpäisty (testit + skannaukset ok)
2. Buildaa kontti, tagi sisältää PR-numeron (`pr-42`)
3. Deployaa PR-ympäristöön (preview/review app)
4. Asettaa commit-statuksen linkillä ympäristöön
5. **PR:n sulkeutuessa** (merge/close): cleanup-job tuhoaa ympäristön
Tämä on **consumerin vastuulla** — provider tarjoaa tarvittavat
skriptit (`publish-git-pages.sh`, `report-status.sh`), mutta
trigger-ehto, kontin buildaus ja ympäristön hallinta kuuluvat
consumerin pipelineen.
---
## `ci-master.yml` — Main-branch build
**Trigger:** `workflow_call` — kutsutaan main-branchiin pushattaessa
**Rooli:** Buildaa artifaktin (kontti, JAR, npm-paketti tms.) ja julkaisee
sen rekisteriin. Jos sama commit on jo buildattu (version tag on olemassa),
build skipataan ja siirrytään suoraan test flow'hun.
**Provider-Consumer-malli (ADR 0005):** Provider orkestroi idempotent
build-logiikan (`isArtifactBuilt`-tarkistus), mutta consumer omistaa
build-stepit — valitsee työkalut ja artifaktityypin.
### isArtifactBuilt-check
Ennen buildia tarkistetaan, onko tälle commitille jo olemassa versiotagi:
```bash
TAG=$(git tag --points-at HEAD | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
if [ -n "$TAG" ]; then
echo "artifact_already_built=true" >> $GITHUB_ENV
echo "artifact_version=$TAG" >> $GITHUB_ENV
fi
```
Jos tagi löytyy, build- ja push-stepit skipataan. Committia vastaan on
jo olemassa artifakti rekisterissä — uudelleenbuildaus aiheuttaisi
versiokonflikteja ja tuhlaisi CI-aikaa.
### Steppi-kaavio
```mermaid
%%{init: {'theme': 'base', 'flowchart': {'arrowheadScale': 2}}}%%
flowchart TD
CHECK{"isArtifactBuilt?
git tag --points-at HEAD"}
CHECK -- "ei" --> QG["quality-gate
testit + skannaukset"]
QG --> BUILD["build-artifact
consumer: docker build /
mvn package / npm build"]
BUILD --> PUSH["push registry
gitea packages /
docker registry"]
PUSH --> TAG["tag-commit
tagittaa commitin
versiolla (esim. 1.2.3.${RUN})"]
CHECK -- "kyllä" --> K8S["continueToTestFlow
(future: K8s-testit
test plan -mukaan)"]
TAG --> K8S
FAIL("fail") -. "quality-gate
ei läpäisty" .-> END
K8S --> END(["end
commit-status"])
style CHECK fill:#f59e0b,color:#111827
style QG fill:#059669,color:#ffffff
style BUILD fill:#0891b2,color:#ffffff
style PUSH fill:#dc2626,color:#ffffff
style TAG fill:#f59e0b,color:#111827
style K8S fill:#7c3aed,color:#ffffff
style FAIL fill:#dc2626,color:#ffffff
style END fill:#2563eb,color:#ffffff
linkStyle default stroke:#9ca3af,stroke-width:3px
```
### Elinkaari
1. **isArtifactBuilt?** — tarkista onko tagi olemassa
2. **quality-gate** — jos ei tagia, aja `quality-gate.yml` (testit, skannaukset)
3. **build-artifact** — jos quality-gate läpäisty, buildaa artifakti
4. **push registry** — julkaise rekisteriin (Gitea Packages, Docker registry, jne.)
5. **tag-commit** — tagittaa commitin versiolla (esim. `1.2.3.<run_number>`)
6. **continueToTestFlow***(future)* aja K8s-testit test plan -mukaan
7. **commit-status** — aseta lopullinen status
### Concurrency
```yaml
concurrency:
group: master-${{ github.repository }}
cancel-in-progress: false
```
Vain yksi master-build kerrallaan per repo. Ei cancel-in-progress —
käynnissä olevan buildin annetaan valmistua.
---
## `deploy.yml` — GitOps-deployment
**Trigger:** `workflow_dispatch` (aina dispatchataan toisesta workflow'sta)
**Elinkaari:**
```
start → read-yaml → update-value → commit → push → report-cross-repo → end
```
### Inputs (dispatch-parametrit)
| Parametri | Kuvaus |
|-----------|--------|
| `environment` | Ympäristön nimi (korvaa `{.environment}`) |
| `version` | Uusi konttiversio |
| `root_commit` | Mikropalvelun commit josta deploy käynnistyi |
| `root_repo` | Mikropalvelun repo |
| `root_build_url` | URL mikropalvelun buildiin |
### Mitä deploy tekee
1. Lukee `{projectFolder}/{fileName}` YAML-tiedoston (korvaa `{.environment}``environment`)
2. Päivittää `{property}`-avaimen arvoksi `{version}`
3. `git add`, `git commit -m "deploy {version} to {environment}"`
4. `git push origin HEAD:master`
5. Raportoi statuksen:
- Helm-repon committiin: **"from {root_commit}"**, URL → root-build
- Mikropalvelun committiin (`root_commit`): **"deployed to {environment}"**, URL → Helm-commit
6. Palauttaa Helm-commitin hashin (`outputs.commit`)
### Concurrency
```yaml
concurrency:
group: deploy-${{ github.repository }}-${{ inputs.environment }}
cancel-in-progress: false
``` ```
--- ---
## `test.yml` — Test flow -steppi ## Consumer-esimerkki (`example-*`)
**Trigger:** `workflow_dispatch` (dispatchataan deploy-workflow'n jälkeen) ### `example-feature.yml` — Feature-haaran CI
**Elinkaari:** **Trigger:** `push` [branches-ignore: main]
``` ```
start → version-check → run-tests → push-reports → report-cross-repo → end load-config → bats + cucumber → report-summary (always)
``` ```
### Inputs (dispatch-parametrit) ### `example-main.yml` — Main-haaran CI
| Parametri | Kuvaus | **Trigger:** `push` [branches: main]
|-----------|--------|
| `environment` | Testiympäristö |
| `version` | Testattava konttiversio |
| `tags` | Cucumber-tagit |
| `versionApiUrl` | URL version tarkistukseen |
| `versionCheckScript` | Polku version check -skriptiin |
| `root_commit` | Mikropalvelun commit |
| `root_repo` | Mikropalvelun repo |
| `deploy_commit` | Helm-repon commit (deployattu versio) |
| `deploy_repo` | Helm-repo |
### Version check ```
load-config → check-version →
Ennen testejä varmistetaan, että ympäristössä pyörii oikea versio: [artifact exists] → done
[no artifact] → bats + cucumber → report-summary (always) → docker-build-push
```yaml
- name: Check deployed version
if: inputs.versionCheckScript || inputs.versionApiUrl
run: |
if [ -n "${{ inputs.versionCheckScript }}" ]; then
bash "${{ inputs.versionCheckScript }}" "${{ inputs.versionApiUrl }}" "${{ inputs.version }}"
fi
``` ```
Version check -skripti pollaa Fibonacci-backoffilla — ks. [config-model.md](config-model.md). ### `example-bats-tests.yml` — Bats unit-testit
### Cross-repo-raportointi **Trigger:** `workflow_call`
Testien jälkeen raportoidaan kolmeen committiin: Ajaa Bats-testit Docker-kontissa, generoi coveragen (`bashcov`), julkaisee
raportit git-pagesiin, asettaa commit-statuksen linkillä raporttiin.
1. Testi-repon oma commit: testin status ### `example-cucumber-tests.yml` — Cucumber hyväksymätestit
2. Mikropalvelun commit (`root_commit`): "testit OK/epäonnistui"
3. Helm-repon commit (`deploy_commit`): "testattu v{version}"
### Concurrency **Trigger:** `workflow_call`
```yaml Ajaa Cucumber-testit Node-kontissa, julkaisee raportit git-pagesiin, asettaa
concurrency: commit-statuksen linkillä raporttiin.
group: test-${{ inputs.environment }}
cancel-in-progress: false ### `report-summary.yml` — Raporttien koontinäkymä
```
**Trigger:** `workflow_call` — ajetaan `if: always()` testien jälkeen
**Inputs:** `env_json`, `suites` (space-separated lista suite-nimistä)
Generoi Markdown-taulukon `GITHUB_STEP_SUMMARY`:yn kaikista julkaistuista
raporteista. Renderöityy HTML:ksi Gitea 1.27+ Summary-välilehdellä.
Forward-compatibeli — ei haittaa vanhemmilla Gitea-versioilla.
---
## Suunnitteilla
- `deploy.yml` — GitOps-deployment (dispatch-workflow.sh-pohjainen)
- `test.yml` — Klusteritason test flow
+30 -13
View File
@@ -16,24 +16,41 @@ curl_with_host() {
declare -A BRANCH_CACHE declare -A BRANCH_CACHE
branch_exists() { branch_exists() {
local owner="$1" repo="$2" branch="$3" key="${owner}/${repo}/${branch}" local owner="$1" repo="$2" branch="$3" key="${owner}/${repo}/${branch}"
local status local status attempt
[ -z "$GITEA_API_URL" ] && return 0 [ -z "$GITEA_API_URL" ] && return 0
[ -z "$GITEA_TOKEN" ] && return 0 [ -z "$GITEA_TOKEN" ] && return 0
if [ "${BRANCH_CACHE[$key]:-}" = "1" ]; then if [ "${BRANCH_CACHE[$key]:-}" = "1" ]; then
return 0 return 0
fi fi
status=$(curl -sS -o /dev/null -w "%{http_code}" \ # Retry up to 2 times on API errors (hardcoded)
-H "Authorization: token ${GITEA_TOKEN}" \ for attempt in 1 2 3; do
"${GITEA_API_URL}/api/v1/repos/${owner}/${repo}/branches/${branch}" 2>/dev/null || echo "000") status=$(curl -sS -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
if [ "$status" = "200" ]; then "${GITEA_API_URL}/api/v1/repos/${owner}/${repo}/branches/${branch}" 2>/dev/null || echo "000")
BRANCH_CACHE[$key]=1
return 0 if [ "$status" = "200" ]; then
fi BRANCH_CACHE[$key]=1
return 1 return 0
fi
if [ "$status" = "404" ]; then
return 1
fi
# API error - retry if not last attempt
if [ "$attempt" -lt 3 ]; then
sleep 10
continue
fi
done
# All retries failed - keep report (fail-safe)
echo " WARN: Gitea API error for ${owner}/${repo}/${branch} (status ${status}) after 3 attempts - KEEPING report"
BRANCH_CACHE[$key]=1
return 0
} }
default_max_age=$(jq -r '.branches.default.maxAgeDays // 90' "$CONFIG") default_max_age=$(jq -r '.branches.default.maxAgeDays // 90' "$CONFIG")
+112
View File
@@ -0,0 +1,112 @@
# Docker Registry Setup
Pipeline rakentaa Docker-kontin ja pushee sen haluttuun registryyn.
---
## 1. Konfiguroi `gitea-env.conf`
```
# DOCKER_REGISTRY on muotoa: registry.example.com/org
#
# host+org: registry.example.com/org
#
# Pipeline rakentaa kuvan: ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${VERSION}
DOCKER_REGISTRY=gitea.app.keskikuja.site/niko # PAKOLLINEN — tyhjä ei käy
DOCKER_IMAGE_NAME=gitea-ci-library-test-image # PAKOLLINEN — pelkkä kuvan nimi
DOCKER_UI_URL= # valinnainen — tarkista Giteasta kontin oma UI-osoite ja laita se tähän ilman versiota. Workflow liittää perään /VERSION
```
| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `DOCKER_REGISTRY` | **kyllä** | Registry + mahdollinen organisaatio. **Tyhjä pysäyttää workflow'n.** |
| `DOCKER_IMAGE_NAME` | **kyllä** | Pelkkä kuvan nimi. |
| `DOCKER_UI_URL` | ei | Base-URL kontin UI-sivulle (ilman versiota). Osoite riippuu onko kontti linkitetty repoon vai ei — tarkista Giteasta. Workflow liittää perään `/VERSION`. |
**Koko image-ref = `${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${VERSION}`**
Esim. `gitea.app.keskikuja.site/niko/gitea-ci-library-test-image:0.1.0`
---
## 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 `docker 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 |
|---|---|
| `DOCKER_PASSWORD` | Edellisessä vaiheessa luotu PAT |
`DOCKER_USERNAME`-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. Artifactory, Docker Hub), lisää myös:
| Secret | Arvo |
|---|---|
| `DOCKER_USERNAME` | Registryn käyttäjätunnus |
---
## 4. Tarkistuslista ennen ajoa
- [ ] `DOCKER_REGISTRY` asetettu `gitea-env.conf`issa
- [ ] `DOCKER_IMAGE_NAME` asetettu `gitea-env.conf`issa
- [ ] PAT luotu Giteassa scopella `package` Read and Write
- [ ] `DOCKER_PASSWORD`-secret tallennettu repositoryn Secretsiin (se PAT)
- [ ] (tarvittaessa) `DOCKER_USERNAME`-secret — oletus `github.actor`
---
## 5. Esimerkkejä eri polkurakenteista
### 5a. Pelkkä hosti — Artifactory
```
DOCKER_REGISTRY=ngdo-docker.artifactorypro.shared.pub.tds.tieto.com
DOCKER_IMAGE_NAME=microservice-temperature-store
DOCKER_UI_URL=https://artifactorypro.shared.pub.tds.tieto.com/ui/repos/tree/General/ngdo-docker.artifactorypro.shared.pub.tds.tieto.com/microservice-temperature-store
```
- Kontti: `ngdo-docker.../microservice-temperature-store:0.1.0`
- Secret `DOCKER_USERNAME` = service account -tunnus
- Secret `DOCKER_PASSWORD` = API-token
### 5b. Hosti + org — Gitea user-taso
```
DOCKER_REGISTRY=gitea.app.keskikuja.site/niko
DOCKER_IMAGE_NAME=gitea-ci-library-test-image
DOCKER_UI_URL= # tarkista Giteasta kontin UI-osoite
```
- Kontti: `gitea.app.keskikuja.site/niko/gitea-ci-library-test-image:0.1.0`
- Paketti käyttäjän `niko` alla. Linkitys repoon tehdään Gitean UI:sta: paketin sivulta (Package → Settings) → linkitä repositoryyn.
```
DOCKER_REGISTRY=docker.io/library
DOCKER_IMAGE_NAME=oma-kuva
DOCKER_UI_URL=https://hub.docker.com/r/library/oma-kuva
```
- Secret `DOCKER_USERNAME` = Docker Hub -käyttäjä
- Secret `DOCKER_PASSWORD` = Access Token (ei salasana)
+25 -25
View File
@@ -1,27 +1,27 @@
{ {
"name": "gitea-ci-library", "name": "gitea-ci-library",
"version": "0.1.0", "version": "0.2.0",
"description": "", "description": "",
"main": "cucumber.js", "main": "cucumber.js",
"directories": { "directories": {
"doc": "docs", "doc": "docs",
"test": "tests" "test": "tests"
}, },
"scripts": { "scripts": {
"test": "npm run test:bats && npm run test:cucumber", "test": "npm run test:bats && npm run test:cucumber",
"test:bats": "mkdir -p reports && docker run --rm -v \"$(pwd):/repo:ro\" -v \"$(pwd)/reports:/repo/reports\" -w /repo --entrypoint bash bats/bats:latest -c 'apk add -q python3 curl jq lsof ruby && gem install bashcov -q > /dev/null 2>&1; bats tests/'", "test:bats": "mkdir -p reports && docker run --rm -v \"$(pwd):/repo:ro\" -v \"$(pwd)/reports:/repo/reports\" -w /repo --entrypoint bash bats/bats:latest -c 'apk add -q python3 curl jq lsof ruby && gem install bashcov -q > /dev/null 2>&1; bats tests/'",
"test:bats:coverage": "mkdir -p reports && docker run --rm -v \"$(pwd):/repo\" -v \"$(pwd)/reports:/repo/reports\" -w /repo --entrypoint bash bats/bats:latest -c 'apk add -q python3 curl jq lsof ruby && gem install bashcov -q > /dev/null 2>&1; bashcov -- bats tests/'", "test:bats:coverage": "mkdir -p reports && docker run --rm -v \"$(pwd):/repo\" -v \"$(pwd)/reports:/repo/reports\" -w /repo --entrypoint bash bats/bats:latest -c 'apk add -q python3 curl jq lsof ruby && gem install bashcov -q > /dev/null 2>&1; bashcov -- bats tests/'",
"test:cucumber": "docker run --rm -v \"$(pwd):/repo:ro\" -v \"$(pwd)/node_modules:/repo/node_modules\" -w /repo --entrypoint bash node:22 -c 'apt-get update -qq && apt-get install -y -qq jq lsof && npm ci && npx cucumber-js tests/features/ --tags @mock and ~@wip'" "test:cucumber": "docker run --rm -v \"$(pwd):/repo:ro\" -v \"$(pwd)/node_modules:/repo/node_modules\" -w /repo --entrypoint bash node:22 -c 'apt-get update -qq && apt-get install -y -qq jq lsof && npm ci && npx cucumber-js tests/features/ --tags @mock and ~@wip'"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "ssh://git@gitea.app.keskikuja.site:30009/niko/gitea-ci-library.git" "url": "ssh://git@gitea.app.keskikuja.site:30009/niko/gitea-ci-library.git"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"type": "commonjs", "type": "commonjs",
"devDependencies": { "devDependencies": {
"@cucumber/cucumber": "^13.0.0" "@cucumber/cucumber": "^13.0.0"
} }
} }
+10 -5
View File
@@ -1,17 +1,22 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateStatus
STATE="${1:-}" STATE="${1:-}"
DESCRIPTION="${2:-}" DESCRIPTION="${2:-}"
KEY="${3:-commit-${GITHUB_SHA:0:8}}" KEY="${3:-commit-${GITHUB_SHA:0:8}}"
SUITE="${4:-}" SUITE="${4:-}"
CUSTOM_URL="${5:-}"
[ -z "$STATE" ] && echo "ERROR: state argument is required" >&2 && exit 1 [ -z "$STATE" ] && echo "ERROR: state argument is required" >&2 && exit 1
[ -z "$DESCRIPTION" ] && echo "ERROR: description argument is required" >&2 && exit 1 [ -z "$DESCRIPTION" ] && echo "ERROR: description argument is required" >&2 && exit 1
[ -z "${GITEA_API_URL:-}" ] && echo "ERROR: GITEA_API_URL is not set" >&2 && exit 1 [ -z "${GITEA_API_URL:-}" ] && echo "ERROR: GITEA_API_URL is not set" >&2 && exit 1
[ -z "${GITEA_TOKEN:-}" ] && echo "ERROR: GITEA_TOKEN is not set" >&2 && exit 1 [ -z "${GITEA_TOKEN:-}" ] && echo "ERROR: GITEA_TOKEN is not set" >&2 && exit 1
if [ -n "$SUITE" ]; then if [ -n "$CUSTOM_URL" ]; then
URL="$CUSTOM_URL"
elif [ -n "$SUITE" ]; then
SUITE="${SUITE%/}/" SUITE="${SUITE%/}/"
URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/${SUITE}" URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/${SUITE}"
else else
@@ -28,15 +33,15 @@ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "$GITEA_API_URL/api/v1/repos/$REPO/statuses/$COMMIT" \ -X POST "$GITEA_API_URL/api/v1/repos/$REPO/statuses/$COMMIT" \
-H "Authorization: token $GITEA_TOKEN" \ -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"state\":\"$STATE\",\"target_url\":\"$URL\",\"description\":\"$DESCRIPTION\",\"context\":\"$KEY\"}") -d "{\"state\":\"$STATE\",\"target_url\":\"$URL\",\"description\":\"$DESCRIPTION\",\"context\":\"$KEY\"}" || true)
if [ "$HTTP_CODE" = "201" ]; then if [ "$HTTP_CODE" = "201" ]; then
exit 0 exit 0
fi fi
if [ -z "$HTTP_CODE" ]; then if [ -z "$HTTP_CODE" ] || [ "$HTTP_CODE" = "000" ]; then
echo "ERROR: Failed to connect to Gitea API at $GITEA_API_URL" >&2 echo "gitea-ci-library - ERROR: Failed to connect to Gitea API at $GITEA_API_URL" >&2
else else
echo "ERROR: API returned HTTP $HTTP_CODE" >&2 echo "gitea-ci-library - ERROR: gitea-ci-library, API returned HTTP $HTTP_CODE" >&2
fi fi
exit 1 exit 1
@@ -6,10 +6,15 @@ 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 () {
execSync(`bash -c 'source "${MOCK_SCRIPT}" && mock_start'`, { 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'`, {
cwd: PROJECT_ROOT, cwd: PROJECT_ROOT,
stdio: 'ignore', encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
}); });
const trimmed = out.trim();
if (!trimmed.startsWith('2') && !trimmed.startsWith('4')) {
throw new Error(`Mock server failed to start (HTTP ${trimmed})`);
}
}); });
After({ tags: '@mock' }, function () { After({ tags: '@mock' }, function () {