feat: Tekton-based integration testing (proper K8s CI/CD)
CI / CI (pull_request) Canceled after 41s

Replace ad-hoc K8s Job with proper Tekton TaskRun:

k8s/tekton/integration-test-task.yaml:
  - Tekton Task for integration testing
  - Two stages: migrate + test
  - Runs existing Rust integration tests:
    * it_phase3_phase4 (ingest + persistence)
    * it_unified_query_4_6 (query endpoint)
    * it_temporal_filtering_4_2_fixed (temporal)
    * mem_ingest (extraction pipeline)
    * mem_cli::query (query handler)
  - Reports results to /tekton/results/summary
  - Resource limits: 1Gi mem, 500m CPU

.gitea/workflows/build.yaml:
  - Integrated Tekton trigger after image push
  - Create TaskRun with image SHA
  - Wait for completion (5m timeout)
  - Gate image promotion on test passing
  - Only promote to :latest if tests pass

Pattern (from homelab-frontend):
  1. Build image → push with SHA
  2. Trigger Tekton TaskRun
  3. Wait for result
  4. Gate promotion
  5. Promote to :latest only if tests pass

Benefits:
  ✓ Proper K8s CI/CD framework
  ✓ Reusable Task
  ✓ Better logging/results
  ✓ Proper resource mgmt
  ✓ Matches homelab pattern

Requires:
  - Tekton Pipelines installed in cluster
  - KUBECONFIG_B64 secret in Forgejo
This commit is contained in:
2026-09-14 23:00:57 +09:00
parent ce6c93d3b5
commit a72719a68f
6 changed files with 244 additions and 328 deletions
+94 -1
View File
@@ -71,7 +71,100 @@ jobs:
docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}"
echo "Pushed: ${IMAGE}:${{ steps.sha.outputs.short_sha }}"
- name: Prune unused images and cleanup
- name: Install kubectl
run: |
apt-get update
apt-get install -y kubectl
- name: Setup kubeconfig for Tekton
run: |
mkdir -p ~/.kube
echo "${KUBECONFIG_B64}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
kubectl cluster-info 2>&1 | head -3
echo "✓ kubeconfig ready"
env:
KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }}
- name: Trigger Tekton TaskRun (integration tests)
id: tekton
run: |
SHA="${{ steps.sha.outputs.short_sha }}"
RUN_NAME="integration-test-${SHA}"
NAMESPACE="poimen"
IMAGE="${REGISTRY}/riotpiao-poimen/poimen-memory:${SHA}"
echo "Triggering Tekton TaskRun: ${RUN_NAME}"
echo "Image: ${IMAGE}"
echo ""
# Create TaskRun
cat <<YAML | kubectl create -f -
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: ${RUN_NAME}
namespace: ${NAMESPACE}
labels:
commit-sha: "${SHA}"
spec:
taskRef:
name: poimen-integration-test
params:
- name: image
value: "${IMAGE}"
YAML
echo "✓ TaskRun created"
echo ""
echo "Waiting for completion (timeout 5m)..."
# Wait for TaskRun to complete
if kubectl wait taskrun/${RUN_NAME} -n ${NAMESPACE} \
--for=condition=Succeeded --timeout=5m 2>/dev/null; then
echo "result=pass" >> $GITHUB_OUTPUT
echo "✓ Tests passed"
else
echo "result=fail" >> $GITHUB_OUTPUT
echo "✗ Tests failed or timed out"
fi
# Print test summary
echo ""
echo "=== TaskRun Status ==="
REASON=$(kubectl get taskrun ${RUN_NAME} -n ${NAMESPACE} \
-o jsonpath='{.status.conditions[0].reason}')
SUMMARY=$(kubectl get taskrun ${RUN_NAME} -n ${NAMESPACE} \
-o jsonpath='{.status.results[?(@.name=="summary")].value}')
echo "Status: ${REASON}"
echo "Summary: ${SUMMARY}"
# Print logs
echo ""
echo "=== Test Logs ==="
POD=$(kubectl get pod -n ${NAMESPACE} \
-l tekton.dev/taskRun=${RUN_NAME} -o name | head -1)
kubectl logs -n ${NAMESPACE} "${POD}" -c step-test 2>/dev/null | tail -200 || true
- name: Gate on test result
if: steps.tekton.outputs.result != 'pass'
run: |
echo "✗ Integration tests FAILED"
echo "Image NOT promoted to :latest"
exit 1
- name: Promote image to latest
run: |
docker login -u "${REGISTRY_USER}" -p "${REGISTRY_TOKEN}" "${REGISTRY}"
docker tag "${IMAGE}:${{ steps.sha.outputs.short_sha }}" "${IMAGE}:latest"
docker push "${IMAGE}:latest"
echo "✓ Promoted to :latest"
env:
REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
- name: Cleanup
if: always()
run: |
docker image prune -a --force 2>&1 | tail -3 || true
cargo clean || true
-133
View File
@@ -1,133 +0,0 @@
name: Integration Test
on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]
workflow_dispatch:
inputs:
image_sha:
description: 'Image SHA to test (defaults to latest on main)'
required: false
env:
REGISTRY: forgejo.riotpiao.com
IMAGE: forgejo.riotpiao.com/riotpiao-poimen/poimen-memory
NAMESPACE: poimen
jobs:
integration-test:
name: K8s Integration Test
runs-on: rust
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get image SHA
id: image
run: |
if [ -n "${{ github.event.inputs.image_sha }}" ]; then
SHA="${{ github.event.inputs.image_sha }}"
else
SHA="$(git rev-parse --short HEAD)"
fi
echo "sha=$SHA" >> $GITHUB_OUTPUT
echo "Image SHA: $SHA"
- name: Install kubectl
run: |
apt-get update
apt-get install -y kubectl postgresql-client
- name: Setup kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG_B64 }}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
# Verify cluster access
kubectl cluster-info
kubectl get nodes
- name: Verify image exists in registry
run: |
IMAGE="${{ env.IMAGE }}:${{ steps.image.outputs.sha }}"
echo "Checking if image exists: $IMAGE"
# Use registry API to verify image exists
if docker pull "$IMAGE" 2>/dev/null; then
echo "✓ Image found in registry"
else
echo "✗ Image not found"
exit 1
fi
env:
DOCKER_CONFIG: /tmp/docker
continue-on-error: true
- name: Apply integration test Job
run: |
IMAGE_SHA="${{ steps.image.outputs.sha }}"
echo "Creating integration test Job with image: $IMAGE_SHA"
echo ""
# Substitute image SHA in manifest
cat k8s/test/integration-test-job.yaml | \
sed "s|IMAGE_SHA|$IMAGE_SHA|g" | \
kubectl apply -f - -n ${{ env.NAMESPACE }}
echo "✓ Job submitted"
echo ""
# Wait for job to complete
kubectl wait --for=condition=complete job/poimen-memory-integration-test \
-n ${{ env.NAMESPACE }} \
--timeout=600s || {
echo ""
echo "✗ Job did not complete in time"
echo ""
echo "Pod logs:"
kubectl logs -l test=integration -n ${{ env.NAMESPACE }} --all-containers=true --tail=100
exit 1
}
- name: Collect test results
if: always()
run: |
echo "=========================================="
echo "Integration Test Results"
echo "=========================================="
echo ""
echo "Job status:"
kubectl describe job poimen-memory-integration-test -n ${{ env.NAMESPACE }} | tail -20
echo ""
echo "Pod logs:"
kubectl logs -l test=integration -n ${{ env.NAMESPACE }} --all-containers=true || true
echo ""
# Get job status
STATUS=$(kubectl get job poimen-memory-integration-test \
-n ${{ env.NAMESPACE }} \
-o jsonpath='{.status.succeeded}')
if [ "$STATUS" = "1" ]; then
echo "✓ Integration test PASSED"
exit 0
else
echo "✗ Integration test FAILED"
exit 1
fi
- name: Cleanup test Job
if: always()
run: |
echo "Cleaning up test resources..."
kubectl delete job poimen-memory-integration-test \
-n ${{ env.NAMESPACE }} \
--ignore-not-found=true
echo "✓ Cleanup complete"