diff --git a/.github/workflows/loadtest.yml b/.github/workflows/loadtest.yml index c997e13..dbe5d9c 100644 --- a/.github/workflows/loadtest.yml +++ b/.github/workflows/loadtest.yml @@ -54,7 +54,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.26' cache: false - name: Set up Docker Buildx diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index c428826..9b403bf 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -22,6 +22,7 @@ env: KUBERNETES_VERSION: "1.30.0" KIND_VERSION: "0.23.0" REGISTRY: ghcr.io + RELOADER_EDITION: oss jobs: qa: @@ -154,6 +155,7 @@ jobs: VERSION=merge-${{ steps.generate_tag.outputs.GIT_TAG }} COMMIT=${{github.event.pull_request.head.sha}} BUILD_DATE=${{ steps.prep.outputs.created }} + EDITION=${{ env.RELOADER_EDITION }} BUILD_PARAMETERS=${{ env.BUILD_PARAMETERS }} cache-to: type=inline @@ -173,6 +175,10 @@ jobs: pull: true push: false build-args: | + VERSION=merge-${{ steps.generate_tag.outputs.GIT_UBI_TAG }} + COMMIT=${{github.event.pull_request.head.sha}} + BUILD_DATE=${{ steps.prep.outputs.created }} + EDITION=${{ env.RELOADER_EDITION }} BUILD_PARAMETERS=${{ env.BUILD_PARAMETERS }} BUILDER_IMAGE=${{ env.GHCR_IMAGE_REPOSITORY }}:${{ steps.highest_tag.outputs.tag }} cache-to: type=inline diff --git a/.github/workflows/pull_request_docs.yaml b/.github/workflows/pull_request_docs.yaml index dd416bd..7416237 100644 --- a/.github/workflows/pull_request_docs.yaml +++ b/.github/workflows/pull_request_docs.yaml @@ -10,6 +10,7 @@ on: - 'Dockerfile-docs' - 'docs-nginx.conf' - 'docs/**' + - '!docs/plans/**' - 'theme_common' - 'theme_override' - 'deployments/kubernetes/chart/reloader/README.md' diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index dda9a1c..6340f0f 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -15,6 +15,7 @@ env: KIND_VERSION: "0.23.0" HELM_REGISTRY_URL: "https://stakater.github.io/stakater-charts" REGISTRY: ghcr.io + RELOADER_EDITION: oss jobs: build: @@ -103,7 +104,12 @@ jobs: file: ${{ env.DOCKER_FILE_PATH }} pull: true push: true - build-args: BUILD_PARAMETERS=${{ env.BUILD_PARAMETERS }} + build-args: | + VERSION=merge-${{ github.event.number }} + COMMIT=${{ github.sha }} + BUILD_DATE=${{ steps.prep.outputs.created }} + EDITION=${{ env.RELOADER_EDITION }} + BUILD_PARAMETERS=${{ env.BUILD_PARAMETERS }} cache-to: type=inline platforms: linux/amd64,linux/arm,linux/arm64 tags: | @@ -152,6 +158,7 @@ jobs: VERSION=merge-${{ github.event.number }} COMMIT=${{ github.sha }} BUILD_DATE=${{ steps.prep.outputs.created }} + EDITION=${{ env.RELOADER_EDITION }} BUILD_PARAMETERS=${{ env.BUILD_PARAMETERS }} cache-to: type=inline platforms: linux/amd64,linux/arm,linux/arm64 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6bd392f..32cecd6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,6 +11,7 @@ env: KUBERNETES_VERSION: "1.30.0" KIND_VERSION: "0.23.0" REGISTRY: ghcr.io + RELOADER_EDITION: oss jobs: release: @@ -110,6 +111,7 @@ jobs: VERSION=${{ steps.generate_tag.outputs.RELEASE_VERSION }} COMMIT=${{ github.sha }} BUILD_DATE=${{ steps.prep.outputs.created }} + EDITION=${{ env.RELOADER_EDITION }} labels: | org.opencontainers.image.source=${{ github.event.repository.clone_url }} org.opencontainers.image.created=${{ steps.prep.outputs.created }} @@ -160,6 +162,7 @@ jobs: VERSION=${{ steps.generate_tag.outputs.RELEASE_VERSION }} COMMIT=${{ github.sha }} BUILD_DATE=${{ steps.prep.outputs.created }} + EDITION=${{ env.RELOADER_EDITION }} labels: | org.opencontainers.image.source=${{ github.event.repository.clone_url }} org.opencontainers.image.created=${{ steps.prep.outputs.created }} diff --git a/Dockerfile b/Dockerfile index 53cc26d..e76b396 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ ARG BUILDER_IMAGE ARG BASE_IMAGE # Build the manager binary -FROM --platform=${BUILDPLATFORM} ${BUILDER_IMAGE:-golang:1.25.5} AS builder +FROM --platform=${BUILDPLATFORM} ${BUILDER_IMAGE:-golang:1.26} AS builder ARG TARGETOS ARG TARGETARCH @@ -12,6 +12,7 @@ ARG GOPRIVATE ARG COMMIT ARG VERSION ARG BUILD_DATE +ARG EDITION=oss WORKDIR /workspace @@ -36,7 +37,8 @@ RUN CGO_ENABLED=0 \ GO111MODULE=on \ go build -ldflags="-s -w -X github.com/stakater/Reloader/pkg/common.Version=${VERSION} \ -X github.com/stakater/Reloader/pkg/common.Commit=${COMMIT} \ - -X github.com/stakater/Reloader/pkg/common.BuildDate=${BUILD_DATE}" \ + -X github.com/stakater/Reloader/pkg/common.BuildDate=${BUILD_DATE} \ + -X github.com/stakater/Reloader/pkg/common.Edition=${EDITION}" \ -installsuffix 'static' -mod=mod -a -o manager ./ # Use distroless as minimal base image to package the manager binary diff --git a/VERSION b/VERSION index f86e029..323afbc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.12 +1.4.14 diff --git a/deployments/kubernetes/chart/reloader/Chart.yaml b/deployments/kubernetes/chart/reloader/Chart.yaml index 536fd6b..4fb1a70 100644 --- a/deployments/kubernetes/chart/reloader/Chart.yaml +++ b/deployments/kubernetes/chart/reloader/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 name: reloader description: Reloader chart that runs on kubernetes -version: 2.2.7 -appVersion: v1.4.12 +version: 2.2.9 +appVersion: v1.4.14 keywords: - Reloader - kubernetes diff --git a/deployments/kubernetes/chart/reloader/README.md b/deployments/kubernetes/chart/reloader/README.md index b3ba973..d72cec4 100644 --- a/deployments/kubernetes/chart/reloader/README.md +++ b/deployments/kubernetes/chart/reloader/README.md @@ -139,21 +139,26 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}} #### 🔄 `reloadOnCreate` Behavior **When true:** -✅ New ConfigMaps/Secrets trigger rolling updates -✅ New deployments referencing existing resources reload +✅ New ConfigMaps/Secrets trigger rolling updates for referencing workloads + +**When false:** +❌ ConfigMaps/Secrets creations have no effect on referencing workloads + +#### 🗑️ `reloadOnDelete` Behavior +**When true:** +✅ Deleted ConfigMaps/Secrets trigger rolling updates for referencing workloads + +**When false:** +❌ ConfigMaps/Secrets deletions have no effect on referencing workloads + +#### 🔄 `syncAfterRestart` Behavior +**When true:** ✅ In HA mode, new leader reloads all tracked workloads **When false:** ❌ Updates during leader downtime are missed ⏳ Potential 15s delay window (default `LeaseDuration`) -#### 🗑️ `reloadOnDelete` Behavior -**When true:** -✅ Deleted resources trigger rolling updates of referencing workloads - -**When false:** -❌ Deletions have no effect on referencing pods - #### Default Settings ⚠️ All flags default to `false` (must be enabled explicitly): - `reloadOnCreate` diff --git a/deployments/kubernetes/chart/reloader/values.yaml b/deployments/kubernetes/chart/reloader/values.yaml index a607491..dabffe6 100644 --- a/deployments/kubernetes/chart/reloader/values.yaml +++ b/deployments/kubernetes/chart/reloader/values.yaml @@ -19,7 +19,7 @@ fullnameOverride: "" image: name: stakater/reloader repository: ghcr.io/stakater/reloader - tag: v1.4.12 + tag: v1.4.14 # digest: sha256:1234567 pullPolicy: IfNotPresent @@ -133,7 +133,7 @@ reloader: labels: provider: stakater group: com.stakater.platform - version: v1.4.12 + version: v1.4.14 # Support for extra environment variables. env: # Open supports Key value pair as environment variables. diff --git a/deployments/kubernetes/manifests/deployment.yaml b/deployments/kubernetes/manifests/deployment.yaml index e27dae9..75c66f5 100644 --- a/deployments/kubernetes/manifests/deployment.yaml +++ b/deployments/kubernetes/manifests/deployment.yaml @@ -17,7 +17,7 @@ spec: app: reloader-reloader spec: containers: - - image: "ghcr.io/stakater/reloader:v1.4.12" + - image: "ghcr.io/stakater/reloader:v1.4.14" imagePullPolicy: IfNotPresent name: reloader-reloader env: diff --git a/deployments/kubernetes/reloader.yaml b/deployments/kubernetes/reloader.yaml index 334b2a7..bcd376b 100644 --- a/deployments/kubernetes/reloader.yaml +++ b/deployments/kubernetes/reloader.yaml @@ -141,7 +141,7 @@ spec: fieldPath: metadata.namespace - name: RELOADER_DEPLOYMENT_NAME value: reloader-reloader - image: ghcr.io/stakater/reloader:v1.4.12 + image: ghcr.io/stakater/reloader:v1.4.14 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 5 diff --git a/go.mod b/go.mod index 48f13d8..9e57e3e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/stakater/Reloader -go 1.25.5 +go 1.26 require ( github.com/argoproj/argo-rollouts v1.8.3 @@ -15,7 +15,6 @@ require ( k8s.io/apimachinery v0.35.0 k8s.io/client-go v0.35.0 k8s.io/kubectl v0.35.0 - k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 sigs.k8s.io/secrets-store-csi-driver v1.5.5 ) @@ -65,6 +64,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect diff --git a/internal/pkg/leadership/leadership_test.go b/internal/pkg/leadership/leadership_test.go index eed0705..dc55522 100644 --- a/internal/pkg/leadership/leadership_test.go +++ b/internal/pkg/leadership/leadership_test.go @@ -45,7 +45,7 @@ func TestHealthz(t *testing.T) { want := 200 if got != want { - t.Fatalf("got: %q, want: %q", got, want) + t.Fatalf("got: %d, want: %d", got, want) } // Have the liveness probe serve a 500 @@ -63,7 +63,7 @@ func TestHealthz(t *testing.T) { want = 500 if got != want { - t.Fatalf("got: %q, want: %q", got, want) + t.Fatalf("got: %d, want: %d", got, want) } } @@ -89,7 +89,7 @@ func TestRunLeaderElection(t *testing.T) { want := 500 if got != want { - t.Fatalf("got: %q, want: %q", got, want) + t.Fatalf("got: %d, want: %d", got, want) } // Cancel the leader election context, so leadership is released and @@ -108,7 +108,7 @@ func TestRunLeaderElection(t *testing.T) { want = 500 if got != want { - t.Fatalf("got: %q, want: %q", got, want) + t.Fatalf("got: %d, want: %d", got, want) } } diff --git a/pkg/common/metainfo.go b/pkg/common/metainfo.go index b792c52..b11c344 100644 --- a/pkg/common/metainfo.go +++ b/pkg/common/metainfo.go @@ -18,11 +18,12 @@ import ( var Version = "dev" var Commit = "unknown" var BuildDate = "unknown" +var Edition = "oss" const ( MetaInfoConfigmapName = "reloader-meta-info" MetaInfoConfigmapLabelKey = "reloader.stakater.com/meta-info" - MetaInfoConfigmapLabelValue = "reloader-oss" + MetaInfoConfigmapLabelValue = "reloader" ) // MetaInfo contains comprehensive metadata about the Reloader instance. @@ -47,6 +48,9 @@ type BuildInfo struct { CommitHash string `json:"commitHash"` // CommitTime is the timestamp of the Git commit used to build this binary CommitTime time.Time `json:"commitTime"` + + // Edition indicates the edition of Reloader (e.g., OSS, Enterprise) + Edition string `json:"edition"` } func NewBuildInfo() *BuildInfo { @@ -55,6 +59,7 @@ func NewBuildInfo() *BuildInfo { ReleaseVersion: Version, CommitHash: Commit, CommitTime: ParseUTCTime(BuildDate), + Edition: Edition, } return metaInfo diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..6190971 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,252 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO="stakater/Reloader" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +confirm() { + local msg="$1" + echo -en "${YELLOW}$msg [y/N]:${NC} " + read -r answer + [[ "$answer" =~ ^[Yy]$ ]] +} + +usage() { + cat < + +Automates the full Reloader release process. + +Arguments: + APP_VERSION Application version without 'v' prefix (e.g. 1.5.0, 1.5.0-alpha) + CHART_VERSION Helm chart version (e.g. 2.3.0, 2.3.0-rc.1) + +Prerequisites: + - gh CLI authenticated with repo access + - git configured with push access to $REPO + +Example: + $0 1.5.0 2.3.0 +EOF + exit 1 +} + +# --- Input validation --- +[[ $# -ne 2 ]] && usage + +APP_VERSION="$1" +CHART_VERSION="$2" + +# Strip 'v' prefix if provided +APP_VERSION="${APP_VERSION#v}" +CHART_VERSION="${CHART_VERSION#v}" + +# Validate semver format (with optional prerelease suffix e.g. 1.5.0-alpha, 1.5.0-rc.1) +SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+([-][a-zA-Z0-9.]+)?$' + +if ! [[ "$APP_VERSION" =~ $SEMVER_RE ]]; then + error "APP_VERSION '$APP_VERSION' is not valid semver (expected X.Y.Z or X.Y.Z-prerelease)" + exit 1 +fi + +if ! [[ "$CHART_VERSION" =~ $SEMVER_RE ]]; then + error "CHART_VERSION '$CHART_VERSION' is not valid semver (expected X.Y.Z or X.Y.Z-prerelease)" + exit 1 +fi + +# Check prerequisites +if ! command -v gh &> /dev/null; then + error "gh CLI is not installed. Install from https://cli.github.com/" + exit 1 +fi + +if ! gh auth status &> /dev/null; then + error "gh CLI is not authenticated. Run 'gh auth login' first." + exit 1 +fi + +RELEASE_BRANCH="release-v${APP_VERSION}" +TAG="v${APP_VERSION}" + +info "Release plan:" +info " App version: $APP_VERSION (tag: $TAG)" +info " Chart version: $CHART_VERSION" +info " Release branch: $RELEASE_BRANCH" +echo "" + +# ============================================================================= +# Phase 1: Create release branch +# ============================================================================= +info "Phase 1: Create release branch '$RELEASE_BRANCH' from master" + +if git ls-remote --heads origin "$RELEASE_BRANCH" | grep -q "$RELEASE_BRANCH"; then + warn "Branch '$RELEASE_BRANCH' already exists on remote." + if ! confirm "Continue using existing branch?"; then + error "Aborted." + exit 1 + fi +else + if ! confirm "Create and push branch '$RELEASE_BRANCH' from master?"; then + error "Aborted." + exit 1 + fi + git fetch origin master + git push origin origin/master:refs/heads/"$RELEASE_BRANCH" + info "Branch '$RELEASE_BRANCH' created and pushed." +fi +echo "" + +# ============================================================================= +# Phase 2: Trigger Init Release workflow and merge its PR +# ============================================================================= +info "Phase 2: Trigger Init Release workflow" + +if ! confirm "Trigger 'Init Release' workflow for branch '$RELEASE_BRANCH' with version '$APP_VERSION'?"; then + error "Aborted." + exit 1 +fi + +gh workflow run init-branch-release.yaml \ + --repo "$REPO" \ + -f TARGET_BRANCH="$RELEASE_BRANCH" \ + -f TARGET_VERSION="$APP_VERSION" + +info "Workflow triggered. Waiting for version bump PR to be created..." + +# Poll for the PR (created by the workflow targeting the release branch) +MAX_ATTEMPTS=30 +SLEEP_INTERVAL=10 +PR_NUMBER="" + +for i in $(seq 1 $MAX_ATTEMPTS); do + PR_NUMBER=$(gh pr list \ + --repo "$REPO" \ + --base "$RELEASE_BRANCH" \ + --search "Bump version to $APP_VERSION" \ + --json number \ + --jq '.[0].number // empty' 2>/dev/null || true) + + if [[ -n "$PR_NUMBER" ]]; then + info "Found PR #$PR_NUMBER" + break + fi + echo -n "." + sleep "$SLEEP_INTERVAL" +done + +if [[ -z "$PR_NUMBER" ]]; then + error "Timed out waiting for Init Release PR. Check workflow status at:" + error " https://github.com/$REPO/actions/workflows/init-branch-release.yaml" + exit 1 +fi + +info "PR: https://github.com/$REPO/pull/$PR_NUMBER" + +if ! confirm "Merge PR #$PR_NUMBER (version bump to $APP_VERSION)?"; then + error "Aborted. PR is still open: https://github.com/$REPO/pull/$PR_NUMBER" + exit 1 +fi + +gh pr merge "$PR_NUMBER" --repo "$REPO" --merge +info "PR #$PR_NUMBER merged." +echo "" + +# ============================================================================= +# Phase 3: Create GitHub release +# ============================================================================= +info "Phase 3: Create GitHub release '$TAG' targeting '$RELEASE_BRANCH'" +info "This will trigger the release workflow (Docker image builds, GoReleaser)." + +if ! confirm "Create GitHub release '$TAG'?"; then + error "Aborted." + exit 1 +fi + +gh release create "$TAG" \ + --repo "$REPO" \ + --target "$RELEASE_BRANCH" \ + --title "Release $TAG" \ + --generate-notes + +info "GitHub release created: https://github.com/$REPO/releases/tag/$TAG" +info "Release workflow will run in the background." +echo "" + +# ============================================================================= +# Phase 4: Bump Helm chart and create PR +# ============================================================================= +info "Phase 4: Bump Helm chart version to $CHART_VERSION (appVersion: v$APP_VERSION)" + +HELM_BRANCH="release-helm-chart-v${CHART_VERSION}" + +if ! confirm "Create branch '$HELM_BRANCH', bump chart files, and open PR with 'release/helm-chart' label?"; then + error "Aborted." + exit 1 +fi + +# Create branch from latest master +git fetch origin master +git checkout -b "$HELM_BRANCH" origin/master + +# Bump Chart.yaml: version and appVersion +CHART_FILE="deployments/kubernetes/chart/reloader/Chart.yaml" +sed -i "s/^version:.*/version: ${CHART_VERSION}/" "$CHART_FILE" +sed -i "s/^appVersion:.*/appVersion: v${APP_VERSION}/" "$CHART_FILE" + +# Bump values.yaml: image.tag +VALUES_FILE="deployments/kubernetes/chart/reloader/values.yaml" +sed -i "s/^\( tag:\).*/\1 v${APP_VERSION}/" "$VALUES_FILE" + +# Show changes for review +info "Changes:" +git diff + +git add "$CHART_FILE" "$VALUES_FILE" +git commit -m "Bump helm chart to ${CHART_VERSION} and appVersion to v${APP_VERSION}" +git push origin "$HELM_BRANCH" + +HELM_PR_URL=$(gh pr create \ + --repo "$REPO" \ + --base master \ + --head "$HELM_BRANCH" \ + --title "Bump Helm chart to ${CHART_VERSION} (appVersion v${APP_VERSION})" \ + --body "Bump Helm chart version to ${CHART_VERSION} and appVersion to v${APP_VERSION}." \ + --label "release/helm-chart") + +HELM_PR_NUMBER=$(echo "$HELM_PR_URL" | grep -o '[0-9]*$') +info "Helm chart PR created: $HELM_PR_URL" + +if ! confirm "Merge Helm chart PR #$HELM_PR_NUMBER?"; then + error "Aborted. PR is still open: $HELM_PR_URL" + exit 1 +fi + +gh pr merge "$HELM_PR_NUMBER" --repo "$REPO" --merge +info "Helm chart PR #$HELM_PR_NUMBER merged." + +# Return to previous branch +git checkout - + +echo "" +info "=============================================" +info "Release $TAG complete!" +info "=============================================" +info "" +info "Summary:" +info " - Release branch: $RELEASE_BRANCH" +info " - GitHub release: https://github.com/$REPO/releases/tag/$TAG" +info " - Helm chart: $CHART_VERSION (appVersion: v$APP_VERSION)" +info "" +info "The release workflow is running in the background." +info "Monitor at: https://github.com/$REPO/actions" diff --git a/test/loadtest/go.mod b/test/loadtest/go.mod index e96ed76..08230ca 100644 --- a/test/loadtest/go.mod +++ b/test/loadtest/go.mod @@ -1,6 +1,6 @@ module github.com/stakater/Reloader/test/loadtest -go 1.25 +go 1.26 require ( github.com/spf13/cobra v1.8.1