diff --git a/CHANGELOG.md b/CHANGELOG.md index ae568c5..cbbda94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,3 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ### Added - Initial release. +- Dockerfile: scratch-based image with static musl binary (~4 MB compressed). +- Helm chart: deployment, service, headless, configmap, secret, HPA, optional PVC, StatefulSet for Meilisearch, Meilisearch service, optional Redis deployment, serviceaccount, PrometheusRule, ServiceMonitor, Grafana dashboard. +- `values.schema.json` rejects incompatible configs: SQLite with HA, HPA without Redis, local rate limits in multi-replica, scoped key rotation >= max age. +- Argo WorkflowTemplate `miroir-ci`: checkout → lint → test → musl build → Kaniko push (tag-gated) → GitHub release (tag-gated). +- Argo WorkflowTemplate `miroir-ci-smoke`: quick lint+test on push. +- ArgoCD Application `miroir-dev-ardenone-cluster` deployed to ardenone-cluster. +- `scripts/bump-version.sh` for coordinated Cargo.toml + Chart.yaml version bumps. +- `scripts/release-ready-check.sh` validates version consistency across Cargo.toml, Chart.yaml, CHANGELOG.md. diff --git a/k8s/argo-workflows/miroir-ci-smoke.yaml b/k8s/argo-workflows/miroir-ci-smoke.yaml new file mode 100644 index 0000000..a81ec07 --- /dev/null +++ b/k8s/argo-workflows/miroir-ci-smoke.yaml @@ -0,0 +1,55 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: miroir-ci-smoke + namespace: argo-workflows + labels: + app: miroir-ci-smoke +spec: + entrypoint: smoke + serviceAccountName: argo-workflow + arguments: + parameters: + - name: branch + value: main + templates: + - name: smoke + container: + image: rust:1.87-slim + command: [bash, -c] + args: + - | + set -e + + apt-get update -qq && apt-get install -y -qq git pkg-config libssl-dev >/dev/null 2>&1 + + BRANCH="{{workflow.parameters.branch}}" + git clone --depth 1 --branch "$BRANCH" \ + "https://x-access-token:${GH_TOKEN}@github.com/jedarden/miroir.git" \ + /workspace + cd /workspace + + echo "=== cargo fmt --check ===" + cargo fmt --all -- --check + + echo "=== cargo clippy -D warnings ===" + cargo clippy --all-targets -- -D warnings + + echo "=== cargo test --all ===" + cargo test --all + + echo "=== smoke passed ===" + env: + - name: GH_TOKEN + valueFrom: + secretKeyRef: + name: github-webhook-secret + key: token + resources: + requests: + cpu: 1000m + memory: 2Gi + limits: + cpu: 4000m + memory: 8Gi + activeDeadlineSeconds: 600 diff --git a/k8s/argo-workflows/miroir-ci.yaml b/k8s/argo-workflows/miroir-ci.yaml new file mode 100644 index 0000000..1d22303 --- /dev/null +++ b/k8s/argo-workflows/miroir-ci.yaml @@ -0,0 +1,279 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: miroir-ci + namespace: argo-workflows + labels: + app: miroir-ci +spec: + entrypoint: pipeline + serviceAccountName: argo-workflow + arguments: + parameters: + - name: repo + value: https://github.com/jedarden/miroir.git + - name: revision + value: main + - name: tag + value: "" + volumeClaimTemplates: + - metadata: + name: workspace + spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 8Gi + volumes: + - name: ghcr-config + secret: + secretName: ghcr-credentials + items: + - key: .dockerconfigjson + path: config.json + templates: + - name: pipeline + dag: + tasks: + - name: checkout + template: git-checkout + - name: lint + template: cargo-lint + dependencies: [checkout] + - name: test + template: cargo-test + dependencies: [checkout] + - name: build + template: cargo-build + dependencies: [lint, test] + - name: docker + template: docker-build-push + dependencies: [build] + when: "'{{workflow.parameters.tag}}' != ''" + - name: release + template: create-github-release + dependencies: [build] + when: "'{{workflow.parameters.tag}}' != ''" + + - name: git-checkout + activeDeadlineSeconds: 300 + container: + image: alpine/git:2.43.0 + command: [sh, -c] + args: + - | + set -e + REPO="{{workflow.parameters.repo}}" + REPO_PATH="${REPO#https://}" + git clone --depth 1 --branch "{{workflow.parameters.revision}}" \ + "https://x-access-token:${GH_TOKEN}@${REPO_PATH}" /workspace/src + env: + - name: GH_TOKEN + valueFrom: + secretKeyRef: + name: github-token + key: token + volumeMounts: + - name: workspace + mountPath: /workspace + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + + - name: cargo-lint + activeDeadlineSeconds: 900 + container: + image: rust:1.87-slim + command: [bash, -c] + args: + - | + set -e + apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev >/dev/null 2>&1 + cd /workspace/src + export CARGO_TARGET_DIR=/workspace/target-lint + cargo fmt --check + cargo clippy --all-targets -- -D warnings + volumeMounts: + - name: workspace + mountPath: /workspace + resources: + requests: + cpu: 1000m + memory: 2Gi + limits: + cpu: 2000m + memory: 4Gi + + - name: cargo-test + activeDeadlineSeconds: 900 + container: + image: rust:1.87-slim + command: [bash, -c] + args: + - | + set -e + apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev >/dev/null 2>&1 + cd /workspace/src + export CARGO_TARGET_DIR=/workspace/target-test + cargo test --all --all-features + volumeMounts: + - name: workspace + mountPath: /workspace + resources: + requests: + cpu: 2000m + memory: 4Gi + limits: + cpu: 4000m + memory: 8Gi + + - name: cargo-build + activeDeadlineSeconds: 1800 + container: + image: rust:1.87-slim + command: [bash, -c] + args: + - | + set -e + apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev musl-tools >/dev/null 2>&1 + cd /workspace/src + rustup target add x86_64-unknown-linux-musl + + cargo build --release --target x86_64-unknown-linux-musl \ + -p miroir-proxy -p miroir-ctl + + mkdir -p /workspace/dist + cp target/x86_64-unknown-linux-musl/release/miroir-proxy \ + /workspace/dist/miroir-proxy-linux-amd64 + cp target/x86_64-unknown-linux-musl/release/miroir-ctl \ + /workspace/dist/miroir-ctl-linux-amd64 + + sha256sum /workspace/dist/miroir-proxy-linux-amd64 \ + > /workspace/dist/miroir-proxy-linux-amd64.sha256 + sha256sum /workspace/dist/miroir-ctl-linux-amd64 \ + > /workspace/dist/miroir-ctl-linux-amd64.sha256 + + # Copy proxy binary to source dir for docker context (Dockerfile expects it there) + cp /workspace/dist/miroir-proxy-linux-amd64 \ + /workspace/src/miroir-proxy-linux-amd64 + + echo "=== build checksums ===" + cat /workspace/dist/miroir-proxy-linux-amd64.sha256 + cat /workspace/dist/miroir-ctl-linux-amd64.sha256 + volumeMounts: + - name: workspace + mountPath: /workspace + resources: + requests: + cpu: 4000m + memory: 8Gi + limits: + cpu: 4000m + memory: 8Gi + + - name: docker-build-push + activeDeadlineSeconds: 1800 + container: + image: gcr.io/kaniko-project/executor:v1.23.0-debug + command: [/busybox/sh, -c] + args: + - | + set -e + TAG="{{workflow.parameters.tag}}" + REGISTRY="ghcr.io/jedarden/miroir" + VERSION="${TAG#v}" + + DESTS="--destination=${REGISTRY}:${TAG}" + + # Stable release (vX.Y.Z): also push float tags and :latest + # Pre-release (vX.Y.Z-rc.N) and branch builds: exact tag only + if echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + MINOR=$(echo "$TAG" | sed 's/^v\([0-9]*\.[0-9]*\)\..*/\1/') + MAJOR=$(echo "$TAG" | sed 's/^v\([0-9]*\)\..*/\1/') + DESTS="${DESTS} --destination=${REGISTRY}:${MINOR}" + DESTS="${DESTS} --destination=${REGISTRY}:${MAJOR}" + DESTS="${DESTS} --destination=${REGISTRY}:latest" + fi + + /kaniko/executor \ + --dockerfile=/workspace/src/Dockerfile \ + --context=/workspace/src \ + --build-arg=VERSION=${VERSION} \ + --build-arg=REVISION={{workflow.parameters.revision}} \ + $DESTS \ + --cache=true \ + --cache-repo=${REGISTRY}/cache \ + --snapshot-mode=redo \ + --use-new-run=true + volumeMounts: + - name: workspace + mountPath: /workspace + - name: ghcr-config + mountPath: /kaniko/.docker + resources: + requests: + cpu: 2000m + memory: 4Gi + limits: + cpu: 4000m + memory: 8Gi + + - name: create-github-release + activeDeadlineSeconds: 600 + container: + image: ghcr.io/cli/cli:2.49.0 + command: [sh, -c] + args: + - | + set -e + TAG="{{workflow.parameters.tag}}" + VER="${TAG#v}" + + # Extract release notes from CHANGELOG.md for this version + NOTES=$(awk -v ver="$VER" ' + found && /^## \[/ { exit } + $0 ~ ("^## \\[" ver "\\]") { found=1; next } + found { print } + ' /workspace/src/CHANGELOG.md) + + if [ -z "$NOTES" ]; then + NOTES="Release ${TAG}" + fi + + # Skip if release already exists + if gh release view "${TAG}" --repo jedarden/miroir >/dev/null 2>&1; then + echo "Release ${TAG} already exists, skipping." + exit 0 + fi + + gh release create "${TAG}" \ + --repo jedarden/miroir \ + --title "miroir ${TAG}" \ + --notes "${NOTES}" \ + --target "{{workflow.parameters.revision}}" \ + /workspace/dist/miroir-proxy-linux-amd64 \ + /workspace/dist/miroir-proxy-linux-amd64.sha256 \ + /workspace/dist/miroir-ctl-linux-amd64 \ + /workspace/dist/miroir-ctl-linux-amd64.sha256 + + echo "Release ${TAG} created successfully." + env: + - name: GH_TOKEN + valueFrom: + secretKeyRef: + name: github-token + key: token + volumeMounts: + - name: workspace + mountPath: /workspace + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi diff --git a/k8s/argocd/miroir-dev-application.yaml b/k8s/argocd/miroir-dev-application.yaml new file mode 100644 index 0000000..f003ec5 --- /dev/null +++ b/k8s/argocd/miroir-dev-application.yaml @@ -0,0 +1,88 @@ +--- +# ArgoCD Application: miroir-dev on ardenone-cluster +# Deploy to: jedarden/declarative-config → k8s/ardenone-cluster/miroir-dev/ +# +# Prerequisites: +# - Namespace: miroir-dev (namespace.yml) +# - ExternalSecret or manual Secret: miroir-dev-keys +# (keys: masterKey, nodeMasterKey, adminApiKey) +# - Helm chart published to ghcr.io/jedarden/charts/miroir +# +# This file is the source of truth. Copy to declarative-config for ArgoCD sync. +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: miroir-dev-ardenone-cluster + namespace: argocd + labels: + app.kubernetes.io/name: miroir + app.kubernetes.io/instance: miroir-dev + app.kubernetes.io/component: application +spec: + project: default + source: + repoURL: ghcr.io/jedarden/charts + chart: miroir + targetRevision: 0.1.0 + helm: + valuesObject: + miroir: + replicas: 1 + shards: 64 + replicationFactor: 1 + replicaGroups: 1 + existingSecret: miroir-dev-keys + logLevel: info + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 250m + memory: 512Mi + taskStore: + backend: sqlite + path: /data/miroir-tasks.db + meilisearch: + enabled: true + replicas: 2 + nodesPerGroup: 2 + persistence: + enabled: true + size: 10Gi + resources: + limits: + cpu: 1000m + memory: 2Gi + requests: + cpu: 250m + memory: 512Mi + redis: + enabled: false + serviceMonitor: + enabled: true + interval: 30s + prometheusRule: + enabled: true + destination: + server: https://k3s-server-a.ardenone.com:6443 + namespace: miroir-dev + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: true + syncOptions: + - Validate=true + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true + - RespectIgnoreDifferences=true + - ServerSideApply=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + revisionHistoryLimit: 10