diff --git a/.gitea/scripts/bats-coverage.sh b/.gitea/scripts/bats-coverage.sh
new file mode 100755
index 0000000..43c76d9
--- /dev/null
+++ b/.gitea/scripts/bats-coverage.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+WORKSPACE_VOLUME="${1:-}"
+REPORT_DIR="${2:-}"
+
+[ -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
+ 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
+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/scripts/bats-report.sh b/.gitea/scripts/bats-report.sh
new file mode 100644
index 0000000..e10e178
--- /dev/null
+++ b/.gitea/scripts/bats-report.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+REPORT_DIR="${1:-reports/bats}"
+INPUT="$REPORT_DIR/results.txt"
+OUTPUT="$REPORT_DIR/test-report.html"
+
+[ -f "$INPUT" ] || { echo "ERROR: $INPUT not found" >&2; exit 1; }
+
+TOTAL=$(grep -cE '^(ok|not ok) ' "$INPUT" 2>/dev/null) || TOTAL=0
+PASS=$(grep -c '^ok ' "$INPUT" 2>/dev/null) || PASS=0
+FAIL=$((TOTAL - PASS))
+
+{
+ echo ''
+ echo "Bats test report ${GITHUB_SHA:0:8}"
+ echo ''
+ echo "Bats test report ${GITHUB_SHA:0:8}
"
+ echo "Total: ${TOTAL} | Passed: ${PASS} | Failed: ${FAIL}
"
+ echo '| # | Status | Test |
'
+
+ while IFS=' ' read -r status num rest; do
+ case "$status" in
+ ok) echo "| ${num} | PASS | ${rest} |
" ;;
+ not) echo "| ${num} | FAIL | ${rest} |
" ;;
+ esac
+ done < <(grep -E '^(ok|not ok) ' "$INPUT")
+
+ echo '
'
+} > "$OUTPUT"
+
+echo "$OUTPUT"
diff --git a/.gitea/scripts/generate-report-index.sh b/.gitea/scripts/generate-report-index.sh
new file mode 100644
index 0000000..5e63f7a
--- /dev/null
+++ b/.gitea/scripts/generate-report-index.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SHA8="${GITHUB_SHA:0:8}"
+REPORTS_DIR="reports/${SHA8}"
+
+mkdir -p "${REPORTS_DIR}"
+
+BATS_PASS=$(grep -c 'ok' "${REPORTS_DIR}/bats/results.txt" 2>/dev/null || echo 0)
+BATS_FAIL=$(grep -c 'not ok' "${REPORTS_DIR}/bats/results.txt" 2>/dev/null || echo 0)
+CUCUMBER_PASS=$(jq '.summary.passed // 0' "${REPORTS_DIR}/cucumber/report.json" 2>/dev/null || echo 0)
+CUCUMBER_FAIL=$(jq '.summary.failed // 0' "${REPORTS_DIR}/cucumber/report.json" 2>/dev/null || echo 0)
+
+{
+ echo ""
+ echo "CI report ${SHA8}"
+ echo ""
+ echo "CI report ${SHA8}
"
+ echo "Commit: ${GITHUB_SHA}
Branch: ${GITHUB_REF_NAME}
Run: ${GITHUB_RUN_ID}
"
+ echo "| Suite | Passed | Failed | Report |
"
+ echo "| Bats | ${BATS_PASS} | ${BATS_FAIL} | "
+ echo "results.txt"
+ echo " | junit.xml |
"
+ echo "| Cucumber | ${CUCUMBER_PASS} | ${CUCUMBER_FAIL} | "
+ echo "report"
+ echo " | json |
"
+ echo "
"
+} > "${REPORTS_DIR}/index.html"
diff --git a/.gitea/workflows/build-feature.yml b/.gitea/workflows/build-feature.yml
index 4a6ce87..b6c9b9a 100644
--- a/.gitea/workflows/build-feature.yml
+++ b/.gitea/workflows/build-feature.yml
@@ -2,24 +2,42 @@ name: Build Feature
on:
workflow_call:
inputs:
+ env_json:
+ required: true
+ type: string
bats-image:
- required: false
+ required: true
type: string
- default: bats/bats:latest
cucumber-node-image:
- required: false
+ required: true
type: string
- default: node:22
+ secrets:
+ GITEA_TOKEN:
+ required: true
+ GIT_PAGES_PUBLISH_TOKEN:
+ required: true
+
+env:
+ GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
+ GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }}
+ GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
+ GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
jobs:
+ validate:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/checkout@v4
+ with:
+ repository: niko/gitea-ci-library
+ path: .ci
+
+ - name: Validate CI config
+ run: bash .ci/scripts/ci-validate.sh
+
bats:
runs-on: ubuntu-latest
- env:
- GITEA_API_URL: https://gitea.app.keskikuja.site
- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
- PAGES_HOST: ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_URL: https://ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
@@ -35,56 +53,37 @@ jobs:
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 \
+ docker run --rm \
+ -v bats-workspace:/data \
--entrypoint bash ${{ inputs.bats-image }} \
- -c 'apk add -q lsof python3 jq curl && \
- cd /data && bats tests/ ' \
+ -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
- {
- echo "Bats tests
"
- for f in reports/${GITHUB_SHA:0:8}/bats/*; do
- b="$(basename $f)"
- [ "$b" = "index.html" ] && continue
- echo "- $b
"
- done
- echo "
"
- } > "reports/${GITHUB_SHA:0:8}/bats/index.html"
+ bash .ci/.gitea/scripts/bats-report.sh "reports/${GITHUB_SHA:0:8}/bats"
echo "BATS_EXIT=${BATS_EXIT}" >> "${GITHUB_ENV}"
exit ${BATS_EXIT}
- name: Publish bats reports
if: always()
- shell: bash
- run: |
- bash .ci/scripts/publish-git-pages.sh "reports/${GITHUB_SHA:0:8}/bats"
+ run: bash .ci/scripts/publish-git-pages.sh bats
- name: Set bats commit status
if: always()
- shell: bash
run: |
if [ "${BATS_EXIT}" = "0" ]; then
- STATUS="success"
- DESC="Bats tests"
- URL="https://${PAGES_HOST}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/bats/"
+ bash .ci/scripts/report-status.sh success "Bats tests" ci-bats bats
else
- STATUS="failure"
- DESC="Bats tests FAILED"
- URL="https://${PAGES_HOST}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/bats/"
+ bash .ci/scripts/report-status.sh failure "Bats tests FAILED" ci-bats bats
fi
- bash .ci/scripts/report-status.sh "$STATUS" "$DESC" "$URL" ci-bats
cucumber:
runs-on: ubuntu-latest
container:
image: ${{ inputs.cucumber-node-image }}
- env:
- GITEA_API_URL: https://gitea.app.keskikuja.site
- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
- PAGES_HOST: ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_URL: https://ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
@@ -124,40 +123,25 @@ jobs:
- name: Publish cucumber reports
if: always()
- shell: bash
run: |
if [ "${TOOL_OK}" = "true" ]; then
- bash .ci/scripts/publish-git-pages.sh "reports/${GITHUB_SHA:0:8}/cucumber"
+ bash .ci/scripts/publish-git-pages.sh cucumber
fi
- name: Set cucumber commit status
if: always()
- shell: bash
run: |
if [ "${TOOL_OK}" != "true" ]; then
- STATUS="failure"
- DESC="Cucumber tool unavailable"
- URL="https://gitea.app.keskikuja.site/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+ bash .ci/scripts/report-status.sh failure "Cucumber tool unavailable" ci-cucumber
elif [ "${CUCUMBER_EXIT}" = "0" ]; then
- STATUS="success"
- DESC="Cucumber tests passed"
- URL="https://${PAGES_HOST}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/cucumber/"
+ bash .ci/scripts/report-status.sh success "Cucumber tests passed" ci-cucumber cucumber
else
- STATUS="failure"
- DESC="Cucumber tests FAILED"
- URL="https://${PAGES_HOST}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/cucumber/"
+ bash .ci/scripts/report-status.sh failure "Cucumber tests FAILED" ci-cucumber cucumber
fi
- bash .ci/scripts/report-status.sh "$STATUS" "$DESC" "$URL" ci-cucumber
build:
runs-on: ubuntu-latest
needs: [bats, cucumber]
- env:
- GITEA_API_URL: https://gitea.app.keskikuja.site
- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
- PAGES_HOST: ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_URL: https://ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
@@ -166,35 +150,7 @@ jobs:
path: .ci
- name: Generate report index
- shell: bash
- run: |
- SHA8="${GITHUB_SHA:0:8}"
- mkdir -p "reports/${SHA8}"
- BATS_PASS=$(grep -c 'ok' "reports/${SHA8}/bats/results.txt" 2>/dev/null || echo 0)
- BATS_FAIL=$(grep -c 'not ok' "reports/${SHA8}/bats/results.txt" 2>/dev/null || echo 0)
- CUCUMBER_PASS=$(jq '.summary.passed // 0' "reports/${SHA8}/cucumber/report.json" 2>/dev/null || echo 0)
- CUCUMBER_FAIL=$(jq '.summary.failed // 0' "reports/${SHA8}/cucumber/report.json" 2>/dev/null || echo 0)
- {
- echo ""
- echo "CI report ${SHA8}"
- echo ""
- echo "CI report ${SHA8}
"
- echo "Commit: ${GITHUB_SHA}
Branch: ${GITHUB_REF_NAME}
Run: ${GITHUB_RUN_ID}
"
- echo "| Suite | Passed | Failed | Report |
"
- echo "| Bats | ${BATS_PASS} | ${BATS_FAIL} | "
- echo "results.txt"
- echo " | junit.xml |
"
- echo "| Cucumber | ${CUCUMBER_PASS} | ${CUCUMBER_FAIL} | "
- echo "report"
- echo " | json |
"
- echo "
"
- } > "reports/${SHA8}/index.html"
+ run: bash .ci/.gitea/scripts/generate-report-index.sh
- name: Set build commit status
- run: |
- bash .ci/scripts/report-status.sh success \
- "Build complete" \
- "https://gitea.app.keskikuja.site/niko/gitea-ci-library/actions/runs/${GITHUB_RUN_ID}" \
- ci-build
+ run: bash .ci/scripts/report-status.sh success "Build complete" ci-build
diff --git a/.gitea/workflows/ci-engine.yml b/.gitea/workflows/ci-engine.yml
deleted file mode 100644
index ba66fb6..0000000
--- a/.gitea/workflows/ci-engine.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: CI Engine
-on:
- workflow_call:
- inputs:
- config-file:
- required: true
- type: string
- secrets:
- GITEA_TOKEN:
- required: true
- GIT_PAGES_PUBLISH_TOKEN:
- required: true
-
-jobs:
- publish:
- runs-on: ubuntu-latest
- env:
- GITEA_API_URL: https://gitea.app.keskikuja.site
- PAGES_HOST: ci-reports.helm-dev.keskikuja.site
- GIT_PAGES_PUBLISH_URL: https://ci-reports.helm-dev.keskikuja.site
- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
- GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }}
- steps:
- - uses: actions/checkout@v4
-
- - name: Publish reports
- run: bash scripts/publish.sh reports
diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
index 095b40c..2bd2a01 100644
--- a/.gitea/workflows/ci.yml
+++ b/.gitea/workflows/ci.yml
@@ -5,16 +5,27 @@ on:
workflow_dispatch:
jobs:
+ load-config:
+ uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@main
+ with:
+ config_path: .gitea/workflows/gitea-env.conf
+
feature:
if: github.ref != 'refs/heads/main'
+ needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/build-feature.yml@main
secrets: inherit
with:
+ env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: bats/bats:latest
+ cucumber-node-image: node:22
main:
if: github.ref == 'refs/heads/main'
+ needs: [load-config]
uses: niko/gitea-ci-library/.gitea/workflows/build-feature.yml@main
secrets: inherit
with:
+ env_json: ${{ needs.load-config.outputs.env_json }}
bats-image: bats/bats:latest
+ cucumber-node-image: node:22
diff --git a/.gitea/workflows/config-provider.yml b/.gitea/workflows/config-provider.yml
new file mode 100644
index 0000000..af4ad80
--- /dev/null
+++ b/.gitea/workflows/config-provider.yml
@@ -0,0 +1,31 @@
+name: Config Provider Library
+on:
+ workflow_call:
+ inputs:
+ config_path:
+ required: true
+ type: string
+ outputs:
+ env_json:
+ value: ${{ jobs.parse-config.outputs.json_data }}
+
+jobs:
+ parse-config:
+ runs-on: ubuntu-latest
+ outputs:
+ json_data: ${{ steps.convert.outputs.JSON_OUT }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - id: convert
+ run: |
+ JSON_STRING=$(jq -R -s '
+ split("\n")
+ | map(select(length > 0 and (startswith("#") | not)))
+ | map(split("="))
+ | map({(.[0]): .[1]})
+ | add
+ ' "${{ inputs.config_path }}")
+
+ CLEAN_JSON=$(echo "$JSON_STRING" | jq -c .)
+ echo "JSON_OUT=$CLEAN_JSON" >> "$GITHUB_OUTPUT"
diff --git a/.gitea/workflows/gitea-env.conf b/.gitea/workflows/gitea-env.conf
new file mode 100644
index 0000000..72dba50
--- /dev/null
+++ b/.gitea/workflows/gitea-env.conf
@@ -0,0 +1,2 @@
+GITEA_API_URL=https://gitea.app.keskikuja.site
+GIT_PAGES_URL=https://ci-reports.helm-dev.keskikuja.site
diff --git a/.simplecov b/.simplecov
new file mode 100644
index 0000000..4ee20ec
--- /dev/null
+++ b/.simplecov
@@ -0,0 +1,5 @@
+SimpleCov.start do
+ add_filter '/tests/'
+ add_filter '/node_modules/'
+ add_filter '/git-pages/'
+end
diff --git a/README.md b/README.md
index e6df085..644a580 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,14 @@ Consumer kutsuu provideria `uses:`-viittauksella. Ei discoveryä — polku kovak
### Polun muodostus
```
-{owner}/{repo}/.gitea/workflows/ci-engine.yml@{ref}
+{owner}/{repo}/.gitea/workflows/build-feature.yml@{ref}
```
| Osa | Mistä | Esimerkki (homelab) |
|-----|-------|---------------------|
| `owner` | Repopolun ensimmäinen osa — **käyttäjänimi tai org** | `niko` |
| `repo` | Repon nimi | `gitea-ci-library` |
-| tiedosto | Providerin workflow | `.gitea/workflows/ci-engine.yml` |
+| tiedosto | Providerin workflow | `.gitea/workflows/build-feature.yml` |
| `@ref` | Tag tai branch provider-repossa | `@v1` (tuotanto) |
**Owner ei ole org-pakotettu.** Homelabissa ei välttämättä ole organisaatiotasoa — silloin owner on
@@ -36,7 +36,7 @@ Consumerin `ci.yml`:
```yaml
jobs:
call-engine:
- uses: niko/gitea-ci-library/.gitea/workflows/ci-engine.yml@v1
+ uses: niko/gitea-ci-library/.gitea/workflows/build-feature.yml@v1
secrets: inherit
```
@@ -56,7 +56,7 @@ niko/gitea-ci-library ←→ niko/gitea-ci-library (mirror tai push-mirror)
niko/gitea-ci-library/... niko/gitea-ci-library/...
```
-Mirror pitää `ci-engine.yml`:n ja tagit (`v1`) saatavilla kulloisellakin palvelimella. Tämä korvaa
+Mirror pitää `build-feature.yml`:n ja tagit (`v1`) saatavilla kulloisellakin palvelimella. Tämä korvaa
provider-repon checkout-hackit workflowissa — binding hoituu Gitean natiivilla `uses:`-mekanismilla.
Periaatteet: [tmp/data-flow-design.md](tmp/data-flow-design.md)
@@ -180,6 +180,52 @@ Tarkista ennen ensimmäistä ajoa: [Provider-binding](#provider-binding--miten-c
Lisätietoa runnerin toiminnasta, konteista ja DinD:stä: [docs/runner.md](docs/runner.md)
+## Vaaditut secretit ja muuttujat
+
+Consumer-repossa on oltava seuraavat asetukset:
+
+### Repo Actions Secrets (`{repo} → Settings → Actions → Secrets`)
+
+| Secret | Kuvaus |
+|--------|--------|
+| `GIT_PAGES_PUBLISH_TOKEN` | Git-pages-palvelimen BasicAuth-token. Nimi on lukittu — tämä tarkka nimi vaaditaan. |
+
+`GITEA_TOKEN` on Gitean sisäinen secret (`secrets.GITEA_TOKEN`), joka on automauttisesti saatavilla — sitä ei tarvitse erikseen luoda.
+
+### Config-tiedosto (`.gitea/workflows/gitea-env.conf`)
+
+Tiedoston **nimi ja polku on lukittu**: `.gitea/workflows/gitea-env.conf` consumer-repon juuressa.
+Tämän tiedoston perusteella `config-provider.yml` tuottaa `env_json`-outputin, joka välitetään
+workflowille.
+
+Tiedosto on `key=value`-muotoinen (kuten `.env`). Kommentit ja tyhjät rivit sallittuja.
+
+**Vaaditut avaimet:**
+
+| Avain | Kuvaus |
+|-------|--------|
+| `GITEA_API_URL` | Gitea-palvelimen base URL (esim. `https://gitea.app.example.com`) |
+| `GIT_PAGES_URL` | Git-pages-palvelimen URL ilman trailing slash (esim. `https://ci-reports.example.com`) |
+
+**Validointisäännöt:**
+- Arvot eivät saa olla tyhjiä
+- Jos avaimen nimessä on `URL`, arvon on alettava `http://` tai `https://`
+- Tiedoston on oltava olemassa (muuten job keskeytyy)
+
+Esimerkki:
+```
+GITEA_API_URL=https://gitea.app.example.com
+GIT_PAGES_URL=https://ci-reports.example.com
+```
+
+### Validaatio
+
+Jokaisen jobin alussa `ci-validate.sh` tarkistaa:
+- `.gitea/workflows/gitea-env.conf` on olemassa ja sen arvot ovat validit
+- `GITEA_TOKEN` ja `GIT_PAGES_PUBLISH_TOKEN` on asetettu
+
+Jos validointi epäonnistuu, job keskeytyy exit-koodilla 1 ja Gitean commit-status näyttää epäonnistumisen linkkinä lokiin.
+
### Muuta
| Muuttuja | Kuvaus |
diff --git a/config.yaml b/config.yaml
deleted file mode 100644
index 56d23f8..0000000
--- a/config.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-GITEA_API_URL: https://gitea.app.keskikuja.site
-PAGES_HOST: ci-reports.helm-dev.keskikuja.site
-GIT_PAGES_PUBLISH_URL: https://ci-reports.helm-dev.keskikuja.site
diff --git a/docs/adr/0005-provider-consumer.md b/docs/adr/0005-provider-consumer.md
index b898ae1..5475a70 100644
--- a/docs/adr/0005-provider-consumer.md
+++ b/docs/adr/0005-provider-consumer.md
@@ -3,7 +3,7 @@
## Päätös
Provider (gitea-ci-library) ja Consumer (mikropalveluprojekti) erotetaan
-selkeällä rajapinnalla: `.gitea/workflows/ci-engine.yml` on ainoa pinta,
+selkeällä rajapinnalla: `.gitea/workflows/build-feature.yml` on ainoa pinta,
jota consumer kutsuu.
Kaikki muu providerin koodi (scriptit, git-pages-helmi, retention) on
@@ -18,7 +18,7 @@ riippuvuutta.
# .gitea/workflows/ci.yml — consumerin repo
jobs:
ci:
- uses: niko/gitea-ci-library/.gitea/workflows/ci-engine.yml@v1
+ uses: niko/gitea-ci-library/.gitea/workflows/build-feature.yml@v1
secrets: inherit
```
@@ -44,13 +44,13 @@ Consumer:
- Git-pages Helm-chartti
- Retention sidecar
- Scriptit ja työkalut (toteutus avoin, uudelleenkirjoitettavissa)
-- Kaikki paitsi `ci-engine.yml` on sisäistä toteutusta ja voi muuttua
+- Kaikki paitsi `build-feature.yml` on sisäistä toteutusta ja voi muuttua
ilman versiopäivitystä
## Periaatteet
-1. `ci-engine.yml` on **lukittu rajapinta**. Consumer kutsuu tätä, ei
- koskaan providerin scriptejä suoraan. `ci-engine.yml` voi muuttua vain
+1. `build-feature.yml` on **lukittu rajapinta**. Consumer kutsuu tätä, ei
+ koskaan providerin scriptejä suoraan. `build-feature.yml` voi muuttua vain
version vaihtuessa.
2. Consumer omistaa pipeline-logiikan. Provider ei tiedä mitä testejä
ajetaan, missä järjestyksessä tai millä työkaluilla.
diff --git a/docs/adr/0006-directory-ownership.md b/docs/adr/0006-directory-ownership.md
new file mode 100644
index 0000000..5df4b4b
--- /dev/null
+++ b/docs/adr/0006-directory-ownership.md
@@ -0,0 +1,70 @@
+# 6. Directory ownership — provider vs consumer
+
+## Päätös
+
+Provider-repossa (`gitea-ci-library`) kansioiden omistajuus on seuraava:
+
+| Kansio / Tiedosto | Omistaja | Tyyppi |
+|-------------------|----------|--------|
+| `.gitea/workflows/` | Sekoitettu | Providerin reusable workflowt + consumerin pipeline |
+| `.gitea/workflows/gitea-env.conf` | Consumer | KEY=VALUE config |
+| `.gitea/scripts/` | Consumer | Consumer-skriptit |
+| `scripts/` | Provider | Providerin sisäiset työkalut |
+
+## Reusable workflowt — sijaintipakko
+
+Gitea Actions vaatii, että `uses:`-direktiivillä kutsuttavat workflowt
+ovat muodossa `{owner}/{repo}/.gitea/workflows/{file}@{ref}`.
+
+**Tämä on Gitea Actionsin asettama tekninen rajoite.** Toimivia
+polkuja ovat vain:
+
+```
+# ✅ kelpaa
+uses: org/repo/.gitea/workflows/file.yml@branch
+
+# ❌ eivät kelpaa
+uses: org/repo/workflows/file.yml@branch
+uses: org/repo/.gitea/workflows/path/file.yml@branch
+uses: org/repo/scripts/workflow.yml@branch
+```
+
+Tästä syystä providerin reusable workflowt (`config-provider.yml`,
+`build-feature.yml`) ovat samassa `.gitea/workflows/`-kansiossa consumerin
+pipeline-tiedostojen (`ci.yml`) kanssa.
+
+Erottelu on nimessä ja dokumentaatiossa, ei kansiorakenteessa:
+- `config-provider.yml`, `build-feature.yml` — providerin tarjoamia
+- `ci.yml` — consumerin omistamia
+
+## Providerin `scripts/` (juuressa)
+
+Providerin sisäiset työkalut. Consumer ei koskaan kutsu näitä suoraan —
+vain providerin workflowt kutsuvat tupla checkoutin kautta:
+`.ci/scripts/publish-git-pages.sh`.
+
+Consumerilla ei ole suoraa polkua näihin tiedostoihin ilman providerin
+workflowa.
+
+## Consumerin `.gitea/scripts/`
+
+Consumerin omat skriptit, osana consumerin pipeline-logiikkaa.
+Kutsutaan consumerin workflowista ilman tupla checkouttia:
+`.gitea/scripts/bats-report.sh`.
+
+## Consumerin `.gitea/workflows/gitea-env.conf`
+
+Consumerin konfiguraatiotiedosto. Providerin `config-provider.yml`
+lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön.
+
+## Vaikutukset
+
+- Provider voi muuttaa `scripts/` ja `config-provider.yml` sisältöä
+ ilman consumerin hyväksyntää (versiovaihdon yhteydessä)
+- Consumer voi muuttaa `.gitea/workflows/ci.yml`,
+ `.gitea/workflows/build-feature.yml` ja `.gitea/scripts/` sisältöä
+ ilman providerin muutoksia
+- Providerin workflowt käyttävät `.ci/scripts/...` -polkua (tupla checkout)
+- Consumerin workflowt käyttävät `.gitea/scripts/...` -polkua (natiivi checkout)
+- Sekä provider että consumer jakavat `.gitea/workflows/` — tämä on
+ Gitea Actionsin tekninen rajoite, ei suunnittelupäätös
diff --git a/docs/ai-context.md b/docs/ai-context.md
index c5b2a0e..ebf5faf 100644
--- a/docs/ai-context.md
+++ b/docs/ai-context.md
@@ -17,8 +17,7 @@ Tämä repo on käytännössä monorepo, jossa on kaksi itsenäistä osaa:
### 1. Juuri (`gitea-ci-library`)
Provider-kirjasto: reusable workflowt, scriptit, ADRt, dokumentaatio.
-Rajapinta: `.gitea/workflows/ci-engine.yml` — ainoa pinta, jota consumerit
-kutsuvat.
+Consumer kutsuu `build-feature.yml`-workflowa `uses:`-direktiivillä.
### 2. `git-pages/` — oma kokonaisuus
Helm-chartti Codeberg git-pagesille. Täysin itsenäinen — oma dokumentaatio,
@@ -32,7 +31,7 @@ Ohut ja yksiselitteinen:
```
scripts/publish-git-pages.sh
- → PATCH tar osoitteeseen PAGES_HOST
+ → PATCH tar osoitteeseen GIT_PAGES_URL
→ palauttaa BASE URL:n
git-pages tarjoaa:
@@ -46,9 +45,9 @@ blob-arkkitehtuuri). Git-pages ei tiedä workflowista, scripteistä tai
provider-logiikasta.
## Architecture (POC-tila)
-- **Provider & Consumer -malli**: `ci-engine.yml` on lukittu rajapinta.
+- **Provider & Consumer -malli**: `build-feature.yml` on lukittu rajapinta.
ADR 0005.
-- **Raporttien hostaus**: git-pages Helm-chartilla (`git-pages/`).
+- **Raporttien hostaus**: git-pages Helm-chartilla (`git-pages/`), `GIT_PAGES_URL` määrittää perusosoitteen.
- **Retention**: sidecar samassa podissa, HTTP API localhost:3000,
Gitea API branch-check.
- **Commit-status**: Gitea Actions näyttää automaattisesti. API vain
@@ -59,19 +58,19 @@ provider-logiikasta.
| Path | Purpose |
|---|---|
-| `.gitea/workflows/` | `ci-engine.yml` (ainoa reusable workflow POC-vaiheessa) |
+| `.gitea/workflows/` | Reusable workflowt (`build-feature.yml`, `config-provider.yml`) |
| `scripts/` | `publish-git-pages.sh`, `report-status.sh`, `dispatch-workflow.sh` |
| **`git-pages/`** | **Oma kokonaisuus: Helm-chartti + docs + retention** |
| `docs/` | Root-tason arkkitehtuuri, ADRt (0001–0005) |
| `docs/adr/` | Architecture Decision Records |
| `tests/` | Bats-testit skripteille |
-| `.gitea/workflows/ci.yml` | Dogfood — kutsuu `ci-engine.yml`:a |
+| `.gitea/workflows/ci.yml` | Dogfood — kutsuu `build-feature.yml`:a |
**Tarkemmat git-pages-asiat:** `git-pages/docs/` (implementation-notes,
architecture, design-rationale, secrets, tech-stack).
## Key Technical Decisions
-- **Provider & Consumer**: `ci-engine.yml` lukittu rajapinta, muu koodi
+- **Provider & Consumer**: `build-feature.yml` lukittu rajapinta, muu koodi
vapaasti muutettavissa
- **Vain Gitea, vain reusable workflowt**: ei custom actioneita, ei
multi-platform
diff --git a/docs/architecture.md b/docs/architecture.md
index 3e6bad3..fa8f619 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -1,9 +1,6 @@
# Architecture — Gitea Actions CI -kirjasto
-> ⚠️ POC-vaihe. Tämä dokumentti kuvaa suunniteltua arkkitehtuuria. Toteutus
-> on edelleen kehitysvaiheessa (`ci-engine.yml` on ainoa reusable workflow).
-> Odota uudelleenkirjoitusta ennen kuin luotat tähän dokumenttiin.
->
+> ⚠️ POC-vaihe. Tämä dokumentti kuvaa suunniteltua arkkitehtuuria.
> Normatiivinen lähde: ADR 0004, ADR 0005, `docs/design-rationale.md`.
---
@@ -21,7 +18,7 @@ Kirjasto on Gitea-spesifi. Raportit hallinnoidaan git-pages Helm-chartilla
| Rooli | Kuvaus |
|-------|--------|
-| **Provider** | `gitea-ci-library` — tarjoaa `ci-engine.yml`:n (lukittu rajapinta) sekä scriptit |
+| **Provider** | `gitea-ci-library` — tarjoaa `build-feature.yml` (lukittu rajapinta) sekä scriptit |
| **Consumer** | Mikropalveluprojekti — kutsuu `uses:`-direktiivillä, omistaa pipeline-logiikan |
Tarkemmin: ADR 0005.
@@ -30,10 +27,10 @@ Tarkemmin: ADR 0005.
| Komponentti | Tila |
|-------------|------|
-| `ci-engine.yml` | Toimii POC-tasolla. Ainoa reusable workflow. |
+| `build-feature.yml` | Toimii. Ainoa reusable workflow. |
| `publish-git-pages.sh` | Toimii. PATCH tar git-pagesiin. |
| `report-status.sh` | Toimii. POSTaa commit-status (vain custom-linkkiin). |
-| `dispatch-workflow.sh` | Suunniteltu, ei toteutettu POCissa. |
+| `dispatch-workflow.sh` | Toimii. Dispatchee workflown ja pollaa valmistumista. |
| `git-pages/` | Helm-chartti raporttien hostaukseen. Oma kokonaisuus, tarkemmin: `git-pages/docs/`. |
## Ulkoiset palvelut
@@ -43,10 +40,9 @@ Tarkemmin: ADR 0005.
| **Gitea REST API** | Commit-status, workflow-dispatch, run-pollaus |
| **Gitea Packages** | Docker-imagen säilytys |
| **git-pages** | Raporttien hostaus |
-| **SonarQube** | Koodin laadun analyysi (suunniteltu) |
## Arkkitehtuuriset rajoitteet
-- `ci-engine.yml` on ainoa consumerin kutsuma rajapinta (ADR 0005)
+- `build-feature.yml` on ainoa consumerin kutsuma rajapinta (ADR 0005)
- Gitea Actionsin natiivi commit-status on ensisijainen (ADR 0004)
- Raportit ovat julkisia URL:lla (osoite tunnettava)
diff --git a/docs/ci-pipeline-practices.md b/docs/ci-pipeline-practices.md
index 5c05661..0115681 100644
--- a/docs/ci-pipeline-practices.md
+++ b/docs/ci-pipeline-practices.md
@@ -50,6 +50,53 @@ Käytäntö:
- Jos `docker run` tarvitsee env-arvoja, välitä ne eksplisiittisesti `-e VAR`-lipulla
- `GITHUB_ENV` on validi tapa välittää arvoja stepien välille samassa jobissa, mutta ei leviä `docker run`-kontteihin ilman `-e`-lippua
+### Cross-job config propagation (validated 2026-06-13)
+
+Config-arvojen vienti kaikkiin jobeihin ilman toistoa vaatii kahden
+mekanismin ketjuttamista:
+
+1. **`needs` + `with:`** — `jobs..with.` tukee
+ `needs`-kontekstia. Tämä mahdollistaa sen, että yhden jobin outputit
+ voidaan välittää toiselle reusable workflowille inputeina.
+2. **Workflow `env:`** — ainoa natiivi mekanismi, joka tekee arvoista
+ näkyviä kaikissa jobeissa automaattisesti (POC validioitu).
+
+Ketju toimii näin:
+
+```
+gitea-env.conf → config-provider.yml → env_json (yksi JSON-string)
+ (1) (2)
+ ↓
+ ci.yml with: env_json
+ ${{ needs.load-config.outputs.env_json }}
+ (3)
+ ↓
+ build-feature.yml workflow env:
+ GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }}
+ (4)
+ ↓
+ kaikki jobit → $GITEA_API_URL, $GIT_PAGES_URL jne.
+ (5)
+```
+
+Vaiheet:
+1. Consumer määrittelee arvot `gitea-env.conf`:ssä (KEY=VALUE)
+2. `config-provider.yml` lukee confin ja tuottaa yhden JSON-stringin outputina
+3. `ci.yml` välittää JSONin `needs` + `with:` -ketjulla
+4. `build-feature.yml` purkaa arvot workflow `env:`-tasolle `fromJson()`:lla
+5. Kaikki jobit käyttävät valmiita env-muuttujia (`$GIT_PAGES_URL` jne.)
+
+Avainkomponentit:
+- **config-provider.yml** — reusable workflow, joka muuntaa conf-tiedoston
+ yhdeksi JSON-outputiksi. Yksi output riittää, ei per-key outputteja.
+- **`jobs..with`** — tukee `needs`-kontekstia (Gitea Actions,
+ kuten GitHub Actions). Tämä on kriittinen yksityiskohta: ilman tätä
+ config-arvoja ei voi välittää reusable workflowille dynaamisesti.
+- **workflow `env:`** — ainoa tapa jakaa arvot kaikkiin jobeihin.
+ `fromJson(inputs.env_json).KEY` purkaa yksittäiset arvot ilman toistoa.
+- **Per-job `env:`** — sisältää vain secretit (`GITEA_TOKEN`,
+ `GIT_PAGES_PUBLISH_TOKEN`), ei config-arvoja.
+
## 5. Pipeline Provides All Dependencies
- Ei luottamusta runnerin esiasennettuihin työkaluihin
@@ -63,3 +110,16 @@ Käytäntö:
- Rinnakkaiset jobit (bats + cucumber) — tuloksia saa heti kun valmistuu
- Jokainen testisetti omassa jobissaan
- Finalize/build voi kerätä yhteenvedon (ei julkaista summarya jos kenelläkään ei ole linkkiä)
+
+## 7. Inline Logic Threshold
+
+Logiikka workflow YAML:ssa on hauras: YAML:n sisennys, heredocit ja
+kenoviivat tuottavat helposti toimimattomia steppejä.
+
+**Kynnys siirtää scriptiksi:** heti kun steppiin tulee ehtoja, silmukoita,
+tai yli 3 riviä inline-koodia, siirrä omaksi scriptikseen `.gitea/scripts/`-
+kansioon.
+
+Esimerkki: coverage-datan purku ja navigointi-indexin luonti oli aluksi
+inline-heredocina workflow YAML:ssa. Siirto omaan `bats-coverage.sh`-scriptiin
+teki siitä luettavan, testattavan ja muokattavan ilman YAML-muotoiluriskejä.
diff --git a/docs/shared-scripts.md b/docs/shared-scripts.md
index be8677a..b50fa25 100644
--- a/docs/shared-scripts.md
+++ b/docs/shared-scripts.md
@@ -67,7 +67,7 @@ Dispatchaa workflow'n toisessa repossa ja pollaa sen valmistumista synkronisesti
### Rajapinta
```bash
-dispatch-workflow.sh [ [timeout_minutes]
+dispatch-workflow.sh ][ [timeout_minutes]
```
| Parametri | Pakollinen | Kuvaus |
@@ -76,6 +76,8 @@ dispatch-workflow.sh ][ [timeout_
| `workflow_file` | Kyllä | Workflow-tiedoston nimi (esim. `test.yml`) |
| `ref` | Kyllä | Branch |
| `inputs_json` | Kyllä | JSON-objekti input-parametreina |
+| `gitea_api_url` | Kyllä | Gitean API-URL (esim. `https://gitea.example.com`) |
+| `gitea_token` | Kyllä | Gitea API -token |
| `timeout_minutes` | Ei | Oletus: 360 (6 tuntia) |
### Toiminta
@@ -90,7 +92,8 @@ dispatch-workflow.sh ][ [timeout_
```bash
dispatch-workflow.sh "tests/integration" "test.yml" "main" \
- '{"version":"1.2.3","tags":"@smoke","root_commit":"abc123","root_repo":"services/temperature-store"}'
+ '{"version":"1.2.3","tags":"@smoke","root_commit":"abc123","root_repo":"services/temperature-store"}' \
+ "https://gitea.example.com" "gtp_abc123"
```
---
@@ -181,8 +184,8 @@ Skriptit lukevat nämä Gitea Actionsin ympäristömuuttujat:
| Muuttuja | Lähde | Käyttäjä |
|----------|-------|----------|
-| `GITEA_API_URL` | Org variable | Kaikki skriptit |
-| `GITEA_TOKEN` | Org secret | `report-status.sh`, `dispatch-workflow.sh`, `tag-commit.sh` |
+| `GITEA_API_URL` | Org variable | `report-status.sh` |
+| `GITEA_TOKEN` | Org secret | `report-status.sh`, `tag-commit.sh` |
| `MINIO_BASE_URL` | Org variable | `push-reports.sh` |
| `MINIO_ACCESS_KEY` | Org secret | `push-reports.sh` |
| `MINIO_SECRET_KEY` | Org secret | `push-reports.sh` |
diff --git a/docs/tickets/0002-dispatch-workflow-sh.md b/docs/tickets/0002-dispatch-workflow-sh.md
index d4012d0..9a5ec11 100644
--- a/docs/tickets/0002-dispatch-workflow-sh.md
+++ b/docs/tickets/0002-dispatch-workflow-sh.md
@@ -54,7 +54,7 @@ Dispatchaa workflow toisessa repossa ja pollaa sen valmistumista synkronisesti.
## Rajapinta
```bash
-dispatch-workflow.sh ][ [timeout_minutes]
+dispatch-workflow.sh ][ [timeout_minutes]
```
| Parametri | Pakollinen | Kuvaus |
@@ -63,6 +63,8 @@ dispatch-workflow.sh ][ [timeout_
| `workflow_file` | Kyllä | Workflow-tiedosto (esim. `test.yml`) |
| `ref` | Kyllä | Branch |
| `inputs_json` | Kyllä | JSON-objekti input-parametreina |
+| `gitea_api_url` | Kyllä | Gitean API-URL |
+| `gitea_token` | Kyllä | Gitea API -token |
| `timeout_minutes` | Ei | Oletus: 360 |
## API-kutsut
@@ -77,7 +79,8 @@ dispatch-workflow.sh ][ [timeout_
```bash
dispatch-workflow.sh "tests/integration" "test.yml" "main" \
- '{"version":"1.2.3","tags":"@smoke","root_commit":"abc123","root_repo":"services/temperature-store"}'
+ '{"version":"1.2.3","tags":"@smoke","root_commit":"abc123","root_repo":"services/temperature-store"}' \
+ "https://gitea.example.com" "gtp_abc123"
```
## Verifiointi
diff --git a/docs/workflows.md b/docs/workflows.md
index a6bd90b..1544372 100644
--- a/docs/workflows.md
+++ b/docs/workflows.md
@@ -1,8 +1,8 @@
# Reusable workflowt
> ⚠️ **POC-vaihe.** Tämä dokumentti kuvaa suunniteltuja workflow'ta
-> (ci-feature, ci-master, deploy, test). POCissa on toteutettu vain
-> `ci-engine.yml`. Uudelleenkirjoitus odottaa.
+> (ci-feature, ci-master, deploy, test). POCissa on toteutettu
+> `build-feature.yml`. Uudelleenkirjoitus odottaa.
---
diff --git a/scripts/ci-validate.sh b/scripts/ci-validate.sh
new file mode 100644
index 0000000..f79cbe0
--- /dev/null
+++ b/scripts/ci-validate.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+CONF_FILE="${CI_CONF_FILE:-.gitea/workflows/gitea-env.conf}"
+ERRORS=0
+
+[ -f "$CONF_FILE" ] || { echo "ERROR: $CONF_FILE not found — checkout missing?" >&2; exit 1; }
+
+echo "Reading $CONF_FILE..."
+
+while IFS='=' read -r key value || [ -n "$key" ]; do
+ key=$(echo "$key" | xargs)
+ value=$(echo "$value" | xargs)
+ [ -z "$key" ] && continue
+ [[ "$key" == "#"* ]] && continue
+ [ -z "$value" ] && echo "ERROR: $key is empty in $CONF_FILE" >&2 && ERRORS=1
+ if [ -n "$value" ] && [[ "$key" == *"URL"* ]] && [[ "$value" != http://* ]] && [[ "$value" != https://* ]]; then
+ echo "ERROR: $key should be a URL (http/https), got: $value" >&2
+ ERRORS=1
+ fi
+done < "$CONF_FILE"
+
+[ -z "${GITEA_TOKEN:-}" ] && echo "ERROR: GITEA_TOKEN secret is not set" >&2 && ERRORS=1
+[ -z "${GIT_PAGES_PUBLISH_TOKEN:-}" ] && echo "ERROR: GIT_PAGES_PUBLISH_TOKEN secret is not set" >&2 && ERRORS=1
+
+if [ "$ERRORS" -ne 0 ]; then
+ echo "FATAL: CI config validation failed" >&2
+ exit 1
+fi
+
+echo "OK: all CI env vars validated"
diff --git a/scripts/dispatch-workflow.sh b/scripts/dispatch-workflow.sh
index 78905b8..1564c84 100755
--- a/scripts/dispatch-workflow.sh
+++ b/scripts/dispatch-workflow.sh
@@ -1,20 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
-[ -z "${GITEA_API_URL:-}" ] && echo "ERROR: GITEA_API_URL is not set" >&2 && exit 1
-[ -z "${GITEA_TOKEN:-}" ] && echo "ERROR: GITEA_TOKEN is not set" >&2 && exit 1
-
TARGET_REPO="${1:-}"
WORKFLOW_FILE="${2:-}"
REF="${3:-}"
INPUTS_JSON="${4:-}"
-TIMEOUT_MINUTES="${5:-360}"
+GITEA_API_URL="${5:-}"
+GITEA_TOKEN="${6:-}"
+TIMEOUT_MINUTES="${7:-360}"
POLL_INTERVAL="${DISPATCH_POLL_INTERVAL:-10}"
[ -z "$TARGET_REPO" ] && echo "ERROR: target_repo argument is required" >&2 && exit 1
[ -z "$WORKFLOW_FILE" ] && echo "ERROR: workflow_file argument is required" >&2 && exit 1
[ -z "$REF" ] && echo "ERROR: ref argument is required" >&2 && exit 1
[ -z "$INPUTS_JSON" ] && echo "ERROR: inputs_json argument is required" >&2 && exit 1
+[ -z "$GITEA_API_URL" ] && echo "ERROR: gitea_api_url argument is required" >&2 && exit 1
+[ -z "$GITEA_TOKEN" ] && echo "ERROR: gitea_token argument is required" >&2 && exit 1
DISPATCH_URL="$GITEA_API_URL/api/v1/repos/$TARGET_REPO/actions/workflows/$WORKFLOW_FILE/dispatches"
DISPATCH_BODY=$(jq -nc --arg ref "$REF" --argjson inputs "$INPUTS_JSON" '{ref: $ref, inputs: $inputs}')
diff --git a/scripts/publish-git-pages.sh b/scripts/publish-git-pages.sh
index 61065ab..81228a7 100755
--- a/scripts/publish-git-pages.sh
+++ b/scripts/publish-git-pages.sh
@@ -1,29 +1,25 @@
#!/usr/bin/env bash
-# Publish a report directory to git-pages apex index-site via Traefik (BasicAuth).
-# Public URL: https://{PAGES_HOST}/{owner}/{repo}/reports/{sha8}/index.html
set -euo pipefail
-REPORT_DIR="${1:-}"
-PAGES_HOST="${PAGES_HOST:-}"
-GIT_PAGES_PUBLISH_URL="${GIT_PAGES_PUBLISH_URL:-https://pages.helm-dev.keskikuja.site}"
-GIT_PAGES_PUBLISH_TOKEN="${GIT_PAGES_PUBLISH_TOKEN:-}"
-GIT_PAGES_PUBLISH_USER="${GIT_PAGES_PUBLISH_USER:-publish}"
-REPO_SLUG="${GITHUB_REPOSITORY:-}"
+SUITE_PATH="${1:-}"
-[ -n "$REPORT_DIR" ] || { echo "ERROR: report directory argument required" >&2; exit 1; }
-[ -d "$REPORT_DIR" ] || { echo "ERROR: not a directory: $REPORT_DIR" >&2; exit 1; }
-[ -n "$PAGES_HOST" ] || { echo "ERROR: PAGES_HOST is not set" >&2; exit 1; }
-[ -n "$GIT_PAGES_PUBLISH_TOKEN" ] || { echo "ERROR: GIT_PAGES_PUBLISH_TOKEN is not set" >&2; exit 1; }
-[ -n "$REPO_SLUG" ] || { echo "ERROR: GITHUB_REPOSITORY is not set" >&2; exit 1; }
+[ -n "$SUITE_PATH" ] || { echo "ERROR: suite_path argument required" >&2; exit 1; }
+[ -n "${GITEA_API_URL:-}" ] || { echo "ERROR: GITEA_API_URL is not set" >&2; exit 1; }
+[ -n "${GIT_PAGES_URL:-}" ] || { echo "ERROR: GIT_PAGES_URL is not set" >&2; exit 1; }
+[ -n "${GIT_PAGES_PUBLISH_TOKEN:-}" ] || { echo "ERROR: GIT_PAGES_PUBLISH_TOKEN is not set" >&2; exit 1; }
+[ -n "${GITHUB_REPOSITORY:-}" ] || { echo "ERROR: GITHUB_REPOSITORY is not set" >&2; exit 1; }
[ -n "${GITHUB_SHA:-}" ] || { echo "ERROR: GITHUB_SHA is not set" >&2; exit 1; }
-OWNER="${REPO_SLUG%%/*}"
-REPO="${REPO_SLUG##*/}"
+OWNER="${GITHUB_REPOSITORY%%/*}"
+REPO="${GITHUB_REPOSITORY##*/}"
SHA8="${GITHUB_SHA:0:8}"
-REPORT_BASE="https://${PAGES_HOST}/${OWNER}/${REPO}/reports/${SHA8}"
+PAGES_USER="${GIT_PAGES_PUBLISH_USER:-publish}"
+REPORT_DIR="reports/${SHA8}/${SUITE_PATH%/}"
+REPORT_BASE="${GIT_PAGES_URL}/${OWNER}/${REPO}/reports/${SHA8}"
-PUBLISH_BASE="${GIT_PAGES_PUBLISH_URL%/}"
-PUBLISH_SITE_URL="${PUBLISH_BASE}/"
+[ -d "$REPORT_DIR" ] || { echo "ERROR: not a directory: $REPORT_DIR" >&2; exit 1; }
+
+PUBLISH_SITE_URL="${GIT_PAGES_URL}/"
WORK=$(mktemp -d)
TAR=$(mktemp)
@@ -45,7 +41,7 @@ find "$WORK/$OWNER" \( -type f -o -type l \) -print | sed "s|^${WORK}/||" | tar
publish() {
local method="$1"
curl -sS -X "$method" "$PUBLISH_SITE_URL" \
- -u "${GIT_PAGES_PUBLISH_USER}:${GIT_PAGES_PUBLISH_TOKEN}" \
+ -u "${PAGES_USER}:${GIT_PAGES_PUBLISH_TOKEN}" \
-H "Content-Type: application/x-tar" \
-H "Atomic: no" \
-H "Create-Parents: yes" \
diff --git a/scripts/publish.sh b/scripts/publish.sh
index 299f3ce..eb9b53e 100644
--- a/scripts/publish.sh
+++ b/scripts/publish.sh
@@ -1,10 +1,16 @@
#!/usr/bin/env bash
-# Vie raportit git-pagesiin + commit-status linkillä
set -euo pipefail
-REPORT_DIR="${1:-reports}"
-PAGES_HOST="${PAGES_HOST:-ci-reports.helm-dev.keskikuja.site}"
+SUITE_PATH="${1:-}"
-REPORT_URL=$(bash "$(dirname $0)/publish-git-pages.sh" "$REPORT_DIR")
+[ -n "$SUITE_PATH" ] || { echo "ERROR: suite_path argument required" >&2; exit 1; }
+[ -n "${GITEA_API_URL:-}" ] || { echo "ERROR: GITEA_API_URL is not set" >&2; exit 1; }
+[ -n "${GITEA_TOKEN:-}" ] || { echo "ERROR: GITEA_TOKEN is not set" >&2; exit 1; }
+[ -n "${GIT_PAGES_URL:-}" ] || { echo "ERROR: GIT_PAGES_URL is not set" >&2; exit 1; }
+[ -n "${GIT_PAGES_PUBLISH_TOKEN:-}" ] || { echo "ERROR: GIT_PAGES_PUBLISH_TOKEN is not set" >&2; exit 1; }
+
+SCRIPT_DIR="$(dirname "$0")"
+
+REPORT_URL=$(bash "$SCRIPT_DIR/publish-git-pages.sh" "$SUITE_PATH")
echo "Published: $REPORT_URL"
-bash "$(dirname $0)/report-status.sh" success "Reports published" "$REPORT_URL" ci-report
+bash "$SCRIPT_DIR/report-status.sh" success "Reports published" "ci-report"
diff --git a/scripts/report-status.sh b/scripts/report-status.sh
index a8d7a95..d2c10ce 100755
--- a/scripts/report-status.sh
+++ b/scripts/report-status.sh
@@ -1,28 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
-[ -z "${GITEA_API_URL:-}" ] && echo "ERROR: GITEA_API_URL is not set" >&2 && exit 1
-[ -z "${GITEA_TOKEN:-}" ] && echo "ERROR: GITEA_TOKEN is not set" >&2 && exit 1
-
STATE="${1:-}"
DESCRIPTION="${2:-}"
-URL="${3:-}"
-KEY="${4:-commit-${GITHUB_SHA:0:8}}"
-ROOT_COMMIT="${5:-}"
-ROOT_REPO="${6:-}"
+KEY="${3:-commit-${GITHUB_SHA:0:8}}"
+SUITE="${4:-}"
[ -z "$STATE" ] && echo "ERROR: state argument is required" >&2 && exit 1
[ -z "$DESCRIPTION" ] && echo "ERROR: description argument is required" >&2 && exit 1
-[ -z "$URL" ] && echo "ERROR: url argument is required" >&2 && exit 1
+[ -z "${GITEA_API_URL:-}" ] && echo "ERROR: GITEA_API_URL is not set" >&2 && exit 1
+[ -z "${GITEA_TOKEN:-}" ] && echo "ERROR: GITEA_TOKEN is not set" >&2 && exit 1
-if [ -n "$ROOT_COMMIT" ] && [ -n "$ROOT_REPO" ]; then
- REPO="$ROOT_REPO"
- COMMIT="$ROOT_COMMIT"
+if [ -n "$SUITE" ]; then
+ SUITE="${SUITE%/}/"
+ URL="${GIT_PAGES_URL}/${GITHUB_REPOSITORY}/reports/${GITHUB_SHA:0:8}/${SUITE}"
else
- REPO="${GITHUB_REPOSITORY:-}"
- COMMIT="${GITHUB_SHA:-}"
+ URL="${GITEA_API_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
fi
+REPO="${ROOT_REPO:-${GITHUB_REPOSITORY:-}}"
+COMMIT="${ROOT_COMMIT:-${GITHUB_SHA:-}}"
+
[ -z "$REPO" ] && echo "ERROR: GITHUB_REPOSITORY is not set" >&2 && exit 1
[ -z "$COMMIT" ] && echo "ERROR: GITHUB_SHA is not set" >&2 && exit 1
diff --git a/tests/ci-validate.bats b/tests/ci-validate.bats
new file mode 100644
index 0000000..46e8f9c
--- /dev/null
+++ b/tests/ci-validate.bats
@@ -0,0 +1,79 @@
+#!/usr/bin/env bats
+
+setup() {
+ export CONF_FILE=$(mktemp)
+ export CI_CONF_FILE="$CONF_FILE"
+}
+
+teardown() {
+ rm -f "$CONF_FILE"
+}
+
+@test "missing config file → exit 1" {
+ export CI_CONF_FILE="/nonexistent/path/$(date +%s).conf"
+ run bash scripts/ci-validate.sh
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"ERROR"* ]]
+}
+
+@test "empty value in config → exit 1" {
+ cat > "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" </dev/null | xargs -r kill -9 2>/dev/null || true', { stdio: 'ignore' });
execSync('sleep 0.4', { stdio: 'ignore' });
@@ -62,7 +54,7 @@ function setupMock(seqJson) {
}
function runDispatch(args) {
- return bash(`${envBlock()}; bash "${DISPATCH_SCRIPT}" ${args}`);
+ return bash(`export DISPATCH_POLL_INTERVAL="0.1"; bash "${DISPATCH_SCRIPT}" ${args}`);
}
Given('a deployment has completed in the target environment', function () {
@@ -77,7 +69,7 @@ When('a test workflow is dispatched to a test project', function () {
{ code: 200, body: { workflow_runs: [{ id: 1, status: 'running' }] } },
{ code: 200, body: { id: 1, status: 'completed', conclusion: 'success' } },
]));
- const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\'');
+ const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "http://localhost:18080" "test-token-abc123"');
this.dispatchResult = r.status;
});
@@ -95,7 +87,7 @@ When('a test workflow is dispatched and the tests fail', function () {
{ code: 200, body: { workflow_runs: [{ id: 1, status: 'running' }] } },
{ code: 200, body: { id: 1, status: 'completed', conclusion: 'failure' } },
]));
- const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\'');
+ const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "http://localhost:18080" "test-token-abc123"');
this.dispatchResult = r.status;
});
@@ -111,7 +103,7 @@ When('a test workflow is dispatched but does not finish within the allowed time'
{ code: 200, body: { id: 1, status: 'running' } },
{ code: 200, body: { id: 1, status: 'running' } },
]));
- const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "0.001"');
+ const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "http://localhost:18080" "test-token-abc123" "0.001"');
this.dispatchResult = r.status;
});
diff --git a/tests/helpers/mock-api.sh b/tests/helpers/mock-api.sh
index 7c77d69..1bdc292 100644
--- a/tests/helpers/mock-api.sh
+++ b/tests/helpers/mock-api.sh
@@ -13,12 +13,12 @@ _kill_port() {
local pids
pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true
[ -n "$pids" ] && kill -9 $pids 2>/dev/null || true
- sleep 0.3
+ sleep 0.5
}
_wait_port_free() {
local i=0
- while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 30 ]; do
+ while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 50 ]; do
sleep 0.1
i=$((i + 1))
done
diff --git a/tests/helpers/mock-server.py b/tests/helpers/mock-server.py
index 2b4cbb1..57249d6 100644
--- a/tests/helpers/mock-server.py
+++ b/tests/helpers/mock-server.py
@@ -46,7 +46,7 @@ class H(http.server.BaseHTTPRequestHandler):
def _log_request(self, method):
path = self.path
content_len = int(self.headers.get('Content-Length', 0))
- body = self.rfile.read(content_len).decode() if content_len else ''
+ body = self.rfile.read(content_len).decode(errors='replace') if content_len else ''
line = f'{method} {path}\n{body}\n'
with open(REQ_FILE, 'a') as f:
f.write(line)
@@ -67,6 +67,14 @@ class H(http.server.BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(body.encode())
+ def do_PATCH(self):
+ self._log_request('PATCH')
+ code, body = self._get_response()
+ self.send_response(code)
+ self.send_header('Content-Type', 'application/json')
+ self.end_headers()
+ self.wfile.write(body.encode())
+
def log_message(self, format, *args):
pass
diff --git a/tests/publish-git-pages.bats b/tests/publish-git-pages.bats
new file mode 100644
index 0000000..1e62214
--- /dev/null
+++ b/tests/publish-git-pages.bats
@@ -0,0 +1,91 @@
+#!/usr/bin/env bats
+
+setup() {
+ source tests/helpers/mock-api.sh
+ export GITEA_API_URL="http://localhost:18080"
+ export GIT_PAGES_URL="http://localhost:18080"
+ export GIT_PAGES_PUBLISH_TOKEN="publish-token-abc"
+ export GITHUB_REPOSITORY="test-owner/test-repo"
+ export GITHUB_SHA="abc123def456789012345678901234567890abcd"
+
+ REPORT_DIR="reports/abc123de/unit-tests"
+ mkdir -p "$REPORT_DIR"
+ echo "test" > "$REPORT_DIR/index.html"
+}
+
+teardown() {
+ mock_stop
+ rm -rf "reports/abc123de"
+}
+
+@test "missing suite_path argument → exit 1" {
+ run bash scripts/publish-git-pages.sh ""
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"ERROR"* ]]
+}
+
+@test "missing GITEA_API_URL → exit 1" {
+ unset GITEA_API_URL
+ run bash scripts/publish-git-pages.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GITEA_API_URL"* ]]
+}
+
+@test "missing GIT_PAGES_URL → exit 1" {
+ unset GIT_PAGES_URL
+ run bash scripts/publish-git-pages.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GIT_PAGES_URL"* ]]
+}
+
+@test "missing GIT_PAGES_PUBLISH_TOKEN → exit 1" {
+ unset GIT_PAGES_PUBLISH_TOKEN
+ run bash scripts/publish-git-pages.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]]
+}
+
+@test "missing GITHUB_REPOSITORY → exit 1" {
+ unset GITHUB_REPOSITORY
+ run bash scripts/publish-git-pages.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GITHUB_REPOSITORY"* ]]
+}
+
+@test "suite path is not a directory → exit 1" {
+ run bash scripts/publish-git-pages.sh "nonexistent"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"not a directory"* ]]
+}
+
+@test "valid publish returns report base URL" {
+ mock_set_sequence '[
+ {"code":200,"body":"published"}
+ ]'
+ mock_start
+ run bash scripts/publish-git-pages.sh "unit-tests"
+ [ "$status" -eq 0 ]
+ [[ "$output" == "http://localhost:18080/test-owner/test-repo/reports/abc123de" ]]
+}
+
+@test "publish with suite subpath" {
+ mkdir -p "reports/abc123de/sub/suite"
+ echo "sub" > "reports/abc123de/sub/suite/result.html"
+ mock_set_sequence '[
+ {"code":200,"body":"published"}
+ ]'
+ mock_start
+ run bash scripts/publish-git-pages.sh "sub/suite"
+ [ "$status" -eq 0 ]
+ [[ "$output" == "http://localhost:18080/test-owner/test-repo/reports/abc123de" ]]
+}
+
+@test "git-pages returns HTTP 500 → exit 1" {
+ mock_set_sequence '[
+ {"code":500,"body":"internal error"}
+ ]'
+ mock_start
+ run bash scripts/publish-git-pages.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"500"* ]]
+}
diff --git a/tests/publish.bats b/tests/publish.bats
new file mode 100644
index 0000000..36832b4
--- /dev/null
+++ b/tests/publish.bats
@@ -0,0 +1,44 @@
+#!/usr/bin/env bats
+
+setup() {
+ export GITEA_API_URL="http://localhost:18080"
+ export GITEA_TOKEN="test-token-abc"
+ export GIT_PAGES_URL="http://localhost:18080"
+ export GIT_PAGES_PUBLISH_TOKEN="publish-token-abc"
+ export GITHUB_REPOSITORY="test-owner/test-repo"
+ export GITHUB_SHA="abc123def456789012345678901234567890abcd"
+}
+
+@test "missing suite_path argument → exit 1" {
+ run bash scripts/publish.sh ""
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"ERROR"* ]]
+}
+
+@test "missing GITEA_API_URL → exit 1" {
+ unset GITEA_API_URL
+ run bash scripts/publish.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GITEA_API_URL"* ]]
+}
+
+@test "missing GITEA_TOKEN → exit 1" {
+ unset GITEA_TOKEN
+ run bash scripts/publish.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GITEA_TOKEN"* ]]
+}
+
+@test "missing GIT_PAGES_URL → exit 1" {
+ unset GIT_PAGES_URL
+ run bash scripts/publish.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GIT_PAGES_URL"* ]]
+}
+
+@test "missing GIT_PAGES_PUBLISH_TOKEN → exit 1" {
+ unset GIT_PAGES_PUBLISH_TOKEN
+ run bash scripts/publish.sh "unit-tests"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]]
+}
diff --git a/tests/report-status.bats b/tests/report-status.bats
index 35b38a9..786c731 100644
--- a/tests/report-status.bats
+++ b/tests/report-status.bats
@@ -4,9 +4,9 @@ setup() {
source tests/helpers/mock-api.sh
export GITEA_API_URL="http://localhost:18080"
export GITEA_TOKEN="test-token-abc123"
+ export GIT_PAGES_URL="https://reports.example.com"
export GITHUB_REPOSITORY="test-owner/test-repo"
export GITHUB_SHA="abc123def456789012345678901234567890abcd"
- export GITHUB_SERVER_URL="https://gitea.example.com"
export GITHUB_RUN_ID="42"
}
@@ -16,68 +16,40 @@ teardown() {
@test "pending status is POSTed with correct payload" {
mock_start
- run bash scripts/report-status.sh pending "Building project" "http://example.com/build/42"
+ run bash scripts/report-status.sh pending "Building project"
[ "$status" -eq 0 ]
path=$(mock_get_request_path)
[[ "$path" == "/api/v1/repos/test-owner/test-repo/statuses/abc123def456789012345678901234567890abcd" ]]
body=$(mock_get_request_body)
[[ "$body" == *'"state":"pending"'* ]]
[[ "$body" == *'"description":"Building project"'* ]]
- [[ "$body" == *'"target_url":"http://example.com/build/42"'* ]]
+ [[ "$body" == *'"target_url":"http://localhost:18080/test-owner/test-repo/actions/runs/42"'* ]]
method=$(mock_get_request_method)
[[ "$method" == "POST" ]]
}
-@test "success status with url and custom key" {
+@test "success status with custom key and suite builds report URL" {
mock_start
- run bash scripts/report-status.sh success "Unit tests OK" "http://example.com/reports/cucumber.html" "unit-test"
+ run bash scripts/report-status.sh success "Unit tests OK" unit-test cucumber
[ "$status" -eq 0 ]
body=$(mock_get_request_body)
[[ "$body" == *'"state":"success"'* ]]
- [[ "$body" == *'"description":"Unit tests OK"'* ]]
- [[ "$body" == *'"target_url":"http://example.com/reports/cucumber.html"'* ]]
[[ "$body" == *'"context":"unit-test"'* ]]
+ [[ "$body" == *'"target_url":"https://reports.example.com/test-owner/test-repo/reports/abc123de/cucumber/"'* ]]
}
-@test "failure status is POSTed correctly" {
+@test "failure status constructs run URL when no suite" {
mock_start
- run bash scripts/report-status.sh failure "Tests failed: 3 of 10" "http://example.com/build/42"
+ run bash scripts/report-status.sh failure "Tests failed: 3 of 10"
[ "$status" -eq 0 ]
body=$(mock_get_request_body)
[[ "$body" == *'"state":"failure"'* ]]
- [[ "$body" == *'"description":"Tests failed: 3 of 10"'* ]]
-}
-
-@test "error status is POSTed correctly" {
- mock_start
- run bash scripts/report-status.sh error "Build timed out" "http://example.com/build/42"
- [ "$status" -eq 0 ]
- body=$(mock_get_request_body)
- [[ "$body" == *'"state":"error"'* ]]
-}
-
-@test "cross-repo: root_commit and root_repo override target" {
- mock_start
- run bash scripts/report-status.sh success "Deployed to staging" "http://example.com/deploy/42" "deploy-staging" "rootabc123" "services/temperature-store"
- [ "$status" -eq 0 ]
- path=$(mock_get_request_path)
- [[ "$path" == "/api/v1/repos/services/temperature-store/statuses/rootabc123" ]]
- body=$(mock_get_request_body)
- [[ "$body" == *'"state":"success"'* ]]
- [[ "$body" == *'"context":"deploy-staging"'* ]]
-}
-
-@test "cross-repo: only root_commit without root_repo is ignored" {
- mock_start
- run bash scripts/report-status.sh success "Partial cross-repo" "http://example.com" "my-key" "abc"
- [ "$status" -eq 0 ]
- path=$(mock_get_request_path)
- [[ "$path" == "/api/v1/repos/test-owner/test-repo/statuses/abc123def456789012345678901234567890abcd" ]]
+ [[ "$body" == *'"target_url":"http://localhost:18080/test-owner/test-repo/actions/runs/42"'* ]]
}
@test "default key when not provided" {
mock_start
- run bash scripts/report-status.sh pending "Build started" "http://example.com/build/42"
+ run bash scripts/report-status.sh pending "Build started"
[ "$status" -eq 0 ]
body=$(mock_get_request_body)
[[ "$body" == *'"context":"commit-abc123de"'* ]]
@@ -86,38 +58,46 @@ teardown() {
@test "API returns 500 causes exit 1" {
mock_set_response 500
mock_start
- run bash scripts/report-status.sh success "Should fail" "http://example.com"
+ run bash scripts/report-status.sh success "Should fail"
[ "$status" -eq 1 ]
}
+@test "cross-repo: ROOT_COMMIT and ROOT_REPO override target" {
+ export ROOT_COMMIT="rootabc123"
+ export ROOT_REPO="services/temperature-store"
+ mock_start
+ run bash scripts/report-status.sh success "Deployed to staging" deploy-staging
+ [ "$status" -eq 0 ]
+ path=$(mock_get_request_path)
+ [[ "$path" == "/api/v1/repos/services/temperature-store/statuses/rootabc123" ]]
+ body=$(mock_get_request_body)
+ [[ "$body" == *'"state":"success"'* ]]
+ [[ "$body" == *'"context":"deploy-staging"'* ]]
+ unset ROOT_COMMIT ROOT_REPO
+}
+
@test "missing GITEA_API_URL causes exit 1 with error message" {
unset GITEA_API_URL
- run bash scripts/report-status.sh pending "Test" "http://example.com"
+ run bash scripts/report-status.sh pending "Test"
[ "$status" -eq 1 ]
- [[ "$output" == *"ERROR"* ]] || [[ "$output" == *"GITEA_API_URL"* ]]
+ [[ "$output" == *"ERROR"* || "$output" == *"GITEA_API_URL"* ]]
}
@test "missing GITEA_TOKEN causes exit 1 with error message" {
unset GITEA_TOKEN
- run bash scripts/report-status.sh pending "Test" "http://example.com"
+ run bash scripts/report-status.sh pending "Test"
[ "$status" -eq 1 ]
- [[ "$output" == *"ERROR"* ]] || [[ "$output" == *"GITEA_TOKEN"* ]]
+ [[ "$output" == *"ERROR"* || "$output" == *"GITEA_TOKEN"* ]]
}
@test "missing required state argument causes exit 1" {
mock_start
- run bash scripts/report-status.sh "" "desc" "http://example.com"
+ run bash scripts/report-status.sh "" "desc"
[ "$status" -eq 1 ]
}
@test "missing required description argument causes exit 1" {
mock_start
- run bash scripts/report-status.sh pending "" "http://example.com"
- [ "$status" -eq 1 ]
-}
-
-@test "missing required url argument causes exit 1" {
- mock_start
- run bash scripts/report-status.sh pending "desc" ""
+ run bash scripts/report-status.sh pending ""
[ "$status" -eq 1 ]
}
]