Compare commits
22 Commits
0.2.3
...
a039e6637e
| Author | SHA1 | Date | |
|---|---|---|---|
| a039e6637e | |||
| 0a9a9c88f1 | |||
| bfd0428a78 | |||
| 4f20f5ae2f | |||
| bd93ef2f8f | |||
| f06cb112d8 | |||
| d57f56f196 | |||
| 277c0f882d | |||
| cc7f4f0976 | |||
| 6621b3f336 | |||
| c0012ba6fa | |||
| 908aee9f8b | |||
| ad4c2cd570 | |||
| 1e10633e60 | |||
| 737b2fc3f2 | |||
| 65d385f9b9 | |||
| 2e5a4dca3a | |||
| 29fde14445 | |||
| f17ea7936e | |||
| 153205be40 | |||
| f3a54d6ed3 | |||
| 8622b6fc4e |
@@ -1,37 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
WORKSPACE_VOLUME="${1:-}"
|
||||
REPORT_DIR="${2:-}"
|
||||
REPORT_DIR="${1:-}"
|
||||
COVERAGE_DIR="${REPORT_DIR}/coverage"
|
||||
|
||||
[ -n "$WORKSPACE_VOLUME" ] || { echo "ERROR: workspace volume name required" >&2; exit 1; }
|
||||
[ -n "$REPORT_DIR" ] || { echo "ERROR: report directory required" >&2; exit 1; }
|
||||
|
||||
HAS_COVERAGE=false
|
||||
COVERAGE_SRC=""
|
||||
if docker run --rm -v "$WORKSPACE_VOLUME":/data alpine sh -c '[ -d /data/coverage ] && ls -A /data/coverage | grep -q .' 2>/dev/null; then
|
||||
COVERAGE_SRC="/data/coverage"
|
||||
if [ -d coverage ]; then
|
||||
mkdir -p "$COVERAGE_DIR"
|
||||
cp -a coverage/. "$COVERAGE_DIR/"
|
||||
fi
|
||||
|
||||
if [ -n "$COVERAGE_SRC" ]; then
|
||||
mkdir -p "$REPORT_DIR/coverage"
|
||||
docker run --rm -v "$WORKSPACE_VOLUME":/data alpine tar c -C "$COVERAGE_SRC" . | tar x -C "$REPORT_DIR/coverage"
|
||||
HAS_COVERAGE=true
|
||||
if [ -d "$COVERAGE_DIR" ] && [ ! -f "$COVERAGE_DIR/index.html" ]; then
|
||||
SHA8="${GITHUB_SHA:0:8}"
|
||||
{
|
||||
echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
|
||||
echo "<title>Coverage report ${SHA8}</title>"
|
||||
echo '<style>body{font-family:sans-serif;margin:2em}h1{color:#1e293b}ul{list-style:none;padding:0}li{margin:.5em 0}a{color:#2563eb}</style>'
|
||||
echo "</head><body><h1>Coverage report <code>${SHA8}</code></h1><ul>"
|
||||
while IFS= read -r -d '' f; do
|
||||
base=$(basename "$f")
|
||||
name="${base%.*}"
|
||||
name="${name//-/ }"
|
||||
echo "<li><a href=\"${base}\">${name^}</a></li>"
|
||||
done < <(find "$COVERAGE_DIR" -maxdepth 1 -type f \( -name '*.html' -o -name '*.txt' \) ! -name index.html -print0 2>/dev/null || true)
|
||||
echo '</ul></body></html>'
|
||||
} > "$COVERAGE_DIR/index.html"
|
||||
fi
|
||||
|
||||
cat > "$REPORT_DIR/index.html" << EOF
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">
|
||||
<title>Bats report ${GITHUB_SHA:0:8}</title>
|
||||
<style>body{font-family:sans-serif;margin:2em;max-width:960px}
|
||||
h1{color:#1e293b}a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}
|
||||
</style></head><body>
|
||||
<h1>Bats report <code>${GITHUB_SHA:0:8}</code></h1>
|
||||
<ul>
|
||||
<li><a href="test-report.html">Test results</a></li>
|
||||
EOF
|
||||
|
||||
if [ "$HAS_COVERAGE" = true ]; then
|
||||
echo '<li><a href="coverage/index.html">Coverage report</a></li>' >> "$REPORT_DIR/index.html"
|
||||
fi
|
||||
|
||||
echo '</ul></body></html>' >> "$REPORT_DIR/index.html"
|
||||
|
||||
@@ -16,6 +16,8 @@ on:
|
||||
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GIT_TAG_PREFIX: ${{ fromJson(inputs.env_json).GIT_TAG_PREFIX || '' }}
|
||||
VERSION_FILE: ${{ fromJson(inputs.env_json).VERSION_FILE || '' }}
|
||||
|
||||
jobs:
|
||||
check:
|
||||
@@ -25,45 +27,17 @@ jobs:
|
||||
version: ${{ steps.set-outputs.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: niko/gitea-ci-library
|
||||
path: .ci
|
||||
|
||||
- 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
|
||||
env:
|
||||
SERVER_URL: ${{ gitea.server_url }}
|
||||
REPO: ${{ github.repository }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: bash .ci/scripts/check-version.sh
|
||||
|
||||
- name: Set job outputs
|
||||
id: set-outputs
|
||||
|
||||
@@ -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: false
|
||||
type: string
|
||||
default: 'latest'
|
||||
secrets:
|
||||
DOCKER_USERNAME:
|
||||
required: false
|
||||
DOCKER_PASSWORD:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build and push container
|
||||
env:
|
||||
DOCKER_REGISTRY: ${{ fromJson(inputs.env_json).DOCKER_REGISTRY || '' }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
if [ -z "${DOCKER_REGISTRY}" ]; then echo "ERROR: DOCKER_REGISTRY not set in conf"; exit 1; fi
|
||||
REGISTRY="${DOCKER_REGISTRY}"
|
||||
REGISTRY_HOST="${REGISTRY%%/*}"
|
||||
DOCKERFILE="${{ inputs.dockerfile_path }}"
|
||||
IMAGE_NAME="${{ inputs.image_name }}"
|
||||
TAG="${{ inputs.tag }}"
|
||||
|
||||
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}" .
|
||||
|
||||
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"
|
||||
@@ -23,6 +23,7 @@ env:
|
||||
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' }}
|
||||
GIT_TAG_PREFIX: ${{ fromJson(inputs.env_json).GIT_TAG_PREFIX || '' }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
|
||||
concurrency:
|
||||
@@ -44,24 +45,33 @@ jobs:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
if [ -z "${DOCKER_REGISTRY}" ]; then echo "ERROR: DOCKER_REGISTRY not set in env.conf"; exit 1; fi
|
||||
if [ -z "${DOCKER_IMAGE_NAME}" ]; then echo "ERROR: DOCKER_IMAGE_NAME not set in env.conf"; exit 1; fi
|
||||
REGISTRY="${DOCKER_REGISTRY}"
|
||||
IMAGE="${DOCKER_IMAGE_NAME}"
|
||||
REGISTRY_HOST="${REGISTRY%%/*}"
|
||||
|
||||
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%%/*}"
|
||||
-t "${IMAGE}:${VERSION}" \
|
||||
-t "${IMAGE}:latest" .
|
||||
|
||||
FULL_IMAGE="${REGISTRY}/${IMAGE}:${VERSION}"
|
||||
echo "Pushing ${FULL_IMAGE} ..."
|
||||
|
||||
docker tag "${DOCKER_IMAGE_NAME}:${VERSION}" "$FULL_IMAGE"
|
||||
docker tag "${IMAGE}:${VERSION}" "$FULL_IMAGE"
|
||||
echo "$DOCKER_PASSWORD" | docker login "$REGISTRY_HOST" -u "$DOCKER_USERNAME" --password-stdin
|
||||
docker push "$FULL_IMAGE"
|
||||
|
||||
FULL_LATEST="${REGISTRY}/${IMAGE}:latest"
|
||||
echo "Pushing ${FULL_LATEST} ..."
|
||||
docker tag "${IMAGE}:latest" "$FULL_LATEST"
|
||||
docker push "$FULL_LATEST"
|
||||
|
||||
docker logout "$REGISTRY_HOST"
|
||||
|
||||
- name: Report status SUCCESS
|
||||
@@ -69,7 +79,7 @@ jobs:
|
||||
run: |
|
||||
CONTAINER_URL=""
|
||||
if [ -n "${DOCKER_UI_URL:-}" ] && [ -n "${VERSION:-}" ]; then
|
||||
CONTAINER_URL="${DOCKER_UI_URL}/${VERSION}"
|
||||
CONTAINER_URL="${DOCKER_UI_URL}/${DOCKER_IMAGE_NAME}/${VERSION}"
|
||||
fi
|
||||
bash .ci/scripts/report-status.sh success "Docker build & push ${VERSION} OK" ci-docker-build-push "" "$CONTAINER_URL"
|
||||
|
||||
@@ -94,7 +104,7 @@ jobs:
|
||||
"$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\"}")
|
||||
-d "{\"tag_name\": \"${GIT_TAG_PREFIX}${VERSION}\", \"message\": \"Build #$RUN_NUMBER\", \"target\": \"$SHA\"}")
|
||||
|
||||
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "409" ]; then
|
||||
exit 0
|
||||
|
||||
@@ -6,8 +6,9 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
bats-image:
|
||||
required: true
|
||||
required: false
|
||||
type: string
|
||||
default: gitea.app.keskikuja.site/niko/ci-bats:git
|
||||
secrets:
|
||||
GITEA_TOKEN:
|
||||
required: true
|
||||
@@ -23,6 +24,8 @@ env:
|
||||
jobs:
|
||||
bats:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ inputs.bats-image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
@@ -31,34 +34,18 @@ jobs:
|
||||
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}
|
||||
mkdir -p reports/bats
|
||||
bashcov -- bats tests/ > reports/bats/results.txt 2>&1
|
||||
|
||||
- name: Publish bats reports
|
||||
- name: Post-process coverage
|
||||
if: always()
|
||||
run: bash .ci/scripts/publish-git-pages.sh bats
|
||||
run: bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
|
||||
|
||||
- name: Report status
|
||||
- name: Post-process test report
|
||||
if: always()
|
||||
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
|
||||
run: bash .ci/.gitea/scripts/bats-report.sh reports/bats
|
||||
|
||||
- name: Report
|
||||
if: always()
|
||||
run: bash .ci/scripts/ci-report.sh "Bats test report" unit-tests bats ${{ job.status }}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI Container Build Bats
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
config_path:
|
||||
required: true
|
||||
type: string
|
||||
default: '.gitea/workflows/example-gitea-env.conf'
|
||||
description: 'Polku .gitea-env.conf-tiedostoon'
|
||||
dockerfile_path:
|
||||
required: true
|
||||
type: string
|
||||
default: 'Dockerfile.ci-bats'
|
||||
description: 'Polku Dockerfileen'
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
default: 'ci-bats'
|
||||
description: 'Kontin nimi ilman registry-polkua'
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
default: 'latest'
|
||||
description: 'Image-tägi'
|
||||
|
||||
jobs:
|
||||
load-config:
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
config_path: ${{ inputs.config_path }}
|
||||
|
||||
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: ${{ inputs.dockerfile_path }}
|
||||
image_name: ${{ inputs.image_name }}
|
||||
tag: ${{ inputs.tag }}
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI Container Build Cucumber
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
config_path:
|
||||
required: true
|
||||
type: string
|
||||
default: '.gitea/workflows/example-gitea-env.conf'
|
||||
description: 'Polku .gitea-env.conf-tiedostoon'
|
||||
dockerfile_path:
|
||||
required: true
|
||||
type: string
|
||||
default: 'Dockerfile.ci-cucumber'
|
||||
description: 'Polku Dockerfileen'
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
default: 'ci-cucumber'
|
||||
description: 'Kontin nimi ilman registry-polkua'
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
default: 'latest'
|
||||
description: 'Image-tägi'
|
||||
|
||||
jobs:
|
||||
load-config:
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
config_path: ${{ inputs.config_path }}
|
||||
|
||||
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: ${{ inputs.dockerfile_path }}
|
||||
image_name: ${{ inputs.image_name }}
|
||||
tag: ${{ inputs.tag }}
|
||||
@@ -6,8 +6,9 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
cucumber-node-image:
|
||||
required: true
|
||||
required: false
|
||||
type: string
|
||||
default: gitea.app.keskikuja.site/niko/ci-cucumber:latest
|
||||
secrets:
|
||||
GITEA_TOKEN:
|
||||
required: true
|
||||
@@ -33,38 +34,14 @@ jobs:
|
||||
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
|
||||
mkdir -p reports/cucumber
|
||||
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}
|
||||
--format json:reports/cucumber/results.json \
|
||||
--format html:reports/cucumber/test-report.html 2>&1
|
||||
|
||||
- name: Publish cucumber reports
|
||||
if: always()
|
||||
run: bash .ci/scripts/publish-git-pages.sh cucumber
|
||||
|
||||
- name: Report status
|
||||
- name: Report
|
||||
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
|
||||
run: bash .ci/scripts/ci-report.sh "Cucumber test report" acc-tests cucumber ${{ job.status }}
|
||||
|
||||
@@ -20,7 +20,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
bats-image: bats/bats:latest
|
||||
|
||||
cucumber:
|
||||
name: Cucumber tests
|
||||
@@ -29,13 +28,12 @@ jobs:
|
||||
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/example-report-summary.yml@main
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
suites: bats cucumber
|
||||
|
||||
@@ -2,5 +2,5 @@ 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
|
||||
DOCKER_UI_URL=https://gitea.app.keskikuja.site/niko/-/packages/container
|
||||
#DOCKERFILE=Dockerfile.platform
|
||||
|
||||
@@ -29,7 +29,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
bats-image: bats/bats:latest
|
||||
|
||||
cucumber:
|
||||
name: Cucumber tests
|
||||
@@ -39,7 +38,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
cucumber-node-image: node:22
|
||||
|
||||
build-push:
|
||||
name: Build & Push Docker
|
||||
@@ -55,7 +53,7 @@ jobs:
|
||||
name: Report Summary
|
||||
needs: [load-config, build-push]
|
||||
if: always()
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/example-report-summary.yml@main
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
suites: bats cucumber
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
name: CI Git-Pages Main
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- git-pages/**
|
||||
- .gitea/workflows/helm-build-push.yml
|
||||
- .gitea/workflows/git-pages.*
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
load-config:
|
||||
name: Load git-pages.gitea-env.conf to pipeline env
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
config_path: .gitea/workflows/git-pages.gitea-env.conf
|
||||
|
||||
check-version:
|
||||
name: Check existing artifact
|
||||
needs: [load-config]
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/check-version.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
|
||||
helm-push:
|
||||
name: Build & Push Helm chart
|
||||
needs: [load-config, check-version]
|
||||
if: needs.check-version.outputs.artifact_exists != 'true'
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/helm-build-push.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
version: ${{ needs.check-version.outputs.version }}
|
||||
chart_path: git-pages
|
||||
|
||||
report-summary:
|
||||
name: Report Summary
|
||||
needs: [load-config, helm-push]
|
||||
if: always()
|
||||
uses: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
suites: ""
|
||||
@@ -0,0 +1,5 @@
|
||||
GITEA_API_URL=https://gitea.app.keskikuja.site
|
||||
HELM_REGISTRY=gitea.app.keskikuja.site/niko
|
||||
HELM_UI_URL=https://gitea.app.keskikuja.site/niko/-/packages/container
|
||||
GIT_TAG_PREFIX=git-pages/
|
||||
VERSION_FILE=git-pages/Chart.yaml
|
||||
@@ -0,0 +1,104 @@
|
||||
name: Helm Build & Push
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
env_json:
|
||||
required: true
|
||||
type: string
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
chart_path:
|
||||
required: false
|
||||
type: string
|
||||
default: '.'
|
||||
secrets:
|
||||
GITEA_TOKEN:
|
||||
required: true
|
||||
HELM_USER:
|
||||
required: false
|
||||
HELM_PASSWORD:
|
||||
required: true
|
||||
|
||||
env:
|
||||
GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
HELM_REGISTRY: ${{ fromJson(inputs.env_json).HELM_REGISTRY || '' }}
|
||||
HELM_UI_URL: ${{ fromJson(inputs.env_json).HELM_UI_URL || '' }}
|
||||
GIT_TAG_PREFIX: ${{ fromJson(inputs.env_json).GIT_TAG_PREFIX || '' }}
|
||||
CHART_PATH: ${{ inputs.chart_path }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-push:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine/helm:3.19.0
|
||||
steps:
|
||||
- name: Install Node.js for actions/checkout
|
||||
# COMPROMISE: Requires internet access.
|
||||
# Does NOT work in air-gapped environments.
|
||||
# Replace with a custom image (e.g., extending alpine/helm + nodejs) if needed.
|
||||
run: apk add --no-cache nodejs
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: niko/gitea-ci-library
|
||||
path: .ci
|
||||
|
||||
- name: Package Helm chart
|
||||
run: |
|
||||
helm dependency update "${CHART_PATH}"
|
||||
helm package "${CHART_PATH}" \
|
||||
--version "${VERSION}" \
|
||||
--app-version "${VERSION}" \
|
||||
--destination /tmp/helm-packages
|
||||
|
||||
- name: Push to OCI registry
|
||||
env:
|
||||
HELM_USER: ${{ secrets.HELM_USER || github.actor }}
|
||||
HELM_PASSWORD: ${{ secrets.HELM_PASSWORD }}
|
||||
run: |
|
||||
REGISTRY="${HELM_REGISTRY:?HELM_REGISTRY not set in env.conf}"
|
||||
echo "$HELM_PASSWORD" | helm registry login "${REGISTRY}" \
|
||||
-u "$HELM_USER" \
|
||||
--password-stdin
|
||||
helm push /tmp/helm-packages/*.tgz "oci://${REGISTRY}"
|
||||
helm registry logout "${REGISTRY}"
|
||||
|
||||
- name: Report status with UI link
|
||||
if: success() && env.HELM_UI_URL != ''
|
||||
run: |
|
||||
CHART_NAME=$(grep '^name:' "${CHART_PATH}/Chart.yaml" | awk '{print $2}')
|
||||
UI_URL="${HELM_UI_URL}/${CHART_NAME}/${VERSION}"
|
||||
bash .ci/scripts/report-status.sh success "Helm chart ${VERSION}" ci-helm-build-push "" "$UI_URL"
|
||||
|
||||
tag-commit:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-push]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Create git tag
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
SERVER_URL: ${{ gitea.server_url }}
|
||||
RUN_NUMBER: ${{ github.run_number }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
|
||||
"$SERVER_URL/api/v1/repos/${{ github.repository }}/tags" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\": \"${GIT_TAG_PREFIX}${VERSION}\", \"message\": \"Build #$RUN_NUMBER\", \"target\": \"$SHA\"}")
|
||||
|
||||
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "409" ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
@@ -7,3 +7,4 @@ tmp/
|
||||
coverage/
|
||||
.DS_Store
|
||||
reports/
|
||||
.vscode/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM bats/bats:1.11.0
|
||||
RUN apk add --no-cache lsof python3 jq curl ruby nodejs git && \
|
||||
gem install bashcov -v 3.3.0
|
||||
@@ -0,0 +1,7 @@
|
||||
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
|
||||
ENV NODE_PATH=/usr/local/lib/node_modules
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
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
|
||||
**Consumer-käyttöönotto:** [skills/consumer-pipelines/SKILL.md](skills/consumer-pipelines/SKILL.md) — pipeline-standardit ja säännöt consumer-projekteille
|
||||
|
||||
**Single repo & monorepo:** Kirjasto toimii molemmissa. Monorepo-tuki
|
||||
polkusuodatuksella, komponenttikohtaisilla versioilla ja git-tägien
|
||||
etuliitteillä — jokainen komponentti julkaistaan itsenäisesti omassa
|
||||
tahdissaan. Katso [skills/consumer-pipelines/SKILL.md](skills/consumer-pipelines/SKILL.md).
|
||||
|
||||
## Provider-binding — miten consumer löytää providerin
|
||||
|
||||
@@ -118,8 +123,8 @@ Hae token Giteasta:
|
||||
|
||||
```bash
|
||||
GITEA_URL="https://<gitea-server-url>"
|
||||
GITEA_ACTIONS_TOKEN="<registration-token>"
|
||||
GITEA_ACTIONS_NAMESPACE="gitea-actions"
|
||||
GITEA_ACTIONS_TOKEN="<registration-token>"
|
||||
```
|
||||
|
||||
### 3. Tee secret vain init install yhteydessä
|
||||
@@ -153,6 +158,7 @@ helm upgrade --install act-runner gitea/actions \
|
||||
--set giteaRootURL="$GITEA_URL" \
|
||||
--set existingSecret=act-runner-token \
|
||||
--set existingSecretKey=token \
|
||||
--set statefulset.replicas=3 \
|
||||
--set statefulset.runner.tag=1.0.8 \
|
||||
--set statefulset.dind.tag=29.5.2-dind \
|
||||
--set-string 'statefulset.runner.config=log:
|
||||
|
||||
+29
-6
@@ -1,6 +1,6 @@
|
||||
# AI Context: Gitea Actions CI -kirjasto
|
||||
|
||||
**Updated**: 2026-06-15 (siivottu, provider/consumer-erottelu valmis)
|
||||
**Updated**: 2026-06-19 (provider/consumer dual role, Project Skills -aktivointi)
|
||||
|
||||
## Project Overview
|
||||
Gitea Actions reusable workflow -kirjasto mikropalveluiden build-, testaus-,
|
||||
@@ -11,8 +11,8 @@ käyttävät kirjastoa `uses:`-direktiivillä.
|
||||
## Monorepo: kaksi erillistä kokonaisuutta
|
||||
|
||||
### 1. Juuri (`gitea-ci-library`)
|
||||
Provider-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio.
|
||||
Consumer kutsuu provider-workflowta `uses:`-direktiivillä.
|
||||
Provider- ja consumer-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio,
|
||||
ja consumer-esimerkit (dogfood). Consumer kutsuu provider-workflowta `uses:`-direktiivillä.
|
||||
|
||||
### 2. `git-pages/` — oma kokonaisuus
|
||||
Helm-chartti Codeberg git-pagesille. Täysin itsenäinen — oma dokumentaatio,
|
||||
@@ -26,21 +26,26 @@ kuuluu `git-pages/docs/`-alle, ei juuren `docs/`-kansioon.
|
||||
| `.gitea/workflows/config-provider.yml` | Provider: lataa + validoi config-tiedoston, tuottaa `env_json` |
|
||||
| `.gitea/workflows/check-version.yml` | Provider: tarkistaa onko commitille jo artifact, laskee version |
|
||||
| `.gitea/workflows/docker-build-push.yml` | Provider: buildaa + puskea Docker-imagen, tagittaa commitin |
|
||||
| `.gitea/workflows/ci-container-build-push.yml` | Provider: buildaa + puskea CI-työkalukontin |
|
||||
| `.gitea/workflows/example-*` | **Consumer-esimerkki**: tämän repon oma CI (dogfood) |
|
||||
| `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 (0004–0008) |
|
||||
| `skills/consumer-pipelines/` | Consumer-pipeline-standardit (ks. Project Skills). Koskee vain consumer-puolta |
|
||||
| `skills/ci-container-build/` | CI-kontin build-workflow'n template (ks. Project Skills) |
|
||||
| `docs/adr/` | Architecture Decision Records |
|
||||
| `git-pages/` | Raporttien hostaus (Helm-chartti) |
|
||||
| `tests/` | Bats-testit skripteille |
|
||||
|
||||
### Provider workflowt (3 kpl)
|
||||
### Provider workflowt (5 kpl)
|
||||
|
||||
| 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. |
|
||||
| `ci-container-build-push.yml` | `env_json`, `dockerfile_path`, `image_name`, `tag` | — | Buildaa CI-työkalukontin, puskea rekisteriin. Ei versiointia eikä git-tägäystä. |
|
||||
| `report-summary.yml` | `env_json`, `suites` | — | Generoi `GITHUB_STEP_SUMMARY`-taulukon raporttilinkeillä (Gitea 1.27+) |
|
||||
|
||||
### Example-tiedostot (consumer-referenssi)
|
||||
|
||||
@@ -50,12 +55,30 @@ kuuluu `git-pages/docs/`-alle, ei juuren `docs/`-kansioon.
|
||||
| `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-report-summary.yml` | workflow_call | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä (Gitea 1.27+) |
|
||||
| `example-gitea-env.conf` | — | KEY=VALUE config tälle repolle |
|
||||
|
||||
## Provider & Consumer Dual Role
|
||||
|
||||
Tämä repo on **yhtä aikaa sekä provider että consumer**. Eri puolilla on eri säännöt:
|
||||
|
||||
- **Provider-puoli**: `.gitea/workflows/*.yml` (pl. `example-*`), `scripts/` — reusable workflowt joita muut projektit kutsuvat. Saa käyttää `docker run` -komentoja (esim. `docker-build-push.yml`). Consumer-pipeline-standardit (`skills/consumer-pipelines/`) eivät koske provideria.
|
||||
- **Consumer-puoli**: `.gitea/workflows/example-*`, `.gitea/scripts/` — tämän repon oma CI (dogfood), toimii consumer-esimerkkinä. Käyttää `@main`-refiä provider-viittauksissa (sama repo). Noudattaa `skills/consumer-pipelines/`-sääntöjä.
|
||||
- **Ulkoiset consumerit** käyttävät `@v1`-tagia provider-viittauksissa.
|
||||
|
||||
## Project Skills (skills/)
|
||||
|
||||
Tämä projekti sisältää omia `.ai/skills/`-järjestelmästä riippumattomia skillejä `skills/`-kansiossa. Jokainen alihakemisto sisältää `SKILL.md`:n jossa on `activation-gate`-kenttä.
|
||||
|
||||
**Sääntö:** Uuden tehtävän alussa skannaa `skills/*/SKILL.md` ja arvioi jokaisen `activation-gate` annettua tehtävää vasten. Jos gate matchaa, lataa skill aktiiviseksi ohjeeksi ennen toimenpiteitä.
|
||||
|
||||
| Skill | Gate | Kuvaus |
|
||||
|---|---|---|
|
||||
| `skills/consumer-pipelines/` | Consumer-pipeline-muutokset | Consumer-pipeline-standardit: reitittimen puhtaus, exit-koodi, konttipolitiikka, raportointi, nimeäminen. Koskee vain consumer-puolta. |
|
||||
| `skills/ci-container-build/` | CI-kontin build | CI-kontin build-workflown template ja Dockerfile-ohjeet |
|
||||
|
||||
## Key Technical Decisions
|
||||
|
||||
- **Provider & Consumer -malli**: `example-*`-tiedostot ovat consumer-esimerkkejä, provider-workflowt reusableja. ADR 0005.
|
||||
- **Provider & Consumer -malli**: Tämä repo on sekä provider että consumer. Provider-workflowt reusableja muille, `example-*`-tiedostot tämän repon oma consumer-CI (dogfood). ADR 0005.
|
||||
- **Vain Gitea, vain reusable workflowt**: ei custom actioneita, ei multi-platform
|
||||
- **Commit-status API vain raporttilinkeille**: Tool-jobit luottavat natiiviin. Test-jobit käyttävät API:a koska se on ainoa tapa upottaa raporttilinkki. ADR 0004, 0007.
|
||||
- **Exit-koodi on ainoa onnistumisen mittari**: Ei pipeä, ei tiedostoheuristiikkaa. ADR 0008.
|
||||
|
||||
@@ -33,7 +33,7 @@ Tarkemmin: ADR 0005.
|
||||
| `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 |
|
||||
| `example-report-summary.yml` | Consumer | `GITHUB_STEP_SUMMARY`-taulukko (Gitea 1.27+) |
|
||||
| `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 |
|
||||
|
||||
@@ -1,446 +0,0 @@
|
||||
# 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ä.
|
||||
+1
-1
@@ -38,4 +38,4 @@ suoraan Gitea UI:ssa.
|
||||
| **Multi-Git-platform** | Vain Gitea — yksi alusta kunnolla (periaate 10) |
|
||||
| **Custom actionit** | Reusable workflow on kevyempi ja natiivimpi (periaate 2) |
|
||||
| **Ulkoinen orkestraattori** | Gitean `needs` + `if` hoitaa ohjauksen |
|
||||
| **Artifactory/Nexus** | Gitea Packages riittää MVP:ssä |
|
||||
| **Artifactory/Nexus** | Build & push toimii Docker-standardilla. UI-tason linkitys (`report-summary`) vaatii Nexus/Artifactory-spesifin URL-rakenteen — ei vielä toteutettu, toteutetaan tarvittaessa |
|
||||
|
||||
+65
-3
@@ -63,15 +63,77 @@ checkout → laske versio package.json + git-tageista → output
|
||||
|
||||
**Trigger:** `workflow_call`
|
||||
|
||||
**Inputs:** `env_json`, `version`
|
||||
**Inputs:**
|
||||
|
||||
| Parametri | Pakollinen | Kuvaus |
|
||||
|-----------|------------|--------|
|
||||
| `env_json` | Kyllä | Konffi `gitea-env.conf`:stä |
|
||||
| `version` | Kyllä | Version string (check-version output) |
|
||||
|
||||
**`env_json`-avaimet:**
|
||||
|
||||
| Avain | Pakollinen | Kuvaus |
|
||||
|-------|------------|--------|
|
||||
| `DOCKER_REGISTRY` | Kyllä | Registry (esim. `gitea.app.keskikuja.site/niko`) |
|
||||
| `DOCKER_IMAGE_NAME` | Kyllä | Kuvan nimi ilman registry-polkua |
|
||||
| `DOCKER_UI_URL` | Ei | Registry UI -linkki raportointia varten |
|
||||
| `DOCKERFILE` | Ei | Dockerfile-polku, oletus `Dockerfile` |
|
||||
| `GITEA_API_URL` | Kyllä | Gitean API-URL |
|
||||
| `GIT_TAG_PREFIX` | Ei | Tag-prefix (esim. `docker/`) |
|
||||
|
||||
**Secrets:** `GITEA_TOKEN`, `DOCKER_USERNAME`, `DOCKER_PASSWORD`
|
||||
|
||||
**Steppi-kaavio:**
|
||||
```
|
||||
build-push (build + push samassa jobissa, ei levyn kautta) → tag-commit
|
||||
build-push (build + push, labelit: commit+date) → tag-commit (git-tagin luonti)
|
||||
```
|
||||
|
||||
**Huomio:** Ei käytä `container:`-direktiiviä — ajaa suoraan runnerilla,
|
||||
joten `actions/checkout` toimii ilman node-asennuksia.
|
||||
|
||||
---
|
||||
|
||||
### `helm-build-push.yml` — Helm chart build & push
|
||||
|
||||
**Trigger:** `workflow_call`
|
||||
|
||||
**Inputs:**
|
||||
|
||||
| Parametri | Pakollinen | Kuvaus |
|
||||
|-----------|------------|--------|
|
||||
| `env_json` | Kyllä | Konffi `gitea-env.conf`:stä |
|
||||
| `version` | Kyllä | Version string (check-version output) |
|
||||
| `chart_path` | Ei | Polku Chart.yaml-hakemistoon, oletus `.` |
|
||||
|
||||
**`env_json`-avaimet:**
|
||||
|
||||
| Avain | Pakollinen | Kuvaus |
|
||||
|-------|------------|--------|
|
||||
| `HELM_REGISTRY` | Kyllä | OCI-registry (esim. `gitea.app.keskikuja.site/niko`) |
|
||||
| `HELM_UI_URL` | Ei | Registry UI -linkki raportointia varten |
|
||||
| `GITEA_API_URL` | Kyllä | Gitean API-URL |
|
||||
| `GIT_TAG_PREFIX` | Ei | Tag-prefix (esim. `helm/`) |
|
||||
|
||||
**Secrets:** `GITEA_TOKEN`, `HELM_USER`, `HELM_PASSWORD`
|
||||
|
||||
**Steppi-kaavio:**
|
||||
```
|
||||
build-push (helm package → helm push OCI) → tag-commit (git-tagin luonti)
|
||||
```
|
||||
|
||||
**Steppien kuvaus `build-push`-jobissa:**
|
||||
1. **Node.js-asennus** — `apk add --no-cache nodejs` (vaaditaan `actions/checkout`-actionia varten)
|
||||
2. **Checkout** — sovellusrepo ja gitea-ci-library `.ci/`-polkuun
|
||||
3. **Package** — `helm package` versiolla `$VERSION`
|
||||
4. **Push OCI** — `helm push` registryyn autentikoinnilla
|
||||
5. **Report status** — commit-status + UI-linkki
|
||||
|
||||
**Kompromissi:** Kontti `alpine/helm` ei sisällä node.js:ää, mutta
|
||||
`actions/checkout@v4` on JavaScript-action ja vaatii sen. Siksi nodejs
|
||||
asennetaan lennossa ennen checkouttia. Tämä vaatii internet-yhteyden
|
||||
eikä toimi air gap -ympäristössä. Korvaa tarvittaessa custom-kontilla
|
||||
(jossa helm + nodejs, ks. `skills/ci-container-build/SKILL.md`).
|
||||
|
||||
---
|
||||
|
||||
## Consumer-esimerkki (`example-*`)
|
||||
@@ -108,7 +170,7 @@ raportit git-pagesiin, asettaa commit-statuksen linkillä raporttiin.
|
||||
Ajaa Cucumber-testit Node-kontissa, julkaisee raportit git-pagesiin, asettaa
|
||||
commit-statuksen linkillä raporttiin.
|
||||
|
||||
### `example-report-summary.yml` — Raporttien koontinäkymä
|
||||
### `report-summary.yml` — Raporttien koontinäkymä
|
||||
|
||||
**Trigger:** `workflow_call` — ajetaan `if: always()` testien jälkeen
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# Helm Registry Setup (OCI)
|
||||
|
||||
Pipeline paketoi Helm chartin OCI-artefaktiksi ja pushee sen OCI-rekisteriin.
|
||||
|
||||
---
|
||||
|
||||
## 1. Konfiguroi `gitea-env.conf`
|
||||
|
||||
```
|
||||
# HELM_REGISTRY on muotoa: registry.example.com/org
|
||||
#
|
||||
# host+org: registry.example.com/org
|
||||
#
|
||||
# Pipeline rakentaa OCI-refin: oci://${HELM_REGISTRY}/<chart-name>:${VERSION}
|
||||
# (chart-name tulee Chart.yaml:n name-kentästä)
|
||||
|
||||
HELM_REGISTRY=gitea.app.keskikuja.site/niko # PAKOLLINEN — tyhjä ei käy
|
||||
HELM_UI_URL= # valinnainen — tarkista Giteasta kontin oma UI-osoite, workflow liittää perään /chart-name/VERSION
|
||||
GIT_TAG_PREFIX=git-pages/ # valinnainen — monorepo-tägäys
|
||||
VERSION_FILE=git-pages/Chart.yaml # valinnainen — jos Chart.yaml ei rootissa
|
||||
```
|
||||
|
||||
| Kenttä | Pakollinen | Kuvaus |
|
||||
|---|---|---|
|
||||
| `HELM_REGISTRY` | **kyllä** | Registry host + owner (esim. `gitea.app.site/niko`). **Tyhjä pysäyttää workflow'n.** |
|
||||
| `HELM_UI_URL` | ei | Base-URL OCI-paketin UI-sivulle (ilman chart-nimeä ja versiota). Osoite riippuu onko paketti linkitetty repoon vai ei — tarkista Giteasta. Workflow liittää perään `/chart-name/VERSION`. Jos tyhjä, commit-statusia ei erikseen aseteta. |
|
||||
| `GIT_TAG_PREFIX` | ei | Etuliite git-tägille. Pakollinen monorepossa, jotta tagit eivät sekoitu muihin komponentteihin. |
|
||||
| `VERSION_FILE` | ei | Polku version lähteeseen (Chart.yaml, package.json, VERSION). Oletus: juuren `Chart.yaml`. |
|
||||
|
||||
**OCI-ref = `oci://${HELM_REGISTRY}/<chart-name>:${VERSION}`**
|
||||
Esim. `oci://gitea.app.keskikuja.site/niko/git-pages:1.2.3`
|
||||
|
||||
Chartin nimi (`<chart-name>`) määräytyy `Chart.yaml`-tiedoston `name`-kentästä.
|
||||
|
||||
---
|
||||
|
||||
## 2. Luo PAT (Personal Access Token) Giteassa
|
||||
|
||||
**Gitea → oma profiili (oikea yläkulma) → Settings → Applications → Manage Access Tokens → Generate New Token**
|
||||
|
||||
Valitse scope:
|
||||
|
||||
| Scope | Pääsy |
|
||||
|---|---|
|
||||
| `package` | **Read and Write** |
|
||||
|
||||
> Tämä token toimii salasanana `helm registry login` -komennossa. Muut scopet (kuten `repository`) eivät riitä — konttirekisteri vaatii nimenomaan `package`-scopen.
|
||||
|
||||
Tokenin arvo näytetään **vain kerran** luomisen yhteydessä. Kopioi se talteen.
|
||||
|
||||
---
|
||||
|
||||
## 3. Tallenna PAT repositoryn Secretsiin
|
||||
|
||||
Nämä ovat kaksi eri paikkaa:
|
||||
- **Access Tokenit** (User Settings) = missä luot tokenin
|
||||
- **Repository Secrets** (Repository Settings) = minne talletat sen workflow'n käyttöön
|
||||
|
||||
**Repository → Settings → Actions → Secrets → Add new secret**
|
||||
|
||||
| Secret | Arvo |
|
||||
|---|---|
|
||||
| `HELM_PASSWORD` | Edellisessä vaiheessa luotu PAT |
|
||||
|
||||
`HELM_USER`-secretiä **ei tarvita**. Workflow käyttää automaattisesti `${{ github.actor }}` (workflowin käynnistäjä).
|
||||
|
||||
Jos registry vaatii eri käyttäjätunnuksen kuin `github.actor` (esim. Harbor, Artifactory), lisää myös:
|
||||
|
||||
| Secret | Arvo |
|
||||
|---|---|
|
||||
| `HELM_USER` | Registryn käyttäjätunnus |
|
||||
|
||||
---
|
||||
|
||||
## 4. Tarkistuslista ennen ajoa
|
||||
|
||||
- [ ] `HELM_REGISTRY` asetettu `gitea-env.conf`issa
|
||||
- [ ] (tarvittaessa) `HELM_UI_URL` asetettu — ilman tätä commit-statusia ei erikseen aseteta
|
||||
- [ ] PAT luotu Giteassa scopella `package` Read and Write
|
||||
- [ ] `HELM_PASSWORD`-secret tallennettu repositoryn Secretsiin (se PAT)
|
||||
- [ ] (tarvittaessa) `HELM_USER`-secret — oletus `github.actor`
|
||||
|
||||
---
|
||||
|
||||
## 5. Esimerkkejä eri polkurakenteista
|
||||
|
||||
### 5a. Hosti + org — Gitea user-taso
|
||||
|
||||
```
|
||||
HELM_REGISTRY=gitea.app.keskikuja.site/niko
|
||||
```
|
||||
|
||||
- OCI-ref: `oci://gitea.app.keskikuja.site/niko/git-pages:1.2.3`
|
||||
- Paketti käyttäjän `niko` alla. Linkitys repoon tehdään Gitean UI:sta: paketin sivulta (Package → Settings) → linkitä repositoryyn.
|
||||
- `HELM_PASSWORD` = Gitea PAT scopella `package`
|
||||
|
||||
### 5b. Hosti + org — Harbor
|
||||
|
||||
```
|
||||
HELM_REGISTRY=harbor.example.com/projekti
|
||||
```
|
||||
|
||||
- `HELM_USER` = Harbor-käyttäjä
|
||||
- `HELM_PASSWORD` = Harbor-token
|
||||
|
||||
### 5c. Artifactory
|
||||
|
||||
```
|
||||
HELM_REGISTRY=artifactory.example.com/helm-local
|
||||
```
|
||||
|
||||
- `HELM_USER` = service account
|
||||
- `HELM_PASSWORD` = API-token
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
RAW_VERSION=""
|
||||
|
||||
if [ -n "${VERSION_FILE-}" ] && [ -f "${VERSION_FILE-}" ]; then
|
||||
RAW_VERSION=$(tr -d "$(printf '\xef\xbb\xbf')" < "${VERSION_FILE}" | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p')
|
||||
if [ -z "${RAW_VERSION}" ]; then
|
||||
if echo "${VERSION_FILE}" | grep -q -E '\.json$'; then
|
||||
RAW_VERSION=$(jq -r '.version' "${VERSION_FILE}")
|
||||
else
|
||||
RAW_VERSION=$(cat "${VERSION_FILE}" | tr -d '[:space:]')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${RAW_VERSION}" ]; then
|
||||
if [ -f VERSION ]; then
|
||||
RAW_VERSION=$(cat VERSION | tr -d '[:space:]')
|
||||
elif [ -f package.json ]; then
|
||||
RAW_VERSION=$(jq -r '.version' package.json)
|
||||
elif [ -f pom.xml ]; then
|
||||
RAW_VERSION=$(grep -oP '<version>\K[^<]+' pom.xml | head -1)
|
||||
elif [ -f Chart.yaml ]; then
|
||||
RAW_VERSION=$(tr -d "$(printf '\xef\xbb\xbf')" < Chart.yaml | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p')
|
||||
else
|
||||
echo "ERROR: No version source found (VERSION_FILE, VERSION, package.json, pom.xml, Chart.yaml)" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
BASE_VERSION=$(echo "$RAW_VERSION" | cut -d'.' -f1-2)
|
||||
echo "gitea-ci-library - Tunnistettu Major.Minor versio: $BASE_VERSION"
|
||||
|
||||
TAGS_JSON=$(curl -s -f -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${SERVER_URL}/api/v1/repos/${REPO}/tags")
|
||||
|
||||
TAG=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX-}" --arg sha "${SHA}" '
|
||||
if type == "array" then
|
||||
.[] | select(.commit.sha == $sha and (.name | startswith($prefix))) | .name
|
||||
else empty end' | head -1)
|
||||
|
||||
mkdir -p /tmp/build-ctx
|
||||
|
||||
if [ -n "$TAG" ]; then
|
||||
echo "ARTIFACT_EXISTS=true" > /tmp/build-ctx/build.env
|
||||
echo "NEXT_VERSION=$TAG" >> /tmp/build-ctx/build.env
|
||||
echo "gitea-ci-library - Artefakti löytyi jo tagilla: $TAG."
|
||||
else
|
||||
echo "ARTIFACT_EXISTS=false" > /tmp/build-ctx/build.env
|
||||
|
||||
HIGHEST_PATCH=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX-}" --arg bv "${GIT_TAG_PREFIX-}${BASE_VERSION}." '
|
||||
if type == "array" then .[] | .name | select(startswith($bv)) | sub($bv; "") | tonumber else empty end' | sort -rn | head -1)
|
||||
|
||||
if [ -z "$HIGHEST_PATCH" ]; then NEXT_PATCH=0; else NEXT_PATCH=$((HIGHEST_PATCH + 1)); fi
|
||||
FULL_VERSION="${BASE_VERSION}.${NEXT_PATCH}"
|
||||
|
||||
echo "NEXT_VERSION=$FULL_VERSION" >> /tmp/build-ctx/build.env
|
||||
echo "gitea-ci-library - Uusi vapaa versio: $FULL_VERSION"
|
||||
fi
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
DESCRIPTION="${1:-}"
|
||||
CONTEXT="${2:-}"
|
||||
SUITE="${3:-}"
|
||||
STATUS="${4:-success}"
|
||||
|
||||
[ -n "$DESCRIPTION" ] || { echo "ERROR: description argument required" >&2; exit 1; }
|
||||
[ -n "$CONTEXT" ] || { echo "ERROR: context argument required" >&2; exit 1; }
|
||||
[ -n "$SUITE" ] || { echo "ERROR: suite argument required" >&2; exit 1; }
|
||||
|
||||
REPORT_DIR="reports/${SUITE}"
|
||||
|
||||
if [ ! -d "$REPORT_DIR" ]; then
|
||||
echo "ERROR: $REPORT_DIR not found" >&2
|
||||
sh .ci/scripts/report-status.sh failure "$DESCRIPTION" "$CONTEXT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILE_COUNT=0
|
||||
SUBDIR_COUNT=0
|
||||
ENTRIES=""
|
||||
|
||||
for f in "$REPORT_DIR"/*; do
|
||||
[ -f "$f" ] || continue
|
||||
base=$(basename "$f")
|
||||
[ "$base" = "index.html" ] && continue
|
||||
FILE_COUNT=$((FILE_COUNT + 1))
|
||||
ENTRIES="${ENTRIES}file:${base}
|
||||
"
|
||||
done
|
||||
|
||||
for d in "$REPORT_DIR"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
base=$(basename "$d")
|
||||
[ -f "$d/index.html" ] || continue
|
||||
SUBDIR_COUNT=$((SUBDIR_COUNT + 1))
|
||||
ENTRIES="${ENTRIES}dir:${base}
|
||||
"
|
||||
done
|
||||
|
||||
TOTAL=$((FILE_COUNT + SUBDIR_COUNT))
|
||||
|
||||
if [ "$TOTAL" -eq 0 ]; then
|
||||
echo "ERROR: no reportable items in $REPORT_DIR" >&2
|
||||
sh .ci/scripts/report-status.sh failure "$DESCRIPTION" "$CONTEXT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SHA8=$(echo "${GITHUB_SHA:-xxxxxxxx}" | cut -c1-8)
|
||||
|
||||
humanize() {
|
||||
name="$1"
|
||||
name=$(echo "$name" | sed -e 's/\.[^.]*$//' -e 's/[-_]/ /g')
|
||||
first=$(echo "$name" | cut -c1 | tr '[:lower:]' '[:upper:]')
|
||||
rest=$(echo "$name" | cut -c2-)
|
||||
echo "${first}${rest}"
|
||||
}
|
||||
|
||||
generate_index() {
|
||||
{
|
||||
echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
|
||||
echo "<title>$DESCRIPTION</title>"
|
||||
echo '<style>body{font-family:sans-serif;margin:2em;max-width:960px}h1{color:#1e293b}ul{list-style:none;padding:0}li{margin:.5em 0;padding:.5em;background:#f8fafc;border-radius:6px}a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}</style>'
|
||||
echo "</head><body><h1>$DESCRIPTION</h1><ul>"
|
||||
|
||||
echo "$ENTRIES" | while IFS= read -r entry; do
|
||||
[ -z "$entry" ] && continue
|
||||
entry_type=$(echo "$entry" | cut -d: -f1)
|
||||
entry_name=$(echo "$entry" | cut -d: -f2-)
|
||||
if [ "$entry_type" = "file" ]; then
|
||||
echo "<li><a href=\"$entry_name\">$(humanize "$entry_name")</a></li>"
|
||||
else
|
||||
cap=$(echo "$entry_name" | sed 's/\(.\).*/\1/' | tr '[:lower:]' '[:upper:]')$(echo "$entry_name" | sed 's/.//')
|
||||
echo "<li><a href=\"$entry_name/index.html\">${cap}</a></li>"
|
||||
fi
|
||||
done
|
||||
|
||||
echo '</ul></body></html>'
|
||||
} > "$REPORT_DIR/index.html"
|
||||
}
|
||||
|
||||
STAGED="reports/${SHA8}/${SUITE}"
|
||||
mkdir -p "$STAGED"
|
||||
|
||||
if [ "$TOTAL" -eq 1 ]; then
|
||||
cp -a "$REPORT_DIR/." "$STAGED/"
|
||||
sh .ci/scripts/publish-git-pages.sh "$SUITE"
|
||||
|
||||
first_entry=$(echo "$ENTRIES" | head -1)
|
||||
first_type=$(echo "$first_entry" | cut -d: -f1)
|
||||
first_name=$(echo "$first_entry" | cut -d: -f2-)
|
||||
|
||||
if [ "$first_type" = "file" ]; then
|
||||
SINGLE_ENTRY="$first_name"
|
||||
else
|
||||
SINGLE_ENTRY="${first_name}/index.html"
|
||||
fi
|
||||
|
||||
URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${SHA8}/${SUITE}/${SINGLE_ENTRY}"
|
||||
sh .ci/scripts/report-status.sh "$STATUS" "$DESCRIPTION" "$CONTEXT" "" "$URL"
|
||||
else
|
||||
generate_index
|
||||
cp -a "$REPORT_DIR/." "$STAGED/"
|
||||
sh .ci/scripts/publish-git-pages.sh "$SUITE"
|
||||
sh .ci/scripts/report-status.sh "$STATUS" "$DESCRIPTION" "$CONTEXT" "$SUITE"
|
||||
fi
|
||||
|
||||
rm -rf "$STAGED"
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
SUITE_PATH="${1:-}"
|
||||
|
||||
@@ -12,7 +12,7 @@ SUITE_PATH="${1:-}"
|
||||
|
||||
OWNER="${GITHUB_REPOSITORY%%/*}"
|
||||
REPO="${GITHUB_REPOSITORY##*/}"
|
||||
SHA8="${GITHUB_SHA:0:8}"
|
||||
SHA8=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||||
PAGES_USER="${GIT_PAGES_PUBLISH_USER:-publish}"
|
||||
REPORT_DIR="reports/${SHA8}/${SUITE_PATH%/}"
|
||||
REPORT_BASE="${GIT_PAGES_URL}/${OWNER}/${REPO}/reports/${SHA8}"
|
||||
@@ -33,13 +33,66 @@ else
|
||||
fi
|
||||
mkdir -p "$TARGET"
|
||||
cp -a "$REPORT_DIR/." "$TARGET/"
|
||||
cat > "$WORK/${OWNER}/${REPO}/reports/${SHA8}/.meta" <<EOF
|
||||
|
||||
if [ ! -f "$TARGET/index.html" ]; then
|
||||
ITEM_LIST=""
|
||||
ITEM_COUNT=0
|
||||
|
||||
for f in "$TARGET"/*; do
|
||||
[ -f "$f" ] || continue
|
||||
base=$(basename "$f")
|
||||
[ "$base" = "index.html" ] && continue
|
||||
ITEM_LIST="${ITEM_LIST}file:${base}
|
||||
"
|
||||
ITEM_COUNT=$((ITEM_COUNT + 1))
|
||||
done
|
||||
|
||||
for d in "$TARGET"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
base=$(basename "$d")
|
||||
[ -f "$d/index.html" ] || continue
|
||||
ITEM_LIST="${ITEM_LIST}dir:${base}
|
||||
"
|
||||
ITEM_COUNT=$((ITEM_COUNT + 1))
|
||||
done
|
||||
|
||||
if [ "$ITEM_COUNT" -gt 1 ]; then
|
||||
{
|
||||
echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
|
||||
echo "<title>Test report ${SHA8}</title>"
|
||||
echo '<style>body{font-family:sans-serif;margin:2em;max-width:960px}'
|
||||
echo 'h1{color:#1e293b}ul{list-style:none;padding:0}'
|
||||
echo 'li{margin:.5em 0;padding:.5em;background:#f8fafc;border-radius:6px}'
|
||||
echo 'a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}'
|
||||
echo '</style></head><body>'
|
||||
echo "<h1>Test report <code>${SHA8}</code></h1><ul>"
|
||||
|
||||
echo "$ITEM_LIST" | while IFS= read -r item; do
|
||||
[ -z "$item" ] && continue
|
||||
item_type=$(echo "$item" | cut -d: -f1)
|
||||
item_name=$(echo "$item" | cut -d: -f2-)
|
||||
label=$(echo "$item_name" | sed -e 's/\.[^.]*$//' -e 's/[-_]/ /g')
|
||||
first=$(echo "$label" | cut -c1 | tr '[:lower:]' '[:upper:]')
|
||||
rest=$(echo "$label" | cut -c2-)
|
||||
if [ "$item_type" = "file" ]; then
|
||||
echo "<li><a href=\"$item_name\">${first}${rest}</a></li>"
|
||||
else
|
||||
echo "<li><a href=\"$item_name/index.html\">${first}${rest}</a></li>"
|
||||
fi
|
||||
done
|
||||
|
||||
echo '</ul></body></html>'
|
||||
} > "$TARGET/index.html"
|
||||
fi
|
||||
fi
|
||||
|
||||
cat > "$TARGET/.meta" <<EOF
|
||||
{"branch":"${GITHUB_REF_NAME:-}","sha":"${GITHUB_SHA}","published_at":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
|
||||
EOF
|
||||
find "$WORK/$OWNER" \( -type f -o -type l \) -print | sed "s|^${WORK}/||" | tar -cf "$TAR" -C "$WORK" -T -
|
||||
|
||||
publish() {
|
||||
local method="$1"
|
||||
method="$1"
|
||||
curl -sS -X "$method" "$PUBLISH_SITE_URL" \
|
||||
-u "${PAGES_USER}:${GIT_PAGES_PUBLISH_TOKEN}" \
|
||||
-H "Content-Type: application/x-tar" \
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateStatus
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
STATE="${1:-}"
|
||||
DESCRIPTION="${2:-}"
|
||||
KEY="${3:-commit-${GITHUB_SHA:0:8}}"
|
||||
SHA8=$(echo "${GITHUB_SHA:-}" | cut -c1-8)
|
||||
KEY="${3:-commit-${SHA8}}"
|
||||
SUITE="${4:-}"
|
||||
CUSTOM_URL="${5:-}"
|
||||
|
||||
@@ -18,7 +17,8 @@ if [ -n "$CUSTOM_URL" ]; then
|
||||
URL="$CUSTOM_URL"
|
||||
elif [ -n "$SUITE" ]; then
|
||||
SUITE="${SUITE%/}/"
|
||||
URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/${SUITE}"
|
||||
SHA8_CUT=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||||
URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${SHA8_CUT}/${SUITE}"
|
||||
else
|
||||
URL="${GITEA_API_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
name: ci-container-build
|
||||
description: |
|
||||
Creating or modifying CI container build workflows. Activates when consumer
|
||||
needs to build a custom CI container image (multi-tool image for tests,
|
||||
linting, validation) that is pushed to a container registry for use in
|
||||
other pipeline jobs.
|
||||
activation-gate: |
|
||||
User mentions CI container, custom CI image, ci-container-build, Dockerfile
|
||||
for CI tools, multi-tool container, or needs to build/push a container that
|
||||
other CI jobs will use as their runtime environment.
|
||||
category: ci
|
||||
impact: high
|
||||
---
|
||||
|
||||
# CI Container Build — Template
|
||||
|
||||
Template jolla consumer luo oman CI-kontin build-workflown.
|
||||
Kontti sisältää useamman työkalun yhdistelmän (esim. helm + kubeconform + xsltproc),
|
||||
jota muut jobit käyttävät ajonaikaisena ympäristönä.
|
||||
|
||||
## Rakenne
|
||||
|
||||
Vain `workflow_dispatch` — **ei automaattista buildausta koskaan**. Kontti buildataan
|
||||
manuaalisesti Gitea Actions UI:sta. Tiedoston ilmestyessä main-haaraan workflow näkyy
|
||||
välittömästi Actions-tabissa ajettavana.
|
||||
|
||||
Build-prosessi lataa ensin `config-provider.yml`:llä `DOCKER_REGISTRY`:n
|
||||
conf-tiedostosta, sitten buildaa ja puskaa kontin.
|
||||
|
||||
Kun kontti on pushattu registryyn, se on muiden pipeline-jobien käytettävissä
|
||||
`latest`-tägillä — rebuild = käyttöönotto. Mitään versioviittauksia ei tarvitse
|
||||
päivittää.
|
||||
|
||||
## Offline-periaate (DoD)
|
||||
|
||||
CI-kontin **Definition of Done**:
|
||||
|
||||
> Kontti ei lataa mitään pipeline-vaiheessa (`workflow run` -stepit) eikä kontin
|
||||
> runtime-prosessissa (`container:` / `docker run`). Kaikki riippuvuudet
|
||||
> (kielikohtaiset paketit, työkalut, binäärit) on joko:
|
||||
> - Pre-cachattu kontin **build-vaiheessa** Dockerfilessä, TAI
|
||||
> - Kopioitu multi-stage buildilla toisesta imagesta (`COPY --from`)
|
||||
>
|
||||
> Ainoa sallittu lataushetki on `docker build`. Sen jälkeen kontti toimii
|
||||
> ilman verkkoyhteyttä.
|
||||
|
||||
**Miksi:** Toistettavuus, air gap -yhteensopivuus, nopeus. Pipeline ei saa
|
||||
epäonnistua sen takia että ulkoinen registry on alhaalla tai että `go mod download`
|
||||
joutuu latamaan 100 modulia jokaisella testiajolla.
|
||||
|
||||
**Kielikohtaiset pre-cachet:** Jos kontissa ajetaan kielikohtaista testiä
|
||||
(Go, Java, Node, Python, ...), kaikki kielikohtaiset riippuvuudet on
|
||||
pre-cachattava Dockerfilessä build-vaiheessa:
|
||||
- Go: `COPY go.mod go.sum ./` → `RUN go mod download`
|
||||
- Java/Maven: `COPY pom.xml ./` → `RUN mvn dependency:go-offline`
|
||||
- Node: `COPY package.json package-lock.json ./` → `RUN npm ci --omit=dev`
|
||||
- Python: `COPY requirements.txt ./` → `RUN pip wheel --wheel-dir=/wheels -r requirements.txt` → `COPY --from` käyttöön
|
||||
|
||||
## Nimeäminen
|
||||
|
||||
CI-kontin build-workflow noudattaa samaa nimeämiskonventiota kuin muutkin
|
||||
tiedostot `.gitea/workflows/`-kansiossa:
|
||||
|
||||
```
|
||||
<komponentti>.ci-feature.yml ← feature-haaran reititin
|
||||
<komponentti>.ci-main.yml ← main-haaran reititin
|
||||
<komponentti>.<testityyppi>.yml ← yksittäinen testi tai operaatio
|
||||
<komponentti>.ci-container-build-<kontti>.yml ← CI-kontin build-workflow
|
||||
<komponentti>.gitea-env.conf ← komponenttikohtainen konfiguraatio
|
||||
```
|
||||
|
||||
Single repossa `<komponentti>` jätetään pois — tiedostot ovat suoraan `ci-feature.yml`,
|
||||
`ci-main.yml`, `<testityyppi>.yml`, `ci-container-build-<kontti>.yml`.
|
||||
|
||||
Monorepossa prefiksi pitää komponentin tiedostot yhdessä: `ls <komponentti>.*` löytää kaikki
|
||||
kerralla. **Olemassaolevia prefiksejä ei saa poistaa.**
|
||||
|
||||
Esimerkkejä:
|
||||
```
|
||||
.gitea/workflows/chart.ci-container-build-helm.yml
|
||||
.gitea/workflows/api.ci-container-build-node.yml
|
||||
.gitea/workflows/ci-container-build-bats.yml ← single repo
|
||||
```
|
||||
|
||||
## Template
|
||||
|
||||
> **Korvaa kaikki `__SUURAAKKOSET__`-placeholderit projektin todellisilla arvoilla.**
|
||||
> Ainoastaan `${{ ... }}`-syntaksilla merkityt Gitea Actions -muuttujat ovat ajonaikaisia
|
||||
> eikä niitä korvata.
|
||||
|
||||
Luo `.gitea/workflows/__KOMPONENTTI__.ci-container-build-__KONTTI__.yml`
|
||||
(single repo: `ci-container-build-__KONTTI__.yml`):
|
||||
|
||||
```yaml
|
||||
name: CI Container Build & Push
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
config_path:
|
||||
required: true
|
||||
type: string
|
||||
description: 'Polku .gitea-env.conf-tiedostoon (esim. .gitea/workflows/chart.gitea-env.conf)'
|
||||
dockerfile_path:
|
||||
required: true
|
||||
type: string
|
||||
description: 'Polku Dockerfileen (esim. ci-helm.Dockerfile)'
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
description: 'Kontin nimi ilman registry-polkua (esim. ci-helm)'
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
default: 'latest'
|
||||
description: 'Image-tägi'
|
||||
|
||||
jobs:
|
||||
load-config:
|
||||
uses: __OWNER__/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
config_path: ${{ inputs.config_path }}
|
||||
|
||||
build-push:
|
||||
needs: [load-config]
|
||||
uses: __OWNER__/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
dockerfile_path: ${{ inputs.dockerfile_path }}
|
||||
image_name: ${{ inputs.image_name }}
|
||||
tag: ${{ inputs.tag }}
|
||||
```
|
||||
|
||||
### Käyttö
|
||||
|
||||
**Gitea Actions UI:sta** (heti kun tiedosto on main-haarassa):
|
||||
|
||||
```
|
||||
Gitea → Actions → CI Container Build & Push → Run workflow
|
||||
|
||||
config_path: .gitea/workflows/__KOMPONENTTI__.gitea-env.conf
|
||||
dockerfile_path: ci-__TYÖKALU__.Dockerfile
|
||||
image_name: ci-__TYÖKALU__
|
||||
tag: latest
|
||||
```
|
||||
|
||||
### Dockerfile
|
||||
|
||||
Dockerfile yhdistää tarvitut työkalut yhteen konttiin.
|
||||
**Kaikki riippuvuudet ladataan build-vaiheessa — kontti on täysin itseriittoinen.**
|
||||
|
||||
```dockerfile
|
||||
# Tapa A: COPY --from toisesta imagesta
|
||||
FROM __BASE_IMAGE__:__VERSION__
|
||||
COPY --from=__SOURCE_IMAGE__:__VERSION__ /path/to/binary /usr/local/bin/
|
||||
RUN apk add --no-cache __PAKETIT__
|
||||
|
||||
# Tapa B: Build-vaiheen curl-lataus
|
||||
FROM __BASE_IMAGE__:__VERSION__
|
||||
RUN apk add --no-cache curl __PAKETIT__ && \
|
||||
curl -fsSL __URL__/__BINARY__.tar.gz | tar xz -C /usr/local/bin && \
|
||||
apk del curl
|
||||
|
||||
# Tapa C: Multi-stage + kielikohtainen pre-cache
|
||||
FROM __BASE_IMAGE__:__VERSION__ AS deps
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
FROM deps AS build
|
||||
COPY . .
|
||||
RUN go test -c -o /tmp/test.bin ./...
|
||||
|
||||
FROM __BASE_IMAGE__:__VERSION__
|
||||
COPY --from=deps /go/pkg/mod /go/pkg/mod
|
||||
COPY --from=build /tmp/test.bin /usr/local/bin/test
|
||||
```
|
||||
|
||||
`COPY --from` on kevyempi (ei curl-asennusta). `curl` (Tapa B) on sallittu
|
||||
vain build-vaiheessa — `apk del curl` poistaa työkalun ennen runtimea.
|
||||
Tapa C pre-cacheaa kielikohtaiset riippuvuudet ja tuottaa täysin
|
||||
offline-runtime-kontin.
|
||||
|
||||
## Mitä EI kannata tehdä
|
||||
|
||||
- Älä lisää `workflow_call`-triggariä — CI-konttia ei koskaan buildata automaattisesti
|
||||
- Älä poista `<komponentti>.`-prefiksiä olemassaolevista tiedostoista — ne kuuluvat monorepo-nimeämiskonventioon
|
||||
- Älä sisällytä CI-konttiin mitään sovelluskoodia — vain työkalut
|
||||
- Älä koskaan lataa mitään pipeline- tai runtime-vaiheessa — kaikki lataukset kuuluvat `docker build` -vaiheeseen (Offline-periaate)
|
||||
- Älä jätä kielikohtaisia riippuvuuksia pre-cachaamatta — `go mod download`, `npm install`, `mvn dependency:go-offline` jne. ajetaan Dockerfilessä, ei pipelinessä
|
||||
@@ -0,0 +1,569 @@
|
||||
# Consumer Pipelines — Reference
|
||||
|
||||
Mallipohjat, esimerkit ja konfiguraatiot. Katso säännöt `SKILL.md`:stä.
|
||||
|
||||
## Pre-cache-esimerkit (Offline Container)
|
||||
|
||||
Alla Dockerfile-esimerkit kielikohtaisista pre-cacheista. Kaikki ajetaan
|
||||
build-vaiheessa — kontti on täysin itseriittoinen eikä lataa mitään
|
||||
pipeline- tai runtime-vaiheessa.
|
||||
|
||||
### Go
|
||||
|
||||
```dockerfile
|
||||
FROM golang:1.24-alpine AS deps
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
FROM deps AS test-build
|
||||
COPY . .
|
||||
RUN go test -c -o /tmp/test.bin ./...
|
||||
|
||||
FROM alpine:3.21
|
||||
RUN apk add --no-cache git nodejs
|
||||
COPY --from=deps /go/pkg/mod /go/pkg/mod
|
||||
COPY --from=test-build /tmp/test.bin /usr/local/bin/test
|
||||
```
|
||||
|
||||
### Node.js
|
||||
|
||||
```dockerfile
|
||||
FROM node:22-alpine AS deps
|
||||
WORKDIR /build
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
FROM node:22-alpine
|
||||
RUN apk add --no-cache git
|
||||
COPY --from=deps /build/node_modules /app/node_modules
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
```
|
||||
|
||||
### Java / Maven
|
||||
|
||||
```dockerfile
|
||||
FROM maven:3.9-eclipse-temurin-21 AS deps
|
||||
WORKDIR /build
|
||||
COPY pom.xml ./
|
||||
RUN mvn dependency:go-offline -B
|
||||
|
||||
FROM maven:3.9-eclipse-temurin-21 AS build
|
||||
COPY --from=deps /root/.m2 /root/.m2
|
||||
COPY . .
|
||||
RUN mvn package -B -DskipTests
|
||||
|
||||
FROM eclipse-temurin:21-jre
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=build /build/target/*.jar /app/app.jar
|
||||
WORKDIR /app
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.12-alpine AS deps
|
||||
WORKDIR /build
|
||||
COPY requirements.txt ./
|
||||
RUN pip wheel --wheel-dir=/wheels -r requirements.txt
|
||||
|
||||
FROM python:3.12-alpine
|
||||
RUN apk add --no-cache git
|
||||
COPY --from=deps /build/wheels /wheels
|
||||
COPY --from=deps /build/requirements.txt /
|
||||
RUN pip install --no-index --find-links=/wheels -r /requirements.txt && rm -rf /wheels
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
```
|
||||
|
||||
### Helm + Node.js (korvaa helm-build-push.yml:n runtime-apk)
|
||||
|
||||
```dockerfile
|
||||
FROM alpine/helm:3.16.0 AS helm-bin
|
||||
FROM node:22-alpine
|
||||
RUN apk add --no-cache git
|
||||
COPY --from=helm-bin /usr/bin/helm /usr/local/bin/helm
|
||||
```
|
||||
|
||||
Tämä kontti korvaa `helm-build-push.yml`:n `alpine/helm:3.19.0`-image-riippuvuuden
|
||||
ja poistaa tarpeen asentaa node.js runtime-vaiheessa.
|
||||
|
||||
## Reititin — täydellinen esimerkki
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
load-config:
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
|
||||
secrets: inherit
|
||||
|
||||
<test-1>:
|
||||
needs: [load-config]
|
||||
uses: ./.gitea/workflows/<component>.<test-1>.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
|
||||
<test-2>:
|
||||
needs: [load-config]
|
||||
uses: ./.gitea/workflows/<component>.<test-2>.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
|
||||
report-summary:
|
||||
needs: [load-config, <test-1>, <test-2>]
|
||||
if: always()
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
suites: <suite-1> <suite-2>
|
||||
```
|
||||
|
||||
## CI-kontin build — parametroitu workflow
|
||||
|
||||
CI-kontin build on `workflow_dispatch`-triggeröity job, joka näkyy Gitea Actionsissa kuten Jenkinsin
|
||||
parametroitu job — käyttäjä antaa inputit UI:sta ennen ajoa.
|
||||
|
||||
```yaml
|
||||
name: CI Container Build <työkalu>
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
config_path:
|
||||
required: true
|
||||
type: string
|
||||
default: '.gitea/workflows/<komponentti>.gitea-env.conf'
|
||||
description: 'Polku .gitea-env.conf-tiedostoon'
|
||||
dockerfile_path:
|
||||
required: true
|
||||
type: string
|
||||
default: '<komponentti>/Dockerfile.ci-<työkalu>'
|
||||
description: 'Polku Dockerfileen'
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
default: 'ci-<työkalu>'
|
||||
description: 'Kontin nimi ilman registry-polkua'
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
default: 'latest'
|
||||
description: 'Image-tägi'
|
||||
|
||||
jobs:
|
||||
load-config:
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
config_path: ${{ inputs.config_path }}
|
||||
|
||||
build-push:
|
||||
needs: [load-config]
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
dockerfile_path: ${{ inputs.dockerfile_path }}
|
||||
image_name: ${{ inputs.image_name }}
|
||||
tag: ${{ inputs.tag }}
|
||||
```
|
||||
|
||||
### CI-kontin ajaminen testijobissa
|
||||
|
||||
**Ainoa sallittu tapa** consumer-puolella on `container:`-direktiivi. `docker run` komennolla
|
||||
kontin käynnistäminen stepin sisällä on anti-pattern. `container:`-direktiivillä kaikki stepit
|
||||
ajetaan samassa kontissa — tiedostot ovat suoraan filesystemillä eikä erillistä
|
||||
volyyminhallintaa tarvita.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
<työkalu>:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ inputs.<image-name> }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: <owner>/gitea-ci-library
|
||||
path: .ci
|
||||
|
||||
- name: Run <työkalu>
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "reports/<suite>"
|
||||
<komento> > "reports/<suite>/results.txt" 2>&1
|
||||
|
||||
- name: Post-process reports
|
||||
if: always()
|
||||
run: |
|
||||
<mahdollinen_raporttien_jälkikäsittely>
|
||||
|
||||
- name: Report
|
||||
if: always()
|
||||
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
|
||||
```
|
||||
|
||||
**Usean runnerin cache-ongelma:** Jos eri kerroilla käynnistyy eri runnereita,
|
||||
niillä voi olla eri versio `latest`-imagen digesteistä. Ratkaisuja:
|
||||
- Rebuildaa kontti ja aja `docker pull <image>` manuaalisesti kaikilla runnereilla
|
||||
- Käytä versioitua tagia (`v2`, `v3`, ...) ja päivitä workflow'n default buildauksen jälkeen
|
||||
|
||||
**Mallit:**
|
||||
- `example-cucumber-tests.yml` — ei post-processia
|
||||
- `example-bats-tests.yml` — post-process coverage + report
|
||||
|
||||
## Raporttitasot — tarkat YAML-mallit
|
||||
|
||||
### Taso 1: Ei jälkikäsittelyä
|
||||
|
||||
```yaml
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "reports/<suite>"
|
||||
<testikomento>
|
||||
|
||||
- name: Report
|
||||
if: always()
|
||||
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
|
||||
```
|
||||
|
||||
### Taso 2: Jälkikäsittely tarvitaan
|
||||
|
||||
```yaml
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "reports/<suite>"
|
||||
<testikomento> > "reports/<suite>/results.txt" 2>&1
|
||||
|
||||
- name: Post-process coverage
|
||||
if: always()
|
||||
run: <siirrä coverage-data reports/<suite>/coverage/-hakemistoon>
|
||||
|
||||
- name: Post-process test report
|
||||
if: always()
|
||||
run: <HTML-generointi raa'asta outputista>
|
||||
|
||||
- name: Report
|
||||
if: always()
|
||||
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
|
||||
```
|
||||
|
||||
### Väärin vs oikein — yksi asia per step
|
||||
|
||||
```yaml
|
||||
# VÄÄRIN — helm template fail → kubeconform jää ajamatta, report jää tekemättä
|
||||
- name: Run tests
|
||||
run: |
|
||||
helm template ... > /tmp/manifests.yaml
|
||||
kubeconform ... > results.txt 2>&1
|
||||
|
||||
# OIKEIN — erilliset stepit
|
||||
- name: Helm template
|
||||
run: helm template platform-helm/ -f values.yaml > /tmp/manifests.yaml 2>&1
|
||||
|
||||
- name: Kubeconform
|
||||
if: success()
|
||||
run: |
|
||||
mkdir -p reports/kubeconform
|
||||
kubeconform ... > reports/kubeconform/results.txt 2>&1
|
||||
|
||||
- name: Report
|
||||
if: always()
|
||||
run: bash .ci/scripts/ci-report.sh "Helm kubeconform" helm-test kubeconform ${{ job.status }}
|
||||
```
|
||||
|
||||
### Väärin vs oikein — post-process
|
||||
|
||||
```yaml
|
||||
# VÄÄRIN — jos coverage epäonnistuu, report jää generoimatta
|
||||
- name: Post-process reports
|
||||
run: |
|
||||
bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
|
||||
bash .ci/.gitea/scripts/bats-report.sh reports/bats
|
||||
|
||||
# OIKEIN — erilliset stepit if: always()
|
||||
- name: Post-process coverage
|
||||
if: always()
|
||||
run: bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
|
||||
|
||||
- name: Post-process test report
|
||||
if: always()
|
||||
run: bash .ci/.gitea/scripts/bats-report.sh reports/bats
|
||||
```
|
||||
|
||||
## Raportin julkaisukelpoisuus
|
||||
|
||||
`ci-report.sh` päättää onko raportti julkaisukelpoinen skannaamalla `reports/<suite>/`-hakemistoa.
|
||||
|
||||
### Mitä skannataan
|
||||
|
||||
| Mitä | Sääntö |
|
||||
|---|---|
|
||||
| **Tiedostot (FILES)** | Kaikki `reports/<suite>/`-juuressa olevat tiedostot paitsi `index.html` |
|
||||
| **Alihakemistot (SUBDIRS)** | Vain ne, joissa on `index.html` |
|
||||
|
||||
### Julkaisukelpoisuus
|
||||
|
||||
| Tila | Seuraus |
|
||||
|---|---|
|
||||
| `FILES + SUBDIRS = 0` | **Failure** — `ci-report.sh` palauttaa virheen, raporttia ei julkaista |
|
||||
| `FILES + SUBDIRS = 1` | Suora linkki itemiin — ei generoi index-sivua |
|
||||
| `FILES + SUBDIRS > 1` | Generoi `reports/<suite>/index.html`-sivun, linkit kaikkiin itemeihin |
|
||||
|
||||
### Hakemistorakenne
|
||||
|
||||
```
|
||||
reports/<suite>/
|
||||
├── results.txt ← testin stdout (skannataan FILES)
|
||||
├── test-report.html ← generoitu HTML (skannataan FILES)
|
||||
└── <mikä tahansa>/ ← alihakemisto (skannataan SUBDIRS)
|
||||
└── index.html ← VAIN jos tämä on olemassa
|
||||
```
|
||||
|
||||
### Esimerkki: coverage-näkymä
|
||||
|
||||
```
|
||||
reports/<suite>/coverage/index.html ← on olemassa
|
||||
```
|
||||
|
||||
Coverage-dataa ei siirretä automaattisesti. Testin tai post-process-stepin pitää
|
||||
siirtää coverage `reports/<suite>/coverage/`-hakemistoon ja varmistaa että `index.html` on mukana.
|
||||
|
||||
**Provider vastuulla:** `ci-report.sh` (provider-skripti) hoitaa sekä hakemistorakenteen
|
||||
skannauksen, `index.html`-generoinnin että julkaisun git-pagesiin. Consumer tuottaa
|
||||
vain raakatiedostot `reports/<suite>/`-hakemistoon — `ci-report.sh` päättää
|
||||
julkaisukelpoisuuden ja generoi tarvittavan navigaation.
|
||||
|
||||
## Debug-ohje: raportti ei näy
|
||||
|
||||
### 1. Aja lokaalisti samalla komennolla kuin CI
|
||||
|
||||
```bash
|
||||
mkdir -p reports/bats
|
||||
bashcov -- bats tests/ > reports/bats/results.txt 2>&1
|
||||
echo "exit: $?"
|
||||
ls -la reports/bats/
|
||||
```
|
||||
|
||||
### 2. Lisää `echo "DEBUG: ..." >&2` ennen ja jälkeen kriittisen operaation
|
||||
|
||||
```bash
|
||||
echo "DEBUG: coverage exists? $([ -d coverage ] && echo YES || echo NO)" >&2
|
||||
echo "DEBUG: target/index.html exists? $([ -f reports/suite/coverage/index.html ] && echo YES || echo NO)" >&2
|
||||
```
|
||||
|
||||
### 3. Tarkista kutsuparametrit
|
||||
|
||||
Yleisin virhe: skripti odottaa `$1` = X, mutta kutsuja antaa `$1` = Y ja `$2` = X.
|
||||
|
||||
### 4. Tarkista tiedostopolut
|
||||
|
||||
1. Onko lähdetiedosto olemassa ennen kopiointia?
|
||||
2. Onko kohde olemassa kopioinnin jälkeen?
|
||||
3. Onko `index.html` subdirissä (vaaditaan `ci-report.sh`:lle)?
|
||||
|
||||
### 5. Poista debug-echot kun ongelma on korjattu
|
||||
|
||||
### 6. Älä kokeile — debuggaa
|
||||
|
||||
Kokeilu = arvaus. Debuggaus = lisää echo, aja, lue logi, eristä ongelma. Vasta sitten korjaa.
|
||||
|
||||
## Konfiguraatiotiedosto (.gitea-env.conf)
|
||||
|
||||
Tiedosto on `key=value`-muotoinen (kuten `.env`). Kommentit ja tyhjät rivit sallittuja.
|
||||
|
||||
### Single repo
|
||||
|
||||
```ini
|
||||
# .gitea/workflows/gitea-env.conf
|
||||
GITEA_API_URL=https://gitea.example.com
|
||||
GIT_PAGES_URL=https://reports.example.com
|
||||
```
|
||||
|
||||
### Docker-artifaktin buildaavat projektit
|
||||
|
||||
```ini
|
||||
DOCKER_REGISTRY=gitea.example.com/myorg
|
||||
DOCKER_IMAGE_NAME=my-service
|
||||
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
|
||||
#DOCKERFILE=Dockerfile.platform # valinnainen, oletus Dockerfile
|
||||
```
|
||||
|
||||
`DOCKER_UI_URL` ei sisällä image-nimeä — se on puhdas container-registryn osoite.
|
||||
Image-nimi lisätään automaattisesti URL:iin `docker-build-push.yml`:ssä.
|
||||
|
||||
### Helm-artifaktin buildaavat projektit
|
||||
|
||||
```ini
|
||||
HELM_REGISTRY=gitea.example.com/myorg
|
||||
GIT_TAG_PREFIX=git-pages/
|
||||
VERSION_FILE=git-pages/Chart.yaml
|
||||
```
|
||||
|
||||
| Kenttä | Pakollinen | Kuvaus |
|
||||
|---|---|---|
|
||||
| `HELM_REGISTRY` | **kyllä** | Registry host + owner, esim. `gitea.example.com/myorg`. **Tyhjä pysäyttää workflow'n.** |
|
||||
| `GIT_TAG_PREFIX` | ei | Etuliite git-tägille. Pakollinen monorepossa. |
|
||||
| `VERSION_FILE` | ei | Polku version lähteeseen. Oletus: juuren `Chart.yaml`. |
|
||||
|
||||
### Salaisuudet (Gitea Settings → Secrets)
|
||||
|
||||
| Secret | Pakollinen |
|
||||
|---|---|
|
||||
| `GITEA_TOKEN` | Aina (Gitean sisäinen, automaattisesti saatavilla) |
|
||||
| `GIT_PAGES_PUBLISH_TOKEN` | Aina |
|
||||
| `DOCKER_USERNAME` | Vain jos buildaat kontteja |
|
||||
| `DOCKER_PASSWORD` | Vain jos buildaat kontteja |
|
||||
| `HELM_USER` | Vain jos pushaat Helm chartin OCI-rekisteriin (oletus `github.actor`) |
|
||||
| `HELM_PASSWORD` | Vain jos pushaat Helm chartin OCI-rekisteriin |
|
||||
|
||||
## Monorepo
|
||||
|
||||
Monorepossa yhdessä repossa asuu useampi julkaistava komponentti. Jokaiselle komponentille
|
||||
oma conf-tiedosto `.gitea/workflows/<komponentti>.gitea-env.conf`.
|
||||
|
||||
### Suositus: komponentit omiin juurihakemistoihin
|
||||
|
||||
On suositeltavaa sijoittaa jokaisen komponentin koko lähdekoodi omaan juuritason
|
||||
hakemistoonsa (`api/`, `frontend/`, `shared/`). Tämä helpottaa `paths:`-filtteröintiä,
|
||||
pitää komponentit selkeästi erillään, ja tekee repossa navigoinnista suoraviivaista.
|
||||
|
||||
### Ongelmat ja ratkaisut
|
||||
|
||||
| Ongelma | Ratkaisu |
|
||||
|---|---|
|
||||
| Monta komponenttia, yksi repo — mikä triggeröi? | `paths:`-filtteri: komponentin hakemisto + sen CI-workflow't ja conf-tiedosto |
|
||||
| Jokaisella komponentilla oma versio | `VERSION_FILE=<komponentti>/package.json` confissa |
|
||||
| Git-tägit sekaisin ellei nimiavaruutta | `GIT_TAG_PREFIX=<komponentti>/` confissa → tägi `<komponentti>/1.2.3` |
|
||||
| Eri julkaisutahdit | Riippumattomat CI-triggerit, omat versiopolut |
|
||||
|
||||
### Komponenttikohtainen conf
|
||||
|
||||
```ini
|
||||
# .gitea/workflows/<komponentti>.gitea-env.conf
|
||||
GITEA_API_URL=https://gitea.example.com
|
||||
GIT_PAGES_URL=https://reports.example.com
|
||||
DOCKER_REGISTRY=gitea.example.com/myorg
|
||||
DOCKER_IMAGE_NAME=<image-nimi>
|
||||
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
|
||||
GIT_TAG_PREFIX=<komponentti>/
|
||||
# Jompikumpi — JSON (.version-kenttä) tai plain text:
|
||||
VERSION_FILE=<komponentti>/package.json
|
||||
#VERSION_FILE=<komponentti>/VERSION
|
||||
```
|
||||
|
||||
### Monorepo reititin
|
||||
|
||||
```yaml
|
||||
name: CI <Komponentti> Main
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- <komponentti>/**
|
||||
- .gitea/workflows/<komponentti>.*
|
||||
|
||||
jobs:
|
||||
load-config:
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
config_path: .gitea/workflows/<komponentti>.gitea-env.conf
|
||||
|
||||
check-version:
|
||||
needs: [load-config]
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/check-version.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
|
||||
<testit>:
|
||||
needs: [load-config, check-version]
|
||||
if: needs.check-version.outputs.artifact_exists != 'true'
|
||||
uses: ./.gitea/workflows/<komponentti>.<testi>.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
|
||||
build-push:
|
||||
needs: [load-config, check-version, <testit>]
|
||||
if: needs.check-version.outputs.artifact_exists != 'true'
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
version: ${{ needs.check-version.outputs.version }}
|
||||
|
||||
report-summary:
|
||||
name: Report Summary
|
||||
needs: [load-config, build-push]
|
||||
if: always()
|
||||
uses: <owner>/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
suites: '<suite-1> <suite-2>'
|
||||
```
|
||||
|
||||
### Version elinkaari per komponentti
|
||||
|
||||
`GIT_TAG_PREFIX` takaa että eri komponenttien versiohistoria pysyy erillään.
|
||||
Git-tägi `<komponentti>/0.2.3` ei sekoitu toisen komponentin tägeihin.
|
||||
|
||||
`check-version.yml` suodattaa ja laskee seuraavan patchin vain kyseisen
|
||||
komponentin etuliitteellä. Idempotenttius toimii komponenttikohtaisesti:
|
||||
jos commitilla on jo tägi, pipeline skipataan `if: artifact_exists != 'true'`.
|
||||
|
||||
### Mitä EI kannata tehdä monorepossa
|
||||
|
||||
- Älä aja kaikkia komponentteja samasta triggeristä — `paths:` pitää CI:t erillisinä
|
||||
- Älä käytä samaa versionhallintatiedostoa usealle komponentille
|
||||
- Älä anna monorepo-parametreja pipeline-overrideina — kaikki kuuluu conf-tiedostoon
|
||||
- Älä rajaa `paths:` pelkkään komponentin hakemistoon — CI ei triggeröidy workflow- tai conf-muutoksista
|
||||
|
||||
## Versionhallinta
|
||||
|
||||
`check-version.yml` lukee version automaattisesti prioriteettijärjestyksessä:
|
||||
|
||||
| # | Lähde | Formaatti |
|
||||
|---|---|---|
|
||||
| 1 | `VERSION_FILE` confissa | Määritelty polku |
|
||||
| 2 | `VERSION`-tiedosto (root) | Plain text |
|
||||
| 3 | `package.json` (root) | `.version`-kenttä |
|
||||
| 4 | `pom.xml` (root) | `<version>`-elementti |
|
||||
|
||||
`major.minor` otetaan tästä. Patch lasketaan automaattisesti git-tageista.
|
||||
Esim. `VERSION` = `0.2`, tagit = `0.2.0`, `0.2.1` → seuraava `0.2.2`.
|
||||
|
||||
## Branch protection (PR-gate)
|
||||
|
||||
Gitean Settings → Branches → Add Rule:
|
||||
|
||||
- **Branch:** `main`
|
||||
- **Enable Require Status Checks:** päälle
|
||||
- **Status checks:** valitse testijobien nimet
|
||||
|
||||
## Provider-rajapinnat — referenssi
|
||||
|
||||
### Workflowt
|
||||
|
||||
| Workflow | Käyttötarkoitus |
|
||||
|---|---|
|
||||
| `config-provider.yml` | Lataa + validoi `.conf`, tuottaa `env_json` |
|
||||
| `check-version.yml` | Tarkistaa onko commit buildattu, laskee version |
|
||||
| `docker-build-push.yml` | Buildaa + puskea Docker-imagen, tagittaa commitin |
|
||||
| `helm-build-push.yml` | Paketoi + puskea Helm chartin OCI-rekisteriin, tagittaa commitin |
|
||||
| `report-summary.yml` | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä (Gitea 1.27+) |
|
||||
|
||||
### Skriptit (kutsutaan `.ci/scripts/`-polun kautta)
|
||||
|
||||
| Skripti | Käyttötarkoitus |
|
||||
|---|---|
|
||||
| `ci-report.sh` | Yhdistetty raportointi: julkaisee git-pagesiin ja asettaa commit-statuksen. Korvaa erilliset `publish-git-pages.sh` + `report-status.sh` -kutsut. Käyttö: `bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}` |
|
||||
| `report-status.sh` | POSTaa commit-statuksen linkillä (kutsutaan `ci-report.sh`:n sisältä) |
|
||||
| `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin (kutsutaan `ci-report.sh`:n sisältä) |
|
||||
| `ci-validate.sh` | Validoi `.conf`-tiedoston (kutsutaan `config-provider.yml`:stä) |
|
||||
@@ -0,0 +1,289 @@
|
||||
---
|
||||
name: consumer-pipelines
|
||||
description: |
|
||||
Creating or modifying consumer CI pipelines, .gitea/workflows/ files,
|
||||
reusable test workflows, monorepo CI configuration, or CI routing files
|
||||
(ci-feature.yml, ci-main.yml, ci-*.yml). Activates when the user asks to
|
||||
build, fix, or change consumer-side Gitea Actions pipelines that use
|
||||
gitea-ci-library providers.
|
||||
activation-gate: |
|
||||
User mentions consumer pipelines, ci-feature.yml, ci-main.yml, test
|
||||
workflows, .gitea/workflows/ files, monorepo CI, routing files, or asks
|
||||
to create/modify CI pipelines on top of gitea-ci-library.
|
||||
category: ci
|
||||
impact: high
|
||||
---
|
||||
|
||||
# Consumer Pipelines — Pipeline Standards
|
||||
|
||||
Säännöt joilla consumer-projektit rakentavat CI-pipelinejä `gitea-ci-library`-kirjaston päälle.
|
||||
Nämä eivät ole provider-kirjaston sääntöjä — ne kuvaavat miten consumerin kuuluu käyttää kirjastoa oikein.
|
||||
|
||||
Katso tarkat mallipohjat ja esimerkit `REFERENCE.md`:stä.
|
||||
|
||||
## 1. Reitittimen puhtaus
|
||||
|
||||
Reitittimet (`ci-feature.yml`, `ci-main.yml`) eivät sisällä `run:`-steppejä. Ne koostuvat vain:
|
||||
|
||||
```yaml
|
||||
uses:
|
||||
needs:
|
||||
if:
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json:
|
||||
<parametrit>:
|
||||
```
|
||||
|
||||
Jokainen job vastaa yhtä loogista testiä tai operaatiota. Reititin on orkestraattori — kaikki suorittava
|
||||
logiikka on omassa `workflow_call`-tiedostossaan.
|
||||
|
||||
Katso täydellinen esimerkki `REFERENCE.md`:stä.
|
||||
|
||||
## 2. Yksi asia per tiedosto
|
||||
|
||||
Ei monoliittista `ci-tests.yml`. Jokainen testityyppi tai operaatio on oma `workflow_call`-tiedostonsa.
|
||||
|
||||
**Miksi:**
|
||||
- Testit ajetaan rinnakkain (ei keinotekoisia riippuvuuksia)
|
||||
- Yhden testin fail ei estä muita
|
||||
- Testattavissa itsenäisesti `workflow_dispatch`:llä
|
||||
- Diff näyttää heti mitä testiä muutettiin
|
||||
|
||||
## 3. Exit-koodin käsittely
|
||||
|
||||
`set -e` on oletuksena käytössä Gitea Actions -stepeissä — ensimmäinen feilaava komento pysäyttää stepin
|
||||
ja exit-koodi välittyy natiivisti. Ylimääräistä `EXIT=$?` + `echo >> GITHUB_ENV` -käärettä ei tarvita.
|
||||
|
||||
```yaml
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: |
|
||||
<testikomento> > results.txt 2>&1
|
||||
```
|
||||
|
||||
**Miksi ei pipeä (`| tee`):** `|` syö exit-koodin. Käytä redirectiä `>`.
|
||||
|
||||
**Yksi asia per step:** Älä koskaan niputa useaa komentoa samaan `run:`-blockiin. `bash -e` pysäyttää
|
||||
koko stepin ensimmäisellä failaavalla komennolla, ja loput jäävät ajamatta. Sama pätee post-process-steppeihin.
|
||||
|
||||
## 4. Konttipolitiikka
|
||||
|
||||
1. **Julkiset registry-kontit kiinteällä versiolla** — `alpine/helm:3.19.0`, `node:22`, `maven:3.9-eclipse-temurin-21`.
|
||||
Toistettavuus ja turvallisuus eivät saa riippua ulkoisesta `latest`:sta.
|
||||
2. **Projektin omat CI-kontit `latest`-tägillä** — buildattu `ci-container-build-<kontti>.yml`:llä.
|
||||
Kontin build-pipeline päivittää `latest`:n automaattisesti. Rebuild = käyttöönotto
|
||||
kaikissa pipelineissa ilman versioviittauksien päivittelyä.
|
||||
3. **Ei koskaan `curl`-latauksia CI-ajon sisällä** — työkalujen asennus CI-stepeissä hidastaa,
|
||||
epäluotettavaa, ja vaikeuttaa toistettavuutta.
|
||||
4. **Konttikuva hallitaan workflow'ssa, ei kutsujassa** — jos workflow vaatii tietyn
|
||||
konttikuvan, se määritellään oletuksena (`default:`) workflow'n inputissa.
|
||||
Kutsujan ei tarvitse tietää eikä välittää image-nimeä ellei halua ylikirjoittaa.
|
||||
|
||||
CI-kontin build-workflow'n template: `skills/ci-container-build/SKILL.md`.
|
||||
|
||||
### 4.1 Offline Container -vaatimus (DoD)
|
||||
|
||||
CI-kontin (ja kaikkien pipeline-konttien) on oltava täysin itseriittoisia:
|
||||
|
||||
> Kontti ei lataa mitään pipeline-vaiheessa (`workflow run` -stepit) eikä
|
||||
> kontin runtime-prosessissa (`container:` / `docker run`). Kaikki
|
||||
> riippuvuudet pre-cachataan `docker build` -vaiheessa.
|
||||
> Ainoa sallittu lataushetki on `docker build`.
|
||||
|
||||
**Esimerkkejä rikkomuksista:**
|
||||
- `apk add`, `apt-get install`, `npm install`, `go mod download`, `pip install`
|
||||
pipeline-stepissä
|
||||
- `curl <url> | tar xz` runtime-vaiheessa
|
||||
- Node.js-konttikuva ilman nodea (joudutaan asentamaan lennossa)
|
||||
|
||||
### 4.2 Kielikohtainen pre-cache
|
||||
|
||||
Kun kontissa testataan kielikohtaista koodia, kaikki riippuvuudet on
|
||||
pre-cachattava Dockerfilessä, ei pipeline-stepissä:
|
||||
|
||||
| Kieli | Pre-cache Dockerfilessä |
|
||||
|---|---|
|
||||
| Go | `COPY go.mod go.sum ./` → `RUN go mod download` |
|
||||
| Java/Maven | `COPY pom.xml ./` → `RUN mvn dependency:go-offline` |
|
||||
| Node | `COPY package.json package-lock.json ./` → `RUN npm ci --omit=dev` |
|
||||
| Python | `COPY requirements.txt ./` → `RUN pip install -r requirements.txt` |
|
||||
|
||||
Katso tarkat Dockerfile-esimerkit `REFERENCE.md`:stä.
|
||||
|
||||
### 4.3 CI-kontin ajaminen jobissa
|
||||
|
||||
Ainoa sallittu tapa on `container:`-direktiivi. `docker run` komennolla kontin
|
||||
käynnistäminen stepin sisällä on anti-pattern.
|
||||
|
||||
Katso CI-kontin template `REFERENCE.md`:stä.
|
||||
|
||||
**Huomio `actions/checkout@v4`:stä:** `container:`-direktiivillä kaikki stepit
|
||||
ajetaan kontin *sisällä* — myös `actions/checkout@v4`. Se on JavaScript-action
|
||||
joka vaatii sekä `nodejs` että `git`. Varmista että CI-kontin Dockerfilessä on
|
||||
molemmat — muuten checkout ei toimi ja pipeline failaa.
|
||||
|
||||
## 5. Raporttitasot
|
||||
|
||||
Testi tuottaa raportin `reports/<suite>/`-hakemistoon. Yksi `ci-report.sh`-kutsu hoitaa sekä
|
||||
julkaisun että commit-statuksen.
|
||||
|
||||
### Taso 1: Ei jälkikäsittelyä
|
||||
|
||||
Kun testi tuottaa raportit suoraan (kuten `pytest --html` tai `cucumber-js --format html`):
|
||||
- testi kirjoittaa `reports/<suite>/`-hakemistoon
|
||||
- `ci-report.sh` julkaisee ja asettaa commit-statuksen
|
||||
|
||||
### Taso 2: Jälkikäsittely tarvitaan
|
||||
|
||||
Kun testi tuottaa raakadataa (stdout, coverage-tiedostot) joka pitää muuntaa tai siirtää
|
||||
`reports/<suite>/`-hakemistoon. **Jokainen operaatio omassa stepissään** `if: always()`.
|
||||
|
||||
Tarkat YAML-mallit molemmista tasoista: `REFERENCE.md`.
|
||||
|
||||
**Subdir-sääntö:** Alihakemisto näkyy indexissä VAIN jos se sisältää `index.html`:n.
|
||||
|
||||
## 6. Nimeäminen
|
||||
|
||||
Tiedostonimet `.gitea/workflows/`-kansiossa noudattavat yhtenäistä rakennetta:
|
||||
|
||||
```
|
||||
<komponentti>.ci-feature.yml ← feature-haaran reititin
|
||||
<komponentti>.ci-main.yml ← main-haaran reititin
|
||||
<komponentti>.<testityyppi>.yml ← yksittäinen testi tai operaatio
|
||||
<komponentti>.ci-container-build-<kontti>.yml ← CI-kontin build-workflow
|
||||
<komponentti>.gitea-env.conf ← komponenttikohtainen konfiguraatio
|
||||
```
|
||||
|
||||
Single repossa `<komponentti>` jätetään pois.
|
||||
Monorepossa prefiksi pitää komponentin tiedostot yhdessä.
|
||||
|
||||
## 7. Artifact-kuri
|
||||
|
||||
Gitea Actionsin `upload-artifact` jättää pysyvän tiedoston. Artifakteja ei käytetä
|
||||
`workflow_call`:ien väliseen datan siirtoon ellei se ole teknisesti välttämätöntä.
|
||||
|
||||
**Ensisijainen ratkaisu:** jokainen testi tuottaa tarvitsemansa datan itse. Ei
|
||||
`upload-artifact` + `download-artifact` -riippuvuuksia.
|
||||
|
||||
## 8. Report-Summary — pakollinen jokaisen pipelinen lopuksi
|
||||
|
||||
Jokaisen reitittimen (oli se `ci-main.yml`, `ci-feature.yml` tai mikä tahansa) viimeinen job on `report-summary`.
|
||||
|
||||
**Säännöt:**
|
||||
- `needs:` sisältää **kaikki edeltävät jobit** — summary odottaa että kaikki on valmis (onnistui tai ei)
|
||||
- `if: always()` — ajetaan aina, vaikka pipeline olisi keskeytetty tai joku jobi failannut
|
||||
- `suites:` on välilyönnein eroteltu lista suiten nimistä (esim. `bats cucumber`). Tyhjä merkkijono sallittu jos testisuiteja ei ole.
|
||||
- Provider (`report-summary.yml`) hoitaa summaryn logiikan — reititin vain kutsuu
|
||||
|
||||
**Miksi aina:**
|
||||
- Gitea 1.27+ näyttää `GITHUB_STEP_SUMMARY`:n Actions UI:ssa. Ilman summarya pipeline näyttää epätäydelliseltä.
|
||||
- Summaryyn voidaan myöhemmin lisätä muutakin kuin testilinkkejä (build-artefaktit, deploy-tiedot).
|
||||
- Yhtenäinen rakenne jokaisessa pipeline-parissa vähentää kysymyksiä.
|
||||
|
||||
YAML-malli: `REFERENCE.md`.
|
||||
|
||||
## 9. ADR-yhteenveto — consumerin kannalta oleelliset säännöt
|
||||
|
||||
### Reititin ei sisällä suorittavaa koodia (ADR 0010)
|
||||
|
||||
`ci-feature.yml` ja `ci-main.yml` koostuvat **vain** `uses:`, `needs:` ja `if:`-avainsanoista.
|
||||
Ei `run:`-komentoja, ei inline-skriptejä, ei `actions/checkout`.
|
||||
|
||||
### Yksi steppi = yksi workflow_call-tiedosto
|
||||
|
||||
Jokainen job reitittimessä on oma `workflow_call`-tiedostonsa.
|
||||
Ei kahta eri komentoa samassa workflow'ssa.
|
||||
|
||||
### Provider-versio on `@v1` (ADR 0009)
|
||||
|
||||
Kaikki provider-viittaukset käyttävät `@v1`-tagia. `@main` on vain providerin oman repon
|
||||
sisäiseen dogfood-käyttöön. Breaking changet kielletty — `v1`-rajapinta on pysyvä.
|
||||
|
||||
### Paikalliset `uses:` eivät käytä refiä
|
||||
|
||||
Gitea act runner v1.0.8 muodostaa paikallisista `uses: ./.gitea/workflows/*.yml@main`-viittauksista
|
||||
epävalidin git-refin `main@<sha>`.
|
||||
|
||||
Paikallisista `uses:`-direktiiveistä EI koskaan käytetä `@main`- tai muuta ref-päätettä:
|
||||
- `uses: ./.gitea/workflows/chart.helm-lint.yml` ← oikein
|
||||
- `uses: ./.gitea/workflows/chart.helm-lint.yml@main` ← väärin
|
||||
|
||||
Ilman refiä runner käyttää workflow'ta triggeröivästä commitista.
|
||||
|
||||
### Exit-koodi on ainoa onnistumisen mittari (ADR 0008)
|
||||
|
||||
Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä (`> file 2>&1`).
|
||||
|
||||
### Providerin checkout ei kuulu consumerille
|
||||
|
||||
Providerin scriptit haetaan `actions/checkout`-stepillä `.ci/`-polkuun.
|
||||
Consumer ei kopioi eikä muokkaa providerin tiedostoja.
|
||||
|
||||
## 10. Build & Push -providerit
|
||||
|
||||
### `docker-build-push.yml` — Docker image build & push
|
||||
|
||||
Buildaa ja pushee Docker-imagen OCI-registryyn. Ajaa suoraan runnerilla
|
||||
(ei `container:`-direktiiviä), joten `actions/checkout` toimii natiivisti.
|
||||
|
||||
**`env_json`-avaimet (pakolliset):**
|
||||
|
||||
```yaml
|
||||
DOCKER_REGISTRY: gitea.app.keskikuja.site/niko
|
||||
DOCKER_IMAGE_NAME: my-app
|
||||
```
|
||||
|
||||
**Käyttö reitittimessä:**
|
||||
|
||||
```yaml
|
||||
docker-build-push:
|
||||
uses: OWNER/gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1
|
||||
needs: [check-version]
|
||||
if: needs.check-version.outputs.artifact_exists == 'false'
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
version: ${{ needs.check-version.outputs.version }}
|
||||
```
|
||||
|
||||
Tarkka input/secret-lista: `docs/workflows.md`.
|
||||
|
||||
### `helm-build-push.yml` — Helm chart build & push
|
||||
|
||||
Pakkaa ja pushee Helm-chartin OCI-registryyn. Käyttää `alpine/helm`-konttia.
|
||||
|
||||
**`env_json`-avaimet (pakolliset):**
|
||||
|
||||
```yaml
|
||||
HELM_REGISTRY: gitea.app.keskikuja.site/niko
|
||||
```
|
||||
|
||||
**Käyttö reitittimessä:**
|
||||
|
||||
```yaml
|
||||
helm-build-push:
|
||||
uses: OWNER/gitea-ci-library/.gitea/workflows/helm-build-push.yml@v1
|
||||
needs: [check-version]
|
||||
if: needs.check-version.outputs.artifact_exists == 'false'
|
||||
secrets: inherit
|
||||
with:
|
||||
env_json: ${{ needs.load-config.outputs.env_json }}
|
||||
version: ${{ needs.check-version.outputs.version }}
|
||||
# chart_path: '.' # oletus, vaihda jos Chart.yaml on alihakemistossa
|
||||
```
|
||||
|
||||
**Vanhentunut käytäntö:** Nykyinen `helm-build-push.yml` asentaa node.js:n
|
||||
lennossa `apk add --no-cache nodejs` ennen checkouttia — tämä rikkoo
|
||||
Offline Container -vaatimusta (4.1).
|
||||
|
||||
**Korjaustoimenpide:** Rakenna custom CI-kontti `ci-container-build`-skillillä
|
||||
jossa on helm + nodejs + git (katso pre-cache-esimerkit `REFERENCE.md`:stä),
|
||||
päivitä workflow'n `container: image:` osoittamaan omaan konttiin, ja poista
|
||||
runtime-apk.
|
||||
|
||||
**Yksittäisten Helm-UI-linkkien raportointi:** `HELM_UI_URL` on
|
||||
tarkoitettu yleiselle registry UI:lle — provider muodostaa linkin
|
||||
`${HELM_UI_URL}/${CHART_NAME}/${VERSION}` automaattisesti.
|
||||
|
||||
Tarkka input/secret-lista: `docs/workflows.md`.
|
||||
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
source "$BATS_TEST_DIRNAME/helpers/mock-api.sh"
|
||||
|
||||
setup() {
|
||||
export GITEA_TOKEN=test-token
|
||||
export GIT_TAG_PREFIX=""
|
||||
export REPO="niko/test"
|
||||
export SHA="abc123"
|
||||
rm -rf /tmp/build-ctx
|
||||
}
|
||||
|
||||
teardown() {
|
||||
mock_stop 2>/dev/null || true
|
||||
rm -rf /tmp/build-ctx
|
||||
}
|
||||
|
||||
@test "VERSION_FILE=Chart.yaml extracts version from YAML" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/Chart.yaml"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "false" ]
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "VERSION_FILE=VERSION extracts version from plain text" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "false" ]
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "VERSION_FILE=package.json extracts version from JSON" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/package.json"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "false" ]
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "VERSION_FILE=subdir/Chart.yaml extracts version from monorepo" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/subdir/Chart.yaml"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "false" ]
|
||||
[ "$NEXT_VERSION" = "0.4.0" ]
|
||||
}
|
||||
|
||||
@test "no VERSION_FILE, root VERSION found" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
WORKDIR=$(mktemp -d)
|
||||
cp "$BATS_TEST_DIRNAME/fixtures/check-version/VERSION" "$WORKDIR/VERSION"
|
||||
|
||||
SCRIPT="$PWD/scripts/check-version.sh"
|
||||
run bash -c "cd '$WORKDIR' && exec bash '$SCRIPT'"
|
||||
|
||||
rm -rf "$WORKDIR"
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "no VERSION_FILE, root Chart.yaml found" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
WORKDIR=$(mktemp -d)
|
||||
cp "$BATS_TEST_DIRNAME/fixtures/check-version/Chart.yaml" "$WORKDIR/Chart.yaml"
|
||||
|
||||
SCRIPT="$PWD/scripts/check-version.sh"
|
||||
run bash -c "cd '$WORKDIR' && exec bash '$SCRIPT'"
|
||||
|
||||
rm -rf "$WORKDIR"
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "tag exists for commit sets ARTIFACT_EXISTS=true" {
|
||||
mock_set_sequence '[{"code": 200, "body": [{"name": "0.3.0", "commit": {"sha": "abc123"}}]}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "true" ]
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "tag with prefix filters correctly" {
|
||||
mock_set_sequence '[{"code": 200, "body": [{"name": "git-pages/0.3.0", "commit": {"sha": "abc123"}}, {"name": "docker/0.3.0", "commit": {"sha": "abc123"}}]}]'
|
||||
mock_start
|
||||
|
||||
export GIT_TAG_PREFIX="git-pages/"
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "true" ]
|
||||
[ "$NEXT_VERSION" = "git-pages/0.3.0" ]
|
||||
}
|
||||
|
||||
@test "no tag, new version calculated" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "false" ]
|
||||
[ "$NEXT_VERSION" = "0.3.0" ]
|
||||
}
|
||||
|
||||
@test "highest patch calculated correctly" {
|
||||
mock_set_sequence '[{"code": 200, "body": [{"name": "0.3.0", "commit": {"sha": "def456"}}, {"name": "0.3.1", "commit": {"sha": "def456"}}]}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/VERSION"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
[ "$ARTIFACT_EXISTS" = "false" ]
|
||||
[ "$NEXT_VERSION" = "0.3.2" ]
|
||||
}
|
||||
|
||||
@test "VERSION_FILE=Chart-umbrella.yaml extracts only top-level version" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
export VERSION_FILE="$BATS_TEST_DIRNAME/fixtures/check-version/Chart-umbrella.yaml"
|
||||
run bash scripts/check-version.sh
|
||||
|
||||
echo "STATUS=$status"
|
||||
echo "OUTPUT=$output"
|
||||
[ "$status" -eq 0 ]
|
||||
source /tmp/build-ctx/build.env
|
||||
echo "NEXT_VERSION=$NEXT_VERSION"
|
||||
[ "$NEXT_VERSION" = "0.1.0" ]
|
||||
}
|
||||
|
||||
@test "no version source exits with error" {
|
||||
mock_set_sequence '[{"code": 200, "body": []}]'
|
||||
mock_start
|
||||
|
||||
WORKDIR=$(mktemp -d)
|
||||
SCRIPT="$PWD/scripts/check-version.sh"
|
||||
run bash -c "cd '$WORKDIR' && exec bash '$SCRIPT'"
|
||||
|
||||
rm -rf "$WORKDIR"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"ERROR"* ]]
|
||||
}
|
||||
@@ -19,7 +19,7 @@ teardown() {
|
||||
{"code":200,"body":{"id":1,"status":"completed","conclusion":"success"}}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "$GITEA_API_URL" "test-token-abc123"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ teardown() {
|
||||
{"code":200,"body":{"id":1,"status":"completed","conclusion":"failure"}}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "$GITEA_API_URL" "test-token-abc123"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ teardown() {
|
||||
{"code":200,"body":{"id":1,"status":"completed","conclusion":"cancelled"}}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "$GITEA_API_URL" "test-token-abc123"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ teardown() {
|
||||
{"code":200,"body":{"id":1,"status":"running"}}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123" "0.001"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "$GITEA_API_URL" "test-token-abc123" "0.001"
|
||||
[ "$status" -eq 124 ]
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ teardown() {
|
||||
{"code":500}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "$GITEA_API_URL" "test-token-abc123"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ teardown() {
|
||||
{"code":200,"body":{"id":1,"status":"completed","conclusion":"success"}}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "$GITEA_API_URL" "test-token-abc123"
|
||||
[ "$status" -eq 0 ]
|
||||
path=$(mock_get_first_request_path)
|
||||
[[ "$path" == *"/api/v1/repos/test-owner/test-repo/actions/workflows/test.yml/dispatches"* ]]
|
||||
@@ -126,7 +126,7 @@ teardown() {
|
||||
{"code":200,"body":{"workflow_runs":[]}}
|
||||
]'
|
||||
mock_start
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{}' "http://localhost:18080" "test-token-abc123"
|
||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{}' "$GITEA_API_URL" "test-token-abc123"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"ERROR"* ]]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..');
|
||||
const MOCK_SCRIPT = path.join(PROJECT_ROOT, 'tests', 'helpers', 'mock-api.sh');
|
||||
|
||||
Before({ tags: '@mock' }, function () {
|
||||
const out = execSync(`bash -c 'source "${MOCK_SCRIPT}" && mock_start && sleep 0.3 && curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://localhost:18080/api/v1/repos/health/check'`, {
|
||||
const out = execSync(`bash -c 'source "${MOCK_SCRIPT}" && mock_start && sleep 1 && curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://localhost:18080/api/v1/repos/health/check'`, {
|
||||
cwd: PROJECT_ROOT,
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v2
|
||||
name: agent-platform
|
||||
description: Agent Platform umbrella chart
|
||||
type: application
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
- name: vikunja
|
||||
version: "0.1.0"
|
||||
repository: oci://registry.example.com
|
||||
- name: langfuse
|
||||
version: "0.2.0"
|
||||
repository: oci://registry.example.com
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: test-chart
|
||||
description: Test chart for version extraction
|
||||
type: application
|
||||
version: 0.3.0
|
||||
appVersion: "1.0.0"
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
0.3.0
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version": "0.3.0"}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
<project><version>0.3.0</version></project>
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: subdir-chart
|
||||
description: Chart in subdirectory for monorepo testing
|
||||
type: application
|
||||
version: 0.4.0
|
||||
appVersion: "1.0.0"
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
MOCK_PORT=18080
|
||||
MOCK_PORT=""
|
||||
MOCK_PID=""
|
||||
MOCK_REQUEST_FILE=""
|
||||
MOCK_RESPONSE_CODE=201
|
||||
@@ -9,11 +9,17 @@ MOCK_STATE_FILE="/tmp/mock_api_state"
|
||||
MOCK_SEQUENCE_FILE=""
|
||||
MOCK_CONFIG_FILE=""
|
||||
|
||||
_free_port() {
|
||||
python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()"
|
||||
}
|
||||
|
||||
_kill_port() {
|
||||
local pids
|
||||
pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true
|
||||
[ -n "$pids" ] && kill -9 $pids 2>/dev/null || true
|
||||
[ -n "$pids" ] && kill $pids 2>/dev/null || true
|
||||
sleep 0.5
|
||||
pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true
|
||||
[ -n "$pids" ] && kill -9 $pids 2>/dev/null || true
|
||||
}
|
||||
|
||||
_wait_port_free() {
|
||||
@@ -24,6 +30,18 @@ _wait_port_free() {
|
||||
done
|
||||
}
|
||||
|
||||
_wait_port_ready() {
|
||||
local i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
if nc -z localhost "$MOCK_PORT" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
mock_set_sequence() {
|
||||
MOCK_SEQUENCE_FILE=$(mktemp)
|
||||
echo "$1" | jq -c '.' > "$MOCK_SEQUENCE_FILE"
|
||||
@@ -35,6 +53,12 @@ mock_clear_sequence() {
|
||||
}
|
||||
|
||||
mock_start() {
|
||||
MOCK_PORT=$(_free_port)
|
||||
export MOCK_PORT
|
||||
MOCK_URL="http://localhost:${MOCK_PORT}"
|
||||
export SERVER_URL="$MOCK_URL"
|
||||
export GITEA_API_URL="$MOCK_URL"
|
||||
|
||||
MOCK_RESPONSE_CODE="${MOCK_RESPONSE_CODE:-201}"
|
||||
MOCK_REQUEST_FILE=$(mktemp)
|
||||
echo "$MOCK_REQUEST_FILE" > "$MOCK_STATE_FILE"
|
||||
@@ -55,7 +79,7 @@ mock_start() {
|
||||
nohup python3 "$(dirname "${BASH_SOURCE[0]}")/mock-server.py" "$MOCK_PORT" "$MOCK_CONFIG_FILE" "$MOCK_REQUEST_FILE" \
|
||||
</dev/null >/dev/null 2>&1 &
|
||||
MOCK_PID=$!
|
||||
sleep 0.5
|
||||
_wait_port_ready
|
||||
}
|
||||
|
||||
mock_stop() {
|
||||
|
||||
@@ -63,9 +63,10 @@ teardown() {
|
||||
{"code":200,"body":"published"}
|
||||
]'
|
||||
mock_start
|
||||
export GIT_PAGES_URL="http://localhost:${MOCK_PORT}"
|
||||
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == "http://localhost:18080/test-owner/test-repo/reports/abc123de" ]]
|
||||
[[ "$output" == "${GIT_PAGES_URL}/test-owner/test-repo/reports/abc123de" ]]
|
||||
}
|
||||
|
||||
@test "publish with suite subpath" {
|
||||
@@ -75,9 +76,10 @@ teardown() {
|
||||
{"code":200,"body":"published"}
|
||||
]'
|
||||
mock_start
|
||||
export GIT_PAGES_URL="http://localhost:${MOCK_PORT}"
|
||||
run bash scripts/publish-git-pages.sh "sub/suite"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == "http://localhost:18080/test-owner/test-repo/reports/abc123de" ]]
|
||||
[[ "$output" == "${GIT_PAGES_URL}/test-owner/test-repo/reports/abc123de" ]]
|
||||
}
|
||||
|
||||
@test "git-pages returns HTTP 500 → exit 1" {
|
||||
@@ -85,6 +87,7 @@ teardown() {
|
||||
{"code":500,"body":"internal error"}
|
||||
]'
|
||||
mock_start
|
||||
export GIT_PAGES_URL="http://localhost:${MOCK_PORT}"
|
||||
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"500"* ]]
|
||||
|
||||
@@ -23,7 +23,8 @@ teardown() {
|
||||
body=$(mock_get_request_body)
|
||||
[[ "$body" == *'"state":"pending"'* ]]
|
||||
[[ "$body" == *'"description":"Building project"'* ]]
|
||||
[[ "$body" == *'"target_url":"http://localhost:18080/test-owner/test-repo/actions/runs/42"'* ]]
|
||||
expected_url="${GITEA_API_URL}/test-owner/test-repo/actions/runs/42"
|
||||
[[ "$body" == *"\"target_url\":\"${expected_url}\""* ]]
|
||||
method=$(mock_get_request_method)
|
||||
[[ "$method" == "POST" ]]
|
||||
}
|
||||
@@ -44,7 +45,8 @@ teardown() {
|
||||
[ "$status" -eq 0 ]
|
||||
body=$(mock_get_request_body)
|
||||
[[ "$body" == *'"state":"failure"'* ]]
|
||||
[[ "$body" == *'"target_url":"http://localhost:18080/test-owner/test-repo/actions/runs/42"'* ]]
|
||||
expected_url="${GITEA_API_URL}/test-owner/test-repo/actions/runs/42"
|
||||
[[ "$body" == *"\"target_url\":\"${expected_url}\""* ]]
|
||||
}
|
||||
|
||||
@test "default key when not provided" {
|
||||
|
||||
Reference in New Issue
Block a user