diff --git a/.github/actions/e2e/install-helm/action.yaml b/.github/actions/e2e/install-helm/action.yaml new file mode 100644 index 000000000000..ef0f656034ca --- /dev/null +++ b/.github/actions/e2e/install-helm/action.yaml @@ -0,0 +1,20 @@ +name: InstallHelm +description: 'Installs helm' +inputs: + version: + description: "Version of Helm to install" + required: true +runs: + using: "composite" + steps: + - name: install helm + shell: bash + run: | + TEMPDIR=$(mktemp -d) + curl -fsSL -o "${TEMPDIR}/get_helm.sh" https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 + chmod 700 "${TEMPDIR}/get_helm.sh" + "${TEMPDIR}/get_helm.sh" --version ${{ inputs.version }} + - name: install helm-diff + shell: bash + run: | + helm plugin install https://github.com/databus23/helm-diff || true \ No newline at end of file diff --git a/.github/workflows/release-trigger.yaml b/.github/workflows/release-trigger.yaml deleted file mode 100644 index 0a71d881d10f..000000000000 --- a/.github/workflows/release-trigger.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: ReleaseTrigger -on: - push: - tags: ['v*.*.*'] - branches: [ main ] - -jobs: - release-trigger: - if: github.repository == 'aws/karpenter' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Save tag and SHA as an artifact for other workflows that run on workflow_run to download them - run: | - mkdir -p /tmp/artifacts - TAG=$(git describe --tags --exact-match || echo "no tag") - echo "${TAG}" > /tmp/artifacts/metadata.txt - git rev-parse HEAD >> /tmp/artifacts/metadata.txt - echo "metadata.txt contents:" - cat /tmp/artifacts/metadata.txt - echo "TAG=$TAG" >> $GITHUB_ENV - - uses: ./.github/actions/upload-artifact - - name: Create Github Release - if: env.TAG != 'no tag' - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - - uses: ./.github/actions/authenticate-ghcr - with: - actor: ${{ github.actor }} - secret: ${{ secrets.GITHUB_TOKEN }} - - run: make release-crd diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c46c54131b87..4bbd6ae18b10 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,61 +1,57 @@ name: Release on: - workflow_run: - workflows: [ReleaseTrigger] - types: [completed] + push: + tags: [ 'v*.*.*' ] permissions: - id-token: write - pull-requests: write - contents: write + id-token: write # aws-actions/configure-aws-credentials@v4.0.1 + contents: write # marvinpinto/action-automatic-releases@latest + pull-requests: write # name: Create PR jobs: release: - if: github.event.workflow_run.conclusion == 'success' && contains(fromJson('["push"]'), github.event.workflow_run.event) + if: github.repository == 'aws/karpenter' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: ./.github/actions/download-artifact - - run: | - tag=$(head -n 1 /tmp/artifacts/metadata.txt) - tag_commit=$(tail -n 1 /tmp/artifacts/metadata.txt) - echo "TAG_COMMIT=$tag_commit" >> $GITHUB_ENV - echo "TAG=$tag" >> $GITHUB_ENV - - name: Checkout the repository out again at the given SHA from the artifact - uses: actions/checkout@v3 + - name: Create GitHub Release + uses: 'marvinpinto/action-automatic-releases@latest' with: - fetch-depth: 0 - ref: ${{ env.TAG_COMMIT }} + repo_token: '${{ secrets.GITHUB_TOKEN }}' + prerelease: false + - id: tag + run: | + TAG="$(git describe --tags --exact-match)" + echo TAG="${TAG}" >> "$GITHUB_OUTPUT" - uses: ./.github/actions/install-deps - - uses: ./.github/actions/authenticate-aws - - uses: ./.github/actions/authenticate-ghcr + - uses: ./.github/actions/e2e/install-helm + with: + version: v3.12.3 # Pinned to this version since v3.13.0 has issues with pushing to public ECR: https://github.com/helm/helm/issues/12442 + - uses: aws-actions/configure-aws-credentials@v4.0.1 with: - actor: ${{ github.actor }} - secret: ${{ secrets.GITHUB_TOKEN }} + role-to-assume: 'arn:aws:iam::${{ vars.ECR_ACCOUNT_ID }}:role/${{ vars.ECR_RELEASE_ROLE_NAME }}' + aws-region: ${{ vars.ECR_REGION }} - run: make release - run: make docgen - - name: Autogenerate and commit files for PR for major and minor releases only - if: endsWith(env.TAG ,'.0') - run: make prepare-website + - run: make prepare-website - run: make stable-release-pr - if: env.TAG != 'no tag' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPO: ${{ github.repository }} - name: Create PR - if: env.TAG != 'no tag' + if: steps.tag.outputs.TAG != 'no tag' uses: actions/github-script@v6 with: script: | const { repo, owner } = context.repo; const result = await github.rest.pulls.create({ - title: 'chore: Release ${{ env.TAG }}', + title: 'chore: Release ${{ steps.tag.outputs.TAG }}', owner, repo, - head: 'release-${{ env.TAG }}', + head: 'release-${{ steps.tag.outputs.TAG }}', base: 'main', body: [ - 'Stable Release Changes for ${{ env.TAG }}.', + 'Stable Release Changes for ${{ steps.tag.outputs.TAG }}.', 'Please disregard this PR if it is for a patch release.', 'Please remove the branch after merging.', 'This PR is generated by [StableRelease](https://github.com/aws/karpenter/actions/workflows/stable-release.yml).' diff --git a/hack/release/common.sh b/hack/release/common.sh index ae90701d704d..3a027a96c8b3 100644 --- a/hack/release/common.sh +++ b/hack/release/common.sh @@ -8,12 +8,13 @@ config(){ RELEASE_REPO_ECR=${RELEASE_REPO_ECR:-public.ecr.aws/${ECR_GALLERY_NAME}/} RELEASE_REPO_GH=${RELEASE_REPO_GH:-ghcr.io/${GITHUB_ACCOUNT}/karpenter} - MAIN_GITHUB_ACCOUNT="aws" - PRIVATE_PULL_THROUGH_HOST="${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com" - SNS_TOPIC_ARN="arn:aws:sns:us-east-1:${AWS_ACCOUNT_ID}:KarpenterReleases" + PRIVATE_HOST="${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com" + SNAPSHOT_REPO_ECR=${SNAPSHOT_REPO_ECR:-${PRIVATE_HOST}/karpenter/snapshot/} + CURRENT_MAJOR_VERSION="0" RELEASE_PLATFORM="--platform=linux/amd64,linux/arm64" + MAIN_GITHUB_ACCOUNT="aws" RELEASE_TYPE_STABLE="stable" RELEASE_TYPE_SNAPSHOT="snapshot" } @@ -27,26 +28,35 @@ versionData(){ RELEASE_VERSION_MINOR="${VERSION#*.}" RELEASE_VERSION_MINOR="${RELEASE_VERSION_MINOR%.*}" RELEASE_VERSION_PATCH="${VERSION##*.}" + RELEASE_MINOR_VERSION="v${RELEASE_VERSION_MAJOR}.${RELEASE_VERSION_MINOR}" } -release() { +snapshot() { RELEASE_VERSION=$1 - echo "Release Type: $(releaseType "${RELEASE_VERSION}") + echo "Release Type: snapshot Release Version: ${RELEASE_VERSION} Commit: $(git rev-parse HEAD) Helm Chart Version $(helmChartVersion $RELEASE_VERSION)" - PR_NUMBER=${GH_PR_NUMBER:-} - if [ "${GH_PR_NUMBER+defined}" != defined ]; then - PR_NUMBER="none" - fi + authenticatePrivateRepo + buildImages ${SNAPSHOT_REPO_ECR} + cosignImages + publishHelmChart "karpenter" "${RELEASE_VERSION}" "${SNAPSHOT_REPO_ECR}" + publishHelmChart "karpenter-crd" "${RELEASE_VERSION}" "${SNAPSHOT_REPO_ECR}" +} + +release() { + RELEASE_VERSION=$1 + echo "Release Type: stable +Release Version: ${RELEASE_VERSION} +Commit: $(git rev-parse HEAD) +Helm Chart Version $(helmChartVersion $RELEASE_VERSION)" authenticate - buildImages + buildImages ${RELEASE_REPO_ECR} cosignImages publishHelmChart "karpenter" "${RELEASE_VERSION}" "${RELEASE_REPO_ECR}" publishHelmChart "karpenter-crd" "${RELEASE_VERSION}" "${RELEASE_REPO_ECR}" - notifyRelease "$RELEASE_VERSION" $PR_NUMBER pullPrivateReplica "$RELEASE_VERSION" } @@ -55,11 +65,14 @@ authenticate() { } authenticatePrivateRepo() { - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${PRIVATE_PULL_THROUGH_HOST} + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${PRIVATE_HOST} } buildImages() { - CONTROLLER_IMG=$(GOFLAGS=${GOFLAGS} KO_DOCKER_REPO=${RELEASE_REPO_ECR} ko publish -B -t "${RELEASE_VERSION}" "${RELEASE_PLATFORM}" ./cmd/controller) + RELEASE_REPO=$1 + # Set the SOURCE_DATE_EPOCH and KO_DATA_DATE_EPOCH values for reproducable builds with timestamps + # https://ko.build/advanced/faq/ + CONTROLLER_IMG=$(GOFLAGS=${GOFLAGS} SOURCE_DATE_EPOCH=$(git log -1 --format='%ct') KO_DATA_DATE_EPOCH=$(git log -1 --format='%ct') KO_DOCKER_REPO=${RELEASE_REPO} ko publish -B -t "${RELEASE_VERSION}" "${RELEASE_PLATFORM}" ./cmd/controller) HELM_CHART_VERSION=$(helmChartVersion "$RELEASE_VERSION") IMG_REPOSITORY=$(echo "$CONTROLLER_IMG" | cut -d "@" -f 1 | cut -d ":" -f 1) IMG_TAG=$(echo "$CONTROLLER_IMG" | cut -d "@" -f 1 | cut -d ":" -f 2 -s) @@ -85,23 +98,21 @@ releaseType(){ helmChartVersion(){ RELEASE_VERSION=$1 - if [[ $(releaseType $RELEASE_VERSION) == $RELEASE_TYPE_STABLE ]]; then - echo $RELEASE_VERSION + if [[ $(releaseType "$RELEASE_VERSION") == "$RELEASE_TYPE_STABLE" ]]; then + echo "$RELEASE_VERSION" fi - if [[ $(releaseType $RELEASE_VERSION) == $RELEASE_TYPE_SNAPSHOT ]]; then + if [[ $(releaseType "$RELEASE_VERSION") == "$RELEASE_TYPE_SNAPSHOT" ]]; then echo "v${CURRENT_MAJOR_VERSION}-${RELEASE_VERSION}" fi } buildDate(){ - # TODO restore https://reproducible-builds.org/docs/source-date-epoch/ + # Set the SOURCE_DATE_EPOCH and KO_DATA_DATE_EPOCH values for reproducable builds with timestamps + # https://ko.build/advanced/faq/ DATE_FMT="+%Y-%m-%dT%H:%M:%SZ" - if [ -z "${SOURCE_DATE_EPOCH-}" ]; then - echo $(date -u ${DATE_FMT}) - else - echo$(date -u -d "${SOURCE_DATE_EPOCH}" "${DATE_FMT}" 2>/dev/null || date -u -r "${SOURCE_DATE_EPOCH}" "$(DATE_FMT)" 2>/dev/null || date -u "$(DATE_FMT)") - fi + SOURCE_DATE_EPOCH=$(git log -1 --format='%ct') + echo "$(date -u -r "${SOURCE_DATE_EPOCH}" $DATE_FMT 2>/dev/null)" } cosignImages() { @@ -112,22 +123,10 @@ cosignImages() { "${CONTROLLER_IMG}" } -notifyRelease() { - RELEASE_VERSION=$1 - PR_NUMBER=$2 - RELEASE_TYPE=$(releaseType $RELEASE_VERSION) - LAST_STABLE_RELEASE_TAG=$(git describe --tags --abbrev=0) - MESSAGE="{\"releaseType\":\"${RELEASE_TYPE}\",\"releaseIdentifier\":\"${RELEASE_VERSION}\",\"prNumber\":\"${PR_NUMBER}\",\"githubAccount\":\"${GITHUB_ACCOUNT}\",\"lastStableReleaseTag\":\"${LAST_STABLE_RELEASE_TAG}\"}" - aws sns publish \ - --topic-arn ${SNS_TOPIC_ARN} \ - --message ${MESSAGE} \ - --no-cli-pager -} - pullPrivateReplica(){ authenticatePrivateRepo RELEASE_IDENTIFIER=$1 - PULL_THROUGH_CACHE_PATH="${PRIVATE_PULL_THROUGH_HOST}/ecr-public/${ECR_GALLERY_NAME}/" + PULL_THROUGH_CACHE_PATH="${PRIVATE_HOST}/ecr-public/${ECR_GALLERY_NAME}/" HELM_CHART_VERSION=$(helmChartVersion "$RELEASE_VERSION") docker pull "${PULL_THROUGH_CACHE_PATH}controller:${RELEASE_IDENTIFIER}" helm pull "oci://${PULL_THROUGH_CACHE_PATH}karpenter" --version "${HELM_CHART_VERSION}" @@ -138,13 +137,13 @@ publishHelmChart() { CHART_NAME=$1 RELEASE_VERSION=$2 RELEASE_REPO=$3 - HELM_CHART_VERSION=$(helmChartVersion $RELEASE_VERSION) + HELM_CHART_VERSION=$(helmChartVersion "$RELEASE_VERSION") HELM_CHART_FILE_NAME="${CHART_NAME}-${HELM_CHART_VERSION}.tgz" cd charts helm dependency update "${CHART_NAME}" helm lint "${CHART_NAME}" - helm package "${CHART_NAME}" --version $HELM_CHART_VERSION + helm package "${CHART_NAME}" --version "$HELM_CHART_VERSION" helm push "${HELM_CHART_FILE_NAME}" "oci://${RELEASE_REPO}" rm "${HELM_CHART_FILE_NAME}" cd .. @@ -152,38 +151,47 @@ publishHelmChart() { createNewWebsiteDirectory() { RELEASE_VERSION=$1 - mkdir -p website/content/en/${RELEASE_VERSION} - cp -r website/content/en/preview/* website/content/en/${RELEASE_VERSION}/ - find website/content/en/${RELEASE_VERSION}/ -type f | xargs perl -i -p -e "s/{{< param \"latest_release_version\" >}}/${RELEASE_VERSION}/g;" - find website/content/en/${RELEASE_VERSION}/*/*/*.yaml -type f | xargs perl -i -p -e "s/preview/${RELEASE_VERSION}/g;" -} + versionData "${RELEASE_VERSION}" -editWebsiteConfig() { - RELEASE_VERSION=$1 + mkdir -p "website/content/en/${RELEASE_MINOR_VERSION}" + cp -r website/content/en/preview/* "website/content/en/${RELEASE_MINOR_VERSION}/" + find "website/content/en/${RELEASE_MINOR_VERSION}/" -type f | xargs perl -i -p -e "s/{{< param \"latest_release_version\" >}}/${RELEASE_VERSION}/g;" + find website/content/en/${RELEASE_MINOR_VERSION}/*/*/*.yaml -type f | xargs perl -i -p -e "s/preview/${RELEASE_MINOR_VERSION}/g;" + find "website/content/en/${RELEASE_MINOR_VERSION}/" -type f | xargs perl -i -p -e "s/{{< githubRelRef >}}/\/${RELEASE_VERSION}\//g;" - # sed has a different syntax on mac - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' '/^\/docs\/\*/d' website/static/_redirects - else - sed -i '/^\/docs\/\*/d' website/static/_redirects - fi + rm -rf website/content/en/docs + mkdir -p website/content/en/docs + cp -r website/content/en/${RELEASE_MINOR_VERSION}/* website/content/en/docs/ +} - echo "/docs/* /${RELEASE_VERSION}/:splat" >>website/static/_redirects +removeOldWebsiteDirectories() { + local n=5 + # Get all the directories except the last n directories sorted from earliest to latest version + last_n_versions=$(find website/content/en/* -type d -name "*" -maxdepth 0 | grep -v "preview\|docs" | sort | tail -n "$n") + last_n_versions+=$(echo -e "\nwebsite/content/en/preview") + last_n_versions+=$(echo -e "\nwebsite/content/en/docs") + all=$(find website/content/en/* -type d -name "*" -maxdepth 0) + ## symmetric difference + comm -3 <(sort <<< $last_n_versions) <(sort <<< $all) | tr -d '\t' | xargs -r -n 1 rm -r +} +editWebsiteConfig() { + RELEASE_VERSION=$1 yq -i ".params.latest_release_version = \"${RELEASE_VERSION}\"" website/config.yaml - yq -i ".menu.main[] |=select(.name == \"Docs\") .url = \"${RELEASE_VERSION}\"" website/config.yaml } # editWebsiteVersionsMenu sets relevant releases in the version dropdown menu of the website # without increasing the size of the set. -# TODO: We need to maintain a list of latest minors here only. This is not currently -# easy to achieve, however when we have a major release and we decide to maintain -# a selected minor releases we can maintain that list in the repo and use it in here editWebsiteVersionsMenu() { RELEASE_VERSION=$1 - VERSIONS=(${RELEASE_VERSION}) + versionData "${RELEASE_VERSION}" + VERSIONS=("${RELEASE_MINOR_VERSION}") while IFS= read -r LINE; do SANITIZED_VERSION=$(echo "${LINE}" | sed -e 's/["-]//g' -e 's/ *//g') + # Bail from this config.yaml update if the version is already in the existing list + if [[ "${RELEASE_MINOR_VERSION}" == "${SANITIZED_VERSION}" ]]; then + return + fi VERSIONS+=("${SANITIZED_VERSION}") done < <(yq '.params.versions' website/config.yaml) unset VERSIONS[${#VERSIONS[@]}-2] diff --git a/hack/release/release.sh b/hack/release/release.sh index fc2a4ad028d4..2775e026fb4c 100755 --- a/hack/release/release.sh +++ b/hack/release/release.sh @@ -2,18 +2,19 @@ set -euo pipefail GIT_TAG=$(git describe --exact-match --tags || echo "no tag") +if [[ "$GIT_TAG" == "no tag" ]]; then + echo "Failed to release: commit is untagged" + exit 1 +fi HEAD_HASH=$(git rev-parse HEAD) SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) source "${SCRIPT_DIR}/common.sh" - config -release $HEAD_HASH #release a snapshot version -## Reset changes if it's a snapshot since we don't need to track these changes -## and results in a -dirty commit hash for a stable release -git reset --hard -if [[ $(releaseType $GIT_TAG) == $RELEASE_TYPE_STABLE ]]; then - release $GIT_TAG +# Don't release with a dirty commit! +if [[ "$(git status --porcelain)" != "" ]]; then + exit 1 fi +release "$GIT_TAG"