From 31748e98cc1c5c18c59b7c953ae1c598616e33ab Mon Sep 17 00:00:00 2001 From: moilanik Date: Thu, 18 Jun 2026 06:19:58 +0300 Subject: [PATCH] uusi report logiikka --- .gitea/scripts/bats-coverage.sh | 32 +---- .gitea/workflows/example-bats-tests.yml | 38 ++---- .gitea/workflows/example-cucumber-tests.yml | 32 +---- scripts/ci-report.sh | 88 ++++++++++++ skills/consumer-pipelines/SKILL.md | 144 ++++++++------------ 5 files changed, 167 insertions(+), 167 deletions(-) create mode 100644 scripts/ci-report.sh diff --git a/.gitea/scripts/bats-coverage.sh b/.gitea/scripts/bats-coverage.sh index 43c76d9..2529621 100755 --- a/.gitea/scripts/bats-coverage.sh +++ b/.gitea/scripts/bats-coverage.sh @@ -1,37 +1,11 @@ #!/usr/bin/env bash set -euo pipefail -WORKSPACE_VOLUME="${1:-}" -REPORT_DIR="${2:-}" +REPORT_DIR="${1:-}" -[ -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" -fi - -if [ -n "$COVERAGE_SRC" ]; then +if [ -d coverage ]; 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 + cp -a coverage/. "$REPORT_DIR/coverage/" fi - -cat > "$REPORT_DIR/index.html" << EOF - -Bats report ${GITHUB_SHA:0:8} - -

Bats report ${GITHUB_SHA:0:8}

-' >> "$REPORT_DIR/index.html" diff --git a/.gitea/workflows/example-bats-tests.yml b/.gitea/workflows/example-bats-tests.yml index 9557334..8b78bc1 100644 --- a/.gitea/workflows/example-bats-tests.yml +++ b/.gitea/workflows/example-bats-tests.yml @@ -23,6 +23,8 @@ env: jobs: bats: runs-on: ubuntu-latest + container: + image: ${{ inputs.bats-image }} steps: - uses: actions/checkout@v4 - uses: actions/checkout@v4 @@ -31,34 +33,16 @@ 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 'cd /data && bashcov -- bats tests/' \ - > "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1 - BATS_EXIT=$? - bash .ci/.gitea/scripts/bats-coverage.sh bats-workspace "reports/${GITHUB_SHA:0:8}/bats" - docker volume rm bats-workspace > /dev/null 2>&1 - bash .ci/.gitea/scripts/bats-report.sh "reports/${GITHUB_SHA:0:8}/bats" - echo "BATS_EXIT=${BATS_EXIT}" >> "${GITHUB_ENV}" - exit ${BATS_EXIT} + mkdir -p reports/bats + bashcov -- bats tests/ > reports/bats/results.txt 2>&1 - - name: Publish bats reports - if: always() - run: bash .ci/scripts/publish-git-pages.sh bats - - - name: Report status + - name: Post-process reports 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 + bash .ci/.gitea/scripts/bats-coverage.sh reports/bats + 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 diff --git a/.gitea/workflows/example-cucumber-tests.yml b/.gitea/workflows/example-cucumber-tests.yml index 88fb68b..24acb58 100644 --- a/.gitea/workflows/example-cucumber-tests.yml +++ b/.gitea/workflows/example-cucumber-tests.yml @@ -33,36 +33,14 @@ jobs: path: .ci - name: Run cucumber tests - id: cucumber-tests shell: bash run: | - 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/report.json \ + --format html:reports/cucumber/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 diff --git a/scripts/ci-report.sh b/scripts/ci-report.sh new file mode 100644 index 0000000..1e833ed --- /dev/null +++ b/scripts/ci-report.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +set -euo pipefail + +DESCRIPTION="${1:-}" +CONTEXT="${2:-}" +SUITE="${3:-}" + +[ -n "$DESCRIPTION" ] || { echo "ERROR: description argument required" >&2; exit 1; } +[ -n "$CONTEXT" ] || { echo "ERROR: context argument required" >&2; exit 1; } +[ -n "$SUITE" ] || { echo "ERROR: suite argument required" >&2; exit 1; } + +REPORT_DIR="reports/${SUITE}" + +if [ ! -d "$REPORT_DIR" ]; then + echo "ERROR: $REPORT_DIR not found" >&2 + bash .ci/scripts/report-status.sh failure "$DESCRIPTION" "$CONTEXT" + exit 1 +fi + +cd "$REPORT_DIR" + +FILES=() +while IFS= read -r -d '' f; do + FILES+=("$(basename "$f")") +done < <(find . -maxdepth 1 -type f ! -name index.html -print0 2>/dev/null || true) + +SUBDIRS=() +while IFS= read -r -d '' d; do + name="${d#./}" + [ -f "$name/index.html" ] && SUBDIRS+=("$name") +done < <(find . -maxdepth 1 -type d ! -name . -print0 2>/dev/null || true) + +TOTAL=$(( ${#FILES[@]} + ${#SUBDIRS[@]} )) + +if [ "$TOTAL" -eq 0 ]; then + echo "ERROR: no reportable items in $REPORT_DIR" >&2 + bash .ci/scripts/report-status.sh failure "$DESCRIPTION" "$CONTEXT" + exit 1 +fi + +SHA8="${GITHUB_SHA:0:8}" + +humanize() { + local name="$1" + name="${name%.*}" + name="${name//-/ }" + name="${name//_/ }" + echo "${name^}" +} + +generate_index() { + local html + html='' + html+="$DESCRIPTION" + html+='' + html+="

$DESCRIPTION

' + printf '%s' "$html" > index.html +} + +cd - > /dev/null + +# Stage reports for backward-compatible publish +STAGED="reports/${SHA8}/${SUITE}" +mkdir -p "$STAGED" +cp -a "$REPORT_DIR/." "$STAGED/" + +if [ "$TOTAL" -eq 1 ]; then + if [ ${#FILES[@]} -eq 1 ]; then + ENTRY="${FILES[0]}" + else + ENTRY="${SUBDIRS[0]}/index.html" + fi + bash .ci/scripts/publish-git-pages.sh "$SUITE" + URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${SHA8}/${SUITE}/${ENTRY}" + bash .ci/scripts/report-status.sh success "$DESCRIPTION" "$CONTEXT" "" "$URL" +else + bash .ci/scripts/publish-git-pages.sh "$SUITE" + bash .ci/scripts/report-status.sh success "$DESCRIPTION" "$CONTEXT" "$SUITE" +fi + +rm -rf "$STAGED" diff --git a/skills/consumer-pipelines/SKILL.md b/skills/consumer-pipelines/SKILL.md index 92ba264..732b99c 100644 --- a/skills/consumer-pipelines/SKILL.md +++ b/skills/consumer-pipelines/SKILL.md @@ -79,16 +79,14 @@ Ei monoliittista `ci-tests.yml`. Jokainen testityyppi tai operaatio on oma `work ## 3. Exit-koodin käsittely -Jokainen testi kaappaa komentonsa exit-koodin eksplisiittisesti: +`set -e` on oletuksena käytössä Gitea Actions -stepeissä — ensimmäinen feilaava komento pysäyttää stepin +ja exit-koodi välittyy natiivisti. Ylimääräistä `EXIT=$?` + `echo >> GITHUB_ENV` -käärettä ei tarvita. ```yaml - name: Run tests shell: bash run: | > results.txt 2>&1 - EXIT=$? - echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" - exit ${EXIT} ``` **Miksi ei pipeä (`| tee`):** @@ -101,8 +99,8 @@ Jokainen testi kaappaa komentonsa exit-koodin eksplisiittisesti: > results.txt 2>&1 ``` -Ilman `EXIT=$?` + `exit ${EXIT}` komento voi feilata mutta job menee läpi vihreänä — `container:`-modessa -shellin käyttäytyminen vaihtelee. +`set -e` ei pelasta pipe-tilanteessa — `|` syö exit-koodin kuten ennenkin. Redirectillä exit-koodi +välittyy luonnollisesti. ## 4. Konttipolitiikka @@ -120,38 +118,15 @@ valmiin `ci-container-build-.yml`-pohjan jossa `workflow_dispatch`-tuki ### 4.1 CI-kontin ajaminen jobissa -CI-kontin voi ajaa joko `container:`-direktiivillä (kaikki stepit kontissa) -tai `docker run --rm`:llä stepin sisällä (checkout natiivisti). Molemmat tavat -toimivat. +Ainoa sallittu tapa on `container:`-direktiivi. `docker run` komennolla kontin +käynnistäminen stepin sisällä on anti-pattern. ```yaml -# Tapa A: container:-direktiivi jobs: : runs-on: ubuntu-latest container: image: ${{ inputs. }} - steps: - - uses: actions/checkout@v4 - - uses: actions/checkout@v4 - with: - repository: /gitea-ci-library - path: .ci - - name: Run - shell: bash - run: | - mkdir -p "reports/${GITHUB_SHA:0:8}/" - > "reports/${GITHUB_SHA:0:8}//results.txt" 2>&1 - EXIT=$? - echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" - exit ${EXIT} -``` - -```yaml -# Tapa B: docker run --rm stepin sisällä (kuten example-bats-tests.yml) -jobs: - : - runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/checkout@v4 @@ -162,86 +137,86 @@ jobs: - name: Run shell: bash run: | - docker volume create ws- - tar c . | docker run --rm -i -v ws-:/data alpine tar x -C /data - mkdir -p "reports/${GITHUB_SHA:0:8}/" - set +e - docker run --rm \ - -v ws-:/data \ - --entrypoint bash ${{ inputs. }} \ - -c 'cd /data && ' \ - > "reports/${GITHUB_SHA:0:8}//results.txt" 2>&1 - EXIT=$? - docker volume rm ws- > /dev/null 2>&1 - echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" - exit ${EXIT} + mkdir -p "reports/" + > "reports//results.txt" 2>&1 - - name: Publish reports - if: always() - run: bash .ci/scripts/publish-git-pages.sh - - - name: Report status + - name: Post-process reports if: always() run: | - if [ "${EXIT}" = "0" ]; then - bash .ci/scripts/report-status.sh success "" - else - bash .ci/scripts/report-status.sh failure "" - fi + + + - name: Report + if: always() + run: bash .ci/scripts/ci-report.sh "" ``` -**Malli:** `example-bats-tests.yml` (tapa B). +Jos testi tuottaa raportteja suoraan ilman jälkikäsittelyä, Post-process-steppiä ei tarvita. +Jos jälkikäsittely on tarpeen (coverage-extraktio, HTML-generointi raa'asta outputista), +se tehdään omassa stepissä `if: always()` — katso tarkemmin [Raporttitasot](#5-raporttitasot). + +**Mallit:** +- `example-cucumber-tests.yml` — ei post-processia +- `example-bats-tests.yml` — post-process coverage + report ## 5. Raporttitasot -Testi tuottaa raportin `reports/${GITHUB_SHA:0:8}//`-hakemistoon. `publish-git-pages.sh` julkaisee sen, -`report-status.sh` linkittää commit-statusin siihen. Molemmat `if: always()`. +Testi tuottaa raportin `reports//`-hakemistoon. Yksi `ci-report.sh`-kutsu hoitaa sekä +julkaisun että commit-statuksen — erillistä Publish + Report Status -kaksivaiheisuutta ei tarvita. -### Taso 1: Pelkkä teksti +### Taso 1: Ei jälkikäsittelyä -Kun testi tuottaa vain stdout/stderr — tallennetaan `results.txt`: +Kun testi tuottaa raportit suoraan (kuten `pytest --html` tai `cucumber-js --format html`): ```yaml - name: Run tests shell: bash run: | - mkdir -p "reports/${GITHUB_SHA:0:8}/" - > "reports/${GITHUB_SHA:0:8}//results.txt" 2>&1 - EXIT=$? - echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" - exit ${EXIT} + mkdir -p "reports/" + -- name: Publish reports +- name: Report if: always() - shell: bash - run: bash .ci/scripts/publish-git-pages.sh + run: bash .ci/scripts/ci-report.sh "" +``` -- name: Report status - if: always() +### Taso 2: Jälkikäsittely tarvitaan + +Kun testi tuottaa raakadataa (stdout, coverage-tiedostot) joka pitää muuntaa tai siirtää +`reports//`-hakemistoon, käytetään Post-process-steppiä: + +```yaml +- name: Run tests shell: bash run: | - if [ "${EXIT}" = "0" ]; then - bash .ci/scripts/report-status.sh success "" - else - bash .ci/scripts/report-status.sh failure "" - fi + mkdir -p "reports/" + > "reports//results.txt" 2>&1 + +- name: Post-process reports + if: always() + run: | + + + +- name: Report + if: always() + run: bash .ci/scripts/ci-report.sh "" ``` -### Taso 2: HTML-raportti +**Malli:** `example-bats-tests.yml`. -Kun testi tuottaa strukturoitua dataa (JUnit XML, coverage, tms.) — generoidaan HTML ja `index.html`: +### Monta raportoitavaa tiedostoa + +Kun `reports//`-hakemistossa on useita tiedostoja tai alihakemistoja, +`ci-report.sh` generoi automaattisesti `reports//index.html` jos hakemistossa +on enemmän kuin yksi raportoitava item. ``` -reports/// -├── index.html ← generoitu: linkit alla oleviin +reports// ├── results.txt ← testin stdout ├── junit.xml ← testin JUnit XML -output └── junit.html ← generoitu HTML (xsltproc, tms.) ``` -`index.html` linkittää kaikkiin raporttitiedostoihin. Selain avaa sen ja navigoi sieltä -yksittäisiin raportteihin. - ## 6. Nimeäminen Tiedostonimet `.gitea/workflows/`-kansiossa noudattavat yhtenäistä rakennetta, jotta @@ -457,8 +432,9 @@ Gitean Settings → Branches → Add Rule: | Skripti | Käyttötarkoitus | |---|---| -| `report-status.sh` | POSTaa commit-statuksen linkillä | -| `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin | +| `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 "" ` | +| `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ä) | --- @@ -499,7 +475,7 @@ Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä ### Commit-status vain raporttilinkille (ADR 0007) -`report-status.sh`-skriptiä käytetään VAIN kun on raportti linkitettäväksi. +`ci-report.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