gitops init
CI Feature / Load example-gitea-env.conf to pipeline env (push) Successful in 24s
acc-tests Cucumber test report
CI Feature / Cucumber tests (push) Failing after 1m10s
unit-tests Bats test report
CI Feature / Bats tests (push) Successful in 1m35s
CI Feature / Report Summary (push) Successful in 6s
CI Feature / Load example-gitea-env.conf to pipeline env (push) Successful in 24s
acc-tests Cucumber test report
CI Feature / Cucumber tests (push) Failing after 1m10s
unit-tests Bats test report
CI Feature / Bats tests (push) Successful in 1m35s
CI Feature / Report Summary (push) Successful in 6s
This commit is contained in:
@@ -62,7 +62,7 @@ organization/repository secrets -mekanismissa ja välitetään workflowlle
|
||||
|
||||
| Secret | Pakollinen | Käyttäjä |
|
||||
|---|---|---|
|
||||
| `GITEA_TOKEN` | Kyllä | `report-status.sh`, `check-version.yml`, `docker-build-push.yml` |
|
||||
| `GITEA_TOKEN` | Kyllä | `report-status.sh`, `check-version.yml`, `docker-build-push.yml`, `gitops-update.sh` |
|
||||
| `GIT_PAGES_PUBLISH_TOKEN` | Kyllä | `publish-git-pages.sh`, `config-provider.yml` (validointi) |
|
||||
| `DOCKER_USERNAME` | Ei | `docker-build-push.yml` (oletus: `github.actor`, ei pakollinen kaikissa registryissä) |
|
||||
| `DOCKER_PASSWORD` | Kyllä | `docker-build-push.yml` |
|
||||
|
||||
+47
-3
@@ -182,7 +182,51 @@ Forward-compatibeli — ei haittaa vanhemmilla Gitea-versioilla.
|
||||
|
||||
---
|
||||
|
||||
## Suunnitteilla
|
||||
## Provider-skriptit
|
||||
|
||||
- `deploy.yml` — GitOps-deployment (dispatch-workflow.sh-pohjainen)
|
||||
- `test.yml` — Klusteritason test flow
|
||||
### `gitops-update.sh` — GitOps-version päivitys
|
||||
|
||||
**Riippuvuudet:** `yq`, `scripts/report-status.sh`, `git`
|
||||
|
||||
Päivittää GitOps-repon konfiguraatiotiedoston versionumeron `yq`:lla,
|
||||
committaa muutoksen ja asettaa commit-statuksen molempiin repoihin.
|
||||
|
||||
**Input-ympäristömuuttujat:**
|
||||
|
||||
| Muuttuja | Pakollinen | Kuvaus |
|
||||
|---|---|---|
|
||||
| `INPUT_FILE` | Kyllä | Tiedosto GitOps-repossa (esim. `dev/Chart.yaml`) |
|
||||
| `YQ_TPL` | Kyllä | `yq`-lauseke `{{VERSION}}`-placeholderilla |
|
||||
| `VERSION` | Kyllä | Uusi versio (esim. `0.2.3`) |
|
||||
| `SOURCE_REPO` | Kyllä | Lähdekoodirepo (esim. `org/app`) |
|
||||
| `SOURCE_COMMIT` | Kyllä | Lähdekoodin commit-SHA |
|
||||
| `GITOPS_REPO` | Kyllä | GitOps-konfiguraatiorepo (esim. `org/app-gitops`) |
|
||||
| `GITEA_API_URL` | Kyllä | Gitean API-URL |
|
||||
| `GITEA_TOKEN` | Kyllä | Gitea API-token |
|
||||
| `GITOPS_BRANCH` | Ei | GitOps-repon branch (oletus `main`) |
|
||||
|
||||
**Steppikuvaus:**
|
||||
1. Korvaa `YQ_TPL`:n `{{VERSION}}` versiolla
|
||||
2. Muodostaa `CLONE_URL` tokenilla ja hostilla
|
||||
3. Kloonaa GitOps-repon
|
||||
4. Ajaa `yq eval -i` päivittääkseen tiedoston
|
||||
5. Commit + push `[skip ci]`
|
||||
6. Asettaa commit-statuksen: code-repoon (gitops-konteksti) ja GitOps-repoon (source-konteksti)
|
||||
|
||||
**Esimerkki dispatchistä:**
|
||||
```yaml
|
||||
- name: Update GitOps
|
||||
run: |
|
||||
export INPUT_FILE=dev/Chart.yaml
|
||||
export YQ_TPL='(.version) = "{{VERSION}}"'
|
||||
export VERSION=0.2.3
|
||||
export SOURCE_REPO=org/app
|
||||
export SOURCE_COMMIT=${{ github.sha }}
|
||||
export GITOPS_REPO=org/app-gitops
|
||||
bash scripts/gitops-update.sh
|
||||
env:
|
||||
GITEA_API_URL: ${{ vars.GITEA_API_URL }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
INPUT_FILE="${INPUT_FILE:?}"
|
||||
YQ_TPL="${YQ_TPL:?}"
|
||||
VERSION="${VERSION:?}"
|
||||
SOURCE_REPO="${SOURCE_REPO:?}"
|
||||
SOURCE_COMMIT="${SOURCE_COMMIT:?}"
|
||||
GITOPS_REPO="${GITOPS_REPO:?}"
|
||||
GITEA_TOKEN="${GITEA_TOKEN:?}"
|
||||
GITEA_API_URL="${GITEA_API_URL:?}"
|
||||
GITOPS_BRANCH="${GITOPS_BRANCH:-main}"
|
||||
|
||||
_gitops_substitute() {
|
||||
echo "$1" | sed "s/{{VERSION}}/$2/g"
|
||||
}
|
||||
|
||||
YQ_EXPR=$(_gitops_substitute "${YQ_TPL}" "${VERSION}")
|
||||
|
||||
GITEA_HOST=$(echo "${GITEA_API_URL}" | sed 's|https://||' | sed 's|http://||')
|
||||
CLONE_URL="https://${GITEA_TOKEN}@${GITEA_HOST}/${GITOPS_REPO}.git"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
_gitops_update() {
|
||||
CLONE_DIR=$(mktemp -d)
|
||||
git clone "$CLONE_URL" "$CLONE_DIR"
|
||||
cd "$CLONE_DIR"
|
||||
yq eval -i "$YQ_EXPR" "$INPUT_FILE"
|
||||
git add "$INPUT_FILE"
|
||||
git commit -m "[skip ci] gitops: update version to ${VERSION}"
|
||||
GITOPS_SHA=$(git rev-parse HEAD)
|
||||
git push
|
||||
ROOT_REPO="${SOURCE_REPO}" ROOT_COMMIT="${SOURCE_COMMIT}" \
|
||||
bash "${SCRIPT_DIR}/report-status.sh" success "GitOps updated to ${VERSION}" \
|
||||
"gitops/${SOURCE_REPO}" "" \
|
||||
"${GITEA_API_URL}/${GITOPS_REPO}/commits/${GITOPS_SHA}"
|
||||
ROOT_REPO="${GITOPS_REPO}" ROOT_COMMIT="${GITOPS_SHA}" \
|
||||
bash "${SCRIPT_DIR}/report-status.sh" success "Source build" \
|
||||
"source/${SOURCE_REPO}" "" \
|
||||
"${GITEA_API_URL}/${SOURCE_REPO}/commits/${SOURCE_COMMIT}"
|
||||
}
|
||||
|
||||
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
||||
_gitops_update
|
||||
fi
|
||||
@@ -0,0 +1,15 @@
|
||||
Feature: GitOps version update
|
||||
As a developer
|
||||
I want to automatically update version references in a GitOps repo
|
||||
So that deployment is triggered with the correct artifact version
|
||||
|
||||
Background:
|
||||
Given a project repository exists in Gitea
|
||||
And a commit has been pushed to the repository
|
||||
|
||||
@mock @real
|
||||
Scenario: GitOps repo receives version bump dispatch
|
||||
When a build completes successfully and dispatches a GitOps update
|
||||
Then the GitOps repo has a new commit with the updated version
|
||||
And the code repo shows a gitops status link to the GitOps commit
|
||||
And the GitOps repo shows a source status link to the code commit
|
||||
@@ -0,0 +1,71 @@
|
||||
const { execSync } = require('child_process');
|
||||
const { When, Then } = require('@cucumber/cucumber');
|
||||
const path = require('path');
|
||||
|
||||
const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..');
|
||||
const MOCK_SCRIPT = path.join(PROJECT_ROOT, 'tests', 'helpers', 'mock-api.sh');
|
||||
const GITOPS_SCRIPT = path.join(PROJECT_ROOT, 'scripts', 'gitops-update.sh');
|
||||
|
||||
function bash(cmd) {
|
||||
try {
|
||||
const out = execSync(`bash -c '${cmd}'`, {
|
||||
cwd: PROJECT_ROOT,
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
return { status: 0, stdout: out };
|
||||
} catch (e) {
|
||||
return { status: e.status, stdout: e.stdout || '', stderr: e.stderr || '' };
|
||||
}
|
||||
}
|
||||
|
||||
function getFirstBody() {
|
||||
return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_first_request_body`).stdout.trim();
|
||||
}
|
||||
|
||||
function getFirstPath() {
|
||||
return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_first_request_path`).stdout.trim();
|
||||
}
|
||||
|
||||
function getLastBody() {
|
||||
return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_request_body`).stdout.trim();
|
||||
}
|
||||
|
||||
function getLastPath() {
|
||||
return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_request_path`).stdout.trim();
|
||||
}
|
||||
|
||||
When('a build completes successfully and dispatches a GitOps update', function () {
|
||||
const env = [
|
||||
'INPUT_FILE=dev/Chart.yaml',
|
||||
'YQ_TPL=\'(.version) = "{{VERSION}}"\'',
|
||||
'VERSION=0.2.3',
|
||||
'SOURCE_REPO=niko/app',
|
||||
'SOURCE_COMMIT=abc123def456',
|
||||
'GITOPS_REPO=niko/app-gitops',
|
||||
'GITEA_API_URL=http://localhost:18080',
|
||||
'GITEA_TOKEN=test-token',
|
||||
].join(' ');
|
||||
const r = bash(`${env} bash "${GITOPS_SCRIPT}"`);
|
||||
if (r.status !== 0) throw new Error(`Expected exit 0, got ${r.status}: ${r.stderr}`);
|
||||
});
|
||||
|
||||
Then('the GitOps repo has a new commit with the updated version', function () {
|
||||
const out = bash(`git -C /tmp log --oneline -1 2>/dev/null || echo "no-git-log"`);
|
||||
});
|
||||
|
||||
Then('the code repo shows a gitops status link to the GitOps commit', function () {
|
||||
const body = getFirstBody();
|
||||
if (!body.includes('"state":"success"')) throw new Error('Expected success status');
|
||||
if (!body.includes('"context":"gitops/niko/app"')) throw new Error('Expected gitops context');
|
||||
const pathStr = getFirstPath();
|
||||
if (!pathStr.includes('/repos/niko/app/statuses/')) throw new Error('Expected source repo status path');
|
||||
});
|
||||
|
||||
Then('the GitOps repo shows a source status link to the code commit', function () {
|
||||
const body = getLastBody();
|
||||
if (!body.includes('"state":"success"')) throw new Error('Expected success status');
|
||||
if (!body.includes('"context":"source/niko/app"')) throw new Error('Expected source context');
|
||||
const pathStr = getLastPath();
|
||||
if (!pathStr.includes('/repos/niko/app-gitops/statuses/')) throw new Error('Expected gitops repo status path');
|
||||
});
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
setup() {
|
||||
export INPUT_FILE=dev/Chart.yaml
|
||||
export YQ_TPL='version = "{{VERSION}}"'
|
||||
export VERSION=1.0.0
|
||||
export SOURCE_REPO=niko/app
|
||||
export SOURCE_COMMIT=abc123def456
|
||||
export GITOPS_REPO=niko/app-gitops
|
||||
export GITEA_TOKEN=test-token
|
||||
export GITEA_API_URL=http://localhost:18080
|
||||
}
|
||||
|
||||
teardown() {
|
||||
if type mock_stop &>/dev/null 2>&1; then
|
||||
mock_stop 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
@test "missing GITEA_API_URL causes exit 1" {
|
||||
unset GITEA_API_URL
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"GITEA_API_URL"* ]]
|
||||
}
|
||||
|
||||
@test "missing GITEA_TOKEN causes exit 1" {
|
||||
unset GITEA_TOKEN
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"GITEA_TOKEN"* ]]
|
||||
}
|
||||
|
||||
@test "missing INPUT_FILE causes exit 1" {
|
||||
unset INPUT_FILE
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"INPUT_FILE"* ]]
|
||||
}
|
||||
|
||||
@test "missing YQ_TPL causes exit 1" {
|
||||
unset YQ_TPL
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"YQ_TPL"* ]]
|
||||
}
|
||||
|
||||
@test "missing VERSION causes exit 1" {
|
||||
unset VERSION
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"VERSION"* ]]
|
||||
}
|
||||
|
||||
@test "missing SOURCE_REPO causes exit 1" {
|
||||
unset SOURCE_REPO
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"SOURCE_REPO"* ]]
|
||||
}
|
||||
|
||||
@test "missing SOURCE_COMMIT causes exit 1" {
|
||||
unset SOURCE_COMMIT
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"SOURCE_COMMIT"* ]]
|
||||
}
|
||||
|
||||
@test "_gitops_substitute replaces {{VERSION}}" {
|
||||
run bash -c '
|
||||
source scripts/gitops-update.sh >/dev/null 2>&1
|
||||
_gitops_substitute "(.version) = \"{{VERSION}}\"" "0.2.3"
|
||||
'
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == '(.version) = "0.2.3"' ]]
|
||||
}
|
||||
|
||||
@test "CLONE_URL is constructed correctly from GITEA_API_URL" {
|
||||
export GITEA_API_URL=https://gitea.app.keskikuja.site
|
||||
export GITEA_TOKEN=secret123
|
||||
export GITOPS_REPO=niko/app-gitops
|
||||
run bash -c '
|
||||
source scripts/gitops-update.sh >/dev/null 2>&1
|
||||
echo "$CLONE_URL"
|
||||
'
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "https://secret123@gitea.app.keskikuja.site/niko/app-gitops.git" ]
|
||||
}
|
||||
|
||||
@test "CLONE_URL works with http:// URL" {
|
||||
export GITEA_API_URL=http://localhost:18080
|
||||
export GITEA_TOKEN=token
|
||||
export GITOPS_REPO=owner/repo
|
||||
run bash -c '
|
||||
source scripts/gitops-update.sh >/dev/null 2>&1
|
||||
echo "$CLONE_URL"
|
||||
'
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "https://token@localhost:18080/owner/repo.git" ]
|
||||
}
|
||||
|
||||
@test "_gitops_substitute handles multiple {{VERSION}} occurrences" {
|
||||
run bash -c '
|
||||
source scripts/gitops-update.sh >/dev/null 2>&1
|
||||
_gitops_substitute "version = \"{{VERSION}}\"; tag = \"v{{VERSION}}\"" "1.2.3"
|
||||
'
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == 'version = "1.2.3"; tag = "v1.2.3"' ]]
|
||||
}
|
||||
|
||||
@test "git flow: clone yq add commit push" {
|
||||
source tests/helpers/mock-api.sh
|
||||
mock_set_sequence '[
|
||||
{"code":201},
|
||||
{"code":201}
|
||||
]'
|
||||
mock_start
|
||||
export GIT_CALLS_FILE=$(mktemp)
|
||||
export YQ_CALLS_FILE=$(mktemp)
|
||||
export PATH="${BATS_TEST_DIRNAME}/helpers:$PATH"
|
||||
export INPUT_FILE=dev/Chart.yaml
|
||||
export YQ_TPL='(.version) = "{{VERSION}}"'
|
||||
export VERSION=0.2.3
|
||||
export SOURCE_REPO=niko/app
|
||||
export SOURCE_COMMIT=abc123def456
|
||||
export GITOPS_REPO=niko/app-gitops
|
||||
export GITEA_API_URL=http://localhost:18080
|
||||
export GITEA_TOKEN=test-token
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 0 ]
|
||||
git_calls=$(cat "$GIT_CALLS_FILE")
|
||||
[[ "$git_calls" == *"clone"* ]]
|
||||
[[ "$git_calls" == *"add"* ]]
|
||||
[[ "$git_calls" == *"commit"* ]]
|
||||
[[ "$git_calls" == *"push"* ]]
|
||||
yq_calls=$(cat "$YQ_CALLS_FILE")
|
||||
[[ "$yq_calls" == *"eval -i"* ]]
|
||||
rm -f "$GIT_CALLS_FILE" "$YQ_CALLS_FILE"
|
||||
mock_stop
|
||||
}
|
||||
|
||||
@test "two commit-status calls: code-repo and gitops-repo" {
|
||||
source tests/helpers/mock-api.sh
|
||||
mock_set_sequence '[
|
||||
{"code":201},
|
||||
{"code":201}
|
||||
]'
|
||||
mock_start
|
||||
export GIT_CALLS_FILE=$(mktemp)
|
||||
export YQ_CALLS_FILE=$(mktemp)
|
||||
export PATH="${BATS_TEST_DIRNAME}/helpers:$PATH"
|
||||
export INPUT_FILE=dev/Chart.yaml
|
||||
export YQ_TPL='(.version) = "{{VERSION}}"'
|
||||
export VERSION=0.2.3
|
||||
export SOURCE_REPO=niko/app
|
||||
export SOURCE_COMMIT=abc123def456
|
||||
export GITOPS_REPO=niko/app-gitops
|
||||
export GITEA_API_URL=http://localhost:18080
|
||||
export GITEA_TOKEN=test-token
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 0 ]
|
||||
path1=$(mock_get_first_request_path)
|
||||
body1=$(mock_get_first_request_body)
|
||||
[[ "$path1" == *"/repos/niko/app/statuses/"* ]]
|
||||
[[ "$body1" == *'"context":"gitops/niko/app"'* ]]
|
||||
path2=$(mock_get_request_path)
|
||||
body2=$(mock_get_request_body)
|
||||
[[ "$path2" == *"/repos/niko/app-gitops/statuses/"* ]]
|
||||
[[ "$body2" == *'"context":"source/niko/app"'* ]]
|
||||
rm -f "$GIT_CALLS_FILE" "$YQ_CALLS_FILE"
|
||||
mock_stop
|
||||
}
|
||||
|
||||
@test "missing GITOPS_REPO causes exit 1" {
|
||||
unset GITOPS_REPO
|
||||
run bash scripts/gitops-update.sh
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"GITOPS_REPO"* ]]
|
||||
}
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
echo "git $*" >> "${GIT_CALLS_FILE:-/dev/null}"
|
||||
|
||||
case "$1" in
|
||||
clone)
|
||||
TARGET_DIR="${@: -1}"
|
||||
mkdir -p "$TARGET_DIR"
|
||||
cd "$TARGET_DIR"
|
||||
git init --initial-branch=main 2>/dev/null
|
||||
git config user.email "mock@test.com"
|
||||
git config user.name "Mock Test"
|
||||
mkdir -p "$(dirname "$INPUT_FILE")"
|
||||
echo 'version: 0.1.0' > "$INPUT_FILE"
|
||||
git add -A 2>/dev/null
|
||||
git commit -m "initial" 2>/dev/null
|
||||
echo "Cloning into '$TARGET_DIR'..."
|
||||
;;
|
||||
add|commit|push|config|init)
|
||||
;;
|
||||
rev-parse)
|
||||
echo "mock-sha-9876543210fedcba9876543210fedcba98765432"
|
||||
;;
|
||||
*)
|
||||
echo "git: unknown command: $*" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
echo "yq $*" >> "${YQ_CALLS_FILE:-/dev/null}"
|
||||
Reference in New Issue
Block a user