diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7f161dda6c..c547f772a4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,19 +11,24 @@ updates: prefix: ":seedling:" labels: - "ok-to-test" + # Go - package-ecosystem: "gomod" directory: "/" schedule: interval: "weekly" + day: "monday" ignore: - # Ignore k8s modules as they are upgraded manually - # together with controller-runtime and CAPI dependencies. + # Ignore controller-runtime as its upgraded manually. + - dependency-name: "sigs.k8s.io/controller-runtime" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + # Ignore k8s and its transitives modules as they are upgraded manually + # together with controller-runtime. - dependency-name: "k8s.io/*" - - dependency-name: "sigs.k8s.io/*" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - dependency-name: "sigs.k8s.io/cluster-api/test" update-types: ["version-update:semver-major", "version-update:semver-minor"] commit-message: prefix: ":seedling:" labels: - - "ok-to-test" \ No newline at end of file + - "ok-to-test" diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/pr-golangci-lint.yaml similarity index 78% rename from .github/workflows/golangci-lint.yaml rename to .github/workflows/pr-golangci-lint.yaml index 62cf8b8d46..553f9cba37 100644 --- a/.github/workflows/golangci-lint.yaml +++ b/.github/workflows/pr-golangci-lint.yaml @@ -1,4 +1,4 @@ -name: golangci-lint +name: PR golangci-lint on: pull_request: @@ -17,10 +17,11 @@ jobs: id: vars run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT - name: Set up Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # tag=v4.0.1 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # tag=v4.1.0 with: go-version: ${{ steps.vars.outputs.go_version }} - name: golangci-lint uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # tag=v3.6.0 with: - version: v1.53.3 \ No newline at end of file + version: v1.53.3 + args: --out-format=colored-line-number diff --git a/.github/workflows/pr-md-link-check.yaml b/.github/workflows/pr-md-link-check.yaml new file mode 100644 index 0000000000..87154aacac --- /dev/null +++ b/.github/workflows/pr-md-link-check.yaml @@ -0,0 +1,23 @@ +name: PR check Markdown links + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + paths: + - '**.md' + +# Remove all permissions from GITHUB_TOKEN except metadata. +permissions: {} + +jobs: + markdown-link-check: + name: Broken Links + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # tag=v3.5.3 + - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # tag=v1 + with: + use-quiet-mode: 'yes' + config-file: .markdownlinkcheck.json + check-modified-files-only: 'yes' + base-branch: main diff --git a/.github/workflows/verify-pr.yml b/.github/workflows/pr-verify.yaml similarity index 73% rename from .github/workflows/verify-pr.yml rename to .github/workflows/pr-verify.yaml index e55bc24990..c835c68010 100644 --- a/.github/workflows/verify-pr.yml +++ b/.github/workflows/pr-verify.yaml @@ -1,4 +1,4 @@ -name: Verify PR +name: PR Verify on: pull_request_target: @@ -14,6 +14,6 @@ jobs: steps: - name: Verifier action id: verifier - uses: kubernetes-sigs/kubebuilder-release-tools@v0.3.0 + uses: kubernetes-sigs/kubebuilder-release-tools@4f3d1085b4458a49ed86918b4b55505716715b77 # tag=v0.3.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 966eb893e0..323eb006be 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -23,7 +23,7 @@ jobs: - name: Calculate go version run: echo "go_version=$(make go-version)" >> $GITHUB_ENV - name: Set up Go - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # tag=v4.0.1 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # tag=v4.1.0 with: go-version: ${{ env.go_version }} - name: generate release artifacts diff --git a/.github/workflows/scan.yaml b/.github/workflows/scan.yaml deleted file mode 100644 index f4063e8800..0000000000 --- a/.github/workflows/scan.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: scan-images - -on: - schedule: - # Cron for every Monday at 12:00 UTC. - - cron: "0 12 * * 1" - -# Remove all permissions from GITHUB_TOKEN except metadata. -permissions: {} - -jobs: - scan: - strategy: - fail-fast: false - matrix: - branch: [ main, release-1.6, release-1.5 ] - name: Trivy - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # tag=v3.5.2 - with: - ref: ${{ matrix.branch }} - - name: Calculate go version - id: vars - run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT - - name: Set up Go - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # tag=v3.5.0 - with: - go-version: ${{ steps.vars.outputs.go_version }} - - name: Run verify container script - run: make verify-container-images diff --git a/.github/workflows/weekly-md-link-check.yaml b/.github/workflows/weekly-md-link-check.yaml new file mode 100644 index 0000000000..f03ba9f3f7 --- /dev/null +++ b/.github/workflows/weekly-md-link-check.yaml @@ -0,0 +1,26 @@ +name: Weekly check all Markdown links + +on: + schedule: + # Cron for every Monday at 12:00 UTC. + - cron: "0 12 * * 1" + +# Remove all permissions from GITHUB_TOKEN except metadata. +permissions: {} + +jobs: + markdown-link-check: + name: Broken Links + strategy: + fail-fast: false + matrix: + branch: [ main, release-1.8, release-1.7, release-1.6, release-1.5 ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # tag=v3.5.3 + with: + ref: ${{ matrix.branch }} + - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # tag=v1 + with: + use-quiet-mode: 'yes' + config-file: .markdownlinkcheck.json diff --git a/.github/workflows/weekly-security-scan.yaml b/.github/workflows/weekly-security-scan.yaml new file mode 100644 index 0000000000..2047bcfd9c --- /dev/null +++ b/.github/workflows/weekly-security-scan.yaml @@ -0,0 +1,32 @@ +name: Weekly security scan + +on: + schedule: + # Cron for every Monday at 12:00 UTC. + - cron: "0 12 * * 1" + +# Remove all permissions from GITHUB_TOKEN except metadata. +permissions: {} + +jobs: + scan: + strategy: + fail-fast: false + matrix: + branch: [ main, release-1.8, release-1.7, release-1.6, release-1.5 ] + name: Trivy + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # tag=v3.5.3 + with: + ref: ${{ matrix.branch }} + - name: Calculate go version + id: vars + run: echo "go_version=$(make go-version)" >> $GITHUB_OUTPUT + - name: Set up Go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # tag=v4.1.0 + with: + go-version: ${{ steps.vars.outputs.go_version }} + - name: Run verify security target + run: make verify-security diff --git a/.github/workflows/weekly-test-release.yaml b/.github/workflows/weekly-test-release.yaml new file mode 100644 index 0000000000..f6d2fdc56e --- /dev/null +++ b/.github/workflows/weekly-test-release.yaml @@ -0,0 +1,40 @@ +name: Weekly release test + +# Note: This workflow does not build for releases. It attempts to build release binaries periodically to ensure the repo +# release machinery is in a good state. + +on: + schedule: + # Cron for every day at 12:00 UTC. + - cron: "0 12 * * *" + +# Remove all permissions from GITHUB_TOKEN except metadata. +permissions: {} + +jobs: + weekly-test-release: + name: Test release + strategy: + fail-fast: false + matrix: + branch: [ main, release-1.8, release-1.7, release-1.6, release-1.5 ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # tag=v3.5.3 + with: + ref: ${{ matrix.branch }} + fetch-depth: 0 + - name: Set env + run: echo "RELEASE_TAG=v9.9.9-fake" >> $GITHUB_ENV + - name: Set fake tag for release + run: | + git tag ${{ env.RELEASE_TAG }} + - name: Calculate go version + run: echo "go_version=$(make go-version)" >> $GITHUB_ENV + - name: Set up Go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # tag=v4.1.0 + with: + go-version: ${{ env.go_version }} + - name: Test release + run: | + make release \ No newline at end of file diff --git a/.markdownlinkcheck.json b/.markdownlinkcheck.json new file mode 100644 index 0000000000..78da0b7b6e --- /dev/null +++ b/.markdownlinkcheck.json @@ -0,0 +1,17 @@ +{ + "ignorePatterns": [{ + "pattern": "^http://localhost" + }], + "httpHeaders": [{ + "comment": "Workaround as suggested here: https://github.com/tcort/markdown-link-check/issues/201", + "urls": ["https://docs.github.com/"], + "headers": { + "Accept-Encoding": "zstd, br, gzip, deflate" + } + }], + "timeout": "10s", + "retryOn429": true, + "retryCount": 5, + "fallbackRetryDelay": "30s", + "aliveStatusCodes": [200, 206] +} diff --git a/Makefile b/Makefile index a720692263..8059e62e1b 100644 --- a/Makefile +++ b/Makefile @@ -134,6 +134,8 @@ GO_APIDIFF_BIN := go-apidiff GO_APIDIFF := $(abspath $(TOOLS_BIN_DIR)/$(GO_APIDIFF_BIN)-$(GO_APIDIFF_VER)) GO_APIDIFF_PKG := github.com/joelanford/go-apidiff +SHELLCHECK_VER := v0.9.0 + KPROMO_VER := v4.0.4 KPROMO_BIN := kpromo KPROMO := $(abspath $(TOOLS_BIN_DIR)/$(KPROMO_BIN)-$(KPROMO_VER)) @@ -150,10 +152,15 @@ GINKGO := $(abspath $(TOOLS_BIN_DIR)/$(GINKGO_BIN)-$(GINGKO_VER)) GINKGO_PKG := github.com/onsi/ginkgo/v2/ginkgo GOLANGCI_LINT_BIN := golangci-lint -GOLANGCI_LINT_VER := $(shell cat .github/workflows/golangci-lint.yaml | grep [[:space:]]version: | sed 's/.*version: //') +GOLANGCI_LINT_VER := $(shell cat .github/workflows/pr-golangci-lint.yaml | grep [[:space:]]version: | sed 's/.*version: //') GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER)) GOLANGCI_LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint +GOVULNCHECK_BIN := govulncheck +GOVULNCHECK_VER := v1.0.0 +GOVULNCHECK := $(abspath $(TOOLS_BIN_DIR)/$(GOVULNCHECK_BIN)-$(GOVULNCHECK_VER)) +GOVULNCHECK_PKG := golang.org/x/vuln/cmd/govulncheck + GOVC_VER := $(shell cat go.mod | grep "github.com/vmware/govmomi" | awk '{print $$NF}') GOVC_BIN := govc GOVC := $(abspath $(TOOLS_BIN_DIR)/$(GOVC_BIN)-$(GOVC_VER)) @@ -228,7 +235,7 @@ help: # Display this help .PHONY: generate generate: ## Run all generate targets - $(MAKE) generate-modules generate-manifests generate-go-deepcopy generate-go-conversions + $(MAKE) generate-modules generate-manifests generate-go-deepcopy generate-go-conversions generate-flavors .PHONY: generate-manifests generate-manifests: $(CONTROLLER_GEN) ## Generate manifests e.g. CRD, RBAC etc. @@ -276,6 +283,10 @@ generate-go-conversions: $(CONTROLLER_GEN) $(CONVERSION_GEN) ## Runs Go related generate-modules: ## Run go mod tidy to ensure modules are up to date go mod tidy +.PHONY: generate-doctoc +generate-doctoc: + TRACE=$(TRACE) ./hack/generate-doctoc.sh + .PHONY: generate-e2e-templates generate-e2e-templates: ## Generate e2e cluster templates $(MAKE) release-flavors @@ -311,7 +322,6 @@ generate-e2e-templates: ## Generate e2e cluster templates lint: $(GOLANGCI_LINT) ## Lint the codebase $(MAKE) lint-go-full $(MAKE) lint-markdown - $(MAKE) lint-shell GOLANGCI_LINT_EXTRA_ARGS ?= --fast=true .PHONY: lint-go @@ -326,10 +336,6 @@ lint-go-full: lint-go ## Run slower linters to detect possible issues lint-markdown: ## Lint the project's markdown docker run --rm -v "$$(pwd)":/build$(DOCKER_VOL_OPTS) gcr.io/cluster-api-provider-vsphere/extra/mdlint:0.17.0 -- /md/lint -i contrib/haproxy/openapi -i _releasenotes . -.PHONY: lint-shell -lint-shell: ## Lint the project's shell scripts - docker run --rm -t -v "$$(pwd)":/build:ro gcr.io/cluster-api-provider-vsphere/extra/shellcheck - .PHONY: lint-fix lint-fix: $(GOLANGCI_LINT) ## Lint the codebase and run auto-fixers if supported by the linter GOLANGCI_LINT_EXTRA_ARGS="--fast=false --fix" $(MAKE) lint-go @@ -340,10 +346,10 @@ APIDIFF_OLD_COMMIT ?= $(shell git rev-parse origin/main) apidiff: $(GO_APIDIFF) ## Check for API differences $(GO_APIDIFF) $(APIDIFF_OLD_COMMIT) --print-compatible -ALL_VERIFY_CHECKS = boilerplate modules gen conversions +ALL_VERIFY_CHECKS = boilerplate shellcheck modules gen conversions doctoc flavors .PHONY: verify -verify: $(addprefix verify-,$(ALL_VERIFY_CHECKS)) lint-markdown lint-shell ## Run all verify-* targets +verify: $(addprefix verify-,$(ALL_VERIFY_CHECKS)) lint-markdown ## Run all verify-* targets .PHONY: verify-modules verify-modules: generate-modules ## Verify go modules are up to date @@ -367,14 +373,46 @@ verify-gen: generate ## Verify go generated files are up to date verify-conversions: $(CONVERSION_VERIFIER) ## Verifies expected API conversion are in place $(CONVERSION_VERIFIER) +.PHONY: verify-doctoc +verify-doctoc: generate-doctoc + @if !(git diff --quiet HEAD); then \ + git diff; \ + echo "doctoc is out of date, run make generate-doctoc"; exit 1; \ + fi + .PHONY: verify-boilerplate verify-boilerplate: ## Verify boilerplate text exists in each file TRACE=$(TRACE) ./hack/verify-boilerplate.sh +.PHONY: verify-shellcheck +verify-shellcheck: ## Verify shell files + TRACE=$(TRACE) ./hack/verify-shellcheck.sh $(SHELLCHECK_VER) + .PHONY: verify-container-images verify-container-images: ## Verify container images TRACE=$(TRACE) ./hack/verify-container-images.sh +.PHONY: verify-govulncheck +verify-govulncheck: $(GOVULNCHECK) ## Verify code for vulnerabilities + $(GOVULNCHECK) ./... + +.PHONY: verify-security +verify-security: ## Verify code and images for vulnerabilities + $(MAKE) verify-container-images && R1=$$? || R1=$$?; \ + $(MAKE) verify-govulncheck && R2=$$? || R2=$$?; \ + if [ "$$R1" -ne "0" ] || [ "$$R2" -ne "0" ]; then \ + echo "Check for vulnerabilities failed! There are vulnerabilities to be fixed"; \ + exit 1; \ + fi + +.PHONY: verify-flavors +verify-flavors: $(FLAVOR_DIR) generate-flavors ## Verify generated flavors + @if !(git diff --quiet HEAD -- $(FLAVOR_DIR)); then \ + git diff $(FLAVOR_DIR); \ + echo "flavor files in templates directory are out of date"; exit 1; \ + fi + + ## -------------------------------------- ## Build ## -------------------------------------- @@ -721,6 +759,9 @@ $(GINKGO_BIN): $(GINKGO) ## Build a local copy of ginkgo. .PHONY: $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_BIN): $(GOLANGCI_LINT) ## Build a local copy of golangci-lint. +.PHONY: $(GOVULNCHECK_BIN) +$(GOVULNCHECK_BIN): $(GOVULNCHECK) ## Build a local copy of govulncheck. + .PHONY: $(GOVC_BIN) $(GOVC_BIN): $(GOVC) ## Build a local copy of govc. @@ -770,6 +811,9 @@ $(GINKGO): # Build ginkgo. $(GOLANGCI_LINT): # Build golangci-lint. GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOLANGCI_LINT_PKG) $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER) +$(GOVULNCHECK): # Build govulncheck. + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOVULNCHECK_PKG) $(GOVULNCHECK_BIN) $(GOVULNCHECK_VER) + $(GOVC): # Build GOVC. CGO_ENABLED=0 GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOVC_PKG) $(GOVC_BIN) $(GOVC_VER) diff --git a/docs/release/release-tasks.md b/docs/release/release-tasks.md new file mode 100644 index 0000000000..60f6ae2c02 --- /dev/null +++ b/docs/release/release-tasks.md @@ -0,0 +1,188 @@ +# Release Tasks + +**Notes**: + +- The examples in this document are based on the v1.8 release cycle. + + + + +- [Prepare main branch for development of the new release](#prepare-main-branch-for-development-of-the-new-release) +- [Remove previously deprecated code](#remove-previously-deprecated-code) +- [[Optional] Bump the Kubernetes version](#optional-bump-the-kubernetes-version) +- [Bump dependencies](#bump-dependencies) +- [Create a release branch](#create-a-release-branch) +- [Cut a release](#cut-a-release) +- [[Continuously] Reduce the amount of flaky tests](#continuously-reduce-the-amount-of-flaky-tests) + + + +## Prepare main branch for development of the new release + +TODO(sbueringer): Finalize this section once we do this for the first time + +The goal of this issue is to bump the versions on the main branch so that the upcoming release version +is used for e.g. local development and e2e tests. We also modify tests so that they are testing the previous release. + +This comes down to changing occurrences of the old version to the new version, e.g. `v1.7` to `v1.8`: + +1. Setup E2E tests for the new release: + 1. Goal is that our clusterctl upgrade tests are testing the right versions. For `v1.8` this means: + - v1beta1: `v1.7` (will change with each new release) + - v1alpha4: `v0.8` + 2. Update providers in `vsphere-ci.yaml`, `vsphere-dev.yaml`, `integration-dev.yaml`: + 1. Add a new `v1.7.0` entry. + 2. Remove providers that are not used anymore in clusterctl upgrade tests. + 3. Change `v1.7.99` to `v1.8.99`. + 3. Adjust `metadata.yaml`'s: + 1. Add new release to the top-level `metadata.yaml` + 2. Create a new `v1.7` `metadata.yaml` (`test/e2e/data/shared/v1.7/metadata.yaml`) by copying + `test/e2e/data/shared/main/metadata.yaml` + 3. Add the new v1.8 release to the main `metadata.yaml` (`test/e2e/data/shared/main/metadata.yaml`). + 4. Remove old `metadata.yaml`'s that are not used anymore in clusterctl upgrade tests. + 4. Adjust cluster templates in `test/e2e/data/infrastructure-vsphere`: + 1. Create a new `v1.7` folder. It should be created based on the `main` folder and only contain the templates + we use in the clusterctl upgrade tests (as of today `remote-management`). + 2. Remove old folders that are not used anymore in clusterctl upgrade tests. + 5. Modify the test specs in `test/e2e/capi_clusterctl_upgrade_test.go` (according to the versions we want to test described above). + Please note that both `InitWithKubernetesVersion` and `WorkloadKubernetesVersion` should be the highest mgmt cluster version supported by the respective Cluster API version. +2. Update `clusterctl-settings.json`: `v1.7.99` => `v1.8.99`. +3. Make sure all tests are green (also run `pull-cluster-api-provider-vsphere-e2e-full-main` and `pull-cluster-api-provider-vsphere-conformance-main`). + +Prior art: TODO(sbueringer): link example PR + +## Remove previously deprecated code + +The goal of this task is to remove all previously deprecated code that can be now removed. + +1. Check for deprecated code and remove it. + **Note**: We can't just remove all code flagged with `Deprecated`. In some cases like e.g. in API packages + we have to keep the old code. + +Prior art: TODO(sbueringer): link example PR + +## [Optional] Bump the Kubernetes version + +TODO(sbueringer): Write this when we do it the next time + +## Bump dependencies + +The goal of this task is to ensure that we have relatively up-to-date dependencies at the time of the release. +This reduces the risk that CVEs are found in outdated dependencies after our release. + +We should take a look at the following dependencies: + +- Go dependencies in the `go.mod` file. (usually dependabot takes care of that) +- Tools used in our Makefile (e.g. kustomize). + +## Create a release branch + +The goal of this task is to ensure we have a release branch with test coverage and results in testgrid. While we +add test coverage for the new release branch we will also drop the tests for old release branches if necessary. +The milestone applier should also apply milestones accordingly. +From this point forward changes which should land in the release have to be cherry-picked into the release branch. + +1. Create the release branch locally based on the latest commit on main and push it. + + ```bash + # Create the release branch + git checkout -b release-1.8 + + # Push the release branch + # Note: `upstream` must be the remote pointing to `github.com/kubernetes-sigs/cluster-api`. + git push -u upstream release-1.8 + ``` + +2. Create the milestone for the new release via [GitHub UI](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/milestones/new). +3. Update the [milestone applier config](https://github.com/kubernetes/test-infra/blob/151bab62dc023525f592e6d1fdc2a8de5305cd01/config/prow/plugins.yaml#L523) accordingly (e.g. `release-1.8: v1.8` + and `main: v1.9`) +4. Create new jobs based on the jobs running against our `main` branch: + 1. Copy `config/jobs/kubernetes-sigs/cluster-api-provider-vsphere/cluster-api-provider-vsphere-periodics-main.yaml` to `config/jobs/kubernetes-sigs/cluster-api-provider-vsphere/cluster-api-provider-vsphere-periodics-release-1.8.yaml`. + 2. Copy `config/jobs/kubernetes-sigs/cluster-api-provider-vsphere/cluster-api-provider-vsphere-presubmits-main.yaml` to `config/jobs/kubernetes-sigs/cluster-api-provider-vsphere/cluster-api-provider-vsphere-presubmits-release-1.8.yaml`. + 3. Modify the following: + 1. Rename the jobs, e.g.: `periodic-cluster-api-provider-vsphere-test-main` => `periodic-cluster-api-provider-vsphere-test-release-1-8`. + 2. Change `annotations.testgrid-tab-name`, e.g. `periodic-test-main` => `periodic-test-release-1-8`. + 3. For periodics additionally: + - Change `extra_refs[].base_ref` to `release-1.8` (for repo: `cluster-api-provider-vsphere`). + 4. For presubmits additionally: + - Adjust branches: `^main$` => `^release-1.8$`. +5. Remove tests for old release branches if necessary +6. Verify the jobs and dashboards a day later by taking a look at [testgrid](https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api-provider-vsphere) +7. Update `.github/workflows/weekly-security-scan.yaml` - to setup Trivy and govulncheck scanning - `.github/workflows/weekly-md-link-check.yaml` - to setup link checking in the CAPI book - and `.github/workflows/weekly-test-release.yaml` - to verify the release target is working - for the currently supported branches. + +## Cut a release + +1. Ensure via testgrid that CI is stable before cutting the release + Note: special attention should be given to image scan results, so we can avoid cutting a release with CVEs or document known CVEs in release notes. +2. Create and push the release tags to the GitHub repository: + + ```bash + # Export the tag of the release to be cut, e.g.: + export RELEASE_TAG=v1.8.0-beta.0 + # Create tags locally + # Warning: The test tag MUST NOT be an annotated tag. + git tag -s -a ${RELEASE_TAG} -m ${RELEASE_TAG} + + # Push tags + # Note: `upstream` must be the remote pointing to `github.com/kubernetes-sigs/cluster-api`. + git push upstream ${RELEASE_TAG} + ``` + + This will automatically trigger: + - a [GitHub Action](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/actions/workflows/release.yaml) to create a draft release and + - a [ProwJob](https://prow.k8s.io/?repo=kubernetes-sigs%2Fcluster-api-provider-vsphere&job=post-cluster-api-provider-vsphere-push-images) to publish images to the staging image repository. +3. Promote images from the staging repository to the production registry (`registry.k8s.io/cluster-api-vsphere`): + 1. Wait until images for the tag have been built and pushed to the [staging repository](https://console.cloud.google.com/gcr/images/k8s-staging-capi-vsphere/global/cluster-api-vsphere-controller) by + the [push images job](https://prow.k8s.io/?repo=kubernetes-sigs%2Fcluster-api-provider-vsphere&job=post-cluster-api-provider-vsphere-push-images). + 2. If you don't have a GitHub token, create one by going to your GitHub settings in [Personal access tokens](https://github.com/settings/tokens). Make sure you give the token the `repo` scope. + 3. Create a PR to promote the images to the production registry: + + ```bash + # Export the tag of the release to be cut, e.g.: + export RELEASE_TAG=v1.8.0-beta.0 + export GITHUB_TOKEN= + make promote-images + ``` + + **Notes**: + - `make promote-images` target tries to figure out your Github user handle in order to find the forked [k8s.io](https://github.com/kubernetes/k8s.io) repository. + If you have not forked the repo, please do it before running the Makefile target. + - if `make promote-images` fails with an error like `FATAL while checking fork of kubernetes/k8s.io` you may be able to solve it by manually setting the USER_FORK variable + i.e. `export USER_FORK=` + - `kpromo` uses `git@github.com:...` as remote to push the branch for the PR. If you don't have `ssh` set up you can configure + git to use `https` instead via `git config --global url."https://github.com/".insteadOf git@github.com:`. + - This will automatically create a PR in [k8s.io](https://github.com/kubernetes/k8s.io) and assign the CAPI maintainers. +4. Merge the PR (/lgtm + /hold cancel) and verify the images are available in the production registry: + - Wait for the [promotion prow job](https://prow.k8s.io/?repo=kubernetes%2Fk8s.io&job=post-k8sio-image-promo) to complete successfully. Then verify that the production images are accessible: + + ```bash + docker pull registry.k8s.io/cluster-api-vsphere/cluster-api-vsphere-controller:${RELEASE_TAG} + ``` + +5. Publish the release. + 1. Finalize release notes + 1. Pay close attention to the `## :question: Sort these by hand` section, as it contains items that need to be manually sorted. + 2. Ensure consistent formatting of entries (e.g. prefix). + 3. Merge dependency bump PR entries for the same dependency into a single entry. + 4. Move minor changes into a single line at the end of each section. + 5. Sort entries within a section alphabetically. + 6. Write highlights section based on the initial release notes doc. (for minor releases and important changes only) + 7. **For minor releases** Modify `Changes since v1.x.y` to `Changes since v1.x` + **Note**: The release notes tool includes all merges since the previous release branch was branched of. + 2. Publish the release and ensure release is flagged as `pre-release` for all `beta` and `rc` releases or `latest` for a new release in the most recent release branch. +6. **For minor releases** Update supported versions in versions.md. + +- Cutting a release as of today requires permissions to: + - Create a release tag on the GitHub repository. + - Create/update/publish GitHub releases. + +## [Continuously] Reduce the amount of flaky tests + +The CAPV tests are pretty stable, but there are still some flaky tests from time to time. To reduce the amount of flakes please periodically: + +1. Take a look at recent CI failures via `k8s-triage`: + - [periodic-cluster-api-provider-vsphere-test-main](https://storage.googleapis.com/k8s-triage/index.html?pr=1&job=periodic-cluster-api-provider-vsphere-test-main) + - [periodic-cluster-api-provider-vsphere-test-integration-main](https://storage.googleapis.com/k8s-triage/index.html?pr=1&job=periodic-cluster-api-provider-vsphere-test-integration-main) + - [periodic-cluster-api-provider-vsphere-e2e-full-main](https://storage.googleapis.com/k8s-triage/index.html?pr=1&job=periodic-cluster-api-provider-vsphere-e2e-full-main) + - [periodic-cluster-api-provider-vsphere-conformance-main](https://storage.googleapis.com/k8s-triage/index.html?pr=1&job=periodic-cluster-api-provider-vsphere-conformance-main) +2. Open issues for occurring flakes and ideally fix them or find someone who can. diff --git a/hack/check-shell.sh b/hack/generate-doctoc.sh similarity index 63% rename from hack/check-shell.sh rename to hack/generate-doctoc.sh index db38eb02a8..f7adc62fa4 100755 --- a/hack/check-shell.sh +++ b/hack/generate-doctoc.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # @@ -18,11 +18,14 @@ set -o errexit set -o nounset set -o pipefail -# Change directories to the parent directory of the one in which this -# script is located. -cd "$(dirname "${BASH_SOURCE[0]}")/.." +if [[ "${TRACE-0}" == "1" ]]; then + set -o xtrace +fi -# This file has been deprecated in favor of the Makefile target "lint-shell". -# This file remains as a backwards-compatible stub for the CI tests since -# older release branches still expect this file to exist. -make lint-shell +if [[ -z "$(command -v doctoc)" ]]; then + echo "doctoc is not available on your system, skipping verification" + echo "Note: The doctoc module can be installed via npm (https://www.npmjs.com/package/doctoc)" + exit 0 +fi + +doctoc docs/release/release-tasks.md diff --git a/hack/match-release-tag.sh b/hack/match-release-tag.sh deleted file mode 100755 index 96a6688611..0000000000 --- a/hack/match-release-tag.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash - -# Copyright 2019 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. - -set -o errexit -set -o nounset -set -o pipefail - -# Change directories to the parent directory of the one in which this -# script is located. -cd "$(dirname "${BASH_SOURCE[0]}")/.." - -usage() { - cat <&2; exit 1 - ;; - x) - EXAMPLES=1 - ;; - \?) - { echo "invalid option: -${OPTARG}"; usage; } 1>&2; exit 1 - ;; - :) - echo "option -${OPTARG} requires an argument" 1>&2; exit 1 - ;; - esac -done -shift $((OPTIND-1)) - -# The regular expression matches the following strings: -# * v1.0.0-alpha.0 -# * v1.0.0-beta.0 -# * v1.0.0-rc.0 -# * v1.0.0 -# Any occurence of a digit in the above examples may be multiple digits. -REGEX='^[[:space:]]{0,}v[[:digit:]]{1,}\.[[:digit:]]{1,}\.[[:digit:]]{1,}(-(alpha|beta|rc)\.[[:digit:]]{1,}){0,1}[[:space:]]{0,}$' - -# Match the tag against the regular expression for a release tag. -match() { - if [[ ${1} =~ ${REGEX} ]]; then - echo "yay: ${1}" - else - exit_code="${?}" - echo "nay: ${1}" - return "${exit_code}" - fi -} - -# Run examples to illustrate valid and invalid values. -examples() { - local semvers=" \ - v1.0.0-alpha.0 \ - v1.0.0-beta.0 \ - v1.0.0-rc.0 \ - v1.0.0 \ - v10.0.0 \ - v1.10.0 \ - v1.0.10 \ - v10.0.0-alpha.10 \ - v1.10.0-beta.10 \ - v1.0.10-rc.10 \ - 1.0.0 \ - v1.0.0+rc.0 \ - v10a.0.0 \ - 1.1.0-alpha.1 \ - v1.0.0-alpha.0a" - set +o errexit - for v in ${semvers}; do match "${v}"; done - return 0 -} - -main() { - # Get the tag from the remaining arguments or from "git describe --dirty" - [ "${#}" -eq "0" ] || tag="${1}" - [ -n "${tag-}" ] || tag="$(git describe --dirty)" - - # Match the tag against the regular expression for a release tag. - match "${tag}" 1>&2 -} - -{ [ "${EXAMPLES-}" ] && examples; } || main "${@-}" diff --git a/hack/tools/shellcheck/Dockerfile b/hack/tools/shellcheck/Dockerfile deleted file mode 100644 index 483d05be12..0000000000 --- a/hack/tools/shellcheck/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -# syntax=docker/dockerfile:1.4 - -# Copyright 2019 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. - -################################################################################ -## INSTALL SHELLCHECK ## -################################################################################ -ARG SHELLCHECK_VERSION=v0.6.0 -FROM koalaman/shellcheck:${SHELLCHECK_VERSION} as build - -################################################################################ -## MAIN ## -################################################################################ -FROM debian:stretch-slim -LABEL "maintainer" "Andrew Kutz " -COPY --from=build /bin/shellcheck /bin/ -COPY shellcheck.sh /bin/shellcheck.sh -WORKDIR /build -ENTRYPOINT [ "/bin/shellcheck.sh" ] diff --git a/hack/tools/shellcheck/Makefile b/hack/tools/shellcheck/Makefile deleted file mode 100644 index 0f30c4dc46..0000000000 --- a/hack/tools/shellcheck/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2019 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. - -all: build - -IMAGE_VERSION ?= v0.6.0 -IMAGE_NAME ?= gcr.io/cluster-api-provider-vsphere/extra/shellcheck -IMAGE_TAG ?= $(IMAGE_NAME):$(IMAGE_VERSION) - -build: - docker build --build-arg SHELLCHECK_VERSION=$(IMAGE_VERSION) -t $(IMAGE_TAG) . -.PHONY: build - -push: - docker push $(IMAGE_TAG) -.PHONY: push diff --git a/hack/tools/shellcheck/shellcheck.sh b/hack/utils.sh old mode 100755 new mode 100644 similarity index 79% rename from hack/tools/shellcheck/shellcheck.sh rename to hack/utils.sh index 472ed15fdd..df96561ad1 --- a/hack/tools/shellcheck/shellcheck.sh +++ b/hack/utils.sh @@ -1,5 +1,4 @@ -#!/bin/bash - +#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -o errexit -set -o nounset -set -o pipefail - -find . -path ./vendor -prune -o -name "*.*sh" -type f -print0 | xargs -0 shellcheck "${@}" +# get_root_path returns the root path of the project source tree +get_root_path() { + git rev-parse --show-toplevel +} diff --git a/hack/verify-container-images.sh b/hack/verify-container-images.sh index 0a983ea2e3..d99c2da64a 100755 --- a/hack/verify-container-images.sh +++ b/hack/verify-container-images.sh @@ -66,7 +66,7 @@ NC='\033[0m' # No if [ "$R1" -ne "0" ] then - echo -e "${BRed}Check container images failed! There are vulnerability to be fixed${NC}" + echo -e "${BRed}Check container images failed! There are vulnerabilities to be fixed${NC}" exit 1 fi diff --git a/hack/verify-shellcheck.sh b/hack/verify-shellcheck.sh new file mode 100755 index 0000000000..e29007bad8 --- /dev/null +++ b/hack/verify-shellcheck.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# Copyright 2019 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. + +set -o errexit +set -o nounset +set -o pipefail + +if [[ "${TRACE-0}" == "1" ]]; then + set -o xtrace +fi + +if [ $# -ne 1 ]; then + echo 1>&2 "$0: usage: ./verify-shellcheck.sh " + exit 2 +fi + +VERSION=${1} + +OS="unknown" +if [[ "${OSTYPE}" == "linux"* ]]; then + OS="linux" +elif [[ "${OSTYPE}" == "darwin"* ]]; then + OS="darwin" +fi + +# shellcheck source=./hack/utils.sh +source "$(dirname "$0")/utils.sh" +ROOT_PATH=$(get_root_path) + +# create a temporary directory +TMP_DIR=$(mktemp -d) +OUT="${TMP_DIR}/out.log" + +# cleanup on exit +cleanup() { + ret=0 + if [[ -s "${OUT}" ]]; then + echo "Found errors:" + cat "${OUT}" + ret=1 + fi + echo "Cleaning up..." + rm -rf "${TMP_DIR}" + exit ${ret} +} +trap cleanup EXIT + + +SHELLCHECK="./$(dirname "$0")/tools/bin/shellcheck/${VERSION}/shellcheck" + +if [ ! -f "$SHELLCHECK" ]; then + # install buildifier + cd "${TMP_DIR}" || exit + DOWNLOAD_FILE="shellcheck-${VERSION}.${OS}.x86_64.tar.xz" + curl -L "https://github.com/koalaman/shellcheck/releases/download/${VERSION}/${DOWNLOAD_FILE}" -o "${TMP_DIR}/shellcheck.tar.xz" + tar xf "${TMP_DIR}/shellcheck.tar.xz" + cd "${ROOT_PATH}" + mkdir -p "$(dirname "$0")/tools/bin/shellcheck/${VERSION}" + mv "${TMP_DIR}/shellcheck-${VERSION}/shellcheck" "$SHELLCHECK" +fi + +echo "Running shellcheck..." +cd "${ROOT_PATH}" || exit +FILES=$(find . -name "*.sh") +while read -r file; do + "$SHELLCHECK" -x "$file" >> "${OUT}" 2>&1 +done <<< "$FILES"