Repository: kubernetes/release
Pull request #3765 (2024-09-17 16:02:37)

Add krel validate cmd
#### What type of PR is this?

/kind feature

#### What this PR does / why we need it:

Introduces new `krel validate <release-notes-maps>` command for release notes team to use when checking for invalid formatting. 

The release notes team is blocked whenever invalid yaml is merged into sig-release:

https://github.com/kubernetes/sig-release/pull/2589
https://github.com/kubernetes/sig-release/pull/2541

Invalid yaml can be introduced during the review process when people apply changes or manually edit the maps (not through krel, but in an editor).

#### Examples of runs:

Valid check:
```
krel validate sig-release/releases/release-1.30/release-notes/maps
...
Validating YAML file: sig-release/releases/release-1.30/release-notes/maps/pr-123935-map.yaml
File sig-release/releases/release-1.30/release-notes/maps/pr-123935-map.yaml is valid YAML.
YAML file sig-release/releases/release-1.30/release-notes/maps/pr-123935-map.yaml is valid.
Validating YAML file: sig-release/releases/release-1.30/release-notes/maps/pr-124001-map.yaml
File sig-release/releases/release-1.30/release-notes/maps/pr-124001-map.yaml is valid YAML.
YAML file sig-release/releases/release-1.30/release-notes/maps/pr-124001-map.yaml is valid.
All release notes are valid.
```

Invalid yaml:
```
Validating YAML file: sig-release/releases/release-1.30/release-notes/maps/pr-112957-map.yaml
FATA failed to validate release notes: YAML validation failed for sig-release/releases/release-1.30/release-notes/maps/pr-112957-map.yaml: YAML unmarshal failed for sig-release/releases/release-1.30/release-notes/maps/pr-112957-map.yaml:error converting YAML to JSON: yaml: line 3: found character that cannot start any token 
```

#### Which issue(s) this PR fixes:

Catches occurrences of https://github.com/kubernetes/release/issues/2753 

#### Special notes for your reviewer:

Eventually, this command can be added to release-actions and run as a workflow similar to https://github.com/kubernetes/sig-release/pull/2547. 

#### Does this PR introduce a user-facing change?

```release-note
Introduced new `krel validate` command for validating committed release note edits. 
```


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/release/pull/3765#" title="Author self-approved">npolshakova</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign [puerco](https://github.com/puerco) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Frelease).

<details open>
Needs approval from an approver in each of these files:

- **[cmd/krel/OWNERS](https://github.com/kubernetes/release/blob/master/cmd/krel/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["puerco"]} -->
npolshakova: @satyampsoni this can be used to validate the yamls instead of relying on the script. We can move this to a GitHub action once it goes in. 
k8s-ci-robot: @npolshakova: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-release-verify | 92cdea1f405ee696c979e3cc3fa7b11b0e65d477 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/release/3765/pull-release-verify/1836130696570081280) | true | `/test pull-release-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes&repo=release&pr=3765). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Anpolshakova). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes/release/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @npolshakova: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-release-integration-test | b78255b07627c29166c47dbed3aba3dcb9c956a5 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/release/3765/pull-release-integration-test/1836410774134722560) | true | `/test pull-release-integration-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes&repo=release&pr=3765). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Anpolshakova). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes/release/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
npolshakova: @kubernetes/sig-release-leads @xmudrii Can you give this another pass when you have a chance? Thanks! 

Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 9b7a72fd..9f863bf4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -39,7 +39,7 @@ jobs:
 
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+      uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
       with:
         disable-sudo: true
         egress-policy: block
@@ -53,23 +53,23 @@ jobs:
           objects.githubusercontent.com:443
 
     - name: Checkout repository
-      uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+      uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
 
     - name: Set up Go
       id: go
       uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
       with:
-        go-version: '1.23'
+        go-version: '1.22'
         check-latest: true
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
+      uses: github/codeql-action/init@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
       with:
         languages: ${{ matrix.language }}
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
+      uses: github/codeql-action/autobuild@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
 
     # ℹ️ Command-line programs to run using the OS shell.
     # 📚 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -83,4 +83,4 @@ jobs:
     #   make release
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
+      uses: github/codeql-action/analyze@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml
index 4bf13bc2..00b97732 100644
--- a/.github/workflows/depsreview.yml
+++ b/.github/workflows/depsreview.yml
@@ -12,7 +12,7 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
         with:
           disable-sudo: true
           egress-policy: block
@@ -23,7 +23,7 @@ jobs:
             api.deps.dev:443
 
       - name: 'Checkout Repository'
-        uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
 
       - name: 'Dependency Review'
         uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 51dfc0c6..37cc6e09 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -13,16 +13,17 @@ jobs:
     runs-on: ubuntu-latest
     steps:
 
-      - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
 
       - name: Set up Go
         id: go
         uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
-          go-version: '1.23'
+          go-version: '1.22'
           check-latest: true
 
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
+        uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1
         with:
-          version: v1.61
+          version: v1.59
+          args: --timeout=15m
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 235839cf..b9282275 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,12 +19,12 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
         with:
           disable-sudo: true
           egress-policy: audit
 
-      - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v3.5.2
+      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v3.5.2
         with:
           fetch-depth: 1
 
@@ -32,10 +32,10 @@ jobs:
         id: go
         uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
-          go-version: '1.23'
+          go-version: '1.22'
           check-latest: true
 
-      - uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # v3.6.0
+      - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
 
       - name: Install bom
         uses: kubernetes-sigs/release-actions/setup-bom@2f8b9ec22aedc9ce15039b6c7716aa6c2907df1c # v0.2.0
@@ -48,7 +48,7 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
-      - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+      - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
         with:
           name: artifacts
           path: dist/*
@@ -65,13 +65,13 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
         with:
           disable-sudo: true
           egress-policy: audit
 
       - name: Check out code onto GOPATH
-        uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
         with:
           fetch-depth: 1
 
@@ -88,7 +88,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Release
-        uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
+        uses: softprops/action-gh-release@fb2d03176f42a1f0dd433ca263f314051d3edd44 # v2.0.7
         with:
           files: release.intoto.json
           tag_name: "${{ steps.tag.outputs.tag_name }}"
diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml
index 6f4763fe..6eb59976 100644
--- a/.github/workflows/scorecards-analysis.yml
+++ b/.github/workflows/scorecards-analysis.yml
@@ -28,12 +28,12 @@ jobs:
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
         with:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
+        uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
         with:
           results_file: results.sarif
           results_format: sarif
@@ -55,7 +55,7 @@ jobs:
       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
       # format to the repository Actions tab.
       - name: "Upload artifact"
-        uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+        uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
         with:
           name: SARIF file
           path: results.sarif
@@ -63,6 +63,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
+        uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v2.13.4
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/snapshot.yaml b/.github/workflows/snapshot.yaml
index 93385890..22001737 100644
--- a/.github/workflows/snapshot.yaml
+++ b/.github/workflows/snapshot.yaml
@@ -13,12 +13,12 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
         with:
           disable-sudo: true
           egress-policy: audit
 
-      - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v3.5.2
+      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v3.5.2
         with:
           fetch-depth: 1
 
@@ -26,7 +26,7 @@ jobs:
         id: go
         uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
-          go-version: '1.23'
+          go-version: '1.22'
           check-latest: true
 
       - name: Install bom
@@ -45,7 +45,7 @@ jobs:
           ./dist/publish-release-amd64-linux help
           ./dist/release-notes-amd64-linux version
 
-      - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+      - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
         with:
           name: artifacts
           path: dist/*
@@ -61,13 +61,13 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
         with:
           disable-sudo: true
           egress-policy: audit
 
       - name: Check out code onto GOPATH
-        uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
         with:
           fetch-depth: 1
 
@@ -79,7 +79,7 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
-      - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
+      - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
         with:
           name: release.intoto.json
           path: ./release.intoto.json
diff --git a/.golangci.yml b/.golangci.yml
index ceb26b49..fc859b9a 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,7 +1,7 @@
 ---
 run:
   concurrency: 6
-  timeout: 15m
+  timeout: 5m
 issues:
   exclude-rules:
     # counterfeiter fakes are usually named 'fake_<something>.go'
@@ -33,6 +33,7 @@ linters:
     - errcheck
     - errchkjson
     - errname
+    - exportloopref
     - fatcontext
     - gci
     - ginkgolinter
@@ -148,7 +149,78 @@ linters-settings:
     check-type-assertions: true
     check-blank: true
   gocritic:
-    enable-all: true
+    enabled-checks:
+      - appendCombine
+      - badLock
+      - badRegexp
+      - badSorting
+      - badSyncOnceFunc
+      - boolExprSimplify
+      - builtinShadow
+      - builtinShadowDecl
+      - commentedOutCode
+      - commentedOutImport
+      - deferInLoop
+      - deferUnlambda
+      - docStub
+      - dupImport
+      - dynamicFmtString
+      - emptyDecl
+      - emptyFallthrough
+      - emptyStringTest
+      - equalFold
+      - evalOrder
+      - exposedSyncMutex
+      - externalErrorReassign
+      - filepathJoin
+      - hexLiteral
+      - httpNoBody
+      - hugeParam
+      - importShadow
+      - indexAlloc
+      - initClause
+      - methodExprCall
+      - nestingReduce
+      - nilValReturn
+      - octalLiteral
+      - paramTypeCombine
+      - preferDecodeRune
+      - preferFilepathJoin
+      - preferFprint
+      - preferStringWriter
+      - preferWriteByte
+      - ptrToRefParam
+      - rangeExprCopy
+      - rangeValCopy
+      - redundantSprint
+      - regexpPattern
+      - regexpSimplify
+      - returnAfterHttpError
+      - ruleguard
+      - sliceClear
+      - sloppyReassign
+      - sortSlice
+      - sprintfQuotedString
+      - sqlQuery
+      - stringConcatSimplify
+      - stringXbytes
+      - stringsCompare
+      - syncMapLoadAndDelete
+      - timeExprSimplify
+      - todoCommentWithoutDetail
+      - tooManyResultsChecker
+      - truncateCmp
+      - typeAssertChain
+      - typeDefFirst
+      - typeUnparen
+      - uncheckedInlineErr
+      - unlabelStmt
+      - unnamedResult
+      - unnecessaryBlock
+      - unnecessaryDefer
+      - weakCond
+      - whyNoLint
+      - yodaStyleExpr
   nolintlint:
     # Enable to ensure that nolint directives are all used. Default is true.
     allow-unused: false
diff --git a/Makefile b/Makefile
index acb19e4a..d7ffdb53 100644
--- a/Makefile
+++ b/Makefile
@@ -20,51 +20,29 @@ SHELL:=/usr/bin/env bash
 
 COLOR:=\\033[36m
 NOCOLOR:=\\033[0m
-HELP_WIDTH:=20
 
-.PHONY: help
-help:  ## Display this help
-	@awk \
-		-v "col=${COLOR}" -v "nocol=${NOCOLOR}" \
-		' \
-			BEGIN { \
-				FS = ":.*##" ; \
-				printf "\nUsage:\n  make %s<target>%s\n", col, nocol \
-			} \
-			/^[a-zA-Z_-]+:.*?##/ { \
-				printf "  %s%-${HELP_WIDTH}s%s %s\n", col, $$1, nocol, $$2 \
-			} \
-			/^##@/ { \
-				printf "\n%s\n", substr($$0, 5) \
-			} \
-		' $(MAKEFILE_LIST)
+##@ Verify
 
-##@ Verify:
+.PHONY: verify verify-boilerplate verify-build verify-dependencies verify-go-mod verify-package-specs
 
-.PHONY: verify
 verify: release-tools verify-boilerplate verify-build verify-dependencies verify-go-mod verify-package-specs ## Runs verification scripts to ensure correct execution
 
-.PHONY: verify-boilerplate
 verify-boilerplate: ## Runs the file header check
 	./hack/verify-boilerplate.sh
 
-.PHONY: verify-build
 verify-build: ## Builds the project for a chosen set of platforms
 	./hack/verify-build.sh
 
-.PHONY: verify-dependencies
 verify-dependencies: ## Runs zeitgeist to verify dependency versions
 	./hack/verify-dependencies.sh
 
-.PHONY: verify-go-mod
 verify-go-mod: ## Runs the go module linter
 	./hack/verify-go-mod.sh
 
-.PHONY: verify-package-specs
 verify-package-specs: ## Runs the rpmlint on package specs
 	./hack/verify-package-specs.sh
 
-##@ Tests:
+##@ Tests
 
 .PHONY: test
 test: test-go-unit test-sh ## Runs unit tests to ensure correct execution
@@ -81,19 +59,22 @@ test-go-integration: ## Runs golang integration tests
 test-sh: ## Runs all shellscript tests
 	./hack/test-sh.sh
 
-##@ Tools:
+##@ Tools
 
 RELEASE_TOOLS ?=
 
 .PHONY: release-tools
+
 release-tools: ## Compiles a set of release tools, specified by $RELEASE_TOOLS
 	./compile-release-tools $(RELEASE_TOOLS)
 
-##@ Images:
-
-images := k8s-cloud-builder
+##@ Images
 
 .PHONY: update-images
+
+images := \
+	k8s-cloud-builder
+
 update-images: $(addprefix image-,$(images)) ## Update all images in ./images/
 image-%:
 	$(eval img := $(subst image-,,$@))
@@ -116,16 +97,15 @@ local-image-run: ## Run a locally build image to use the tools of this repositor
 		-w /go/src/k8s.io/release \
 		$(LOCALIMAGE_NAME) bash
 
-##@ Dependencies:
+##@ Dependencies
+
+.SILENT: update-deps update-deps-go update-mocks
+.PHONY:  update-deps update-deps-go update-mocks
 
-.SILENT: update-deps
-.PHONY:  update-deps
 update-deps: update-deps-go ## Update all dependencies for this repo
 	echo -e "${COLOR}Commit/PR the following changes:${NOCOLOR}"
 	git status --short
 
-.SILENT: update-deps-go
-.PHONY:  update-deps-go
 update-deps-go: GO111MODULE=on
 update-deps-go: ## Update all golang dependencies for this repo
 	go get -u -t ./...
@@ -134,8 +114,6 @@ update-deps-go: ## Update all golang dependencies for this repo
 	$(MAKE) test-go-unit
 	./hack/update-all.sh
 
-.SILENT: update-mocks
-.PHONY:  update-mocks
 update-mocks: ## Update all generated mocks
 	go generate ./...
 	for f in $(shell find . -name fake_*.go); do \
@@ -145,3 +123,23 @@ update-mocks: ## Update all generated mocks
 			mv tmp $$f ;\
 		fi \
 	done
+
+##@ Helpers
+
+.PHONY: help
+
+help:  ## Display this help
+	@awk \
+		-v "col=${COLOR}" -v "nocol=${NOCOLOR}" \
+		' \
+			BEGIN { \
+				FS = ":.*##" ; \
+				printf "\nUsage:\n  make %s<target>%s\n", col, nocol \
+			} \
+			/^[a-zA-Z_-]+:.*?##/ { \
+				printf "  %s%-15s%s %s\n", col, $$1, nocol, $$2 \
+			} \
+			/^##@/ { \
+				printf "\n%s%s%s\n", col, substr($$0, 5), nocol \
+			} \
+		' $(MAKEFILE_LIST)
diff --git a/cmd/krel/cmd/announce_build.go b/cmd/krel/cmd/announce_build.go
index 8073702e..d73229b2 100644
--- a/cmd/krel/cmd/announce_build.go
+++ b/cmd/krel/cmd/announce_build.go
@@ -179,7 +179,8 @@ func runBuildBranchAnnounce(opts *buildBranchAnnounceOptions, buildOpts *buildAn
 		return fmt.Errorf("generating the announcement html file: %w", err)
 	}
 
-	return buildOpts.saveAnnouncement(announcement)
+	announcementSubject := fmt.Sprintf("Kubernetes %s branch has been created", opts.branch)
+	return buildOpts.saveAnnouncement(announcementSubject, announcement)
 }
 
 // runBuildReleaseAnnounce build the announcement file when creating a new Kubernetes release.
@@ -224,10 +225,12 @@ func runBuildReleaseAnnounce(opts *buildReleaseAnnounceOptions, buildOpts *build
 		return fmt.Errorf("generating the announcement html file: %w", err)
 	}
 
-	return buildOpts.saveAnnouncement(announcement)
+	announcementSubject := fmt.Sprintf("Kubernetes %s is live!", announceOpts.tag)
+
+	return buildOpts.saveAnnouncement(announcementSubject, announcement)
 }
 
-func (opts *buildAnnounceOptions) saveAnnouncement(announcement bytes.Buffer) error {
+func (opts *buildAnnounceOptions) saveAnnouncement(announcementSubject string, announcement bytes.Buffer) error {
 	logrus.Info("Creating announcement files")
 
 	absOutputPath := filepath.Join(opts.workDir, "announcement.html")
@@ -237,6 +240,12 @@ func (opts *buildAnnounceOptions) saveAnnouncement(announcement bytes.Buffer) er
 		return fmt.Errorf("saving announcement.html: %w", err)
 	}
 
+	absOutputPath = filepath.Join(opts.workDir, "announcement-subject.txt")
+	logrus.Infof("Writing announcement subject to %s", absOutputPath)
+	err = os.WriteFile(absOutputPath, []byte(announcementSubject), os.FileMode(0o644))
+	if err != nil {
+		return fmt.Errorf("saving announcement-subject.txt: %w", err)
+	}
 	logrus.Info("Kubernetes Announcement created.")
 	return nil
 }
diff --git a/cmd/krel/cmd/announce_send.go b/cmd/krel/cmd/announce_send.go
index 301594ee..e50451fb 100644
--- a/cmd/krel/cmd/announce_send.go
+++ b/cmd/krel/cmd/announce_send.go
@@ -117,12 +117,12 @@ func runAnnounce(opts *sendAnnounceOptions, announceRootOpts *announceOptions, r
 
 	tag := util.AddTagPrefix(announceRootOpts.tag)
 	u := fmt.Sprintf(
-		"%s/release/%s/announcement.html",
+		"%s/archive/anago-%s/announcement.html",
 		release.URLPrefixForBucket(release.ProductionBucket), tag,
 	)
 	logrus.Infof("Using announcement remote URL: %s", u)
 
-	content, err := http.NewAgent().Get(u)
+	content, err := http.GetURLResponse(u, false)
 	if err != nil {
 		return fmt.Errorf(
 			"unable to retrieve release announcement form url: %s: %w", u, err,
@@ -181,7 +181,7 @@ func runAnnounce(opts *sendAnnounceOptions, announceRootOpts *announceOptions, r
 	}
 
 	if yes {
-		if err := m.Send(string(content), subject); err != nil {
+		if err := m.Send(content, subject); err != nil {
 			return fmt.Errorf("unable to send mail: %w", err)
 		}
 	}
diff --git a/cmd/krel/cmd/push.go b/cmd/krel/cmd/push.go
index bee4af6a..942d261d 100644
--- a/cmd/krel/cmd/push.go
+++ b/cmd/krel/cmd/push.go
@@ -97,7 +97,10 @@ func init() {
 		&pushBuildOpts.BuildDir,
 		"buildDir",
 		release.BuildDir,
-		"Specify an alternate build directory",
+		fmt.Sprintf(
+			"Specify an alternate build directory (defaults to '%s')",
+			release.BuildDir,
+		),
 	)
 
 	// TODO: Switch to "--registry" once CI no longer uses it
@@ -143,13 +146,6 @@ func init() {
 		"Validate that the remote image digests exists",
 	)
 
-	pushBuildCmd.PersistentFlags().StringVar(
-		&pushBuildOpts.RepoRoot,
-		"repo-root",
-		"",
-		"Specify an alternate Kubernetes repository directory",
-	)
-
 	rootCmd.AddCommand(pushBuildCmd)
 }
 
diff --git a/cmd/krel/cmd/root.go b/cmd/krel/cmd/root.go
index c7cb4a40..e308d0bd 100644
--- a/cmd/krel/cmd/root.go
+++ b/cmd/krel/cmd/root.go
@@ -68,10 +68,9 @@ func init() {
 		"run the command to target the production environment",
 	)
 
-	rootCmd.PersistentFlags().StringVarP(
+	rootCmd.PersistentFlags().StringVar(
 		&rootOpts.logLevel,
 		"log-level",
-		"l",
 		"info",
 		"the logging verbosity, either "+log.LevelNames(),
 	)
diff --git a/cmd/krel/cmd/sign_blobs.go b/cmd/krel/cmd/sign_blobs.go
index b0b7ade0..89e4977f 100644
--- a/cmd/krel/cmd/sign_blobs.go
+++ b/cmd/krel/cmd/sign_blobs.go
@@ -196,7 +196,7 @@ func runSignBlobs(signOpts *signOptions, signBlobOpts *signBlobOptions, args []s
 		}
 	}
 
-	t := throttler.New(int(signOpts.maxWorkers), len(bundle)) //nolint:gosec // overflow is highly unlikely
+	t := throttler.New(int(signOpts.maxWorkers), len(bundle))
 	for _, fileBundle := range bundle {
 		go func(fileBundle signingBundle) {
 			logrus.Infof("Signing %s...", fileBundle.fileToSign)
diff --git a/cmd/krel/cmd/validate.go b/cmd/krel/cmd/validate.go
new file mode 100644
index 00000000..ed6c3b38
--- /dev/null
+++ b/cmd/krel/cmd/validate.go
@@ -0,0 +1,164 @@
+/*
+Copyright 2024 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"sigs.k8s.io/yaml"
+
+	"k8s.io/release/pkg/notes"
+)
+
+type releaseNotesValidateOptions struct {
+	pathToReleaseNotes string
+}
+
+var releaseNotesValidateOpts = &releaseNotesValidateOptions{}
+
+func init() {
+	validateCmd.PersistentFlags().StringVar(
+		&releaseNotesValidateOpts.pathToReleaseNotes,
+		"path-to-release-notes",
+		"",
+		"The path to the release notes to validate. Can be a top level directory or a specific file.",
+	)
+
+	// Add the validation subcommand to the release-notes command
+	releaseNotesCmd.AddCommand(validateCmd)
+}
+
+// validate represents the subcommand for `krel release-notes validate`.
+var validateCmd = &cobra.Command{
+	Use:   "validate",
+	Short: "The subcommand for validating release notes for the Release Notes subteam of SIG Release",
+	Long: `krel release-notes validate <path-to-release-notes>
+
+The 'validate' subcommand of krel has been developed to:
+
+1. Check release notes maps for valid yaml.
+
+2. Check release notes maps for valid punctuation.`,
+	SilenceUsage:  true,
+	SilenceErrors: true,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		// Ensure exactly one argument is provided
+		if releaseNotesValidateOpts.pathToReleaseNotes == "" {
+			return errors.New("path to release notes must be provided via --path-to-release-notes")
+		}
+
+		// Run the PR creation function
+		return runValidateReleaseNotes(releaseNotesValidateOpts.pathToReleaseNotes)
+	},
+}
+
+func runValidateReleaseNotes(releaseNotesPath string) (err error) {
+	// Ensure the path is not empty
+	if releaseNotesPath == "" {
+		return errors.New("release notes path cannot be empty")
+	}
+
+	// Check if the directory exists
+	if _, err := os.Stat(releaseNotesPath); os.IsNotExist(err) {
+		return fmt.Errorf("release notes path %s does not exist", releaseNotesPath)
+	}
+
+	// Validate the YAML files in the directory
+	err = filepath.Walk(releaseNotesPath, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		// Only process YAML files
+		if filepath.Ext(path) == ".yaml" || filepath.Ext(path) == ".yml" {
+			fmt.Printf("Validating YAML file: %s\n", path)
+
+			// Validate YAML
+			if err := ValidateYamlMap(path); err != nil {
+				return fmt.Errorf("validating YAML file %s: %w", path, err)
+			}
+
+			fmt.Printf("YAML file %s is valid.\n", path)
+		}
+		return nil
+	})
+	if err != nil {
+		return fmt.Errorf("validating release notes: %w", err)
+	}
+
+	fmt.Println("All release notes are valid.")
+	return nil
+}
+
+// ValidateYamlMap reads a YAML map file, unmarshals it into a map, and then re-marshals it
+// to validate the correctness of the content.
+func ValidateYamlMap(filePath string) error {
+	// Read the YAML file
+	data, err := os.ReadFile(filePath)
+	if err != nil {
+		return fmt.Errorf("reading file %s: %w", filePath, err)
+	}
+
+	// Unmarshal the YAML data into a map for manipulation and validation
+	var testMap notes.ReleaseNotesMap
+	if err := yaml.Unmarshal(data, &testMap); err != nil {
+		return fmt.Errorf("YAML unmarshaling %s: %w", filePath, err)
+	}
+
+	// Check the map for valid punctuation in the "text" field
+	if err := validateTextFieldPunctuation(&testMap); err != nil {
+		return fmt.Errorf("punctuation check for file %s: %w", filePath, err)
+	}
+
+	// TODO: Add custom validation checks (tense, grammar, spelling, etc.) https://github.com/kubernetes/release/issues/3767
+
+	// Re-marshall the YAML to check if it can be successfully serialized again
+	_, err = yaml.Marshal(testMap)
+	if err != nil {
+		return fmt.Errorf("while re-marshaling map for file %s:%w", filePath, err)
+	}
+
+	fmt.Printf("File %s is valid YAML.\n", filePath)
+	return nil
+}
+
+// validateTextFieldPunctuation checks if the "text" field in a YAML map
+// ends with valid punctuation (., !, ?).
+func validateTextFieldPunctuation(data *notes.ReleaseNotesMap) error {
+	validPunctuation := regexp.MustCompile(`[.!?]$`)
+
+	if data == nil {
+		return errors.New("the release notes map is nil")
+	}
+
+	if data.ReleaseNote.Text == nil {
+		return errors.New("the 'text' release notes map field is nil")
+	}
+
+	text := *data.ReleaseNote.Text
+	if !validPunctuation.MatchString(strings.TrimSpace(text)) {
+		return fmt.Errorf("the 'text' field does not end with valid punctuation: '%s'", text)
+	}
+
+	return nil
+}
diff --git a/cmd/schedule-builder/cmd/markdown.go b/cmd/schedule-builder/cmd/markdown.go
index 50d354c4..ad7a55a9 100644
--- a/cmd/schedule-builder/cmd/markdown.go
+++ b/cmd/schedule-builder/cmd/markdown.go
@@ -285,7 +285,7 @@ func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, eolBranches
 		}
 
 		if refTime.After(upcomingTargetDate) {
-			logrus.Infof("Skipping outdated upcoming release for %s", upcomingRelease.TargetDate)
+			logrus.Infof("Skipping outdated upcoming release for %s (%s)", upcomingRelease.Release, upcomingRelease.TargetDate)
 			continue
 		}
 
diff --git a/dependencies.yaml b/dependencies.yaml
index 3bbc86c4..fb1b5a79 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -22,7 +22,7 @@ dependencies:
 
   # Golang
   - name: "golang"
-    version: 1.22.7
+    version: 1.22.5
     refPaths:
     - path: images/build/cross/Makefile
       match: GO_VERSION\ \?=\ \d+.\d+(alpha|beta|rc)?\.?(\d+)?
@@ -53,19 +53,13 @@ dependencies:
       match: "GO_MAJOR_VERSION: '\\d+.\\d+'"
 
   - name: "golang: 1.<major> (github workflows)"
-    version: 1.23
+    version: 1.22
     refPaths:
     - path: .github/workflows/release.yml
       match: "go-version: '\\d+.\\d+'"
-    - path: .github/workflows/snapshot.yaml
-      match: "go-version: '\\d+.\\d+'"
-    - path: .github/workflows/lint.yml
-      match: "go-version: '\\d+.\\d+'"
-    - path: .github/workflows/codeql-analysis.yml
-      match: "go-version: '\\d+.\\d+'"
 
   - name: "golang: go.mod"
-    version: 1.23
+    version: 1.22
     refPaths:
     - path: go.mod
       match: go \d+.\d+
@@ -77,7 +71,7 @@ dependencies:
       match: go \d+.\d+
 
   - name: "golang: after kubernetes/kubernetes update"
-    version: 1.23.0
+    version: 1.22.5
     refPaths:
     - path: images/releng/k8s-ci-builder/Makefile
       match: GO_VERSION\ \?=\ \d+.\d+(alpha|beta|rc)?\.?(\d+)?
@@ -123,7 +117,7 @@ dependencies:
 
   # go-runner
   - name: "registry.k8s.io/build-image/go-runner (go1.23-bookworm)"
-    version: v2.3.1-go1.23.1-bookworm.0
+    version: v2.3.1-go1.23rc2-bookworm.0
     refPaths:
     - path: images/build/go-runner/variants.yaml
       match: v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)
@@ -137,7 +131,7 @@ dependencies:
       match: REVISION:\ '\d+'
 
   - name: "registry.k8s.io/build-image/go-runner (go1.22-bookworm)"
-    version: v2.3.1-go1.22.7-bookworm.0
+    version: v2.3.1-go1.22.5-bookworm.0
     refPaths:
     - path: images/build/go-runner/variants.yaml
       match: v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)
@@ -152,7 +146,7 @@ dependencies:
 
   # kube-cross
   - name: "registry.k8s.io/build-image/kube-cross (v1.31-go1.23)"
-    version: v1.31.0-go1.23.1-bullseye.0
+    version: v1.31.0-go1.23rc2-bullseye.0
     refPaths:
     - path: images/build/cross/variants.yaml
       match: "IMAGE_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
@@ -172,7 +166,7 @@ dependencies:
       match: REVISION:\ '\d+'
 
   - name: "registry.k8s.io/build-image/kube-cross (v1.31-go1.22)"
-    version: v1.31.0-go1.22.7-bullseye.0
+    version: v1.31.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/build/cross/variants.yaml
       match: "IMAGE_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
@@ -193,7 +187,7 @@ dependencies:
 
 
   - name: "registry.k8s.io/build-image/kube-cross (v1.30-go1.22)"
-    version: v1.30.0-go1.22.7-bullseye.0
+    version: v1.30.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/build/cross/variants.yaml
       match: "IMAGE_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
@@ -213,7 +207,7 @@ dependencies:
       match: REVISION:\ '\d+'
 
   - name: "registry.k8s.io/build-image/kube-cross (v1.29-go1.22)"
-    version: v1.29.0-go1.22.7-bullseye.0
+    version: v1.29.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/build/cross/variants.yaml
       match: "IMAGE_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
@@ -233,7 +227,7 @@ dependencies:
       match: REVISION:\ '\d+'
 
   - name: "registry.k8s.io/build-image/kube-cross (v1.28-go1.22)"
-    version: v1.28.0-go1.22.7-bullseye.0
+    version: v1.28.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/build/cross/variants.yaml
       match: "IMAGE_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
@@ -252,39 +246,52 @@ dependencies:
     - path: images/build/cross/variants.yaml
       match: REVISION:\ '\d+'
 
-  - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.32-cross1.23)"
-    version: v1.32.0-go1.23.0-bullseye.0
+  - name: "registry.k8s.io/build-image/kube-cross (v1.27-go1.22)"
+    version: v1.27.0-go1.22.5-bullseye.0
     refPaths:
-    - path: images/k8s-cloud-builder/variants.yaml
-      match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
+    - path: images/build/cross/variants.yaml
+      match: "IMAGE_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
-  - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.31-cross1.23)"
-    version: v1.31.0-go1.23.0-bullseye.0
+  - name: "registry.k8s.io/build-image/kube-cross: config variant (v1.27-go1.22)"
+    version: go1.22-bullseye
     refPaths:
-    - path: images/k8s-cloud-builder/variants.yaml
-      match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
+    - path: images/build/cross/variants.yaml
+      match: "CONFIG: 'go\\d+.\\d+-bullseye'"
+
+  - name: "registry.k8s.io/build-image/kube-cross: image revision (v1.27-go1.22)"
+    version: 0
+    refPaths:
+    - path: images/build/cross/Makefile
+      match: REVISION \?= \d+
+    - path: images/build/cross/variants.yaml
+      match: REVISION:\ '\d+'
 
-  # TODO: remove this after upgrade to Go 1.23 is finalized and there's on risk of reverting
   - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.31-cross1.22)"
-    version: v1.31.0-go1.22.6-bullseye.0
+    version: v1.31.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/k8s-cloud-builder/variants.yaml
       match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
   - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.30-cross1.22)"
-    version: v1.30.0-go1.22.6-bullseye.0
+    version: v1.30.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/k8s-cloud-builder/variants.yaml
       match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
   - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.29-cross1.22)"
-    version: v1.29.0-go1.22.6-bullseye.0
+    version: v1.29.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/k8s-cloud-builder/variants.yaml
       match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
   - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.28-cross1.22)"
-    version: v1.28.0-go1.22.6-bullseye.0
+    version: v1.28.0-go1.22.5-bullseye.0
+    refPaths:
+    - path: images/k8s-cloud-builder/variants.yaml
+      match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
+
+  - name: "registry.k8s.io/build-image/kube-cross: dependents k8s-cloud-builder (v1.27-cross1.22)"
+    version: v1.27.0-go1.22.5-bullseye.0
     refPaths:
     - path: images/k8s-cloud-builder/variants.yaml
       match: "KUBE_CROSS_VERSION: 'v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
@@ -297,7 +304,7 @@ dependencies:
 
   # Golang (next candidate)
   - name: "golang (next candidate)"
-    version: 1.23.1
+    version: 1.23rc2
     refPaths:
     - path: images/build/cross/variants.yaml
       match: "GO_VERSION: '\\d+.\\d+(alpha|beta|rc)?\\.?(\\d+)?'"
@@ -305,9 +312,8 @@ dependencies:
       match: "GO_VERSION: '\\d+.\\d+(alpha|beta|rc)?\\.?(\\d+)?'"
     - path: images/releng/ci/variants.yaml
       match: "GO_VERSION: '\\d+.\\d+(alpha|beta|rc)?\\.?(\\d+)?'"
-    # TODO: uncomment when we build the k8s-ci-builder with 1.23.0
-    # - path: images/releng/k8s-ci-builder/variants.yaml
-    #   match: "GO_VERSION: '\\d+.\\d+(alpha|beta|rc)?\\.?(\\d+)?'"
+    - path: images/releng/k8s-ci-builder/variants.yaml
+      match: "GO_VERSION: '\\d+.\\d+(alpha|beta|rc)?\\.?(\\d+)?'"
 
   # Golang images (next candidate)
   - name: "gcr.io/k8s-staging-releng/releng-ci: image revision (next candidate)"
@@ -324,7 +330,7 @@ dependencies:
 
   # Golang (previous release branches: 1.30)
   - name: "golang (previous release branches: 1.30)"
-    version: 1.22.7
+    version: 1.22.5
     refPaths:
     - path: images/build/cross/variants.yaml
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
@@ -334,14 +340,14 @@ dependencies:
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
 
   - name: "golang: after kubernetes/kubernetes update (previous release branches: 1.30)"
-    version: 1.22.6
+    version: 1.22.5
     refPaths:
     - path: images/releng/k8s-ci-builder/variants.yaml
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
 
   # Golang (previous release branches: 1.29)
   - name: "golang (previous release branches: 1.29)"
-    version: 1.22.7
+    version: 1.22.5
     refPaths:
     - path: images/build/cross/variants.yaml
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
@@ -351,14 +357,14 @@ dependencies:
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
 
   - name: "golang: after kubernetes/kubernetes update (previous release branches: 1.29)"
-    version: 1.22.6
+    version: 1.22.5
     refPaths:
     - path: images/releng/k8s-ci-builder/variants.yaml
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
 
   # Golang (previous release branches: 1.28)
   - name: "golang (previous release branches: 1.28)"
-    version: 1.22.7
+    version: 1.22.5
     refPaths:
     - path: images/build/cross/variants.yaml
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
@@ -374,7 +380,24 @@ dependencies:
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
 
   - name: "golang: after kubernetes/kubernetes update (previous release branches: 1.28)"
-    version: 1.22.6
+    version: 1.22.5
+    refPaths:
+    - path: images/releng/k8s-ci-builder/variants.yaml
+      match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
+
+  # Golang (previous release branches: 1.27)
+  - name: "golang (previous release branches: 1.27)"
+    version: 1.22.5
+    refPaths:
+    - path: images/build/cross/variants.yaml
+      match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
+    - path: images/build/go-runner/variants.yaml
+      match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
+    - path: images/releng/ci/variants.yaml
+      match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
+
+  - name: "golang: after kubernetes/kubernetes update (previous release branches: 1.27)"
+    version: 1.22.5
     refPaths:
     - path: images/releng/k8s-ci-builder/variants.yaml
       match: \d+.\d+(alpha|beta|rc)?\.?(\d+)?
@@ -388,7 +411,7 @@ dependencies:
 
   # golangci-lint-version
   - name: "golangci-lint"
-    version: v1.61
+    version: v1.59
     refPaths:
     - path: .github/workflows/lint.yml
       match: "version: v\\d+.\\d+?\\.?(\\d+)?"
@@ -426,7 +449,7 @@ dependencies:
       match: "OS_CODENAME: 'bookworm'"
 
   - name: "registry.k8s.io/build-image/debian-base"
-    version: bookworm-v1.0.4
+    version: bookworm-v1.0.3
     refPaths:
     - path: images/build/debian-base/Makefile
       match: IMAGE_VERSION\ \?=\ bookworm-v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)
@@ -434,43 +457,31 @@ dependencies:
       match: "IMAGE_VERSION: 'bookworm-v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
   - name: "registry.k8s.io/build-image/debian-base: dependents"
-    version: bookworm-v1.0.4
+    version: bookworm-v1.0.3
     refPaths:
     - path: images/build/setcap/Makefile
       match: DEBIAN_BASE_VERSION\ \?=\ bookworm-v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)
     - path: images/build/setcap/variants.yaml
       match: "DEBIAN_BASE_VERSION: 'bookworm-v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
-  - name: "registry.k8s.io/build-image/distroless-iptables (distroless-bookworm-go1.23)"
-    version: v0.6.3
+  - name: "registry.k8s.io/build-image/distroless-iptables (distroless-bookworm-go1.22)"
+    version: v0.5.6
     refPaths:
     - path: images/build/distroless-iptables/Makefile
       match: IMAGE_VERSION\ \?=\ v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)
     - path: images/build/distroless-iptables/variants.yaml
       match: IMAGE_VERSION:\ \'v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)\'
 
-  - name: "registry.k8s.io/build-image/go-runner: dependents (distroless-bookworm-go1.23)"
-    version: v2.3.1-go1.23.1-bookworm.0
+  - name: "registry.k8s.io/build-image/go-runner: dependents (distroless-bookworm-go1.22)"
+    version: v2.3.1-go1.22.5-bookworm.0
     refPaths:
     - path: images/build/distroless-iptables/Makefile
       match: GORUNNER_VERSION \?= v\d+\.\d+\.\d+-go\d+.\d+(alpha|beta|rc)?\.?(\d+)?-bookworm\.\d+
     - path: images/build/distroless-iptables/variants.yaml
       match: GORUNNER_VERSION:\ \'v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)\'
 
-  - name: "registry.k8s.io/build-image/distroless-iptables (distroless-bookworm-go1.22)"
-    version: v0.5.8
-    refPaths:
-    - path: images/build/distroless-iptables/variants.yaml
-      match: IMAGE_VERSION:\ \'v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)\'
-
-  - name: "registry.k8s.io/build-image/go-runner: dependents (distroless-bookworm-go1.22)"
-    version: v2.3.1-go1.22.7-bookworm.0
-    refPaths:
-    - path: images/build/distroless-iptables/variants.yaml
-      match: GORUNNER_VERSION:\ \'v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)\'
-
   - name: "registry.k8s.io/build-image/setcap"
-    version: bookworm-v1.0.4
+    version: bookworm-v1.0.3
     refPaths:
     - path: images/build/setcap/Makefile
       match: IMAGE_VERSION\ \?=\ bookworm-v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)
@@ -479,16 +490,20 @@ dependencies:
 
   # Base images (next candidate)
   - name: "registry.k8s.io/build-image/debian-base (next candidate)"
-    version: bookworm-v1.0.4
+    version: bookworm-v1.0.3
     refPaths:
     - path: images/build/debian-base/variants.yaml
       match: "IMAGE_VERSION: 'bookworm-v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
-  - name: "registry.k8s.io/build-image/setcap (next candidate)"
-    version: bookworm-v1.0.4
+  - name: "registry.k8s.io/build-image/debian-base: dependents (next candidate)"
+    version: bookworm-v1.0.3
     refPaths:
     - path: images/build/setcap/variants.yaml
       match: "DEBIAN_BASE_VERSION: 'bookworm-v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
+
+  - name: "registry.k8s.io/build-image/setcap (next candidate)"
+    version: bookworm-v1.0.3
+    refPaths:
     - path: images/build/setcap/variants.yaml
       match: "IMAGE_VERSION: 'bookworm-v((([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)'"
 
diff --git a/gcb/fast-forward/cloudbuild.yaml b/gcb/fast-forward/cloudbuild.yaml
index fd1d3cfb..498830cc 100644
--- a/gcb/fast-forward/cloudbuild.yaml
+++ b/gcb/fast-forward/cloudbuild.yaml
@@ -56,10 +56,6 @@ steps:
   dir: go/src/k8s.io/release
   env:
   - KREL_OUTPUT_PATH=/workspace/bin/krel
-  - "TOOL_ORG=${_TOOL_ORG}"
-  - "TOOL_REPO=${_TOOL_REPO}"
-  - "TOOL_REF=${_TOOL_REF}"
-  - "FORCE_BUILD_KREL=${_FORCE_BUILD_KREL}"
   args:
   - ./hack/get-krel
 
diff --git a/gcb/obs-release/cloudbuild.yaml b/gcb/obs-release/cloudbuild.yaml
index b6d18a18..42f38d47 100644
--- a/gcb/obs-release/cloudbuild.yaml
+++ b/gcb/obs-release/cloudbuild.yaml
@@ -56,10 +56,6 @@ steps:
   dir: go/src/k8s.io/release
   env:
   - KREL_OUTPUT_PATH=/workspace/bin/krel
-  - "TOOL_ORG=${_TOOL_ORG}"
-  - "TOOL_REPO=${_TOOL_REPO}"
-  - "TOOL_REF=${_TOOL_REF}"
-  - "FORCE_BUILD_KREL=${_FORCE_BUILD_KREL}"
   args:
   - ./hack/get-krel
 
diff --git a/gcb/obs-stage/cloudbuild.yaml b/gcb/obs-stage/cloudbuild.yaml
index d56eb556..235bd674 100644
--- a/gcb/obs-stage/cloudbuild.yaml
+++ b/gcb/obs-stage/cloudbuild.yaml
@@ -56,10 +56,6 @@ steps:
   dir: go/src/k8s.io/release
   env:
   - KREL_OUTPUT_PATH=/workspace/bin/krel
-  - "TOOL_ORG=${_TOOL_ORG}"
-  - "TOOL_REPO=${_TOOL_REPO}"
-  - "TOOL_REF=${_TOOL_REF}"
-  - "FORCE_BUILD_KREL=${_FORCE_BUILD_KREL}"
   args:
   - ./hack/get-krel
 
diff --git a/gcb/release/cloudbuild.yaml b/gcb/release/cloudbuild.yaml
index 3cf6b851..869deaa4 100644
--- a/gcb/release/cloudbuild.yaml
+++ b/gcb/release/cloudbuild.yaml
@@ -59,10 +59,6 @@ steps:
   dir: go/src/k8s.io/release
   env:
   - KREL_OUTPUT_PATH=/workspace/bin/krel
-  - "TOOL_ORG=${_TOOL_ORG}"
-  - "TOOL_REPO=${_TOOL_REPO}"
-  - "TOOL_REF=${_TOOL_REF}"
-  - "FORCE_BUILD_KREL=${_FORCE_BUILD_KREL}"
   args:
   - ./hack/get-krel
 
diff --git a/gcb/stage/cloudbuild.yaml b/gcb/stage/cloudbuild.yaml
index f02efc2a..90127a06 100644
--- a/gcb/stage/cloudbuild.yaml
+++ b/gcb/stage/cloudbuild.yaml
@@ -60,10 +60,6 @@ steps:
   dir: go/src/k8s.io/release
   env:
   - KREL_OUTPUT_PATH=/workspace/bin/krel
-  - "TOOL_ORG=${_TOOL_ORG}"
-  - "TOOL_REPO=${_TOOL_REPO}"
-  - "TOOL_REF=${_TOOL_REF}"
-  - "FORCE_BUILD_KREL=${_FORCE_BUILD_KREL}"
   args:
   - ./hack/get-krel
 
diff --git a/go.mod b/go.mod
index 74cfb69f..72fbdc26 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module k8s.io/release
 
-go 1.23
+go 1.22.4
 
 require (
 	cloud.google.com/go/storage v1.39.1
@@ -10,21 +10,21 @@ require (
 	github.com/go-git/go-git/v5 v5.12.0
 	github.com/goark/go-cvss v1.6.7
 	github.com/golang/protobuf v1.5.4
-	github.com/google/go-containerregistry v0.20.2
+	github.com/google/go-containerregistry v0.20.1
 	github.com/google/go-github/v60 v60.0.0
 	github.com/google/safetext v0.0.0-20230106111101-7156a760e523
 	github.com/google/uuid v1.6.0
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/in-toto/in-toto-golang v0.9.0
 	github.com/mattn/go-isatty v0.0.20
-	github.com/maxbrunsfeld/counterfeiter/v6 v6.9.0
+	github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/psampaz/go-mod-outdated v0.9.0
 	github.com/saschagrunert/go-modiff v1.3.5
 	github.com/sendgrid/rest v2.6.9+incompatible
-	github.com/sendgrid/sendgrid-go v3.16.0+incompatible
+	github.com/sendgrid/sendgrid-go v3.14.0+incompatible
 	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
 	github.com/shirou/gopsutil/v3 v3.24.5
 	github.com/shurcooL/githubv4 v0.0.0-20220115235240-a14260e6f8a2
@@ -33,18 +33,18 @@ require (
 	github.com/stretchr/testify v1.9.0
 	github.com/tj/go-spin v1.1.0
 	github.com/yuin/goldmark v1.7.4
-	golang.org/x/net v0.29.0
-	golang.org/x/oauth2 v0.23.0
-	golang.org/x/text v0.18.0
+	golang.org/x/net v0.27.0
+	golang.org/x/oauth2 v0.21.0
+	golang.org/x/text v0.16.0
 	google.golang.org/api v0.172.0
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/apimachinery v0.31.1
-	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
+	k8s.io/apimachinery v0.30.3
+	k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0
 	sigs.k8s.io/bom v0.6.0
 	sigs.k8s.io/mdtoc v1.4.0
 	sigs.k8s.io/promo-tools/v3 v3.6.0
 	sigs.k8s.io/release-sdk v0.12.1
-	sigs.k8s.io/release-utils v0.8.5
+	sigs.k8s.io/release-utils v0.8.3
 	sigs.k8s.io/yaml v1.4.0
 )
 
@@ -67,9 +67,9 @@ require (
 	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
 	github.com/Azure/go-autorest/logger v0.2.1 // indirect
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
-	github.com/BurntSushi/toml v0.3.1 // indirect
+	github.com/BurntSushi/toml v1.2.1 // indirect
 	github.com/MakeNowJust/heredoc/v2 v2.0.1 // indirect
-	github.com/Microsoft/go-winio v0.6.2 // indirect
+	github.com/Microsoft/go-winio v0.6.1 // indirect
 	github.com/OneOfOne/xxhash v1.2.8 // indirect
 	github.com/ProtonMail/go-crypto v1.0.0 // indirect
 	github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
@@ -108,7 +108,7 @@ require (
 	github.com/buildkite/agent/v3 v3.62.0 // indirect
 	github.com/buildkite/go-pipeline v0.3.2 // indirect
 	github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 // indirect
-	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
 	github.com/clbanning/mxj/v2 v2.7.0 // indirect
 	github.com/cloudflare/circl v1.3.7 // indirect
@@ -122,8 +122,9 @@ require (
 	github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
 	github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
 	github.com/dimchansky/utfbom v1.1.1 // indirect
-	github.com/docker/cli v27.1.1+incompatible // indirect
+	github.com/docker/cli v24.0.7+incompatible // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
+	github.com/docker/docker v24.0.9+incompatible // indirect
 	github.com/docker/docker-credential-helpers v0.8.0 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
@@ -133,7 +134,6 @@ require (
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/fvbommel/sortorder v1.0.1 // indirect
-	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/glebarez/go-sqlite v1.22.0 // indirect
 	github.com/go-chi/chi v4.1.2+incompatible // indirect
 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -141,7 +141,7 @@ require (
 	github.com/go-ini/ini v1.67.0 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.3 // indirect
 	github.com/go-jose/go-jose/v4 v4.0.1 // indirect
-	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-ole/go-ole v1.2.6 // indirect
 	github.com/go-openapi/analysis v0.23.0 // indirect
@@ -186,7 +186,7 @@ require (
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
-	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/klauspost/compress v1.17.8 // indirect
 	github.com/knqyf263/go-rpmdb v0.0.0-20230723082926-067d98befa60 // indirect
 	github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
@@ -204,7 +204,7 @@ require (
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/oklog/ulid v1.3.1 // indirect
 	github.com/oleiade/reflections v1.0.1 // indirect
-	github.com/open-policy-agent/opa v0.68.0 // indirect
+	github.com/open-policy-agent/opa v0.63.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.1.0 // indirect
 	github.com/opentracing/opentracing-go v1.2.0 // indirect
@@ -215,10 +215,10 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
-	github.com/prometheus/client_golang v1.20.2 // indirect
-	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.55.0 // indirect
-	github.com/prometheus/procfs v0.15.1 // indirect
+	github.com/prometheus/client_golang v1.19.0 // indirect
+	github.com/prometheus/client_model v0.6.0 // indirect
+	github.com/prometheus/common v0.51.1 // indirect
+	github.com/prometheus/procfs v0.12.0 // indirect
 	github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@@ -253,7 +253,6 @@ require (
 	github.com/tjfoc/gmsm v1.4.1 // indirect
 	github.com/transparency-dev/merkle v0.0.2 // indirect
 	github.com/vbatts/tar-split v0.11.5 // indirect
-	github.com/x448/float16 v0.8.4 // indirect
 	github.com/xanzy/go-gitlab v0.102.0 // indirect
 	github.com/xanzy/ssh-agent v0.3.3 // indirect
 	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
@@ -265,37 +264,37 @@ require (
 	go.mongodb.org/mongo-driver v1.14.0 // indirect
 	go.opencensus.io v0.24.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
-	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
-	go.opentelemetry.io/otel v1.28.0 // indirect
-	go.opentelemetry.io/otel/metric v1.28.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.28.0 // indirect
-	go.opentelemetry.io/otel/trace v1.28.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
+	go.opentelemetry.io/otel v1.24.0 // indirect
+	go.opentelemetry.io/otel/metric v1.24.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.24.0 // indirect
+	go.opentelemetry.io/otel/trace v1.24.0 // indirect
 	go.step.sm/crypto v0.44.2 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.27.0 // indirect
-	golang.org/x/crypto v0.27.0 // indirect
-	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
-	golang.org/x/mod v0.21.0 // indirect
-	golang.org/x/sync v0.8.0 // indirect
-	golang.org/x/sys v0.25.0 // indirect
-	golang.org/x/term v0.24.0 // indirect
-	golang.org/x/time v0.6.0 // indirect
-	golang.org/x/tools v0.25.0 // indirect
+	golang.org/x/crypto v0.25.0 // indirect
+	golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
+	golang.org/x/mod v0.17.0 // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/term v0.22.0 // indirect
+	golang.org/x/time v0.5.0 // indirect
+	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
 	golang.org/x/tools/go/vcs v0.1.0-deprecated // indirect
 	golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
 	google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
-	google.golang.org/grpc v1.66.0 // indirect
-	google.golang.org/protobuf v1.34.2 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
+	google.golang.org/grpc v1.62.1 // indirect
+	google.golang.org/protobuf v1.33.0 // indirect
 	gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/api v0.28.3 // indirect
-	k8s.io/client-go v0.28.3 // indirect
-	k8s.io/klog/v2 v2.130.1 // indirect
+	k8s.io/api v0.28.4 // indirect
+	k8s.io/client-go v0.28.4 // indirect
+	k8s.io/klog/v2 v2.120.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
 	modernc.org/libc v1.37.6 // indirect
 	modernc.org/mathutil v1.6.0 // indirect
diff --git a/go.sum b/go.sum
index cd9b0709..f452d97a 100644
--- a/go.sum
+++ b/go.sum
@@ -92,16 +92,17 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/GoogleCloudPlatform/testgrid v0.0.38 h1:DRF37FwrwKaP3ze+Yvt21WvXoiDEZNYYem2zWcjgU/g=
 github.com/GoogleCloudPlatform/testgrid v0.0.38/go.mod h1:KGQ3wqPuX1xCqeSHvcGmAafuMtV7cRNST9hZS7VCahA=
 github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A=
 github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
-github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
 github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
 github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
@@ -221,13 +222,13 @@ github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HV
 github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q=
 github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
 github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
-github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
 github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
 github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4=
@@ -281,10 +282,12 @@ github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1G
 github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=
 github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
-github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
-github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
+github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
+github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
 github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -318,8 +321,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
 github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
-github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
 github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
 github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
@@ -344,8 +345,8 @@ github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQr
 github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
 github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
-github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
@@ -378,8 +379,7 @@ github.com/go-rod/rod v0.116.0 h1:ypRryjTys3EnqHskJ/TdgodFMvXV0EHvmy4bSkKZgHM=
 github.com/go-rod/rod v0.116.0/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
-github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
 github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
 github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/goark/errs v1.3.2 h1:ifccNe1aK7Xezt4XVYwHUqalmnfhuphnEvh3FshCReQ=
@@ -400,8 +400,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
 github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
 github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
-github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
+github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -457,8 +457,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
-github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
+github.com/google/go-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybXn0TPi4Smd8du0=
+github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
 github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg=
 github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA=
 github.com/google/go-github/v60 v60.0.0 h1:oLG98PsLauFvvu4D/YPxq374jhSxFYdzQGNCyONLfn8=
@@ -483,8 +483,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
-github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
+github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0=
+github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
 github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
@@ -510,8 +510,8 @@ github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORR
 github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
 github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -573,8 +573,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
+github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
 github.com/knqyf263/go-rpmdb v0.0.0-20230723082926-067d98befa60 h1:kTBDRI2BJvNYFjMcOH5zskWS5QYDqUDE0owFpb38Vng=
 github.com/knqyf263/go-rpmdb v0.0.0-20230723082926-067d98befa60/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -603,8 +603,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.9.0 h1:ERhc+PJKEyqWQnKu7/K0frSVGFihYYImqNdqP5r0cN0=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.9.0/go.mod h1:tU2wQdIyJ7fib/YXxFR0dgLlFz3yl4p275UfUKmDFjk=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 h1:NicmruxkeqHjDv03SfSxqmaLuisddudfP3h5wdXFbhM=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I=
 github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
 github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
 github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@@ -649,16 +649,16 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
-github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
-github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
+github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
+github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
-github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
-github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
-github.com/open-policy-agent/opa v0.68.0 h1:Jl3U2vXRjwk7JrHmS19U3HZO5qxQRinQbJ2eCJYSqJQ=
-github.com/open-policy-agent/opa v0.68.0/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w=
+github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE=
+github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk=
+github.com/open-policy-agent/opa v0.63.0 h1:ztNNste1v8kH0/vJMJNquE45lRvqwrM5mY9Ctr9xIXw=
+github.com/open-policy-agent/opa v0.63.0/go.mod h1:9VQPqEfoB2N//AToTxzZ1pVTVPUoF2Mhd64szzjWPpU=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -683,15 +683,15 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
-github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
-github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
+github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
+github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
-github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
+github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
+github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw=
+github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
 github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf h1:014O62zIzQwvoD7Ekj3ePDF5bv9Xxy0w6AZk0qYbjUk=
 github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
 github.com/psampaz/go-mod-outdated v0.9.0 h1:P3f6z6NrAgG1kq1W4xcsa/lL8SM2SEZxAlRvG1AMFBs=
@@ -727,8 +727,8 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
 github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
 github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
 github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
-github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw=
-github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
+github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA=
+github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
@@ -824,8 +824,6 @@ github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG
 github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
 github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
 github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
-github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
-github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4=
 github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@@ -873,22 +871,22 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
-go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
-go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw=
-go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
-go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
-go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
-go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
-go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
-go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
-go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
-go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
+go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
+go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ=
+go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
+go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
+go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
+go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
+go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
+go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
+go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
+go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
 go.step.sm/crypto v0.44.2 h1:t3p3uQ7raP2jp2ha9P6xkQF85TJZh+87xmjSLaib+jk=
 go.step.sm/crypto v0.44.2/go.mod h1:x1439EnFhadzhkuaGX7sz03LEMQ+jV4gRamf5LCZJQQ=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -914,8 +912,8 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
 golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -926,8 +924,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
-golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
+golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -950,8 +948,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -994,15 +992,15 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
-golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
-golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
-golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1014,8 +1012,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1070,8 +1068,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -1080,8 +1078,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
-golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
+golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
+golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1095,13 +1093,13 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
-golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -1148,8 +1146,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
-golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/tools/go/vcs v0.1.0-deprecated h1:cOIJqWBl99H1dH5LWizPa+0ImeeJq3t3cJjaeOWUAL4=
 golang.org/x/tools/go/vcs v0.1.0-deprecated/go.mod h1:zUrvATBAvEI9535oC0yWYsLsHIV4Z7g63sNPVMtuBy8=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1214,10 +1212,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20200804151602-45615f50871c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s=
 google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U=
-google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
-google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
+google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E=
+google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1231,8 +1229,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
-google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
+google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
+google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1245,8 +1243,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
-google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1284,18 +1282,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
-k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
-k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
-k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
-k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
-k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
-k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
-k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
+k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
+k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
+k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
+k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
+k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
+k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
 k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
-k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
-k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
 modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
 modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
@@ -1317,8 +1315,8 @@ sigs.k8s.io/promo-tools/v3 v3.6.0 h1:C2L08ezrWm1aZI8Emd3iZPZQserLPRgzuqQVxvI0PUI
 sigs.k8s.io/promo-tools/v3 v3.6.0/go.mod h1:XJ3jy0hJYs+hWKt8XsLHFzGQV8PUtvllvbxjN/E5RXI=
 sigs.k8s.io/release-sdk v0.12.1 h1:/Q+yWpl33EnFx1b7xh6FnbioWSRUTrVkigL4KZVTrkU=
 sigs.k8s.io/release-sdk v0.12.1/go.mod h1:nnB4tt1g0VXMUCIYzDzPVqNI896OQrWipE6WbyZ6FSk=
-sigs.k8s.io/release-utils v0.8.5 h1:FUtFqEAN621gSXv0L7kHyWruBeS7TUU9aWf76olX7uQ=
-sigs.k8s.io/release-utils v0.8.5/go.mod h1:qsm5bdxdgoHkD8HsXpgme2/c3mdsNaiV53Sz2HmKeJA=
+sigs.k8s.io/release-utils v0.8.3 h1:KtOtA4qDmzJyeQ2zkDsFVI25+NViwms/o5eL2NftFdA=
+sigs.k8s.io/release-utils v0.8.3/go.mod h1:fp82Fma06OXBhEJ+GUJKqvcplDBomruK1R/1fWJnsrQ=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/hack/get-krel b/hack/get-krel
index 1a119cbe..c2abecaf 100755
--- a/hack/get-krel
+++ b/hack/get-krel
@@ -17,42 +17,22 @@
 set -o errexit
 set -o nounset
 set -o pipefail
-set -x
 
 curl_retry() {
     curl -sSfL --retry 5 --retry-delay 3 "$@"
 }
 
-DEFAULT_TOOL_ORG=kubernetes
-DEFAULT_TOOL_REPO=release
-DEFAULT_TOOL_REF=master
-
-TOOL_ORG=${TOOL_ORG:-${DEFAULT_TOOL_ORG}}
-TOOL_REPO=${TOOL_REPO:-${DEFAULT_TOOL_REPO}}
-TOOL_REF=${TOOL_REF:-${DEFAULT_TOOL_REF}}
-
-FORCE_BUILD_KREL=${FORCE_BUILD_KREL:-false}
-
 KREL_OUTPUT_PATH=${KREL_OUTPUT_PATH:-bin/krel}
 echo "Using output path: $KREL_OUTPUT_PATH"
 mkdir -p "$(dirname "$KREL_OUTPUT_PATH")"
 
-if [[ "$FORCE_BUILD_KREL" == false &&
-    "$TOOL_ORG" == "$DEFAULT_TOOL_ORG" &&
-    "$TOOL_REPO" == "$DEFAULT_TOOL_REPO" &&
-    "$TOOL_REF" == "$DEFAULT_TOOL_REF" ]]; then
-
-    LATEST_RELEASE=$(curl_retry https://api.github.com/repos/kubernetes/release/releases/latest | jq -r .tag_name)
-    echo "Using krel release: $LATEST_RELEASE"
+LATEST_RELEASE=$(curl_retry https://api.github.com/repos/kubernetes/release/releases/latest | jq -r .tag_name)
+echo "Using krel release: $LATEST_RELEASE"
 
-    echo "Downloading krel from GCB bucket…"
-    GCB_URL="https://storage.googleapis.com/k8s-artifacts-sig-release/kubernetes/release/$LATEST_RELEASE/krel-amd64-linux"
-    curl_retry "$GCB_URL" -o "$KREL_OUTPUT_PATH"
-    chmod +x "$KREL_OUTPUT_PATH"
-else
-    echo "Building krel from sources"
-    go build -o "$KREL_OUTPUT_PATH" ./cmd/krel
-fi
+echo "Downloading krel from GCB bucket…"
+GCB_URL="https://storage.googleapis.com/k8s-artifacts-sig-release/kubernetes/release/$LATEST_RELEASE/krel-amd64-linux"
+curl_retry "$GCB_URL" -o "$KREL_OUTPUT_PATH"
+chmod +x "$KREL_OUTPUT_PATH"
 
 echo "Done, output of 'krel version':"
 "$KREL_OUTPUT_PATH" version
diff --git a/images/build/cross/Makefile b/images/build/cross/Makefile
index eb53c2cd..67833985 100644
--- a/images/build/cross/Makefile
+++ b/images/build/cross/Makefile
@@ -28,7 +28,7 @@ IMGNAME = kube-cross
 # - v1.100.0-go1.17-bullseye.0 satisfies SemVer regex, while:
 # - v1.100-go1.17-bullseye.0 does not
 KUBERNETES_VERSION ?= v1.31.0
-GO_VERSION ?= 1.22.7
+GO_VERSION ?= 1.22.5
 GO_MAJOR_VERSION ?= 1.22
 OS_CODENAME ?= bullseye
 REVISION ?= 0
diff --git a/images/build/cross/variants.yaml b/images/build/cross/variants.yaml
index 69882cbf..d2937f11 100644
--- a/images/build/cross/variants.yaml
+++ b/images/build/cross/variants.yaml
@@ -1,55 +1,55 @@
 variants:
-  v1.32-go1.23-bullseye:
-    CONFIG: 'go1.23-bullseye'
-    TYPE: 'default'
-    IMAGE_VERSION: 'v1.32.0-go1.23.1-bullseye.0'
-    KUBERNETES_VERSION: 'v1.32.0'
-    GO_VERSION: '1.23.1'
-    GO_MAJOR_VERSION: '1.23'
-    OS_CODENAME: 'bullseye'
-    REVISION: '0'
   v1.31-go1.23-bullseye:
     CONFIG: 'go1.23-bullseye'
     TYPE: 'default'
-    IMAGE_VERSION: 'v1.31.0-go1.23.1-bullseye.0'
+    IMAGE_VERSION: 'v1.31.0-go1.23rc2-bullseye.0'
     KUBERNETES_VERSION: 'v1.31.0'
-    GO_VERSION: '1.23.1'
+    GO_VERSION: '1.23rc2'
     GO_MAJOR_VERSION: '1.23'
     OS_CODENAME: 'bullseye'
     REVISION: '0'
   v1.31-go1.22-bullseye:
     CONFIG: 'go1.22-bullseye'
     TYPE: 'default'
-    IMAGE_VERSION: 'v1.31.0-go1.22.7-bullseye.0'
+    IMAGE_VERSION: 'v1.31.0-go1.22.5-bullseye.0'
     KUBERNETES_VERSION: 'v1.31.0'
-    GO_VERSION: '1.22.7'
+    GO_VERSION: '1.22.5'
     GO_MAJOR_VERSION: '1.22'
     OS_CODENAME: 'bullseye'
     REVISION: '0'
   v1.30-go1.22-bullseye:
     CONFIG: 'go1.22-bullseye'
     TYPE: 'default'
-    IMAGE_VERSION: 'v1.30.0-go1.22.7-bullseye.0'
+    IMAGE_VERSION: 'v1.30.0-go1.22.5-bullseye.0'
     KUBERNETES_VERSION: 'v1.30.0'
-    GO_VERSION: '1.22.7'
+    GO_VERSION: '1.22.5'
     GO_MAJOR_VERSION: '1.22'
     OS_CODENAME: 'bullseye'
     REVISION: '0'
   v1.29-go1.22-bullseye:
     CONFIG: 'go1.22-bullseye'
     TYPE: 'default'
-    IMAGE_VERSION: 'v1.29.0-go1.22.7-bullseye.0'
+    IMAGE_VERSION: 'v1.29.0-go1.22.5-bullseye.0'
     KUBERNETES_VERSION: 'v1.29.0'
-    GO_VERSION: '1.22.7'
+    GO_VERSION: '1.22.5'
     GO_MAJOR_VERSION: '1.22'
     OS_CODENAME: 'bullseye'
     REVISION: '0'
   v1.28-go1.22-bullseye:
     CONFIG: 'go1.22-bullseye'
     TYPE: 'default'
-    IMAGE_VERSION: 'v1.28.0-go1.22.7-bullseye.0'
+    IMAGE_VERSION: 'v1.28.0-go1.22.5-bullseye.0'
     KUBERNETES_VERSION: 'v1.28.0'
-    GO_VERSION: '1.22.7'
+    GO_VERSION: '1.22.5'
+    GO_MAJOR_VERSION: '1.22'
+    OS_CODENAME: 'bullseye'
+    REVISION: '0'
+  v1.27-go1.22-bullseye:
+    CONFIG: 'go1.22-bullseye'
+    TYPE: 'default'
+    IMAGE_VERSION: 'v1.27.0-go1.22.5-bullseye.0'
+    KUBERNETES_VERSION: 'v1.27.0'
+    GO_VERSION: '1.22.5'
     GO_MAJOR_VERSION: '1.22'
     OS_CODENAME: 'bullseye'
     REVISION: '0'
diff --git a/images/build/debian-base/Makefile b/images/build/debian-base/Makefile
index 396723c3..ff837f2c 100755
--- a/images/build/debian-base/Makefile
+++ b/images/build/debian-base/Makefile
@@ -19,7 +19,7 @@ IMAGE ?= $(REGISTRY)/debian-base
 BUILD_IMAGE ?= debian-build
 
 TAG ?= $(shell git describe --tags --always --dirty)
-IMAGE_VERSION ?= bookworm-v1.0.4
+IMAGE_VERSION ?= bookworm-v1.0.3
 CONFIG ?= bookworm
 
 TAR_FILE ?= rootfs.tar
diff --git a/images/build/debian-base/variants.yaml b/images/build/debian-base/variants.yaml
index ccb0acc2..eae25a32 100644
--- a/images/build/debian-base/variants.yaml
+++ b/images/build/debian-base/variants.yaml
@@ -1,4 +1,4 @@
 variants:
   bookworm:
     CONFIG: 'bookworm'
-    IMAGE_VERSION: 'bookworm-v1.0.4'
+    IMAGE_VERSION: 'bookworm-v1.0.3'
diff --git a/images/build/distroless-iptables/Makefile b/images/build/distroless-iptables/Makefile
index 69e36c16..2a34a012 100644
--- a/images/build/distroless-iptables/Makefile
+++ b/images/build/distroless-iptables/Makefile
@@ -18,10 +18,10 @@ REGISTRY?="gcr.io/k8s-staging-build-image"
 IMAGE=$(REGISTRY)/distroless-iptables
 
 TAG ?= $(shell git describe --tags --always --dirty)
-IMAGE_VERSION ?= v0.6.3
+IMAGE_VERSION ?= v0.5.6
 CONFIG ?= distroless-bookworm
 BASEIMAGE ?= debian:bookworm-slim
-GORUNNER_VERSION ?= v2.3.1-go1.23.1-bookworm.0
+GORUNNER_VERSION ?= v2.3.1-go1.22.5-bookworm.0
 
 ARCH?=amd64
 ALL_ARCH = amd64 arm arm64 ppc64le s390x
diff --git a/images/build/distroless-iptables/variants.yaml b/images/build/distroless-iptables/variants.yaml
index 8f7db84f..5ef7892d 100644
--- a/images/build/distroless-iptables/variants.yaml
+++ b/images/build/distroless-iptables/variants.yaml
@@ -1,11 +1,11 @@
 variants:
   distroless-bookworm-go1.23:
     CONFIG: 'distroless-bookworm'
-    IMAGE_VERSION: 'v0.6.3'
+    IMAGE_VERSION: 'v0.6.0'
     BASEIMAGE: 'debian:bookworm-slim'
-    GORUNNER_VERSION: 'v2.3.1-go1.23.1-bookworm.0'
+    GORUNNER_VERSION: 'v2.3.1-go1.23rc1-bookworm.0'
   distroless-bookworm-go1.22:
     CONFIG: 'distroless-bookworm'
-    IMAGE_VERSION: 'v0.5.8'
+    IMAGE_VERSION: 'v0.5.6'
     BASEIMAGE: 'debian:bookworm-slim'
-    GORUNNER_VERSION: 'v2.3.1-go1.22.7-bookworm.0'
+    GORUNNER_VERSION: 'v2.3.1-go1.22.5-bookworm.0'
diff --git a/images/build/go-runner/Makefile b/images/build/go-runner/Makefile
index 5da1e6eb..49de6b53 100644
--- a/images/build/go-runner/Makefile
+++ b/images/build/go-runner/Makefile
@@ -19,7 +19,7 @@ IMGNAME = go-runner
 APP_VERSION = $(shell cat VERSION)
 GO_MAJOR_VERSION ?= 1.22
 REVISION ?= 0
-GO_VERSION ?= 1.22.7
+GO_VERSION ?= 1.22.5
 OS_CODENAME ?= bookworm
 
 # Build args
diff --git a/images/build/go-runner/variants.yaml b/images/build/go-runner/variants.yaml
index 01e1bdef..05bedd2b 100644
--- a/images/build/go-runner/variants.yaml
+++ b/images/build/go-runner/variants.yaml
@@ -1,17 +1,17 @@
 variants:
   go1.23-bookworm:
     CONFIG: 'go1.23-bookworm'
-    IMAGE_VERSION: 'v2.3.1-go1.23.1-bookworm.0'
+    IMAGE_VERSION: 'v2.3.1-go1.23rc2-bookworm.0'
     GO_MAJOR_VERSION: '1.23'
     OS_CODENAME: 'bookworm'
     REVISION: '0'
-    GO_VERSION: '1.23.1'
+    GO_VERSION: '1.23rc2'
     DISTROLESS_IMAGE: 'static-debian12'
   go1.22-bookworm:
     CONFIG: 'go1.22-bookworm'
-    IMAGE_VERSION: 'v2.3.1-go1.22.7-bookworm.0'
+    IMAGE_VERSION: 'v2.3.1-go1.22.5-bookworm.0'
     GO_MAJOR_VERSION: '1.22'
     OS_CODENAME: 'bookworm'
     REVISION: '0'
-    GO_VERSION: '1.22.7'
+    GO_VERSION: '1.22.5'
     DISTROLESS_IMAGE: 'static-debian12'
diff --git a/images/build/setcap/Makefile b/images/build/setcap/Makefile
index d116a82c..50323a64 100644
--- a/images/build/setcap/Makefile
+++ b/images/build/setcap/Makefile
@@ -18,9 +18,9 @@ REGISTRY?="gcr.io/k8s-staging-build-image"
 IMAGE=$(REGISTRY)/setcap
 
 TAG ?= $(shell git describe --tags --always --dirty)
-IMAGE_VERSION ?= bookworm-v1.0.4
+IMAGE_VERSION ?= bookworm-v1.0.3
 CONFIG ?= bookworm
-DEBIAN_BASE_VERSION ?= bookworm-v1.0.4
+DEBIAN_BASE_VERSION ?= bookworm-v1.0.3
 
 ARCH?=amd64
 ALL_ARCH = amd64 arm arm64 ppc64le s390x
diff --git a/images/build/setcap/variants.yaml b/images/build/setcap/variants.yaml
index ebff6d4c..e29ff910 100644
--- a/images/build/setcap/variants.yaml
+++ b/images/build/setcap/variants.yaml
@@ -7,5 +7,5 @@ variants:
   # Debian 12 - Kubernetes 1.28 and newer
   bookworm:
     CONFIG: 'bookworm'
-    IMAGE_VERSION: 'bookworm-v1.0.4'
-    DEBIAN_BASE_VERSION: 'bookworm-v1.0.4'
+    IMAGE_VERSION: 'bookworm-v1.0.3'
+    DEBIAN_BASE_VERSION: 'bookworm-v1.0.3'
diff --git a/images/k8s-cloud-builder/variants.yaml b/images/k8s-cloud-builder/variants.yaml
index ac952d8c..259dddd8 100644
--- a/images/k8s-cloud-builder/variants.yaml
+++ b/images/k8s-cloud-builder/variants.yaml
@@ -1,20 +1,16 @@
 variants:
-  v1.32-cross1.23-bullseye:
-    CONFIG: 'cross1.23'
-    KUBE_CROSS_VERSION: 'v1.32.0-go1.23.0-bullseye.0'
-  v1.31-cross1.23-bullseye:
-    CONFIG: 'cross1.23'
-    KUBE_CROSS_VERSION: 'v1.31.0-go1.23.0-bullseye.0'
-  # TODO: remove this after upgrade to Go 1.23 is finalized and there's on risk of reverting
   v1.31-cross1.22-bullseye:
     CONFIG: 'cross1.22'
-    KUBE_CROSS_VERSION: 'v1.31.0-go1.22.6-bullseye.0'
+    KUBE_CROSS_VERSION: 'v1.31.0-go1.22.5-bullseye.0'
   v1.30-cross1.22-bullseye:
     CONFIG: 'cross1.22'
-    KUBE_CROSS_VERSION: 'v1.30.0-go1.22.6-bullseye.0'
+    KUBE_CROSS_VERSION: 'v1.30.0-go1.22.5-bullseye.0'
   v1.29-cross1.22-bullseye:
     CONFIG: 'cross1.21'
-    KUBE_CROSS_VERSION: 'v1.29.0-go1.22.6-bullseye.0'
+    KUBE_CROSS_VERSION: 'v1.29.0-go1.22.5-bullseye.0'
   v1.28-cross1.22-bullseye:
     CONFIG: 'cross1.20'
-    KUBE_CROSS_VERSION: 'v1.28.0-go1.22.6-bullseye.0'
+    KUBE_CROSS_VERSION: 'v1.28.0-go1.22.5-bullseye.0'
+  v1.27-cross1.22-bullseye:
+    CONFIG: 'cross1.20'
+    KUBE_CROSS_VERSION: 'v1.27.0-go1.22.5-bullseye.0'
diff --git a/images/releng/ci/variants.yaml b/images/releng/ci/variants.yaml
index 7ec6ede8..020499be 100644
--- a/images/releng/ci/variants.yaml
+++ b/images/releng/ci/variants.yaml
@@ -1,16 +1,16 @@
 variants:
   go1.23-bookworm:
     CONFIG: 'go1.23-bookworm'
-    GO_VERSION: '1.23.1'
+    GO_VERSION: '1.23rc2'
     OS_CODENAME: 'bookworm'
     REVISION: '0'
   go1.22-bookworm:
     CONFIG: 'go1.22-bookworm'
-    GO_VERSION: '1.22.7'
+    GO_VERSION: '1.22.5'
     OS_CODENAME: 'bookworm'
     REVISION: '0'
   go1.21-bookworm:
     CONFIG: 'go1.21-bookworm'
-    GO_VERSION: '1.21.13'
+    GO_VERSION: '1.21.12'
     OS_CODENAME: 'bookworm'
     REVISION: '0'
diff --git a/images/releng/k8s-ci-builder/Makefile b/images/releng/k8s-ci-builder/Makefile
index 481fca7f..931da9d0 100644
--- a/images/releng/k8s-ci-builder/Makefile
+++ b/images/releng/k8s-ci-builder/Makefile
@@ -24,7 +24,7 @@ IMAGE = $(REGISTRY)/$(IMGNAME)
 TAG ?= $(shell git describe --tags --always --dirty)
 
 # Build args
-GO_VERSION ?= 1.23.0
+GO_VERSION ?= 1.22.5
 OS_CODENAME ?= bullseye
 IMAGE_ARG ?= $(IMAGE):$(TAG)-$(CONFIG)
 
diff --git a/images/releng/k8s-ci-builder/variants.yaml b/images/releng/k8s-ci-builder/variants.yaml
index 7acf88d5..47822beb 100644
--- a/images/releng/k8s-ci-builder/variants.yaml
+++ b/images/releng/k8s-ci-builder/variants.yaml
@@ -1,29 +1,29 @@
 variants:
   default:
     CONFIG: default
-    GO_VERSION: '1.22.6'
+    GO_VERSION: '1.22.5'
     OS_CODENAME: 'bullseye'
   next:
     CONFIG: next
-    GO_VERSION: '1.23.0'
+    GO_VERSION: '1.23rc2'
     OS_CODENAME: 'bookworm'
-  '1.32':
-    CONFIG: '1.32'
-    GO_VERSION: '1.23.0'
-    OS_CODENAME: 'bullseye'
   '1.31':
     CONFIG: '1.31'
-    GO_VERSION: '1.22.6'
+    GO_VERSION: '1.22.5'
     OS_CODENAME: 'bullseye'
   '1.30':
     CONFIG: '1.30'
-    GO_VERSION: '1.22.6'
+    GO_VERSION: '1.22.5'
     OS_CODENAME: 'bullseye'
   '1.29':
     CONFIG: '1.29'
-    GO_VERSION: '1.22.6'
+    GO_VERSION: '1.22.5'
     OS_CODENAME: 'bullseye'
   '1.28':
     CONFIG: '1.28'
-    GO_VERSION: '1.22.6'
+    GO_VERSION: '1.22.5'
+    OS_CODENAME: 'bullseye'
+  '1.27':
+    CONFIG: '1.27'
+    GO_VERSION: '1.22.5'
     OS_CODENAME: 'bullseye'
diff --git a/pkg/anago/anago.go b/pkg/anago/anago.go
index acace036..ffaa72cd 100644
--- a/pkg/anago/anago.go
+++ b/pkg/anago/anago.go
@@ -29,7 +29,6 @@ import (
 	"sigs.k8s.io/release-utils/util"
 	"sigs.k8s.io/release-utils/version"
 
-	"k8s.io/release/pkg/announce"
 	"k8s.io/release/pkg/release"
 )
 
@@ -52,9 +51,6 @@ const (
 	// releaseNotesJSONFile is the file containing the release notes in json format.
 	releaseNotesJSONFile = workspaceDir + "/src/release-notes.json"
 
-	// announcementHTMLFile is the file containing the release announcement in HTML format.
-	announcementHTMLFile = workspaceDir + "/src/" + announce.AnnouncementFile
-
 	// The default license for all artifacts.
 	LicenseIdentifier = "Apache-2.0"
 )
@@ -399,7 +395,7 @@ func (r *Release) Run() error {
 		return fmt.Errorf("init log file: %w", err)
 	}
 
-	logger := log.NewStepLogger(11)
+	logger := log.NewStepLogger(12)
 	v := version.GetVersionInfo()
 	logger.Infof("Using krel version: %s", v.GitVersion)
 
@@ -435,11 +431,6 @@ func (r *Release) Run() error {
 		logrus.Warnf("Unable to check provenance attestation: %v", err)
 	}
 
-	logger.WithStep().Info("Creating announcement")
-	if err := r.client.CreateAnnouncement(); err != nil {
-		return fmt.Errorf("create announcement: %w", err)
-	}
-
 	logger.WithStep().Info("Pushing artifacts")
 	if err := r.client.PushArtifacts(); err != nil {
 		return fmt.Errorf("push artifacts: %w", err)
@@ -450,11 +441,21 @@ func (r *Release) Run() error {
 		return fmt.Errorf("push git objects: %w", err)
 	}
 
+	logger.WithStep().Info("Creating announcement")
+	if err := r.client.CreateAnnouncement(); err != nil {
+		return fmt.Errorf("create announcement: %w", err)
+	}
+
 	logger.WithStep().Info("Updating GitHub release page")
 	if err := r.client.UpdateGitHubPage(); err != nil {
 		return fmt.Errorf("updating github page: %w", err)
 	}
 
+	logger.WithStep().Info("Archiving release")
+	if err := r.client.Archive(); err != nil {
+		return fmt.Errorf("archive release: %w", err)
+	}
+
 	logger.Info("Release done")
 	return nil
 }
diff --git a/pkg/anago/anagofakes/fake_release_client.go b/pkg/anago/anagofakes/fake_release_client.go
index 78caee11..baa82db4 100644
--- a/pkg/anago/anagofakes/fake_release_client.go
+++ b/pkg/anago/anagofakes/fake_release_client.go
@@ -22,6 +22,16 @@ import (
 )
 
 type FakeReleaseClient struct {
+	ArchiveStub        func() error
+	archiveMutex       sync.RWMutex
+	archiveArgsForCall []struct {
+	}
+	archiveReturns struct {
+		result1 error
+	}
+	archiveReturnsOnCall map[int]struct {
+		result1 error
+	}
 	CheckPrerequisitesStub        func() error
 	checkPrerequisitesMutex       sync.RWMutex
 	checkPrerequisitesArgsForCall []struct {
@@ -151,6 +161,59 @@ type FakeReleaseClient struct {
 	invocationsMutex sync.RWMutex
 }
 
+func (fake *FakeReleaseClient) Archive() error {
+	fake.archiveMutex.Lock()
+	ret, specificReturn := fake.archiveReturnsOnCall[len(fake.archiveArgsForCall)]
+	fake.archiveArgsForCall = append(fake.archiveArgsForCall, struct {
+	}{})
+	stub := fake.ArchiveStub
+	fakeReturns := fake.archiveReturns
+	fake.recordInvocation("Archive", []interface{}{})
+	fake.archiveMutex.Unlock()
+	if stub != nil {
+		return stub()
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeReleaseClient) ArchiveCallCount() int {
+	fake.archiveMutex.RLock()
+	defer fake.archiveMutex.RUnlock()
+	return len(fake.archiveArgsForCall)
+}
+
+func (fake *FakeReleaseClient) ArchiveCalls(stub func() error) {
+	fake.archiveMutex.Lock()
+	defer fake.archiveMutex.Unlock()
+	fake.ArchiveStub = stub
+}
+
+func (fake *FakeReleaseClient) ArchiveReturns(result1 error) {
+	fake.archiveMutex.Lock()
+	defer fake.archiveMutex.Unlock()
+	fake.ArchiveStub = nil
+	fake.archiveReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeReleaseClient) ArchiveReturnsOnCall(i int, result1 error) {
+	fake.archiveMutex.Lock()
+	defer fake.archiveMutex.Unlock()
+	fake.ArchiveStub = nil
+	if fake.archiveReturnsOnCall == nil {
+		fake.archiveReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.archiveReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
 func (fake *FakeReleaseClient) CheckPrerequisites() error {
 	fake.checkPrerequisitesMutex.Lock()
 	ret, specificReturn := fake.checkPrerequisitesReturnsOnCall[len(fake.checkPrerequisitesArgsForCall)]
@@ -822,6 +885,8 @@ func (fake *FakeReleaseClient) ValidateOptionsReturnsOnCall(i int, result1 error
 func (fake *FakeReleaseClient) Invocations() map[string][][]interface{} {
 	fake.invocationsMutex.RLock()
 	defer fake.invocationsMutex.RUnlock()
+	fake.archiveMutex.RLock()
+	defer fake.archiveMutex.RUnlock()
 	fake.checkPrerequisitesMutex.RLock()
 	defer fake.checkPrerequisitesMutex.RUnlock()
 	fake.checkProvenanceMutex.RLock()
diff --git a/pkg/anago/anagofakes/fake_release_impl.go b/pkg/anago/anagofakes/fake_release_impl.go
index 74f47c72..4d8a5ec4 100644
--- a/pkg/anago/anagofakes/fake_release_impl.go
+++ b/pkg/anago/anagofakes/fake_release_impl.go
@@ -30,6 +30,17 @@ import (
 )
 
 type FakeReleaseImpl struct {
+	ArchiveReleaseStub        func(*release.ArchiverOptions) error
+	archiveReleaseMutex       sync.RWMutex
+	archiveReleaseArgsForCall []struct {
+		arg1 *release.ArchiverOptions
+	}
+	archiveReleaseReturns struct {
+		result1 error
+	}
+	archiveReleaseReturnsOnCall map[int]struct {
+		result1 error
+	}
 	BranchNeedsCreationStub        func(string, string, semver.Version) (bool, error)
 	branchNeedsCreationMutex       sync.RWMutex
 	branchNeedsCreationArgsForCall []struct {
@@ -298,6 +309,67 @@ type FakeReleaseImpl struct {
 	invocationsMutex sync.RWMutex
 }
 
+func (fake *FakeReleaseImpl) ArchiveRelease(arg1 *release.ArchiverOptions) error {
+	fake.archiveReleaseMutex.Lock()
+	ret, specificReturn := fake.archiveReleaseReturnsOnCall[len(fake.archiveReleaseArgsForCall)]
+	fake.archiveReleaseArgsForCall = append(fake.archiveReleaseArgsForCall, struct {
+		arg1 *release.ArchiverOptions
+	}{arg1})
+	stub := fake.ArchiveReleaseStub
+	fakeReturns := fake.archiveReleaseReturns
+	fake.recordInvocation("ArchiveRelease", []interface{}{arg1})
+	fake.archiveReleaseMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeReleaseImpl) ArchiveReleaseCallCount() int {
+	fake.archiveReleaseMutex.RLock()
+	defer fake.archiveReleaseMutex.RUnlock()
+	return len(fake.archiveReleaseArgsForCall)
+}
+
+func (fake *FakeReleaseImpl) ArchiveReleaseCalls(stub func(*release.ArchiverOptions) error) {
+	fake.archiveReleaseMutex.Lock()
+	defer fake.archiveReleaseMutex.Unlock()
+	fake.ArchiveReleaseStub = stub
+}
+
+func (fake *FakeReleaseImpl) ArchiveReleaseArgsForCall(i int) *release.ArchiverOptions {
+	fake.archiveReleaseMutex.RLock()
+	defer fake.archiveReleaseMutex.RUnlock()
+	argsForCall := fake.archiveReleaseArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *FakeReleaseImpl) ArchiveReleaseReturns(result1 error) {
+	fake.archiveReleaseMutex.Lock()
+	defer fake.archiveReleaseMutex.Unlock()
+	fake.ArchiveReleaseStub = nil
+	fake.archiveReleaseReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeReleaseImpl) ArchiveReleaseReturnsOnCall(i int, result1 error) {
+	fake.archiveReleaseMutex.Lock()
+	defer fake.archiveReleaseMutex.Unlock()
+	fake.ArchiveReleaseStub = nil
+	if fake.archiveReleaseReturnsOnCall == nil {
+		fake.archiveReleaseReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.archiveReleaseReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
 func (fake *FakeReleaseImpl) BranchNeedsCreation(arg1 string, arg2 string, arg3 semver.Version) (bool, error) {
 	fake.branchNeedsCreationMutex.Lock()
 	ret, specificReturn := fake.branchNeedsCreationReturnsOnCall[len(fake.branchNeedsCreationArgsForCall)]
@@ -1627,6 +1699,8 @@ func (fake *FakeReleaseImpl) ValidateImagesReturnsOnCall(i int, result1 error) {
 func (fake *FakeReleaseImpl) Invocations() map[string][][]interface{} {
 	fake.invocationsMutex.RLock()
 	defer fake.invocationsMutex.RUnlock()
+	fake.archiveReleaseMutex.RLock()
+	defer fake.archiveReleaseMutex.RUnlock()
 	fake.branchNeedsCreationMutex.RLock()
 	defer fake.branchNeedsCreationMutex.RUnlock()
 	fake.checkPrerequisitesMutex.RLock()
diff --git a/pkg/anago/release.go b/pkg/anago/release.go
index 81911124..2b1be6ca 100644
--- a/pkg/anago/release.go
+++ b/pkg/anago/release.go
@@ -90,6 +90,10 @@ type releaseClient interface {
 	// UpdateGitHubPage updates the GitHub release page to with the source code
 	// and release information.
 	UpdateGitHubPage() error
+
+	// Archive copies the release process logs to a bucket and sets private
+	// permissions on it.
+	Archive() error
 }
 
 // DefaultRelease is the default staging implementation used in production.
@@ -150,6 +154,7 @@ type releaseImpl interface {
 	PushBranches(pusher *release.GitObjectPusher, branchList []string) error
 	PushMainBranch(pusher *release.GitObjectPusher) error
 	NewGitPusher(opts *release.GitObjectPusherOptions) (*release.GitObjectPusher, error)
+	ArchiveRelease(options *release.ArchiverOptions) error
 	NormalizePath(store object.Store, pathParts ...string) (string, error)
 	CopyToRemote(store object.Store, src, gcsPath string) error
 	PublishReleaseNotesIndex(
@@ -260,6 +265,11 @@ func (d *defaultReleaseImpl) CreateAnnouncement(options *announce.Options) error
 	return announce.NewAnnounce(options).CreateForRelease()
 }
 
+func (d *defaultReleaseImpl) ArchiveRelease(options *release.ArchiverOptions) error {
+	// Create a new release archiver
+	return release.NewArchiver(options).ArchiveRelease()
+}
+
 func (d *defaultReleaseImpl) UpdateGitHubPage(options *github.Options) error {
 	return github.NewGitHub(options).UpdateGitHubPage()
 }
@@ -417,7 +427,7 @@ func (d *DefaultRelease) PushArtifacts() error {
 		}
 	}
 
-	logrus.Info("Publishing release notes JSON and announcement")
+	logrus.Info("Publishing release notes JSON")
 	objStore := object.NewGCS()
 	objStore.SetOptions(objStore.WithNoClobber(false))
 	gcsReleaseRootPath, err := d.impl.NormalizePath(
@@ -431,17 +441,12 @@ func (d *DefaultRelease) PushArtifacts() error {
 		"/%s/release-notes.json", d.state.versions.Prime(),
 	)
 
-	for from, to := range map[string]string{
-		releaseNotesJSONFile: gcsReleaseNotesPath,
-		announcementHTMLFile: gcsReleaseRootPath + fmt.Sprintf("/%s/%s", d.state.versions.Prime(), announce.AnnouncementFile),
-	} {
-		if err := d.impl.CopyToRemote(
-			objStore,
-			from,
-			to,
-		); err != nil {
-			return fmt.Errorf("copy file notes to bucket: %w", err)
-		}
+	if err := d.impl.CopyToRemote(
+		objStore,
+		releaseNotesJSONFile,
+		gcsReleaseNotesPath,
+	); err != nil {
+		return fmt.Errorf("copy release notes to bucket: %w", err)
 	}
 
 	for _, version := range d.state.versions.Ordered() {
@@ -559,17 +564,6 @@ func (d *DefaultRelease) CreateAnnouncement() error {
 			logrus.Info("Not creating publishing bot issue in mock release")
 		}
 	}
-
-	args := ""
-	if d.options.NoMock {
-		args += " --nomock"
-	}
-	args += " --tag=" + d.state.versions.Prime()
-
-	logrus.Infof(
-		"To announce this release, run:\n\n$ krel announce send%s", args,
-	)
-
 	return nil
 }
 
@@ -606,6 +600,35 @@ func (d *DefaultRelease) UpdateGitHubPage() error {
 	return nil
 }
 
+// Archive stores the release artifact in a bucket along with
+// its logs for long term conservation.
+func (d *DefaultRelease) Archive() error {
+	// Create a new options set for the release archiver
+	archiverOptions := &release.ArchiverOptions{
+		ReleaseBuildDir: filepath.Join(workspaceDir, "src"),
+		LogFile:         d.state.logFile,
+		BuildVersion:    d.options.BuildVersion,
+		PrimeVersion:    d.state.versions.Prime(),
+		Bucket:          d.options.Bucket(),
+	}
+
+	if err := d.impl.ArchiveRelease(archiverOptions); err != nil {
+		return fmt.Errorf("running the release archival process: %w", err)
+	}
+
+	args := ""
+	if d.options.NoMock {
+		args += " --nomock"
+	}
+	args += " --tag=" + d.state.versions.Prime()
+
+	logrus.Infof(
+		"To announce this release, run:\n\n$ krel announce send%s", args,
+	)
+
+	return nil
+}
+
 // CheckProvenance verifies the artifacts staged in the release bucket
 // by verifying the provenance metadata generated during the stage run.
 func (d *DefaultRelease) CheckProvenance() error {
diff --git a/pkg/announce/announce.go b/pkg/announce/announce.go
index 9a670320..8a2a3111 100644
--- a/pkg/announce/announce.go
+++ b/pkg/announce/announce.go
@@ -24,8 +24,10 @@ import (
 	"github.com/sirupsen/logrus"
 )
 
-// AnnouncementFile is the default announcement HTML file.
-const AnnouncementFile = "announcement.html"
+const (
+	announcementFile = "announcement.html"
+	subjectFile      = "announcement-subject.txt"
+)
 
 const branchAnnouncement = `Kubernetes Community,
 <p>
@@ -87,6 +89,7 @@ func (a *Announce) CreateForBranch() error {
 
 	if err := a.impl.Create(
 		a.options.workDir,
+		fmt.Sprintf("Kubernetes %s branch has been created", a.options.branch),
 		fmt.Sprintf(branchAnnouncement, a.options.branch),
 	); err != nil {
 		return fmt.Errorf("creating branch announcement: %w", err)
@@ -133,6 +136,7 @@ func (a *Announce) CreateForRelease() error {
 
 	if err := a.impl.Create(
 		a.options.workDir,
+		fmt.Sprintf("Kubernetes %s is live!", a.options.tag),
 		fmt.Sprintf(releaseAnnouncement,
 			a.options.tag, goVersion, a.options.changelogPath,
 			filepath.Base(a.options.changelogPath), a.options.tag, changelog,
diff --git a/pkg/announce/announcefakes/fake_impl.go b/pkg/announce/announcefakes/fake_impl.go
index 26ec5289..3eed2b97 100644
--- a/pkg/announce/announcefakes/fake_impl.go
+++ b/pkg/announce/announcefakes/fake_impl.go
@@ -22,11 +22,12 @@ import (
 )
 
 type FakeImpl struct {
-	CreateStub        func(string, string) error
+	CreateStub        func(string, string, string) error
 	createMutex       sync.RWMutex
 	createArgsForCall []struct {
 		arg1 string
 		arg2 string
+		arg3 string
 	}
 	createReturns struct {
 		result1 error
@@ -64,19 +65,20 @@ type FakeImpl struct {
 	invocationsMutex sync.RWMutex
 }
 
-func (fake *FakeImpl) Create(arg1 string, arg2 string) error {
+func (fake *FakeImpl) Create(arg1 string, arg2 string, arg3 string) error {
 	fake.createMutex.Lock()
 	ret, specificReturn := fake.createReturnsOnCall[len(fake.createArgsForCall)]
 	fake.createArgsForCall = append(fake.createArgsForCall, struct {
 		arg1 string
 		arg2 string
-	}{arg1, arg2})
+		arg3 string
+	}{arg1, arg2, arg3})
 	stub := fake.CreateStub
 	fakeReturns := fake.createReturns
-	fake.recordInvocation("Create", []interface{}{arg1, arg2})
+	fake.recordInvocation("Create", []interface{}{arg1, arg2, arg3})
 	fake.createMutex.Unlock()
 	if stub != nil {
-		return stub(arg1, arg2)
+		return stub(arg1, arg2, arg3)
 	}
 	if specificReturn {
 		return ret.result1
@@ -90,17 +92,17 @@ func (fake *FakeImpl) CreateCallCount() int {
 	return len(fake.createArgsForCall)
 }
 
-func (fake *FakeImpl) CreateCalls(stub func(string, string) error) {
+func (fake *FakeImpl) CreateCalls(stub func(string, string, string) error) {
 	fake.createMutex.Lock()
 	defer fake.createMutex.Unlock()
 	fake.CreateStub = stub
 }
 
-func (fake *FakeImpl) CreateArgsForCall(i int) (string, string) {
+func (fake *FakeImpl) CreateArgsForCall(i int) (string, string, string) {
 	fake.createMutex.RLock()
 	defer fake.createMutex.RUnlock()
 	argsForCall := fake.createArgsForCall[i]
-	return argsForCall.arg1, argsForCall.arg2
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
 }
 
 func (fake *FakeImpl) CreateReturns(result1 error) {
diff --git a/pkg/announce/impl.go b/pkg/announce/impl.go
index fddd19e6..0faa737c 100644
--- a/pkg/announce/impl.go
+++ b/pkg/announce/impl.go
@@ -38,13 +38,27 @@ type defaultImpl struct{}
 //go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt announcefakes/fake_impl.go > announcefakes/_fake_impl.go && mv announcefakes/_fake_impl.go announcefakes/fake_impl.go"
 
 type impl interface {
-	Create(workDir, message string) error
+	Create(workDir, subject, message string) error
 	GetGoVersion(tag string) (string, error)
 	ReadChangelogFile(file string) ([]byte, error)
 }
 
-func (i *defaultImpl) Create(workDir, message string) error {
-	announcementFile := filepath.Join(workDir, AnnouncementFile)
+func (i *defaultImpl) Create(workDir, subject, message string) error {
+	subjectFile := filepath.Join(workDir, subjectFile)
+	//nolint:gosec // TODO(gosec): G306: Expect WriteFile permissions to be
+	// 0600 or less
+	if err := os.WriteFile(
+		subjectFile, []byte(subject), 0o755,
+	); err != nil {
+		return fmt.Errorf(
+			"writing subject to file %s: %w",
+			subjectFile,
+			err,
+		)
+	}
+	logrus.Debugf("Wrote file %s", subjectFile)
+
+	announcementFile := filepath.Join(workDir, announcementFile)
 	//nolint:gosec // TODO(gosec): G306: Expect WriteFile permissions to be
 	// 0600 or less
 	if err := os.WriteFile(
diff --git a/pkg/announce/options.go b/pkg/announce/options.go
index de887fd9..f03e275d 100644
--- a/pkg/announce/options.go
+++ b/pkg/announce/options.go
@@ -17,8 +17,8 @@ limitations under the License.
 package announce
 
 type Options struct {
-	// workDir is the directory where announcement.html
-	// will be written
+	// workDir is the directory where announcement.html and
+	// announcement-subject.txt will be written
 	workDir string
 	// Release tag we will build the announcement for
 	tag    string
diff --git a/pkg/build/push.go b/pkg/build/push.go
index 11607d22..51779542 100644
--- a/pkg/build/push.go
+++ b/pkg/build/push.go
@@ -168,6 +168,7 @@ func (bi *Instance) Push() error {
 }
 
 func (bi *Instance) findLatestVersion() (latestVersion string, err error) {
+	// Check if latest build uses bazel
 	if bi.opts.RepoRoot == "" {
 		bi.opts.RepoRoot, err = os.Getwd()
 		if err != nil {
@@ -175,18 +176,28 @@ func (bi *Instance) findLatestVersion() (latestVersion string, err error) {
 		}
 	}
 
+	isBazel, err := release.BuiltWithBazel(bi.opts.RepoRoot)
 	if err != nil {
 		return "", fmt.Errorf("identify if release built with Bazel: %w", err)
 	}
 
 	latestVersion = bi.opts.Version
 	if bi.opts.Version == "" {
-		logrus.Info("Using Dockerized build version")
-		version, err := release.ReadDockerizedVersion(bi.opts.RepoRoot)
-		if err != nil {
-			return "", fmt.Errorf("read Dockerized build version: %w", err)
+		if isBazel {
+			logrus.Info("Using Bazel build version")
+			version, err := release.ReadBazelVersion(bi.opts.RepoRoot)
+			if err != nil {
+				return "", fmt.Errorf("read Bazel build version: %w", err)
+			}
+			latestVersion = version
+		} else {
+			logrus.Info("Using Dockerized build version")
+			version, err := release.ReadDockerizedVersion(bi.opts.RepoRoot)
+			if err != nil {
+				return "", fmt.Errorf("read Dockerized build version: %w", err)
+			}
+			latestVersion = version
 		}
-		latestVersion = version
 	}
 
 	logrus.Infof("Using build version: %s", latestVersion)
@@ -215,19 +226,27 @@ func (bi *Instance) findLatestVersion() (latestVersion string, err error) {
 		latestVersion += "-" + bi.opts.VersionSuffix
 	}
 
-	setupBuildDir(bi)
+	setupBuildDir(bi, isBazel)
 
 	return strings.TrimSpace(latestVersion), nil
 }
 
-func setupBuildDir(bi *Instance) {
+func setupBuildDir(bi *Instance, isBazel bool) {
 	if bi.opts.BuildDir == "" {
 		logrus.Info("BuildDir is not set, setting it automatically")
-		logrus.Infof(
-			"Release is build in a container, using BuildDir as %s",
-			release.BuildDir,
-		)
-		bi.opts.BuildDir = release.BuildDir
+		if isBazel {
+			logrus.Infof(
+				"Release is build by bazel, using BuildDir as %s",
+				release.BazelBuildDir,
+			)
+			bi.opts.BuildDir = release.BazelBuildDir
+		} else {
+			logrus.Infof(
+				"Release is build in a container, using BuildDir as %s",
+				release.BuildDir,
+			)
+			bi.opts.BuildDir = release.BuildDir
+		}
 	}
 	// convert buildDir to an absolute path
 	bi.opts.BuildDir = filepath.Join(bi.opts.RepoRoot, bi.opts.BuildDir)
@@ -522,7 +541,7 @@ func (bi *Instance) StageLocalSourceTree(workDir, buildVersion string) error {
 // StageLocalSourceTree to be able to use it during the anago.stage function.
 func (bi *Instance) DeleteLocalSourceTarball(workDir string) error {
 	tarballPath := filepath.Join(workDir, release.SourcesTar)
-	logrus.Info("Removing local source tree tarball " + tarballPath)
+	logrus.Infof("Removing local source tree tarball " + tarballPath)
 	if err := os.RemoveAll(tarballPath); err != nil {
 		return fmt.Errorf("remove local source tarball: %w", err)
 	}
diff --git a/pkg/consts/main.go b/pkg/consts/main.go
index 3cb049fe..3c0ef4c7 100644
--- a/pkg/consts/main.go
+++ b/pkg/consts/main.go
@@ -63,7 +63,7 @@ var (
 )
 
 const (
-	DefaultReleaseDownloadLinkBase = "https://dl.k8s.io/release"
+	DefaultReleaseDownloadLinkBase = "gs://kubernetes-release/release"
 	DefaultRevision                = "0"
 	DefaultSpecTemplatePath        = "cmd/krel/templates/latest"
 )
diff --git a/pkg/gcp/build/build.go b/pkg/gcp/build/build.go
index 30f926e5..0aff634f 100644
--- a/pkg/gcp/build/build.go
+++ b/pkg/gcp/build/build.go
@@ -81,6 +81,12 @@ func PrepareBuilds(o *Options) error {
 		return errors.New("expected a config directory to be provided")
 	}
 
+	if bazelWorkspace := os.Getenv("BUILD_WORKSPACE_DIRECTORY"); bazelWorkspace != "" {
+		if err := os.Chdir(bazelWorkspace); err != nil {
+			return fmt.Errorf("failed to chdir to bazel workspace (%s): %w", bazelWorkspace, err)
+		}
+	}
+
 	if o.BuildDir == "" {
 		o.BuildDir = o.ConfigDir
 	}
diff --git a/pkg/gcp/gcb/gcb.go b/pkg/gcp/gcb/gcb.go
index a29b8aba..215c777e 100644
--- a/pkg/gcp/gcb/gcb.go
+++ b/pkg/gcp/gcb/gcb.go
@@ -218,7 +218,6 @@ func (g *GCB) Submit() error {
 	toolOrg := release.GetToolOrg()
 	toolRepo := release.GetToolRepo()
 	toolRef := release.GetToolRef()
-	forceBuildKrel := release.GetForceBuildKrel()
 
 	if err := gcli.PreCheck(); err != nil {
 		return fmt.Errorf("pre-checking for GCP package usage: %w", err)
@@ -285,12 +284,15 @@ func (g *GCB) Submit() error {
 	}
 
 	// build the GCS bucket string to be used to sign all the artifacts
-	gcsBucket := "gs://" + release.TestBucket
+	bucketPrefix := release.BucketPrefix
+	gcsBucket := "gs://" + bucketPrefix
 	if g.options.NoMock {
-		gcsBucket = strings.ReplaceAll(gcsBucket, release.TestBucket, release.ProductionBucket)
+		gcsBucket = strings.TrimSuffix(gcsBucket, "-")
+	} else {
+		gcsBucket = fmt.Sprintf("%s%s", gcsBucket, "gcb")
 	}
 
-	gcbSubs, gcbSubsErr := g.SetGCBSubstitutions(toolOrg, toolRepo, toolRef, gcsBucket, forceBuildKrel)
+	gcbSubs, gcbSubsErr := g.SetGCBSubstitutions(toolOrg, toolRepo, toolRef, gcsBucket)
 	if gcbSubs == nil || gcbSubsErr != nil {
 		return gcbSubsErr
 	}
@@ -319,13 +321,14 @@ func (g *GCB) Submit() error {
 		gcbSubs["NOMOCK_TAG"] = ""
 		gcbSubs["NOMOCK"] = ""
 
-		userBucket := strings.ReplaceAll(release.TestBucket, "gcb", gcbSubs["GCP_USER_TAG"])
+		userBucket := fmt.Sprintf("%s%s", bucketPrefix, gcbSubs["GCP_USER_TAG"])
 		userBucketSetErr := os.Setenv("USER_BUCKET", userBucket)
 		if userBucketSetErr != nil {
 			return userBucketSetErr
 		}
 
-		testBucketSetErr := os.Setenv("BUCKET", release.TestBucket)
+		testBucket := fmt.Sprintf("%s%s", bucketPrefix, "gcb")
+		testBucketSetErr := os.Setenv("BUCKET", testBucket)
 		if testBucketSetErr != nil {
 			return testBucketSetErr
 		}
@@ -352,13 +355,12 @@ func (g *GCB) Submit() error {
 
 // SetGCBSubstitutions takes a set of `Options` and returns a map of GCB
 // substitutions.
-func (g *GCB) SetGCBSubstitutions(toolOrg, toolRepo, toolRef, gcsBucket, forceBuildKrel string) (map[string]string, error) {
+func (g *GCB) SetGCBSubstitutions(toolOrg, toolRepo, toolRef, gcsBucket string) (map[string]string, error) {
 	gcbSubs := map[string]string{}
 
 	gcbSubs["TOOL_ORG"] = toolOrg
 	gcbSubs["TOOL_REPO"] = toolRepo
 	gcbSubs["TOOL_REF"] = toolRef
-	gcbSubs["FORCE_BUILD_KREL"] = forceBuildKrel
 
 	gcbSubs["K8S_ORG"] = release.GetK8sOrg()
 	if g.options.CustomK8sOrg != "" {
diff --git a/pkg/notes/maps/testdata/applymap-unit-test/maps.yaml b/pkg/notes/maps/testdata/unit/maps.yaml
similarity index 100%
rename from pkg/notes/maps/testdata/applymap-unit-test/maps.yaml
rename to pkg/notes/maps/testdata/unit/maps.yaml
diff --git a/pkg/notes/notes.go b/pkg/notes/notes.go
index b0782563..605b7f5e 100644
--- a/pkg/notes/notes.go
+++ b/pkg/notes/notes.go
@@ -863,7 +863,7 @@ func (g *Gatherer) prsFromCommit(commit *gogithub.RepositoryCommit) (
 // advantage of this to contextualize release note generation with the kind, sig,
 // area, etc labels.
 func labelsWithPrefix(pr *gogithub.PullRequest, prefix string) []string {
-	var labels []string
+	labels := []string{}
 	for _, label := range pr.Labels {
 		if strings.HasPrefix(*label.Name, prefix) {
 			labels = append(labels, strings.TrimPrefix(*label.Name, prefix+"/"))
@@ -1226,7 +1226,7 @@ func (rn *ReleaseNote) ApplyMap(noteMap *ReleaseNotesMap, markdownLinks bool) er
 				indented, rn.PrNumber, rn.PrURL, rn.Author, rn.AuthorURL)
 		}
 		// Add sig labels to markdown
-		if len(rn.SIGs) > 1 {
+		if rn.SIGs != nil {
 			markdown = fmt.Sprintf("%s [%s]", markdown, prettifySIGList(rn.SIGs))
 		}
 		// Uppercase the first character of the markdown to make it look uniform
diff --git a/pkg/release/archive.go b/pkg/release/archive.go
new file mode 100644
index 00000000..ea2bfe5e
--- /dev/null
+++ b/pkg/release/archive.go
@@ -0,0 +1,368 @@
+/*
+Copyright 2020 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package release
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/sirupsen/logrus"
+
+	"sigs.k8s.io/release-sdk/gcli"
+	"sigs.k8s.io/release-sdk/object"
+	"sigs.k8s.io/release-utils/command"
+	"sigs.k8s.io/release-utils/tar"
+	"sigs.k8s.io/release-utils/util"
+)
+
+const (
+	archiveDirPrefix   = "anago-"  // Prefix for archive directories
+	archiveBucketPath  = "archive" // Archive sibdirectory in bucket
+	logsArchiveSubPath = "logs"    // Logs subdirectory
+)
+
+// Archiver stores the release build directory in a bucket
+// along with it's logs.
+type Archiver struct {
+	impl archiverImpl
+	opts *ArchiverOptions
+}
+
+// NewArchiver create a new archiver with the default implementation.
+func NewArchiver(opts *ArchiverOptions) *Archiver {
+	return &Archiver{&defaultArchiverImpl{}, opts}
+}
+
+// SetImpl changes the archiver implementation.
+func (archiver *Archiver) SetImpl(impl archiverImpl) {
+	archiver.impl = impl
+}
+
+// ArchiverOptions set the options used when archiving a release.
+type ArchiverOptions struct {
+	ReleaseBuildDir string // Build directory that will be archived
+	LogFile         string // Log file to process and include in the archive
+	PrimeVersion    string // Final version tag
+	BuildVersion    string // Build version from where this release has cut
+	Bucket          string // Bucket we will use to archive and read staged data
+}
+
+// ArchiveBucketPath returns the bucket path we the release will be stored.
+func (o *ArchiverOptions) ArchiveBucketPath() string {
+	// local archive_bucket="gs://$RELEASE_BUCKET/archive"
+	if o.Bucket == "" || o.PrimeVersion == "" {
+		return ""
+	}
+	gcs := object.NewGCS()
+	archiveBucketPath, err := gcs.NormalizePath(
+		object.GcsPrefix + filepath.Join(o.Bucket, ArchivePath, archiveDirPrefix+o.PrimeVersion),
+	)
+	if err != nil {
+		logrus.Error(err)
+		return ""
+	}
+	return archiveBucketPath
+}
+
+// Validate checks if the set values are correct and complete to
+// start running the archival process.
+func (o *ArchiverOptions) Validate() error {
+	if o.LogFile == "" {
+		return errors.New("release log file was not specified")
+	}
+	if !util.Exists(o.ReleaseBuildDir) {
+		return errors.New("GCB worskapce directory does not exist")
+	}
+	if !util.Exists(o.LogFile) {
+		return errors.New("logs file not found")
+	}
+	if o.BuildVersion == "" {
+		return errors.New("build version tag in archiver options is empty")
+	}
+	if o.PrimeVersion == "" {
+		return errors.New("prime version tag in archiver options is empty")
+	}
+	if o.Bucket == "" {
+		return errors.New("archive bucket is not specified")
+	}
+
+	// Check if the build version is well formed (used for cleaning old staged build)
+	if _, err := util.TagStringToSemver(o.BuildVersion); err != nil {
+		return fmt.Errorf("verifying build version tag: %w", err)
+	}
+
+	// Check if the prime version is well formed
+	if _, err := util.TagStringToSemver(o.PrimeVersion); err != nil {
+		return fmt.Errorf("verifying prime version tag: %w", err)
+	}
+
+	return nil
+}
+
+//counterfeiter:generate . archiverImpl
+type archiverImpl interface {
+	CopyReleaseToBucket(string, string) error
+	DeleteStalePasswordFiles(string) error
+	MakeFilesPrivate(string) error
+	ValidateOptions(*ArchiverOptions) error
+	CopyReleaseLogs([]string, string, string) error
+	CleanStagedBuilds(string, string) error
+}
+
+type defaultArchiverImpl struct{}
+
+// ArchiveRelease stores the release directory and logs in a GCP
+// bucket for archival purposes. Log files are sanitized and made private.
+func (archiver *Archiver) ArchiveRelease() error {
+	// Verify options are complete
+	if err := archiver.impl.ValidateOptions(archiver.opts); err != nil {
+		return fmt.Errorf("validating archive options: %w", err)
+	}
+
+	// TODO: Is this still relevant?
+	// local text="files"
+
+	// # TODO: Copy $PROGSTATE as well to GCS and restore it if found
+	// # also delete if complete or just delete once copied back to $TMPDIR
+	// # This is so failures on GCB can be restarted / reentrant too.
+
+	// if [[ $arg != "--files-only" ]]; then
+	//  dash_args="-rc"
+	//   text="contents"
+	// fi
+
+	// Remove temporary password file so not to alarm passers-by.
+	if err := archiver.impl.DeleteStalePasswordFiles(
+		archiver.opts.ReleaseBuildDir,
+	); err != nil {
+		return fmt.Errorf("looking for stale password files: %w", err)
+	}
+
+	// Clean previous staged builds
+	if err := archiver.impl.CleanStagedBuilds(
+		object.GcsPrefix+filepath.Join(archiver.opts.Bucket, StagePath),
+		archiver.opts.BuildVersion,
+	); err != nil {
+		return fmt.Errorf("deleting previous staged builds: %w", err)
+	}
+
+	// Copy the release to the bucket
+	if err := archiver.impl.CopyReleaseToBucket(
+		archiver.opts.ReleaseBuildDir,
+		archiver.opts.ArchiveBucketPath(),
+	); err != nil {
+		return fmt.Errorf("while copying the release directory: %w", err)
+	}
+
+	// copy_logs_to_workdir
+	if err := archiver.impl.CopyReleaseLogs(
+		[]string{archiver.opts.LogFile},
+		filepath.Join(archiver.opts.ReleaseBuildDir, logsArchiveSubPath),
+		filepath.Join(archiver.opts.ArchiveBucketPath(), logsArchiveSubPath),
+	); err != nil {
+		return fmt.Errorf("copying release logs to archive: %w", err)
+	}
+
+	// Make the logs private (remove AllUsers from the GCS ACL)
+	if err := archiver.impl.MakeFilesPrivate(
+		filepath.Join(archiver.opts.ArchiveBucketPath(), logsArchiveSubPath),
+	); err != nil {
+		return fmt.Errorf("setting private ACL on logs: %w", err)
+	}
+
+	logrus.Info("Release archive complete")
+	return nil
+}
+
+// validateOptions runs the options validation.
+func (a *defaultArchiverImpl) ValidateOptions(o *ArchiverOptions) error {
+	if err := o.Validate(); err != nil {
+		return fmt.Errorf("validating options: %w", err)
+	}
+	return nil
+}
+
+// makeFilesPrivate updates the ACL on all files in a directory.
+func (a *defaultArchiverImpl) MakeFilesPrivate(archiveBucketPath string) error {
+	logrus.Infof("Ensure PRIVATE ACL on %s/*", archiveBucketPath)
+	gcs := object.NewGCS()
+	logsPath, err := gcs.NormalizePath(archiveBucketPath + "/*")
+	if err != nil {
+		return fmt.Errorf("normalizing gcs path to modify ACL: %w", err)
+	}
+	// logrun -s $GSUTIL acl ch -d AllUsers "$archive_bucket/$build_dir/${LOGFILE##*/}*" || true
+	if err := gcli.GSUtil("acl", "ch", "-d", "AllUsers", logsPath); err != nil {
+		return fmt.Errorf("removing public access from files in %s: %w", archiveBucketPath, err)
+	}
+	return nil
+}
+
+// deleteStalePasswordFiles emoves temporary password file so not to alarm passers-by.
+func (a *defaultArchiverImpl) DeleteStalePasswordFiles(releaseBuildDir string) error {
+	if err := command.NewWithWorkDir(
+		releaseBuildDir, "find", "-type", "f", "-name", "rsyncd.password", "-delete",
+	).RunSuccess(); err != nil {
+		return fmt.Errorf("deleting temporary password files: %w", err)
+	}
+
+	// Delete the git remote config to avoid it ending in the stage bucket
+	gitConf := filepath.Join(releaseBuildDir, "k8s.io", "kubernetes", ".git", "config")
+	if util.Exists(gitConf) {
+		if err := os.Remove(gitConf); err != nil {
+			return fmt.Errorf("deleting git remote config: %w", err)
+		}
+	} else {
+		logrus.Warn("git configuration file not found, nothing to remove")
+	}
+
+	return nil
+}
+
+// copyReleaseLogs gets a slice of log file names. Those files are
+// sanitized to remove sensitive data and control characters and then are
+// copied to the GCB working directory.
+func (a *defaultArchiverImpl) CopyReleaseLogs(
+	logFiles []string, targetDir, archiveBucketLogsPath string,
+) (err error) {
+	// Verify the destination bucket address is correct
+	gcs := object.NewGCS()
+	if archiveBucketLogsPath != "" {
+		archiveBucketLogsPath, err = gcs.NormalizePath(archiveBucketLogsPath)
+		if err != nil {
+			return fmt.Errorf("normalizing remote logfile destination: %w", err)
+		}
+	}
+	// Check the destination directory exists
+	if !util.Exists(targetDir) {
+		if err := os.Mkdir(targetDir, os.FileMode(0o755)); err != nil {
+			return fmt.Errorf("creating logs archive directory: %w", err)
+		}
+	}
+	for _, fileName := range logFiles {
+		// Strip the logfiles from control chars and sensitive data
+		if err := util.CleanLogFile(fileName); err != nil {
+			return fmt.Errorf("sanitizing logfile: %w", err)
+		}
+
+		logrus.Infof("Copying %s to %s", fileName, targetDir)
+		if err := util.CopyFileLocal(
+			fileName, filepath.Join(targetDir, filepath.Base(fileName)), true,
+		); err != nil {
+			return fmt.Errorf("copying logfile %s to %s: %w", fileName, targetDir, err)
+		}
+	}
+	// TODO: Grab previous log files from stage and copy them to logs dir
+
+	// Rsync log files to remote location if a bucket is specified
+	if archiveBucketLogsPath != "" {
+		logrus.Infof("Rsyncing logs to remote bucket %s", archiveBucketLogsPath)
+		if err := gcs.RsyncRecursive(targetDir, archiveBucketLogsPath); err != nil {
+			return fmt.Errorf("while synching log files to remote bucket addr: %w", err)
+		}
+	}
+	return nil
+}
+
+// copyReleaseToBucket Copies the release directory to the specified bucket location.
+func (a *defaultArchiverImpl) CopyReleaseToBucket(releaseBuildDir, archiveBucketPath string) error {
+	// TODO: Check if we have write access to the bucket?
+
+	// Create a GCS client to copy the release
+	gcs := object.NewGCS()
+	remoteDest, err := gcs.NormalizePath(archiveBucketPath)
+	if err != nil {
+		return fmt.Errorf("normalizing destination path: %w", err)
+	}
+
+	srcPath := filepath.Join(releaseBuildDir, "k8s.io")
+	tarball := srcPath + ".tar.gz"
+	logrus.Infof("Compressing %s to %s", srcPath, tarball)
+	if err := tar.Compress(tarball, srcPath); err != nil {
+		return fmt.Errorf("create source tarball: %w", err)
+	}
+
+	logrus.Infof("Removing source path %s before syncing", srcPath)
+	if err := os.RemoveAll(srcPath); err != nil {
+		return fmt.Errorf("remove source path: %w", err)
+	}
+
+	logrus.Infof("Rsync %s to %s", releaseBuildDir, remoteDest)
+	if err := gcs.RsyncRecursive(releaseBuildDir, remoteDest); err != nil {
+		return fmt.Errorf("copying release directory to bucket: %w", err)
+	}
+	return nil
+}
+
+// GetLogFiles reads a directory and returns the files that are anago logs.
+func (a *defaultArchiverImpl) GetLogFiles(logsDir string) ([]string, error) {
+	logFiles := []string{}
+	tmpContents, err := os.ReadDir(logsDir)
+	if err != nil {
+		return nil, fmt.Errorf("searching for logfiles in %s: %w", logsDir, err)
+	}
+	for _, finfo := range tmpContents {
+		if strings.HasPrefix(finfo.Name(), "anago") &&
+			strings.Contains(finfo.Name(), ".log") {
+			logFiles = append(logFiles, filepath.Join(logsDir, finfo.Name()))
+		}
+	}
+	return logFiles, nil
+}
+
+// CleanStagedBuilds removes all past staged builds from the same
+// Major.Minor version we are running now.
+func (a *defaultArchiverImpl) CleanStagedBuilds(bucketPath, buildVersion string) error {
+	// Build the prefix we will be looking for
+	semver, err := util.TagStringToSemver(buildVersion)
+	if err != nil {
+		return fmt.Errorf("parsing semver from tag: %w", err)
+	}
+	dirPrefix := fmt.Sprintf("%s%d.%d", util.TagPrefix, semver.Major, semver.Minor)
+
+	// Normalize the bucket parh
+	// Build a GCS object to delete old builds
+	gcs := object.NewGCS()
+	gcs.SetOptions(
+		gcs.WithConcurrent(true),
+		gcs.WithRecursive(true),
+	)
+
+	// Normalize the bucket path
+	path, err := gcs.NormalizePath(bucketPath, dirPrefix+"*")
+	if err != nil {
+		return fmt.Errorf("normalizing stage path: %w", err)
+	}
+
+	// Get all staged build that match the pattern
+	output, err := gcli.GSUtilOutput("ls", "-d", path)
+	if err != nil {
+		return fmt.Errorf("listing bucket contents: %w", err)
+	}
+
+	for _, line := range strings.Fields(output) {
+		if strings.Contains(line, dirPrefix) && !strings.Contains(line, buildVersion) {
+			logrus.Infof("Deleting previous staged build: %s", line)
+			if err := gcs.DeletePath(line); err != nil {
+				return fmt.Errorf("calling gsutil to delete build: %w", err)
+			}
+		}
+	}
+	return nil
+}
diff --git a/pkg/release/doc.go b/pkg/release/doc.go
index 453de79f..41785fd9 100644
--- a/pkg/release/doc.go
+++ b/pkg/release/doc.go
@@ -23,3 +23,4 @@ package release
 //go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt releasefakes/fake_prerequisites_checker_impl.go > releasefakes/_fake_prerequisites_checker_impl.go && mv releasefakes/_fake_prerequisites_checker_impl.go releasefakes/fake_prerequisites_checker_impl.go"
 //go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt releasefakes/fake_image_impl.go > releasefakes/_fake_image_impl.go && mv releasefakes/_fake_image_impl.go releasefakes/fake_image_impl.go"
 //go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt releasefakes/fake_branch_checker_impl.go > releasefakes/_fake_branch_checker_impl.go && mv releasefakes/_fake_branch_checker_impl.go releasefakes/fake_branch_checker_impl.go"
+//go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt releasefakes/fake_archiver_impl.go > releasefakes/_fake_archiver_impl.go && mv releasefakes/_fake_archiver_impl.go releasefakes/fake_archiver_impl.go"
diff --git a/pkg/release/publish.go b/pkg/release/publish.go
index 5b707303..b8472e75 100644
--- a/pkg/release/publish.go
+++ b/pkg/release/publish.go
@@ -17,7 +17,6 @@ limitations under the License.
 package release
 
 import (
-	"bytes"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -95,11 +94,7 @@ func (*defaultPublisher) GSUtilStatus(args ...string) (bool, error) {
 }
 
 func (*defaultPublisher) GetURLResponse(url string) (string, error) {
-	c, err := http.NewAgent().Get(url)
-	if err != nil {
-		return "", err
-	}
-	return string(bytes.TrimSpace(c)), nil
+	return http.GetURLResponse(url, true)
 }
 
 func (d *defaultPublisher) GetReleasePath(
@@ -341,12 +336,10 @@ func (p *Publisher) PublishToGcs(
 		return fmt.Errorf("get marker file destination: %w", publishFileDstErr)
 	}
 
-	logrus.Infof("Using marker path: %s", markerPath)
 	publicLink := fmt.Sprintf("%s/%s", URLPrefixForBucket(markerPath), publishFile)
-	if strings.HasSuffix(markerPath, ProductionBucket+"/release") {
-		publicLink = fmt.Sprintf("%s/release/%s", ProductionBucketURL, publishFile)
+	if strings.HasPrefix(markerPath, ProductionBucket) {
+		publicLink = fmt.Sprintf("%s/%s", ProductionBucketURL, publishFile)
 	}
-	logrus.Infof("Using public link: %s", publicLink)
 
 	uploadDir := filepath.Join(releaseStage, "upload")
 	if err := os.MkdirAll(uploadDir, os.FileMode(0o755)); err != nil {
@@ -360,7 +353,6 @@ func (p *Publisher) PublishToGcs(
 		return fmt.Errorf("write latest version file: %w", err)
 	}
 
-	logrus.Infof("Running `gsutil cp` from %s to: %s", latestFile, publishFileDst)
 	if err := p.client.GSUtil(
 		"-m",
 		"-h", "Content-Type:text/plain",
@@ -374,23 +366,40 @@ func (p *Publisher) PublishToGcs(
 
 	var content string
 	if !privateBucket {
+		// New Kubernetes infra buckets, like k8s-staging-kubernetes, have a
+		// bucket-only ACL policy set, which means attempting to set the ACL on
+		// an object will fail. We should skip this ACL change in those
+		// instances, as new buckets already default to being publicly
+		// readable.
+		//
+		// Ref:
+		// - https://cloud.google.com/storage/docs/bucket-policy-only
+		// - https://github.com/kubernetes/release/issues/904
+		if !strings.HasPrefix(markerPath, object.GcsPrefix+"k8s-") {
+			aclOutput, err := p.client.GSUtilOutput(
+				"acl", "ch", "-R", "-g", "all:R", publishFileDst,
+			)
+			if err != nil {
+				return fmt.Errorf("change %s permissions: %w", publishFileDst, err)
+			}
+			logrus.Infof("Making uploaded version file public: %s", aclOutput)
+		}
+
 		// If public, validate public link
-		logrus.Infof("Validating uploaded version file using HTTP at %s", publicLink)
 		response, err := p.client.GetURLResponse(publicLink)
 		if err != nil {
 			return fmt.Errorf("get content of %s: %w", publicLink, err)
 		}
 		content = response
 	} else {
-		// Use the private location
-		logrus.Infof("Validating uploaded version file using `gsutil cat` at %s", publishFileDst)
-		response, err := p.client.GSUtilOutput("cat", publishFileDst)
+		response, err := p.client.GSUtilOutput("cat", publicLink)
 		if err != nil {
-			return fmt.Errorf("get content of %s: %w", publishFileDst, err)
+			return fmt.Errorf("get content of %s: %w", publicLink, err)
 		}
 		content = response
 	}
 
+	logrus.Infof("Validating uploaded version file at %s", publicLink)
 	if version != content {
 		return fmt.Errorf(
 			"version %s it not equal response %s",
diff --git a/pkg/release/release.go b/pkg/release/release.go
index cb36d3f6..0a422417 100644
--- a/pkg/release/release.go
+++ b/pkg/release/release.go
@@ -55,6 +55,8 @@ const (
 	DefaultRelengStagingTestProject = "k8s-staging-releng-test"
 	DefaultRelengStagingProject     = "k8s-staging-releng"
 	DefaultDiskSize                 = "500"
+	BucketPrefix                    = "kubernetes-release-"
+	BucketPrefixK8sInfra            = "k8s-release-"
 
 	versionReleaseRE   = `v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-?([a-zA-Z0-9]+\.(0|[1-9][0-9]*)\.)?`
 	versionBuildRE     = `([0-9]{1,})\+([0-9a-f]{5,40})`
@@ -101,8 +103,7 @@ const (
 	TestBucket = "kubernetes-release-gcb"
 
 	// ProductionBucket is the default bucket for Kubernetes releases.
-	// Owned by SIG k8s Infra: https://git.k8s.io/community/sig-k8s-infra
-	ProductionBucket = "767373bbdcb8270361b96548387bf2a9ad0d48758c35"
+	ProductionBucket = "kubernetes-release"
 
 	// ProductionBucketURL is the url for the ProductionBucket.
 	ProductionBucketURL = "https://dl.k8s.io"
@@ -122,6 +123,12 @@ const (
 	// BuildDir is the default build output directory.
 	BuildDir = "_output"
 
+	// The default bazel build directory.
+	BazelBuildDir = "bazel-bin/build"
+
+	// Archive path is the root path in the bucket where releases are archived.
+	ArchivePath = "archive"
+
 	// Publishing bot issue repository.
 	PubBotRepoOrg  = "kubernetes"
 	PubBotRepoName = "sig-release"
@@ -159,13 +166,6 @@ func GetToolRef() string {
 	return env.Default("TOOL_REF", DefaultToolRef)
 }
 
-// GetForceBuildKrel checks if the 'FORCE_BUILD_KREL' environment variable is
-// set.  If 'FORCE_BUILD_KREL' is non-empty, it returns the value. Otherwise,
-// it returns "false".
-func GetForceBuildKrel() string {
-	return env.Default("FORCE_BUILD_KREL", "false")
-}
-
 // GetK8sOrg checks if the 'K8S_ORG' environment variable is set.
 // If 'K8S_ORG' is non-empty, it returns the value. Otherwise, it returns DefaultK8sOrg.
 func GetK8sOrg() string {
@@ -191,6 +191,25 @@ func IsDefaultK8sUpstream() bool {
 		GetK8sRef() == DefaultK8sRef
 }
 
+// BuiltWithBazel determines whether the most recent Kubernetes release was built with Bazel.
+func BuiltWithBazel(workDir string) (bool, error) {
+	bazelBuild := filepath.Join(workDir, BazelBuildDir, ReleaseTarsPath, KubernetesTar)
+	dockerBuild := filepath.Join(workDir, BuildDir, ReleaseTarsPath, KubernetesTar)
+	return util.MoreRecent(bazelBuild, dockerBuild)
+}
+
+// ReadBazelVersion reads the version from a Bazel build.
+func ReadBazelVersion(workDir string) (string, error) {
+	version, err := os.ReadFile(filepath.Join(workDir, "bazel-bin", "version"))
+	if os.IsNotExist(err) {
+		// The check for version in bazel-genfiles can be removed once everyone is
+		// off of versions before 0.25.0.
+		// https://github.com/bazelbuild/bazel/issues/8651
+		version, err = os.ReadFile(filepath.Join(workDir, "bazel-genfiles", "version"))
+	}
+	return string(version), err
+}
+
 // ReadDockerizedVersion reads the version from a Dockerized Kubernetes build.
 func ReadDockerizedVersion(workDir string) (string, error) {
 	dockerTarball := filepath.Join(workDir, BuildDir, ReleaseTarsPath, KubernetesTar)
diff --git a/pkg/release/releasefakes/fake_archiver_impl.go b/pkg/release/releasefakes/fake_archiver_impl.go
new file mode 100644
index 00000000..b8551eee
--- /dev/null
+++ b/pkg/release/releasefakes/fake_archiver_impl.go
@@ -0,0 +1,508 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by counterfeiter. DO NOT EDIT.
+package releasefakes
+
+import (
+	"sync"
+
+	"k8s.io/release/pkg/release"
+)
+
+type FakeArchiverImpl struct {
+	CleanStagedBuildsStub        func(string, string) error
+	cleanStagedBuildsMutex       sync.RWMutex
+	cleanStagedBuildsArgsForCall []struct {
+		arg1 string
+		arg2 string
+	}
+	cleanStagedBuildsReturns struct {
+		result1 error
+	}
+	cleanStagedBuildsReturnsOnCall map[int]struct {
+		result1 error
+	}
+	CopyReleaseLogsStub        func([]string, string, string) error
+	copyReleaseLogsMutex       sync.RWMutex
+	copyReleaseLogsArgsForCall []struct {
+		arg1 []string
+		arg2 string
+		arg3 string
+	}
+	copyReleaseLogsReturns struct {
+		result1 error
+	}
+	copyReleaseLogsReturnsOnCall map[int]struct {
+		result1 error
+	}
+	CopyReleaseToBucketStub        func(string, string) error
+	copyReleaseToBucketMutex       sync.RWMutex
+	copyReleaseToBucketArgsForCall []struct {
+		arg1 string
+		arg2 string
+	}
+	copyReleaseToBucketReturns struct {
+		result1 error
+	}
+	copyReleaseToBucketReturnsOnCall map[int]struct {
+		result1 error
+	}
+	DeleteStalePasswordFilesStub        func(string) error
+	deleteStalePasswordFilesMutex       sync.RWMutex
+	deleteStalePasswordFilesArgsForCall []struct {
+		arg1 string
+	}
+	deleteStalePasswordFilesReturns struct {
+		result1 error
+	}
+	deleteStalePasswordFilesReturnsOnCall map[int]struct {
+		result1 error
+	}
+	MakeFilesPrivateStub        func(string) error
+	makeFilesPrivateMutex       sync.RWMutex
+	makeFilesPrivateArgsForCall []struct {
+		arg1 string
+	}
+	makeFilesPrivateReturns struct {
+		result1 error
+	}
+	makeFilesPrivateReturnsOnCall map[int]struct {
+		result1 error
+	}
+	ValidateOptionsStub        func(*release.ArchiverOptions) error
+	validateOptionsMutex       sync.RWMutex
+	validateOptionsArgsForCall []struct {
+		arg1 *release.ArchiverOptions
+	}
+	validateOptionsReturns struct {
+		result1 error
+	}
+	validateOptionsReturnsOnCall map[int]struct {
+		result1 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *FakeArchiverImpl) CleanStagedBuilds(arg1 string, arg2 string) error {
+	fake.cleanStagedBuildsMutex.Lock()
+	ret, specificReturn := fake.cleanStagedBuildsReturnsOnCall[len(fake.cleanStagedBuildsArgsForCall)]
+	fake.cleanStagedBuildsArgsForCall = append(fake.cleanStagedBuildsArgsForCall, struct {
+		arg1 string
+		arg2 string
+	}{arg1, arg2})
+	stub := fake.CleanStagedBuildsStub
+	fakeReturns := fake.cleanStagedBuildsReturns
+	fake.recordInvocation("CleanStagedBuilds", []interface{}{arg1, arg2})
+	fake.cleanStagedBuildsMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeArchiverImpl) CleanStagedBuildsCallCount() int {
+	fake.cleanStagedBuildsMutex.RLock()
+	defer fake.cleanStagedBuildsMutex.RUnlock()
+	return len(fake.cleanStagedBuildsArgsForCall)
+}
+
+func (fake *FakeArchiverImpl) CleanStagedBuildsCalls(stub func(string, string) error) {
+	fake.cleanStagedBuildsMutex.Lock()
+	defer fake.cleanStagedBuildsMutex.Unlock()
+	fake.CleanStagedBuildsStub = stub
+}
+
+func (fake *FakeArchiverImpl) CleanStagedBuildsArgsForCall(i int) (string, string) {
+	fake.cleanStagedBuildsMutex.RLock()
+	defer fake.cleanStagedBuildsMutex.RUnlock()
+	argsForCall := fake.cleanStagedBuildsArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeArchiverImpl) CleanStagedBuildsReturns(result1 error) {
+	fake.cleanStagedBuildsMutex.Lock()
+	defer fake.cleanStagedBuildsMutex.Unlock()
+	fake.CleanStagedBuildsStub = nil
+	fake.cleanStagedBuildsReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) CleanStagedBuildsReturnsOnCall(i int, result1 error) {
+	fake.cleanStagedBuildsMutex.Lock()
+	defer fake.cleanStagedBuildsMutex.Unlock()
+	fake.CleanStagedBuildsStub = nil
+	if fake.cleanStagedBuildsReturnsOnCall == nil {
+		fake.cleanStagedBuildsReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.cleanStagedBuildsReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseLogs(arg1 []string, arg2 string, arg3 string) error {
+	var arg1Copy []string
+	if arg1 != nil {
+		arg1Copy = make([]string, len(arg1))
+		copy(arg1Copy, arg1)
+	}
+	fake.copyReleaseLogsMutex.Lock()
+	ret, specificReturn := fake.copyReleaseLogsReturnsOnCall[len(fake.copyReleaseLogsArgsForCall)]
+	fake.copyReleaseLogsArgsForCall = append(fake.copyReleaseLogsArgsForCall, struct {
+		arg1 []string
+		arg2 string
+		arg3 string
+	}{arg1Copy, arg2, arg3})
+	stub := fake.CopyReleaseLogsStub
+	fakeReturns := fake.copyReleaseLogsReturns
+	fake.recordInvocation("CopyReleaseLogs", []interface{}{arg1Copy, arg2, arg3})
+	fake.copyReleaseLogsMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseLogsCallCount() int {
+	fake.copyReleaseLogsMutex.RLock()
+	defer fake.copyReleaseLogsMutex.RUnlock()
+	return len(fake.copyReleaseLogsArgsForCall)
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseLogsCalls(stub func([]string, string, string) error) {
+	fake.copyReleaseLogsMutex.Lock()
+	defer fake.copyReleaseLogsMutex.Unlock()
+	fake.CopyReleaseLogsStub = stub
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseLogsArgsForCall(i int) ([]string, string, string) {
+	fake.copyReleaseLogsMutex.RLock()
+	defer fake.copyReleaseLogsMutex.RUnlock()
+	argsForCall := fake.copyReleaseLogsArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseLogsReturns(result1 error) {
+	fake.copyReleaseLogsMutex.Lock()
+	defer fake.copyReleaseLogsMutex.Unlock()
+	fake.CopyReleaseLogsStub = nil
+	fake.copyReleaseLogsReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseLogsReturnsOnCall(i int, result1 error) {
+	fake.copyReleaseLogsMutex.Lock()
+	defer fake.copyReleaseLogsMutex.Unlock()
+	fake.CopyReleaseLogsStub = nil
+	if fake.copyReleaseLogsReturnsOnCall == nil {
+		fake.copyReleaseLogsReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.copyReleaseLogsReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseToBucket(arg1 string, arg2 string) error {
+	fake.copyReleaseToBucketMutex.Lock()
+	ret, specificReturn := fake.copyReleaseToBucketReturnsOnCall[len(fake.copyReleaseToBucketArgsForCall)]
+	fake.copyReleaseToBucketArgsForCall = append(fake.copyReleaseToBucketArgsForCall, struct {
+		arg1 string
+		arg2 string
+	}{arg1, arg2})
+	stub := fake.CopyReleaseToBucketStub
+	fakeReturns := fake.copyReleaseToBucketReturns
+	fake.recordInvocation("CopyReleaseToBucket", []interface{}{arg1, arg2})
+	fake.copyReleaseToBucketMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseToBucketCallCount() int {
+	fake.copyReleaseToBucketMutex.RLock()
+	defer fake.copyReleaseToBucketMutex.RUnlock()
+	return len(fake.copyReleaseToBucketArgsForCall)
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseToBucketCalls(stub func(string, string) error) {
+	fake.copyReleaseToBucketMutex.Lock()
+	defer fake.copyReleaseToBucketMutex.Unlock()
+	fake.CopyReleaseToBucketStub = stub
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseToBucketArgsForCall(i int) (string, string) {
+	fake.copyReleaseToBucketMutex.RLock()
+	defer fake.copyReleaseToBucketMutex.RUnlock()
+	argsForCall := fake.copyReleaseToBucketArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseToBucketReturns(result1 error) {
+	fake.copyReleaseToBucketMutex.Lock()
+	defer fake.copyReleaseToBucketMutex.Unlock()
+	fake.CopyReleaseToBucketStub = nil
+	fake.copyReleaseToBucketReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) CopyReleaseToBucketReturnsOnCall(i int, result1 error) {
+	fake.copyReleaseToBucketMutex.Lock()
+	defer fake.copyReleaseToBucketMutex.Unlock()
+	fake.CopyReleaseToBucketStub = nil
+	if fake.copyReleaseToBucketReturnsOnCall == nil {
+		fake.copyReleaseToBucketReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.copyReleaseToBucketReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) DeleteStalePasswordFiles(arg1 string) error {
+	fake.deleteStalePasswordFilesMutex.Lock()
+	ret, specificReturn := fake.deleteStalePasswordFilesReturnsOnCall[len(fake.deleteStalePasswordFilesArgsForCall)]
+	fake.deleteStalePasswordFilesArgsForCall = append(fake.deleteStalePasswordFilesArgsForCall, struct {
+		arg1 string
+	}{arg1})
+	stub := fake.DeleteStalePasswordFilesStub
+	fakeReturns := fake.deleteStalePasswordFilesReturns
+	fake.recordInvocation("DeleteStalePasswordFiles", []interface{}{arg1})
+	fake.deleteStalePasswordFilesMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeArchiverImpl) DeleteStalePasswordFilesCallCount() int {
+	fake.deleteStalePasswordFilesMutex.RLock()
+	defer fake.deleteStalePasswordFilesMutex.RUnlock()
+	return len(fake.deleteStalePasswordFilesArgsForCall)
+}
+
+func (fake *FakeArchiverImpl) DeleteStalePasswordFilesCalls(stub func(string) error) {
+	fake.deleteStalePasswordFilesMutex.Lock()
+	defer fake.deleteStalePasswordFilesMutex.Unlock()
+	fake.DeleteStalePasswordFilesStub = stub
+}
+
+func (fake *FakeArchiverImpl) DeleteStalePasswordFilesArgsForCall(i int) string {
+	fake.deleteStalePasswordFilesMutex.RLock()
+	defer fake.deleteStalePasswordFilesMutex.RUnlock()
+	argsForCall := fake.deleteStalePasswordFilesArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *FakeArchiverImpl) DeleteStalePasswordFilesReturns(result1 error) {
+	fake.deleteStalePasswordFilesMutex.Lock()
+	defer fake.deleteStalePasswordFilesMutex.Unlock()
+	fake.DeleteStalePasswordFilesStub = nil
+	fake.deleteStalePasswordFilesReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) DeleteStalePasswordFilesReturnsOnCall(i int, result1 error) {
+	fake.deleteStalePasswordFilesMutex.Lock()
+	defer fake.deleteStalePasswordFilesMutex.Unlock()
+	fake.DeleteStalePasswordFilesStub = nil
+	if fake.deleteStalePasswordFilesReturnsOnCall == nil {
+		fake.deleteStalePasswordFilesReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.deleteStalePasswordFilesReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) MakeFilesPrivate(arg1 string) error {
+	fake.makeFilesPrivateMutex.Lock()
+	ret, specificReturn := fake.makeFilesPrivateReturnsOnCall[len(fake.makeFilesPrivateArgsForCall)]
+	fake.makeFilesPrivateArgsForCall = append(fake.makeFilesPrivateArgsForCall, struct {
+		arg1 string
+	}{arg1})
+	stub := fake.MakeFilesPrivateStub
+	fakeReturns := fake.makeFilesPrivateReturns
+	fake.recordInvocation("MakeFilesPrivate", []interface{}{arg1})
+	fake.makeFilesPrivateMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeArchiverImpl) MakeFilesPrivateCallCount() int {
+	fake.makeFilesPrivateMutex.RLock()
+	defer fake.makeFilesPrivateMutex.RUnlock()
+	return len(fake.makeFilesPrivateArgsForCall)
+}
+
+func (fake *FakeArchiverImpl) MakeFilesPrivateCalls(stub func(string) error) {
+	fake.makeFilesPrivateMutex.Lock()
+	defer fake.makeFilesPrivateMutex.Unlock()
+	fake.MakeFilesPrivateStub = stub
+}
+
+func (fake *FakeArchiverImpl) MakeFilesPrivateArgsForCall(i int) string {
+	fake.makeFilesPrivateMutex.RLock()
+	defer fake.makeFilesPrivateMutex.RUnlock()
+	argsForCall := fake.makeFilesPrivateArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *FakeArchiverImpl) MakeFilesPrivateReturns(result1 error) {
+	fake.makeFilesPrivateMutex.Lock()
+	defer fake.makeFilesPrivateMutex.Unlock()
+	fake.MakeFilesPrivateStub = nil
+	fake.makeFilesPrivateReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) MakeFilesPrivateReturnsOnCall(i int, result1 error) {
+	fake.makeFilesPrivateMutex.Lock()
+	defer fake.makeFilesPrivateMutex.Unlock()
+	fake.MakeFilesPrivateStub = nil
+	if fake.makeFilesPrivateReturnsOnCall == nil {
+		fake.makeFilesPrivateReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.makeFilesPrivateReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) ValidateOptions(arg1 *release.ArchiverOptions) error {
+	fake.validateOptionsMutex.Lock()
+	ret, specificReturn := fake.validateOptionsReturnsOnCall[len(fake.validateOptionsArgsForCall)]
+	fake.validateOptionsArgsForCall = append(fake.validateOptionsArgsForCall, struct {
+		arg1 *release.ArchiverOptions
+	}{arg1})
+	stub := fake.ValidateOptionsStub
+	fakeReturns := fake.validateOptionsReturns
+	fake.recordInvocation("ValidateOptions", []interface{}{arg1})
+	fake.validateOptionsMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeArchiverImpl) ValidateOptionsCallCount() int {
+	fake.validateOptionsMutex.RLock()
+	defer fake.validateOptionsMutex.RUnlock()
+	return len(fake.validateOptionsArgsForCall)
+}
+
+func (fake *FakeArchiverImpl) ValidateOptionsCalls(stub func(*release.ArchiverOptions) error) {
+	fake.validateOptionsMutex.Lock()
+	defer fake.validateOptionsMutex.Unlock()
+	fake.ValidateOptionsStub = stub
+}
+
+func (fake *FakeArchiverImpl) ValidateOptionsArgsForCall(i int) *release.ArchiverOptions {
+	fake.validateOptionsMutex.RLock()
+	defer fake.validateOptionsMutex.RUnlock()
+	argsForCall := fake.validateOptionsArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *FakeArchiverImpl) ValidateOptionsReturns(result1 error) {
+	fake.validateOptionsMutex.Lock()
+	defer fake.validateOptionsMutex.Unlock()
+	fake.ValidateOptionsStub = nil
+	fake.validateOptionsReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) ValidateOptionsReturnsOnCall(i int, result1 error) {
+	fake.validateOptionsMutex.Lock()
+	defer fake.validateOptionsMutex.Unlock()
+	fake.ValidateOptionsStub = nil
+	if fake.validateOptionsReturnsOnCall == nil {
+		fake.validateOptionsReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.validateOptionsReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeArchiverImpl) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.cleanStagedBuildsMutex.RLock()
+	defer fake.cleanStagedBuildsMutex.RUnlock()
+	fake.copyReleaseLogsMutex.RLock()
+	defer fake.copyReleaseLogsMutex.RUnlock()
+	fake.copyReleaseToBucketMutex.RLock()
+	defer fake.copyReleaseToBucketMutex.RUnlock()
+	fake.deleteStalePasswordFilesMutex.RLock()
+	defer fake.deleteStalePasswordFilesMutex.RUnlock()
+	fake.makeFilesPrivateMutex.RLock()
+	defer fake.makeFilesPrivateMutex.RUnlock()
+	fake.validateOptionsMutex.RLock()
+	defer fake.validateOptionsMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *FakeArchiverImpl) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/pkg/release/version.go b/pkg/release/version.go
index 7107fcdb..a2e2b96a 100644
--- a/pkg/release/version.go
+++ b/pkg/release/version.go
@@ -17,7 +17,6 @@ limitations under the License.
 package release
 
 import (
-	"bytes"
 	"fmt"
 	"strings"
 
@@ -42,11 +41,7 @@ type VersionClient interface {
 type versionClient struct{}
 
 func (*versionClient) GetURLResponse(url string) (string, error) {
-	c, err := http.NewAgent().Get(url)
-	if err != nil {
-		return "", err
-	}
-	return string(bytes.TrimSpace(c)), nil
+	return http.GetURLResponse(url, true)
 }
 
 // VersionType is a simple wrapper around a Kubernetes release version.


Test patch:
diff --git a/cmd/krel/cmd/testdata/validation-data/invalid-indent.yaml b/cmd/krel/cmd/testdata/validation-data/invalid-indent.yaml
new file mode 100644
index 00000000..53386b14
--- /dev/null
+++ b/cmd/krel/cmd/testdata/validation-data/invalid-indent.yaml
@@ -0,0 +1,10 @@
+pr: 126108
+releasenote:
+  text: |-
+    Reduced state change noise when volume expansion fails. Also mark certain failures as infeasible.
+
+  ACTION REQUIRED:  If you are using the `RecoverVolumeExpansionFailure` alpha feature gate
+  then after upgrading to this release, you need to update some objects.
+  For any existing PersistentVolumeClaimss with `status.allocatedResourceStatus` set to either
+  "ControllerResizeFailed" or "NodeResizeFailed", clear the `status.allocatedResourceStatus`.
+pr_body: ""
diff --git a/cmd/krel/cmd/testdata/validation-data/invalid-multi-line.yaml b/cmd/krel/cmd/testdata/validation-data/invalid-multi-line.yaml
new file mode 100644
index 00000000..0c115b20
--- /dev/null
+++ b/cmd/krel/cmd/testdata/validation-data/invalid-multi-line.yaml
@@ -0,0 +1,5 @@
+pr: 125163
+releasenote:
+  text: 'ACTION REQUIRED: The Dynamic Resource Allocation (DRA) driver's DaemonSet must be deployed
+          with a service account that enables writing ResourceSlice and reading ResourceClaim
+          objects.'
diff --git a/cmd/krel/cmd/testdata/validation-data/invalid-yaml-start.yaml b/cmd/krel/cmd/testdata/validation-data/invalid-yaml-start.yaml
new file mode 100644
index 00000000..d1f16b60
--- /dev/null
+++ b/cmd/krel/cmd/testdata/validation-data/invalid-yaml-start.yaml
@@ -0,0 +1,3 @@
+pr: 125157
+releasenote:
+  text: `kubeadm`: The `NodeSwap` check that kubeadm performs during preflight, has a new warning to verify if swap has been configured correctly.
diff --git a/cmd/krel/cmd/testdata/validation-data/missing-punctuation.yaml b/cmd/krel/cmd/testdata/validation-data/missing-punctuation.yaml
new file mode 100644
index 00000000..0af291b3
--- /dev/null
+++ b/cmd/krel/cmd/testdata/validation-data/missing-punctuation.yaml
@@ -0,0 +1,4 @@
+pr: 125157
+releasenote:
+  text: "`kubeadm`: The `NodeSwap` check that kubeadm performs during preflight, has a new warning to verify if swap has been configured correctly"
+pr_body: ""
diff --git a/cmd/krel/cmd/testdata/validation-data/valid.yaml b/cmd/krel/cmd/testdata/validation-data/valid.yaml
new file mode 100644
index 00000000..f179bf2f
--- /dev/null
+++ b/cmd/krel/cmd/testdata/validation-data/valid.yaml
@@ -0,0 +1,4 @@
+pr: 125157
+releasenote:
+  text: "`kubeadm`: The `NodeSwap` check that kubeadm performs during preflight, has a new warning to verify if swap has been configured correctly."
+pr_body: ""
diff --git a/cmd/krel/cmd/validate_test.go b/cmd/krel/cmd/validate_test.go
new file mode 100644
index 00000000..5c0a603c
--- /dev/null
+++ b/cmd/krel/cmd/validate_test.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2024 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRunValidateReleaseNotes(t *testing.T) {
+	testDataPath := "testdata/validation-data"
+
+	// Valid YAML returns no error
+	err := runValidateReleaseNotes(filepath.Join(testDataPath, "valid.yaml"))
+	assert.NoError(t, err, "Expected no error for valid YAML file")
+
+	// Try a non-existent path
+	err = runValidateReleaseNotes("nonexistent/path")
+	assert.Error(t, err, "Expected error for non-existent path")
+	assert.Contains(t, err.Error(), "does not exist", "Error should be about non-existent path")
+
+	// Missing punctuation YAML returns error
+	err = runValidateReleaseNotes(filepath.Join(testDataPath, "missing-punctuation.yaml"))
+	assert.Error(t, err, "Expected error for missing punctuation YAML file")
+	assert.Contains(t, err.Error(), "field does not end with valid punctuation", "Error should be about missing punctuation")
+
+	// Try invalid yaml starting with "`"
+	err = runValidateReleaseNotes(filepath.Join(testDataPath, "invalid-yaml-start.yaml"))
+	assert.Error(t, err, "Expected error for invalid yaml")
+	assert.Contains(t, err.Error(), "YAML unmarshaling testdata/validation-data/invalid-yaml-start", "Error should be about invalid yaml")
+
+	// Try invalid multi line yaml
+	err = runValidateReleaseNotes(filepath.Join(testDataPath, "invalid-multi-line.yaml"))
+	assert.Error(t, err, "Expected error for invalid yaml")
+	assert.Contains(t, err.Error(), "YAML unmarshaling testdata/validation-data/invalid-multi-line.yaml", "Error should be about invalid yaml")
+
+	// Try invalid indent
+	err = runValidateReleaseNotes(filepath.Join(testDataPath, "invalid-indent.yaml"))
+	assert.Error(t, err, "Expected error for invalid yaml")
+	assert.Contains(t, err.Error(), "YAML unmarshaling testdata/validation-data/invalid-indent.yaml", "Error should be about invalid yaml")
+}
diff --git a/cmd/krel/templates/latest/kubelet/kubelet.spec b/cmd/krel/templates/latest/kubelet/kubelet.spec
index 50b419be..0442562a 100644
--- a/cmd/krel/templates/latest/kubelet/kubelet.spec
+++ b/cmd/krel/templates/latest/kubelet/kubelet.spec
@@ -23,13 +23,14 @@ Requires: {{ $dep.Name }} {{ $dep.VersionConstraint }}
 %if "%{_vendor}" == "debbuild"
 Requires: iproute2
 Requires: mount
-Requires: conntrack
 %else
 Requires: iproute
-Requires: conntrack-tools
 %endif
+Requires: socat
 Requires: util-linux
 Requires: ethtool
+Requires: ebtables
+Requires: conntrack
 
 %if "%{_vendor}" == "debbuild"
 BuildRequires: systemd-deb-macros
diff --git a/cmd/krel/templates/latest/metadata.yaml b/cmd/krel/templates/latest/metadata.yaml
index 3729bb38..3b3c6f34 100644
--- a/cmd/krel/templates/latest/metadata.yaml
+++ b/cmd/krel/templates/latest/metadata.yaml
@@ -74,8 +74,6 @@ kubeadm:
     dependencies:
       - name: cri-tools
         versionConstraint: ">= 1.30.0"
-  - versionConstraint: ">= 1.32.0"
-    sourceURLTemplate: "{{ KubernetesURL }}"
 kubectl:
   - versionConstraint: ">= 1.0.0"
     sourceURLTemplate: "{{ KubernetesURL }}"
diff --git a/pkg/anago/anago_test.go b/pkg/anago/anago_test.go
index a4ce4437..e3133a11 100644
--- a/pkg/anago/anago_test.go
+++ b/pkg/anago/anago_test.go
@@ -46,7 +46,6 @@ func mockGenerateReleaseVersionRelease(mock *anagofakes.FakeReleaseClient) {
 	mock.GenerateReleaseVersionReturns(nil)
 }
 
-//nolint:dupl // duplications in those tests are intentional
 func TestRunStage(t *testing.T) {
 	for _, tc := range []struct {
 		prepare     func(*anagofakes.FakeStageClient)
@@ -132,7 +131,6 @@ func TestRunStage(t *testing.T) {
 	}
 }
 
-//nolint:dupl // duplications in those tests are intentional
 func TestRunRelease(t *testing.T) {
 	for _, tc := range []struct {
 		prepare     func(*anagofakes.FakeReleaseClient)
@@ -202,6 +200,13 @@ func TestRunRelease(t *testing.T) {
 			},
 			shouldError: true,
 		},
+		{ // Archive fails
+			prepare: func(mock *anagofakes.FakeReleaseClient) {
+				mockGenerateReleaseVersionRelease(mock)
+				mock.ArchiveReturns(err)
+			},
+			shouldError: true,
+		},
 	} {
 		opts := anago.DefaultReleaseOptions()
 		sut := anago.NewRelease(opts)
diff --git a/pkg/build/build_test.go b/pkg/build/build_test.go
index 9e14cdbb..1493d2e0 100644
--- a/pkg/build/build_test.go
+++ b/pkg/build/build_test.go
@@ -36,11 +36,22 @@ func TestBuildDirFromRepoRoot(t *testing.T) {
 			},
 			expectedBuildDir: "_output",
 		},
+		{
+			name: "non-empty repoRoot, bazel",
+			instance: &Instance{
+				opts: &Options{
+					Version:  "fakeVersion",
+					RepoRoot: "/fake/repo/root",
+				},
+			},
+			isBazel:          true,
+			expectedBuildDir: "/fake/repo/root/bazel-bin/build",
+		},
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
-			setupBuildDir(tc.instance)
+			setupBuildDir(tc.instance, tc.isBazel)
 			if tc.instance.opts.BuildDir != tc.expectedBuildDir {
 				t.Errorf("buildDir mismatched, got: %v, want: %v", tc.instance.opts.BuildDir, tc.expectedBuildDir)
 			}
diff --git a/pkg/gcp/gcb/gcb_test.go b/pkg/gcp/gcb/gcb_test.go
index 43dc5d82..56c184eb 100644
--- a/pkg/gcp/gcb/gcb_test.go
+++ b/pkg/gcp/gcb/gcb_test.go
@@ -157,16 +157,15 @@ func TestSubmitGcbFailure(t *testing.T) {
 
 func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 	testcases := []struct {
-		name           string
-		gcbOpts        *gcb.Options
-		toolOrg        string
-		toolRepo       string
-		toolRef        string
-		forceBuildKrel string
-		expected       map[string]string
-		repoMock       gcb.Repository
-		versionMock    gcb.Version
-		releaseMock    gcb.Release
+		name        string
+		gcbOpts     *gcb.Options
+		toolOrg     string
+		toolRepo    string
+		toolRef     string
+		expected    map[string]string
+		repoMock    gcb.Repository
+		versionMock gcb.Version
+		releaseMock gcb.Release
 	}{
 		{
 			name: "main branch alpha - stage",
@@ -184,7 +183,6 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				"TOOL_ORG":               "",
 				"TOOL_REPO":              "",
 				"TOOL_REF":               "",
-				"FORCE_BUILD_KREL":       "",
 				"TYPE":                   release.ReleaseTypeAlpha,
 				"TYPE_TAG":               release.ReleaseTypeAlpha,
 				"MAJOR_VERSION_TAG":      "1",
@@ -213,7 +211,6 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				"TOOL_ORG":               "",
 				"TOOL_REPO":              "",
 				"TOOL_REF":               "",
-				"FORCE_BUILD_KREL":       "",
 				"TYPE":                   release.ReleaseTypeBeta,
 				"TYPE_TAG":               release.ReleaseTypeBeta,
 				"MAJOR_VERSION_TAG":      "1",
@@ -242,7 +239,6 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				"TOOL_ORG":               "",
 				"TOOL_REPO":              "",
 				"TOOL_REF":               "",
-				"FORCE_BUILD_KREL":       "",
 				"TYPE":                   release.ReleaseTypeRC,
 				"TYPE_TAG":               release.ReleaseTypeRC,
 				"MAJOR_VERSION_TAG":      "1",
@@ -270,7 +266,6 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				"TOOL_ORG":               "",
 				"TOOL_REPO":              "",
 				"TOOL_REF":               "",
-				"FORCE_BUILD_KREL":       "",
 				"TYPE":                   release.ReleaseTypeOfficial,
 				"TYPE_TAG":               release.ReleaseTypeOfficial,
 				"MAJOR_VERSION_TAG":      "1",
@@ -290,13 +285,12 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				ReleaseType: release.ReleaseTypeOfficial,
 				GcpUser:     "test-user",
 			},
-			repoMock:       mockRepo(),
-			versionMock:    mockVersion("v1.16.0"),
-			releaseMock:    mockRelease("v1.16.0"),
-			toolOrg:        "honk",
-			toolRepo:       "best-tools",
-			toolRef:        "tool-branch",
-			forceBuildKrel: "true",
+			repoMock:    mockRepo(),
+			versionMock: mockVersion("v1.16.0"),
+			releaseMock: mockRelease("v1.16.0"),
+			toolOrg:     "honk",
+			toolRepo:    "best-tools",
+			toolRef:     "tool-branch",
 			expected: map[string]string{
 				"RELEASE_BRANCH":         "release-1.16",
 				"TOOL_ORG":               "honk",
@@ -304,7 +298,6 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				"TOOL_REF":               "tool-branch",
 				"TYPE":                   release.ReleaseTypeOfficial,
 				"TYPE_TAG":               release.ReleaseTypeOfficial,
-				"FORCE_BUILD_KREL":       "true",
 				"MAJOR_VERSION_TAG":      "1",
 				"MINOR_VERSION_TAG":      "16",
 				"PATCH_VERSION_TAG":      "0",
@@ -322,19 +315,17 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				ReleaseType: release.ReleaseTypeBeta,
 				GcpUser:     "test-user",
 			},
-			repoMock:       mockRepo(),
-			versionMock:    mockVersion("v1.19.0-alpha.2.763+2da917d3701904"),
-			releaseMock:    mockRelease("1.19.0-beta.0"),
-			toolOrg:        "honk",
-			toolRepo:       "best-tools",
-			toolRef:        "tool-branch",
-			forceBuildKrel: "true",
+			repoMock:    mockRepo(),
+			versionMock: mockVersion("v1.19.0-alpha.2.763+2da917d3701904"),
+			releaseMock: mockRelease("1.19.0-beta.0"),
+			toolOrg:     "honk",
+			toolRepo:    "best-tools",
+			toolRef:     "tool-branch",
 			expected: map[string]string{
 				"RELEASE_BRANCH":         "release-1.19",
 				"TOOL_ORG":               "honk",
 				"TOOL_REPO":              "best-tools",
 				"TOOL_REF":               "tool-branch",
-				"FORCE_BUILD_KREL":       "true",
 				"TYPE":                   release.ReleaseTypeBeta,
 				"TYPE_TAG":               release.ReleaseTypeBeta,
 				"MAJOR_VERSION_TAG":      "1",
@@ -354,16 +345,14 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				ReleaseType: release.ReleaseTypeRC,
 				GcpUser:     "test-user",
 			},
-			repoMock:       mockRepo(),
-			versionMock:    mockVersion("v1.18.6-rc.0.15+e38139724f8f00"),
-			releaseMock:    mockRelease("1.18.6-rc.1"),
-			forceBuildKrel: "false",
+			repoMock:    mockRepo(),
+			versionMock: mockVersion("v1.18.6-rc.0.15+e38139724f8f00"),
+			releaseMock: mockRelease("1.18.6-rc.1"),
 			expected: map[string]string{
 				"RELEASE_BRANCH":         "release-1.18",
 				"TOOL_ORG":               "",
 				"TOOL_REPO":              "",
 				"TOOL_REF":               "",
-				"FORCE_BUILD_KREL":       "false",
 				"TYPE":                   release.ReleaseTypeRC,
 				"TYPE_TAG":               release.ReleaseTypeRC,
 				"MAJOR_VERSION_TAG":      "1",
@@ -391,7 +380,6 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 				"TOOL_ORG":               "",
 				"TOOL_REPO":              "",
 				"TOOL_REF":               "",
-				"FORCE_BUILD_KREL":       "",
 				"TYPE":                   release.ReleaseTypeRC,
 				"TYPE_TAG":               release.ReleaseTypeRC,
 				"MAJOR_VERSION_TAG":      "1",
@@ -414,7 +402,7 @@ func TestSetGCBSubstitutionsSuccess(t *testing.T) {
 		sut.SetReleaseClient(tc.releaseMock)
 
 		subs, err := sut.SetGCBSubstitutions(
-			tc.toolOrg, tc.toolRepo, tc.toolRef, "gs://test-bucket", tc.forceBuildKrel,
+			tc.toolOrg, tc.toolRepo, tc.toolRef, "gs://test-bucket",
 		)
 		require.Nil(t, err)
 
@@ -461,7 +449,7 @@ func TestSetGCBSubstitutionsFailure(t *testing.T) {
 		sut := gcb.New(tc.gcbOpts)
 		sut.SetRepoClient(tc.repoMock)
 		sut.SetVersionClient(tc.versionMock)
-		_, err := sut.SetGCBSubstitutions("", "", "", "", "")
+		_, err := sut.SetGCBSubstitutions("", "", "", "")
 		require.Error(t, err)
 	}
 }
diff --git a/pkg/notes/maps/testdata/no-sigs-unit-test/map.yaml b/pkg/notes/maps/testdata/no-sigs-unit-test/map.yaml
deleted file mode 100644
index d12baa42..00000000
--- a/pkg/notes/maps/testdata/no-sigs-unit-test/map.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-pr: 95000
-releasenote:
-  text: "This test string has been modified"
-datafields:
-  testcase:
-    property: "Markdown"
-    expected: "This test string has been modified (#95000, @arghya88)"
-    name: "Modifying release note markdown"
\ No newline at end of file
diff --git a/pkg/notes/notes_gatherer_test.go b/pkg/notes/notes_gatherer_test.go
index 99fd8486..771b2c7b 100644
--- a/pkg/notes/notes_gatherer_test.go
+++ b/pkg/notes/notes_gatherer_test.go
@@ -108,8 +108,8 @@ func TestListCommits(t *testing.T) {
 			},
 			listCommitsArgValidator: func(t *testing.T, callCount int, ctx context.Context, org, repo string, clo *github.CommitsListOptions) {
 				checkOrgRepo(t, org, repo)
-				if page, minimum, maximum := clo.ListOptions.Page, 1, 100; page < minimum || page > maximum {
-					t.Errorf("Expected page number to be between %d and %d, got: %d", minimum, maximum, page)
+				if page, min, max := clo.ListOptions.Page, 1, 100; page < min || page > max {
+					t.Errorf("Expected page number to be between %d and %d, got: %d", min, max, page)
 				}
 				if a, e := clo.SHA, "the-branch"; a != e {
 					t.Errorf("Expected SHA to be the branch '%s', got: '%s'", e, a)
diff --git a/pkg/notes/notes_map_test.go b/pkg/notes/notes_map_test.go
index 8e13cfb9..e6234a48 100644
--- a/pkg/notes/notes_map_test.go
+++ b/pkg/notes/notes_map_test.go
@@ -27,7 +27,7 @@ func TestNewProviderFromInitString(t *testing.T) {
 		initString   string
 		returnsError bool
 	}{
-		{initString: "maps/testdata/applymap-unit-test/", returnsError: false},
+		{initString: "maps/testdata/unit/", returnsError: false},
 		{initString: "/this/shoud/not/really.exist/as/a/d33rect0ree", returnsError: true},
 		{initString: "gs://bucket-name/map/path/", returnsError: true},
 		{initString: "github://kubernetes/sig-release/maps", returnsError: true},
@@ -44,7 +44,7 @@ func TestNewProviderFromInitString(t *testing.T) {
 }
 
 func TestParseReleaseNotesMap(t *testing.T) {
-	maps, err := ParseReleaseNotesMap("maps/testdata/applymap-unit-test/maps.yaml")
+	maps, err := ParseReleaseNotesMap("maps/testdata/unit/maps.yaml")
 	require.Nil(t, err)
 	require.GreaterOrEqual(t, 6, len(*maps))
 
@@ -59,7 +59,7 @@ func TestGetMapsForPR(t *testing.T) {
 
 	maps, err := provider.GetMapsForPR(95000)
 	require.Nil(t, err)
-	require.GreaterOrEqual(t, 7, len(maps))
+	require.GreaterOrEqual(t, 6, len(maps))
 
 	maps, err = provider.GetMapsForPR(123)
 	require.Nil(t, err)
diff --git a/pkg/notes/notes_test.go b/pkg/notes/notes_test.go
index f514d5d1..69c3bb12 100644
--- a/pkg/notes/notes_test.go
+++ b/pkg/notes/notes_test.go
@@ -354,10 +354,30 @@ https://github.com/kubernetes/website/pull/19630
 	}
 }
 
-func testApplyMapHelper(t *testing.T, testDir string, makeNewNote func() *ReleaseNote) {
+func TestApplyMap(t *testing.T) {
+	makeNewNote := func() ReleaseNote {
+		return ReleaseNote{
+			Commit:   "078b355da3cf56668ca1a8a5e36f2b3b52ff1bd8",
+			Text:     "[ACTION REQUIRED] scheduler alpha metrics binding_duration_seconds and scheduling_algorithm_preemption_evaluation_seconds are deprecated, Both of those metrics are now covered as part of framework_extension_point_duration_seconds, the former as a PostFilter the latter and a Bind plugin. The plan is to remove both in 1.21",
+			Markdown: "[ACTION REQUIRED] scheduler alpha metrics binding_duration_seconds and scheduling_algorithm_preemption_evaluation_seconds are deprecated, Both of those metrics are now covered as part of framework_extension_point_duration_seconds, the former as a PostFilter the latter and a Bind plugin. The plan is to remove both in 1.21",
+			// Documentation:  documentation,
+			Author:         "arghya88",
+			AuthorURL:      "https://github.com/arghya88",
+			PrURL:          "https://github.com/kubernetes/kubernetes/pull/95001",
+			PrNumber:       95000,
+			SIGs:           []string{"instrumentation", "scheduling"},
+			Kinds:          []string{"deprecation", "feature"},
+			Areas:          []string{},
+			Feature:        true,
+			Duplicate:      false,
+			DuplicateKind:  false,
+			ActionRequired: true,
+		}
+	}
+
 	reflectedOriginalNote := reflect.ValueOf(makeNewNote())
 
-	mp, err := NewProviderFromInitString(testDir)
+	mp, err := NewProviderFromInitString("maps/testdata/unit/")
 	require.Nil(t, err)
 
 	// Read the maps from out test directory
@@ -402,8 +422,8 @@ func testApplyMapHelper(t *testing.T, testDir string, makeNewNote func() *Releas
 			actualVal := reflect.Indirect(reflectedNote).FieldByName(property).String()
 			require.Equalf(t, expectedValue, actualVal, "Failed %s", testName)
 			require.NotEqualf(t, expectedValue, originalVal.String(), "Failed %s", testName)
+		// Handle string slice cases
 		case []interface{}:
-			// Handle string slice cases
 			actualVal := reflect.Indirect(reflectedNote).FieldByName(property)
 			actualSlice := make([]string, 0)
 			for i := range actualVal.Len() {
@@ -411,59 +431,14 @@ func testApplyMapHelper(t *testing.T, testDir string, makeNewNote func() *Releas
 			}
 			require.ElementsMatchf(t, expectedValue, actualSlice, "Failed %s", testName)
 		default:
-			require.FailNowf(t, "Unknown case", "Unable to handle case for %s %T", property, expectedValue)
+			require.FailNowf(
+				t, "Unknown case", "Unable to handle case for %s %T",
+				property, testcase.(map[interface{}]interface{})["expected"],
+			)
 		}
 	}
 }
 
-func TestApplyMap(t *testing.T) {
-	makeNewNote := func() *ReleaseNote {
-		return &ReleaseNote{
-			Commit:   "078b355da3cf56668ca1a8a5e36f2b3b52ff1bd8",
-			Text:     "[ACTION REQUIRED] scheduler alpha metrics binding_duration_seconds and scheduling_algorithm_preemption_evaluation_seconds are deprecated, Both of those metrics are now covered as part of framework_extension_point_duration_seconds, the former as a PostFilter the latter and a Bind plugin. The plan is to remove both in 1.21",
-			Markdown: "[ACTION REQUIRED] scheduler alpha metrics binding_duration_seconds and scheduling_algorithm_preemption_evaluation_seconds are deprecated, Both of those metrics are now covered as part of framework_extension_point_duration_seconds, the former as a PostFilter the latter and a Bind plugin. The plan is to remove both in 1.21",
-			// Documentation:  documentation,
-			Author:         "arghya88",
-			AuthorURL:      "https://github.com/arghya88",
-			PrURL:          "https://github.com/kubernetes/kubernetes/pull/95001",
-			PrNumber:       95000,
-			SIGs:           []string{"instrumentation", "scheduling"},
-			Kinds:          []string{"deprecation", "feature"},
-			Areas:          []string{},
-			Feature:        true,
-			Duplicate:      false,
-			DuplicateKind:  false,
-			ActionRequired: true,
-		}
-	}
-
-	testApplyMapHelper(t, "maps/testdata/applymap-unit-test/", makeNewNote)
-}
-
-func TestApplyMapNoSigs(t *testing.T) {
-	makeNewNote := func() *ReleaseNote {
-		return &ReleaseNote{
-			Commit:   "078b355da3cf56668ca1a8a5e36f2b3b52ff1bd8",
-			Text:     "[ACTION REQUIRED] scheduler alpha metrics binding_duration_seconds and scheduling_algorithm_preemption_evaluation_seconds are deprecated, Both of those metrics are now covered as part of framework_extension_point_duration_seconds, the former as a PostFilter the latter and a Bind plugin. The plan is to remove both in 1.21",
-			Markdown: "[ACTION REQUIRED] scheduler alpha metrics binding_duration_seconds and scheduling_algorithm_preemption_evaluation_seconds are deprecated, Both of those metrics are now covered as part of framework_extension_point_duration_seconds, the former as a PostFilter the latter and a Bind plugin. The plan is to remove both in 1.21",
-			// Documentation:  documentation,
-			Author:         "arghya88",
-			AuthorURL:      "https://github.com/arghya88",
-			PrURL:          "https://github.com/kubernetes/kubernetes/pull/95001",
-			PrNumber:       95000,
-			SIGs:           []string{},
-			Kinds:          []string{"deprecation", "feature"},
-			Areas:          []string{},
-			Feature:        true,
-			Duplicate:      false,
-			DuplicateKind:  false,
-			ActionRequired: true,
-		}
-	}
-
-	testApplyMapHelper(t, "maps/testdata/no-sigs-unit-test/", makeNewNote)
-}
-
 func TestDashify(t *testing.T) {
 	t.Parallel()
 
diff --git a/pkg/notes/options/options_test.go b/pkg/notes/options/options_test.go
index 8ecec3d2..bdfdaec7 100644
--- a/pkg/notes/options/options_test.go
+++ b/pkg/notes/options/options_test.go
@@ -56,7 +56,7 @@ type testRepo struct {
 
 func newTestOptions(t *testing.T) *testOptions {
 	testRepo := newTestRepo(t)
-	t.Setenv(github.TokenEnvKey, "token")
+	require.Nil(t, os.Setenv(github.TokenEnvKey, "token"))
 	return &testOptions{
 		Options: &Options{
 			DiscoverMode: RevisionDiscoveryModeNONE,
diff --git a/pkg/release/archive_test.go b/pkg/release/archive_test.go
new file mode 100644
index 00000000..f73f8b36
--- /dev/null
+++ b/pkg/release/archive_test.go
@@ -0,0 +1,82 @@
+/*
+Copyright 2020 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package release_test
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"k8s.io/release/pkg/release"
+	"k8s.io/release/pkg/release/releasefakes"
+)
+
+func TestArchiveRelease(t *testing.T) {
+	err := errors.New("Synthetic error")
+	for _, tc := range []struct {
+		prepare   func(*releasefakes.FakeArchiverImpl)
+		shouldErr bool
+	}{
+		{ // Default: Success
+			prepare:   func(mock *releasefakes.FakeArchiverImpl) {},
+			shouldErr: false,
+		},
+		{ // failure Validate options fails
+			prepare: func(mock *releasefakes.FakeArchiverImpl) {
+				mock.ValidateOptionsReturns(err)
+			},
+			shouldErr: true,
+		},
+		{ // failure CopyReleaseLogsReturns errors
+			prepare: func(mock *releasefakes.FakeArchiverImpl) {
+				mock.CopyReleaseLogsReturns(err)
+			},
+			shouldErr: true,
+		},
+		{ // failure CopyReleaseToBucket fails
+			prepare: func(mock *releasefakes.FakeArchiverImpl) {
+				mock.CopyReleaseToBucketReturns(err)
+			},
+			shouldErr: true,
+		},
+		{ // failure CopyReleaseToBucket fails
+			prepare: func(mock *releasefakes.FakeArchiverImpl) {
+				mock.MakeFilesPrivateReturns(err)
+			},
+			shouldErr: true,
+		},
+		{ // CleanStagedBuilds fails
+			prepare: func(mock *releasefakes.FakeArchiverImpl) {
+				mock.CleanStagedBuildsReturns(err)
+			},
+			shouldErr: true,
+		},
+	} {
+		mock := &releasefakes.FakeArchiverImpl{}
+		sut := release.NewArchiver(&release.ArchiverOptions{})
+		tc.prepare(mock)
+		sut.SetImpl(mock)
+
+		err := sut.ArchiveRelease()
+		if tc.shouldErr {
+			require.NotNil(t, err)
+		} else {
+			require.Nil(t, err)
+		}
+	}
+}
diff --git a/pkg/release/archive_unit_test.go b/pkg/release/archive_unit_test.go
new file mode 100644
index 00000000..b374f501
--- /dev/null
+++ b/pkg/release/archive_unit_test.go
@@ -0,0 +1,168 @@
+/*
+Copyright 2020 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package release
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"sigs.k8s.io/release-sdk/object"
+)
+
+const fictionalTestBucketName = "kubernetes-test-name"
+
+func TestCopyReleaseLogsToWorkDir(t *testing.T) {
+	// create two files to  simulate logs
+	tmp1, err := os.CreateTemp("", "test-copylogs-")
+	require.Nil(t, err)
+	defer os.Remove(tmp1.Name())
+	tmp2, err := os.CreateTemp("", "test-copylogs-")
+	require.Nil(t, err)
+	defer os.Remove(tmp2.Name())
+
+	// Create a target directory to copy the files
+	destDir, err := os.MkdirTemp("", "test-move-logs")
+	require.Nil(t, err, "creating test dir")
+	defer os.RemoveAll(destDir)
+
+	// Put an oauth token in the first log
+	content1 := "7aa33bd2186c40840c4c2df321241e241def98ca:x-oauth-basic\n"
+	require.Nil(t, os.WriteFile(
+		tmp1.Name(), []byte(content1), os.FileMode(0o644),
+	))
+
+	// Write a regular text in the second
+	content2 := "Hello world!\n"
+	require.Nil(t, os.WriteFile(
+		tmp2.Name(), []byte(content2), os.FileMode(0o644),
+	))
+
+	// Create the implementation
+	impl := &defaultArchiverImpl{}
+	// Copy the log files to the mock directory
+	err = impl.CopyReleaseLogs([]string{tmp1.Name(), tmp2.Name()}, destDir, "")
+	require.Nil(t, err)
+
+	// Reopoen the files to check them
+	movedData1, err := os.ReadFile(filepath.Join(destDir, filepath.Base(tmp1.Name())))
+	require.Nil(t, err)
+
+	movedData2, err := os.ReadFile(filepath.Join(destDir, filepath.Base(tmp2.Name())))
+	require.Nil(t, err)
+
+	// The first file should be sanitized. Should differ
+	require.NotEmpty(t, movedData1)
+	require.NotEqual(t, content1, string(movedData1))
+
+	// The second file should be the same.
+	require.Equal(t, content2, string(movedData2))
+}
+
+func TestArchiveBucketPath(t *testing.T) {
+	// By default, the bucket name is empty, resulting in an empty
+	// GCS URI. This is to avoid getting a wrong but valid bucket location
+	DefaultArchiveOptions := &ArchiverOptions{}
+	require.Empty(t, DefaultArchiveOptions.ArchiveBucketPath())
+
+	// Using a bucket name should return a valid URI
+	opts := ArchiverOptions{
+		// Here, we test without "gs://", the gcs package should
+		// normalize the location with or without
+		Bucket:       fictionalTestBucketName,
+		PrimeVersion: "v1.20.0-beta.2",
+	}
+	require.Equal(t,
+		object.GcsPrefix+filepath.Join(fictionalTestBucketName, archiveBucketPath, archiveDirPrefix+opts.PrimeVersion),
+		opts.ArchiveBucketPath(),
+	)
+}
+
+func TestValidateOpts(t *testing.T) {
+	testOpts := &ArchiverOptions{}
+
+	// An empty options struct should not validate:
+	require.NotNil(t, testOpts.Validate())
+
+	// Create a fake directory to test
+	dir, err := os.MkdirTemp("", "archiver-opts-test-")
+	require.Nil(t, err)
+	defer os.RemoveAll(dir)
+
+	// With complete values, still should not validate as most
+	// directories do not exist
+	testOpts.Bucket = "kubernetes-test-name"
+	testOpts.PrimeVersion = "v1.20.0-beta.1"
+	testOpts.BuildVersion = "v1.20.0-beta.0.80+cdfd82733af78c"
+	testOpts.ReleaseBuildDir = filepath.Join(dir, testOpts.BuildVersion)
+	require.NotNil(t, testOpts.Validate())
+
+	// Creating a test log (/workdir/tmp) should still not
+	// validate, build dir is missing
+	tmplog, err := os.CreateTemp("", "anago-test-log-")
+	require.Nil(t, err)
+	testOpts.LogFile = tmplog.Name()
+	require.NotNil(t, testOpts.Validate())
+
+	// Finally create the build dir and we're done
+	require.Nil(t, os.Mkdir(testOpts.ReleaseBuildDir, os.FileMode(0o755)))
+
+	// Should succeed
+	require.Nil(t, testOpts.Validate())
+}
+
+func TestGetLogFiles(t *testing.T) {
+	dir, err := os.MkdirTemp("", "test-get-anago-logs")
+	require.Nil(t, err, "creating test dir")
+	defer os.RemoveAll(dir)
+
+	goodFiles := []string{"anago.log", "anago-stage.log", "anago.log.1", "anago-stage.log.1"}
+	allFiles := goodFiles
+	allFiles = append(allFiles, "test1.txt", "other.log", "anago.txt")
+	for _, fileName := range allFiles {
+		require.Nil(t, os.WriteFile(filepath.Join(dir, fileName), []byte("test"), os.FileMode(0o644)))
+	}
+	impl := &defaultArchiverImpl{}
+	foundPaths, err := impl.GetLogFiles(dir)
+	foundFilenames := []string{}
+	for _, logFile := range foundPaths {
+		foundFilenames = append(foundFilenames, filepath.Base(logFile))
+	}
+	require.Nil(t, err)
+	require.ElementsMatch(t, goodFiles, foundFilenames)
+}
+
+func TestDeleteStalePasswordFiles(t *testing.T) {
+	dir, err := os.MkdirTemp("", "test-delete-stale-pw-")
+	require.Nil(t, err, "creating test dir")
+	defer os.RemoveAll(dir)
+
+	// Create temporary files
+	require.Nil(t, os.WriteFile(filepath.Join(dir, "test.txt"), []byte("Hello World"), os.FileMode(0o644)))
+	require.Nil(t, os.WriteFile(filepath.Join(dir, "rsyncd.password"), []byte("Hello World"), os.FileMode(0o644)))
+
+	// Run the pass clear
+	impl := &defaultArchiverImpl{}
+	err = impl.DeleteStalePasswordFiles(dir)
+	require.Nil(t, err)
+
+	// Check that the pw file is gone while the other files remained
+	require.FileExists(t, filepath.Join(dir, "test.txt"))
+	require.NoFileExists(t, filepath.Join(dir, "rsyncd.password"))
+}
diff --git a/pkg/release/release_test.go b/pkg/release/release_test.go
index 9f69b921..b45f4835 100644
--- a/pkg/release/release_test.go
+++ b/pkg/release/release_test.go
@@ -27,6 +27,7 @@ import (
 	"path/filepath"
 	"strconv"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/require"
 
@@ -59,6 +60,164 @@ func TestGetToolRefSuccess(t *testing.T) {
 	}
 }
 
+func TestBuiltWithBazel(t *testing.T) {
+	baseTmpDir, err := os.MkdirTemp("", "")
+	require.Nil(t, err)
+
+	bazelTmpDir, err := os.MkdirTemp("", "bazel")
+	require.Nil(t, err)
+	dockerTmpDir, err := os.MkdirTemp("", "docker")
+	require.Nil(t, err)
+
+	// Build directories.
+	require.Nil(t, os.MkdirAll(filepath.Join(baseTmpDir, BazelBuildDir, ReleaseTarsPath), os.ModePerm))
+	require.Nil(t, os.MkdirAll(filepath.Join(baseTmpDir, BuildDir, ReleaseTarsPath), os.ModePerm))
+	require.Nil(t, os.MkdirAll(filepath.Join(bazelTmpDir, BazelBuildDir, ReleaseTarsPath), os.ModePerm))
+	require.Nil(t, os.MkdirAll(filepath.Join(dockerTmpDir, BuildDir, ReleaseTarsPath), os.ModePerm))
+
+	// Create test files.
+	baseBazelFile := filepath.Join(baseTmpDir, "bazel-bin", "build", "release-tars", "kubernetes.tar.gz")
+	require.Nil(t, os.WriteFile(
+		baseBazelFile,
+		[]byte("test"),
+		os.FileMode(0o644),
+	))
+	bazelFile := filepath.Join(bazelTmpDir, "bazel-bin", "build", "release-tars", "kubernetes.tar.gz")
+	require.Nil(t, os.WriteFile(
+		bazelFile,
+		[]byte("test"),
+		os.FileMode(0o644),
+	))
+
+	time.Sleep(1 * time.Second)
+
+	baseDockerFile := filepath.Join(
+		baseTmpDir, BuildDir, "release-tars", "kubernetes.tar.gz",
+	)
+	require.Nil(t, os.WriteFile(
+		baseDockerFile,
+		[]byte("test"),
+		os.FileMode(0o644),
+	))
+	dockerFile := filepath.Join(
+		dockerTmpDir, BuildDir, "release-tars", "kubernetes.tar.gz",
+	)
+	require.Nil(t, os.WriteFile(
+		dockerFile,
+		[]byte("test"),
+		os.FileMode(0o644),
+	))
+
+	defer cleanupTmps(t, baseTmpDir, bazelTmpDir, dockerTmpDir)
+
+	type args struct {
+		path string
+	}
+	type want struct {
+		r   bool
+		err error
+	}
+	cases := map[string]struct {
+		args args
+		want want
+	}{
+		"DockerMoreRecent": {
+			args: args{
+				path: baseTmpDir,
+			},
+			want: want{
+				r:   false,
+				err: nil,
+			},
+		},
+		"DockerOnly": {
+			args: args{
+				path: dockerTmpDir,
+			},
+			want: want{
+				r:   false,
+				err: nil,
+			},
+		},
+		"BazelOnly": {
+			args: args{
+				path: bazelTmpDir,
+			},
+			want: want{
+				r:   true,
+				err: nil,
+			},
+		},
+	}
+
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			res, err := BuiltWithBazel(tc.args.path)
+			require.Equal(t, tc.want.err, err)
+			require.Equal(t, tc.want.r, res)
+		})
+	}
+}
+
+func TestReadBazelVersion(t *testing.T) {
+	const version = "1.1.1"
+
+	type want struct {
+		r    string
+		rErr bool
+	}
+
+	cases := map[string]struct {
+		outdir string
+		want   want
+	}{
+		"ReadLegacyVersion": {
+			outdir: "bazel-genfiles",
+			want: want{
+				r:    version,
+				rErr: false,
+			},
+		},
+		"ReadVersion": {
+			outdir: "bazel-bin",
+			want: want{
+				r:    version,
+				rErr: false,
+			},
+		},
+		"ReadVersionError": {
+			outdir: "bazel-random",
+			want: want{
+				rErr: true,
+			},
+		},
+	}
+
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			baseTmpDir, err := os.MkdirTemp("", "")
+			if err != nil {
+				t.Fatalf("unable to create temp dir: %v", err)
+			}
+			defer cleanupTmps(t, baseTmpDir)
+
+			// Build directories.
+			require.Nil(t, os.MkdirAll(filepath.Join(baseTmpDir, tc.outdir), os.ModePerm))
+
+			bazelVersionFile := filepath.Join(baseTmpDir, tc.outdir, "version")
+			require.Nil(t, os.WriteFile(
+				bazelVersionFile,
+				[]byte(version),
+				os.FileMode(0o644),
+			))
+
+			res, err := ReadBazelVersion(baseTmpDir)
+			require.Equal(t, tc.want.rErr, err != nil)
+			require.Equal(t, tc.want.r, res)
+		})
+	}
+}
+
 func TestReadDockerVersion(t *testing.T) {
 	baseTmpDir, err := os.MkdirTemp("", "ahhh")
 	require.Nil(t, err)
diff --git a/pkg/testgrid/testgrid.go b/pkg/testgrid/testgrid.go
index ee9d6c29..62de6c63 100644
--- a/pkg/testgrid/testgrid.go
+++ b/pkg/testgrid/testgrid.go
@@ -45,13 +45,13 @@ func New() *TestGrid {
 //counterfeiter:generate . Client
 //go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt testgridfakes/fake_client.go > testgridfakes/_fake_client.go && mv testgridfakes/_fake_client.go testgridfakes/fake_client.go"
 type Client interface {
-	GetURLResponse(string) ([]byte, error)
+	GetURLResponse(string, bool) (string, error)
 }
 
 type testGridClient struct{}
 
-func (t *testGridClient) GetURLResponse(url string) ([]byte, error) {
-	return http.NewAgent().Get(url)
+func (t *testGridClient) GetURLResponse(url string, trim bool) (string, error) {
+	return http.GetURLResponse(url, trim)
 }
 
 // SetClient can be used to set the internal HTTP client.
@@ -93,12 +93,12 @@ func (t *TestGrid) configFromURL(url string) (cfg *pb.Configuration, err error)
 		}
 	}()
 
-	response, err := t.client.GetURLResponse(url)
+	response, err := t.client.GetURLResponse(url, false)
 	if err != nil {
 		return nil, fmt.Errorf("retrieving remote content: %w", err)
 	}
 
-	if _, err := tmpFile.Write(response); err != nil {
+	if _, err := tmpFile.WriteString(response); err != nil {
 		return nil, fmt.Errorf("writing response to file: %w", err)
 	}
 
diff --git a/pkg/testgrid/testgrid_test.go b/pkg/testgrid/testgrid_test.go
index 9a0c11f1..3489d1de 100644
--- a/pkg/testgrid/testgrid_test.go
+++ b/pkg/testgrid/testgrid_test.go
@@ -50,7 +50,7 @@ func TestBlockingTestsSuccess(t *testing.T) {
 		}},
 	})
 	require.Nil(t, err)
-	client.GetURLResponseReturns(httpRes, nil)
+	client.GetURLResponseReturns(string(httpRes), nil)
 
 	// When
 	res, err := sut.BlockingTests(git.DefaultBranch)
@@ -65,7 +65,7 @@ func TestBlockingTestsSuccess(t *testing.T) {
 func TestBlockingTestsFailureDashboardNotFound(t *testing.T) {
 	// Given
 	sut, client := newSut()
-	client.GetURLResponseReturns(nil, nil)
+	client.GetURLResponseReturns("", nil)
 
 	// When
 	res, err := sut.BlockingTests("")
@@ -78,7 +78,7 @@ func TestBlockingTestsFailureDashboardNotFound(t *testing.T) {
 func TestBlockingTestsFailureHTTP(t *testing.T) {
 	// Given
 	sut, client := newSut()
-	client.GetURLResponseReturns(nil, errors.New(""))
+	client.GetURLResponseReturns("", errors.New(""))
 
 	// When
 	res, err := sut.BlockingTests("")
diff --git a/pkg/testgrid/testgridfakes/fake_client.go b/pkg/testgrid/testgridfakes/fake_client.go
index 7533e42f..e329a00b 100644
--- a/pkg/testgrid/testgridfakes/fake_client.go
+++ b/pkg/testgrid/testgridfakes/fake_client.go
@@ -24,35 +24,37 @@ import (
 )
 
 type FakeClient struct {
-	GetURLResponseStub        func(string) ([]byte, error)
+	GetURLResponseStub        func(string, bool) (string, error)
 	getURLResponseMutex       sync.RWMutex
 	getURLResponseArgsForCall []struct {
 		arg1 string
+		arg2 bool
 	}
 	getURLResponseReturns struct {
-		result1 []byte
+		result1 string
 		result2 error
 	}
 	getURLResponseReturnsOnCall map[int]struct {
-		result1 []byte
+		result1 string
 		result2 error
 	}
 	invocations      map[string][][]interface{}
 	invocationsMutex sync.RWMutex
 }
 
-func (fake *FakeClient) GetURLResponse(arg1 string) ([]byte, error) {
+func (fake *FakeClient) GetURLResponse(arg1 string, arg2 bool) (string, error) {
 	fake.getURLResponseMutex.Lock()
 	ret, specificReturn := fake.getURLResponseReturnsOnCall[len(fake.getURLResponseArgsForCall)]
 	fake.getURLResponseArgsForCall = append(fake.getURLResponseArgsForCall, struct {
 		arg1 string
-	}{arg1})
+		arg2 bool
+	}{arg1, arg2})
 	stub := fake.GetURLResponseStub
 	fakeReturns := fake.getURLResponseReturns
-	fake.recordInvocation("GetURLResponse", []interface{}{arg1})
+	fake.recordInvocation("GetURLResponse", []interface{}{arg1, arg2})
 	fake.getURLResponseMutex.Unlock()
 	if stub != nil {
-		return stub(arg1)
+		return stub(arg1, arg2)
 	}
 	if specificReturn {
 		return ret.result1, ret.result2
@@ -66,41 +68,41 @@ func (fake *FakeClient) GetURLResponseCallCount() int {
 	return len(fake.getURLResponseArgsForCall)
 }
 
-func (fake *FakeClient) GetURLResponseCalls(stub func(string) ([]byte, error)) {
+func (fake *FakeClient) GetURLResponseCalls(stub func(string, bool) (string, error)) {
 	fake.getURLResponseMutex.Lock()
 	defer fake.getURLResponseMutex.Unlock()
 	fake.GetURLResponseStub = stub
 }
 
-func (fake *FakeClient) GetURLResponseArgsForCall(i int) string {
+func (fake *FakeClient) GetURLResponseArgsForCall(i int) (string, bool) {
 	fake.getURLResponseMutex.RLock()
 	defer fake.getURLResponseMutex.RUnlock()
 	argsForCall := fake.getURLResponseArgsForCall[i]
-	return argsForCall.arg1
+	return argsForCall.arg1, argsForCall.arg2
 }
 
-func (fake *FakeClient) GetURLResponseReturns(result1 []byte, result2 error) {
+func (fake *FakeClient) GetURLResponseReturns(result1 string, result2 error) {
 	fake.getURLResponseMutex.Lock()
 	defer fake.getURLResponseMutex.Unlock()
 	fake.GetURLResponseStub = nil
 	fake.getURLResponseReturns = struct {
-		result1 []byte
+		result1 string
 		result2 error
 	}{result1, result2}
 }
 
-func (fake *FakeClient) GetURLResponseReturnsOnCall(i int, result1 []byte, result2 error) {
+func (fake *FakeClient) GetURLResponseReturnsOnCall(i int, result1 string, result2 error) {
 	fake.getURLResponseMutex.Lock()
 	defer fake.getURLResponseMutex.Unlock()
 	fake.GetURLResponseStub = nil
 	if fake.getURLResponseReturnsOnCall == nil {
 		fake.getURLResponseReturnsOnCall = make(map[int]struct {
-			result1 []byte
+			result1 string
 			result2 error
 		})
 	}
 	fake.getURLResponseReturnsOnCall[i] = struct {
-		result1 []byte
+		result1 string
 		result2 error
 	}{result1, result2}
 }