From 0ad1d6398d497bf3267d9089be50e1f36206e588 Mon Sep 17 00:00:00 2001 From: Alexander Berezovsky <aberezovsky@vmware.com> Date: Wed, 1 Jun 2022 21:02:01 +0000 Subject: [PATCH] Add GHA capability to release Apple ARM Support other CLI major versions Major Build-Sign-Upload workflow refactor Co-authored-by: Shwetha Gururaj <gururajsh@vmware.com> Co-authored-by: Juan Diego Gonzalez <gojuan@vmware.com> --- .github/workflows/build-sign-upload.yml | 714 ++++++++++++++---------- .github/workflows/update-repos.yml | 253 +++++---- Makefile | 4 + bin/generate-release-notes | 16 +- 4 files changed, 574 insertions(+), 413 deletions(-) diff --git a/.github/workflows/build-sign-upload.yml b/.github/workflows/build-sign-upload.yml index 7b3d5d13df2..f68fed291a4 100644 --- a/.github/workflows/build-sign-upload.yml +++ b/.github/workflows/build-sign-upload.yml @@ -1,12 +1,45 @@ name: Build-Sign-Upload +# List of env variables + +# GitHub Actions specific +# +# ACTIONS_RUNNER_DEBUG +# ACTIONS_STEP_DEBUG +# +# AWS credentials +# To upload artifacts to S3 for CLAW +# +# AWS_ACCESS_KEY_ID +# AWS_REGION +# AWS_SECRET_ACCESS_KEY +# +# GitHub details +# To publish release draft +# +# GIT_DEPLOY_HOMEBREW_TAP +# GIT_RELEASE_TARGET_REPO +# GIT_REPO_ACCESS_TOKEN +# +# Signing keys +# +# SIGNING_KEY_GPG +# SIGNING_KEY_GPG_ID +# SIGNING_KEY_GPG_PASSPHRASE +# +# SIGNING_KEY_MAC_ID +# SIGNING_KEY_MAC_PASSPHRASE +# SIGNING_KEY_MAC_PFX +# +# SIGNING_KEY_WINDOWS_ID +# SIGNING_KEY_WINDOWS_PASSPHRASE +# SIGNING_KEY_WINDOWS_PFX + + on: push: tags: - 'v7.*' - - 'v8.*' - branches: - - 'v7-gh-actions' permissions: contents: read @@ -18,149 +51,83 @@ defaults: shell: bash jobs: - setup: - name: Setup - runs-on: ubuntu-latest - if: ${{ github.action_repository != 'cloudfoundry/cli' }} - outputs: - secrets-environment: ${{ steps.set-secrets-environment.outputs.secrets-environment }} - build-version: ${{ steps.set-build-version.outputs.build-version }} - go-version: ${{ steps.set-go-version.outputs.go-version }} - major-version: ${{ steps.set-major-version.outputs.major-version }} - steps: - # note the key must be 'id' and not 'name' here, to be accessed via the steps.<step> syntax above - - - name: Set environment - id: set-secrets-environment - run: echo "::set-output name=secrets-environment::PROD" - - - name: Checkout cli - uses: actions/checkout@v2 - - - name: Check if BUILD_VERSION matches tag ${{ github.ref }} - run: | - echo "Git Ref: ${{ github.ref }}" - echo "BUILD_VERSION: $(cat BUILD_VERSION)" - - exit 0 - - - name: Set build version - id: set-build-version - run: | - version=$(cat BUILD_VERSION) - echo "::set-output name=build-version::$version" - - - name: Set go version - id: set-go-version - run: echo "::set-output name=go-version::1.17" - - - name: Set Major version - id: set-major-version - run: | - major=$(cat BUILD_VERSION | cut -c 1-1) - echo "::set-output name=major-version::$major" - # This is for debugging. It's equivalent to fly intecept + # test: + # environment: DEV + # runs-on: ubuntu-latest + # steps: # - name: Setup upterm session # env: - # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + # AWS_REGION: ${{ secrets.AWS_REGION }} + # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # GIT_RELEASE_TARGET_REPO: ${{ secrets.GIT_RELEASE_TARGET_REPO }} + # GIT_REPO_ACCESS_TOKEN: ${{ secrets.GIT_REPO_ACCESS_TOKEN }} + # SIGNING_KEY_GPG: ${{ secrets.SIGNING_KEY_GPG }} + # SIGNING_KEY_GPG_ID: ${{ secrets.SIGNING_KEY_GPG_ID }} + # SIGNING_KEY_GPG_PASSPHRASE: ${{ secrets.SIGNING_KEY_GPG_PASSPHRASE }} + # SIGNING_KEY_MAC_ID: ${{ secrets.SIGNING_KEY_MAC_ID }} + # SIGNING_KEY_MAC_PASSPHRASE: ${{ secrets.SIGNING_KEY_MAC_PASSPHRASE }} + # SIGNING_KEY_MAC_PFX: ${{ secrets.SIGNING_KEY_MAC_PFX }} + # SIGNING_KEY_WINDOWS_ID: ${{ secrets.SIGNING_KEY_WINDOWS_ID }} + # SIGNING_KEY_WINDOWS_PASSPHRASE: ${{ secrets.SIGNING_KEY_WINDOWS_PASSPHRASE }} + # SIGNING_KEY_WINDOWS_PFX: ${{ secrets.SIGNING_KEY_WINDOWS_PFX }} + # SIGNING_TEST_CA_MAC: ${{ secrets.SIGNING_TEST_CA_MAC }} # if: always() # uses: lhotari/action-upterm@v1 # timeout-minutes: 60 - test-rpm-package: - name: Test RPM Artifacts - needs: build-linux - runs-on: ubuntu-latest - container: - image: fedora - steps: - - - name: Download Signed Linux Packages - uses: actions/download-artifact@v2 - with: - name: cf-cli-linux-rpm-packages - - - name: Display structure of downloaded files - run: ls -R - - - name: Test RPMs - run: | - rpm -q --qf 'FN:\t%{FILENAMES}\nNAME:\t%{NAME}\nPGP:\t%{SIGPGP:pgpsig}\nGPG:\t%{SIGGPG:pgpsig}\n' -p *.rpm - - test-deb-package: - name: Test Debian Artifacts - needs: build-linux + setup: + name: Setup + # needs: test runs-on: ubuntu-latest - container: - image: ubuntu - steps: + if: ${{ github.action_repository != 'cloudfoundry/cli' }} - - name: Download Signed Linux Packages - uses: actions/download-artifact@v2 - with: - name: cf-cli-linux-deb-packages + outputs: + secrets-environment: ${{ steps.set-secrets-environment.outputs.secrets-environment }} + go-version: ${{ steps.set-go-version.outputs.go-version }} + #TODO: DEV SHIM + # aws-s3-bucket: cf-cli-dev + aws-s3-bucket: "v${{ steps.parse-semver.outputs.version-major }}-cf-cli-releases" - - name: Display structure of downloaded files - run: | - ls -R - ls *.deb | xargs -n1 dpkg --info + version-build: ${{ steps.parse-semver.outputs.version-build }} + version-major: ${{ steps.parse-semver.outputs.version-major }} + version-minor: ${{ steps.parse-semver.outputs.version-minor }} + version-patch: ${{ steps.parse-semver.outputs.version-patch }} - test-macos: - name: Test macOS Artifacts - needs: build-macos - runs-on: macos-latest steps: + - id: set-secrets-environment + #TODO: DEV SHIM + # run: echo "::set-output name=secrets-environment::DEV" + run: echo "::set-output name=secrets-environment::PROD" - - name: Download Signed macOS Packages - uses: actions/download-artifact@v2 - with: - name: cf-cli-macos-packages + - name: Checkout cli + uses: actions/checkout@v2 - - name: Inspect macOS packages + - name: Check if VERSION_BUILD matches tag ${{ github.ref }} run: | - ls -R - pkgutil --check-signature * - - test-windows: - name: Test Windows Artifacts - needs: - - setup - - build-windows - runs-on: windows-latest - defaults: - run: - shell: pwsh - steps: + echo "Git Ref: ${{ github.ref }}" + echo "VERSION_BUILD: $(cat BUILD_VERSION)" - - name: Download Signed Windows Binaries - uses: actions/download-artifact@v2 - with: - name: cf-cli-windows-binaries + exit 0 - - name: Inspect Windows packages + - name: Parse semver + id: parse-semver run: | - Get-AuthenticodeSignature -Verbose -ErrorAction Stop .\cf-cli_win32.exe - Get-AuthenticodeSignature -Verbose -ErrorAction Stop .\cf-cli_winx64.exe - - - name: Download Signed Windows Binaries - uses: actions/download-artifact@v2 - with: - name: cf-cli-windows-packages + VERSION=$(cat BUILD_VERSION) + VERSION="${VERSION#[vV]}" - - name: Inspect Windows files - run: | - Get-ChildItem -Force + VERSION_MINOR="${VERSION#*.}" + VERSION_MINOR="${VERSION_MINOR%.*}" - - name: View installer signature - run: | - Expand-Archive -DestinationPath winx64 -Path "${env:CF_VERSION}-cli-installer_*_winx64.zip" - Expand-Archive -DestinationPath win32 -Path "${env:CF_VERSION}-cli-installer_*_win32.zip" + echo "::set-output name=version-build::${VERSION}" + echo "::set-output name=version-major::${VERSION%%\.*}" + echo "::set-output name=version-minor::${VERSION_MINOR}" + echo "::set-output name=version-patch::${VERSION##*.}" - Get-AuthenticodeSignature -Verbose -ErrorAction Stop ".\winx64\${env:CF_VERSION}_installer.exe" - Get-AuthenticodeSignature -Verbose -ErrorAction Stop ".\win32\${env:CF_VERSION}_installer.exe" - env: - CF_VERSION: cf${{ needs.setup.outputs.major-version }} + - name: Set go version + id: set-go-version + run: echo "::set-output name=go-version::1.17" build-linux: name: Build Linux @@ -170,14 +137,14 @@ jobs: environment: ${{ needs.setup.outputs.secrets-environment }} env: - MAJOR_VERSION: ${{ needs.setup.outputs.major-version }} - BUILD_VERSION: ${{ needs.setup.outputs.build-version }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Get Build Version id: get_build_version - run: echo "BUILD_VERSION $BUILD_VERSION" + run: echo "VERSION_BUILD $VERSION_BUILD" - name: Checkout cli uses: actions/checkout@v2 @@ -238,22 +205,21 @@ jobs: $SIGNING_KEY_GPG_ID EOF - CF_VERSION=cf${MAJOR_VERSION} - RPM_VERSION=${BUILD_VERSION//-/_} + RPM_VERSION=${VERSION_BUILD//-/_} mkdir -pv $root/packaged echo "Build 32-bit RedHat package" ( pushd cli-ci/ci/installers/rpm - cp $root/out/cf-cli_linux_i686 ${CF_VERSION} - cp ../../license/NOTICE . - cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES LICENSE - cp ../completion/${CF_VERSION} ${CF_VERSION}.bash - echo "Version: ${RPM_VERSION}" > cf-cli.spec - cat ${CF_VERSION}-cli.spec.template >> cf-cli.spec - rpmbuild --target i386 --define "_topdir $(pwd)/build" -bb cf-cli.spec - mv build/RPMS/i386/${CF_VERSION}-cli*.rpm $root/packaged/${CF_VERSION}-cli-installer_${BUILD_VERSION}_i686.rpm + cp $root/out/cf-cli_linux_i686 cf${VERSION_MAJOR} + cp ../../license/NOTICE . + cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES LICENSE + cp ../completion/cf${VERSION_MAJOR} cf${VERSION_MAJOR}.bash + echo "Version: ${RPM_VERSION}" > cf-cli.spec + cat cf${VERSION_MAJOR}-cli.spec.template >> cf-cli.spec + rpmbuild --target i386 --define "_topdir $(pwd)/build" -bb cf-cli.spec + mv build/RPMS/i386/cf${VERSION_MAJOR}-cli*.rpm $root/packaged/cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_i686.rpm popd ) @@ -261,14 +227,14 @@ jobs: echo "Build 64-bit RedHat package" ( pushd cli-ci/ci/installers/rpm - cp $root/out/cf-cli_linux_x86-64 ${CF_VERSION} - cp ../../license/NOTICE . - cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES LICENSE - cp ../completion/${CF_VERSION} ${CF_VERSION}.bash - echo "Version: ${RPM_VERSION}" > cf-cli.spec - cat ${CF_VERSION}-cli.spec.template >> cf-cli.spec - rpmbuild --target x86_64 --define "_topdir $(pwd)/build" -bb cf-cli.spec - mv build/RPMS/x86_64/${CF_VERSION}-cli*.rpm $root/packaged/${CF_VERSION}-cli-installer_${BUILD_VERSION}_x86-64.rpm + cp $root/out/cf-cli_linux_x86-64 cf${VERSION_MAJOR} + cp ../../license/NOTICE . + cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES LICENSE + cp ../completion/cf${VERSION_MAJOR} cf${VERSION_MAJOR}.bash + echo "Version: ${RPM_VERSION}" > cf-cli.spec + cat cf${VERSION_MAJOR}-cli.spec.template >> cf-cli.spec + rpmbuild --target x86_64 --define "_topdir $(pwd)/build" -bb cf-cli.spec + mv build/RPMS/x86_64/cf${VERSION_MAJOR}-cli*.rpm $root/packaged/cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_x86-64.rpm popd ) @@ -284,23 +250,27 @@ jobs: - name: Sign RedHat Packages env: - SIGNING_KEY_GPG_ID: ${{ secrets.SIGNING_KEY_GPG_ID }} + SIGNING_KEY_GPG_ID: ${{ secrets.SIGNING_KEY_GPG_ID }} SIGNING_KEY_GPG_PASSPHRASE: ${{ secrets.SIGNING_KEY_GPG_PASSPHRASE }} run: | set -ex set -o pipefail - SUFFIX=8 - mkdir signed-redhat-installer cat<< EOF >~/.rpmmacros + %_signature gpg %_gpg_name $SIGNING_KEY_GPG_ID + %_gpgbin /usr/bin/gpg2 + %__gpg_sign_cmd %{__gpg} gpg --force-v3-sigs --batch --verbose --no-armor \ + --passphrase "$SIGNING_KEY_GPG_PASSPHRASE" --no-secmem-warning -u "%{_gpg_name}" \ + -sbo %{__signature_filename} --digest-algo sha256 %{__plaintext_filename} EOF cp packaged/cf*.rpm signed-redhat-installer/ - # TODO: consider to add --key-id + #TODO: consider to add --key-id + #TODO: DEV shim rpmsign --addsign signed-redhat-installer/*.rpm - name: Print RPM Signature @@ -316,12 +286,15 @@ jobs: - name: Build Debian Packages + env: + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} run: | set -ex set -o pipefail root=$PWD - CF_VERSION=cf${MAJOR_VERSION} + mkdir -pv $root/packaged-deb echo "Build 32-bit Debian package" @@ -329,30 +302,30 @@ jobs: SIZE="$(BLOCKSIZE=1000 du $root/out/cf-cli_linux_i686 | cut -f 1)" pushd cli-ci/ci/installers/deb - mkdir -p cf/usr/bin cf/usr/share/doc/${CF_VERSION}-cli/ cf/DEBIAN cf/usr/share/bash-completion/completions + mkdir -p cf/usr/bin cf/usr/share/doc/cf${VERSION_MAJOR}-cli/ cf/DEBIAN cf/usr/share/bash-completion/completions cp copyright_preamble cf/DEBIAN/copyright sed 's/^$/ ./' $root/LICENSE >> cf/DEBIAN/copyright cat copyright_comment_header >> cf/DEBIAN/copyright sed 's/^$/ ./' ../../license/3RD-PARTY-LICENSES >> cf/DEBIAN/copyright - cp cf/DEBIAN/copyright cf/usr/share/doc/${CF_VERSION}-cli/copyright + cp cf/DEBIAN/copyright cf/usr/share/doc/cf${VERSION_MAJOR}-cli/copyright - cp ../../license/NOTICE cf/usr/share/doc/${CF_VERSION}-cli - cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf/usr/share/doc/${CF_VERSION}-cli/LICENSE + cp ../../license/NOTICE cf/usr/share/doc/cf${VERSION_MAJOR}-cli + cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf/usr/share/doc/cf${VERSION_MAJOR}-cli/LICENSE - cp control_v${MAJOR_VERSION}.template cf/DEBIAN/control + cp control_v${VERSION_MAJOR}.template cf/DEBIAN/control echo "Installed-Size: ${SIZE}" >> cf/DEBIAN/control - echo "Version: ${BUILD_VERSION}" >> cf/DEBIAN/control + echo "Version: ${VERSION_BUILD}" >> cf/DEBIAN/control echo "Architecture: i386" >> cf/DEBIAN/control - cp ../completion/${CF_VERSION} cf/usr/share/bash-completion/completions/${CF_VERSION} + cp ../completion/cf${VERSION_MAJOR} cf/usr/share/bash-completion/completions/cf${VERSION_MAJOR} - cp $root/out/cf-cli_linux_i686 cf/usr/bin/${CF_VERSION} - ln -frs cf/usr/bin/${CF_VERSION} cf/usr/bin/cf + cp $root/out/cf-cli_linux_i686 cf/usr/bin/cf${VERSION_MAJOR} + ln -frs cf/usr/bin/cf${VERSION_MAJOR} cf/usr/bin/cf - fakeroot dpkg --build cf ${CF_VERSION}-cli-installer_${BUILD_VERSION}_i686.deb - mv ${CF_VERSION}-cli-installer_${BUILD_VERSION}_i686.deb $root/packaged-deb + fakeroot dpkg --build cf cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_i686.deb + mv cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_i686.deb $root/packaged-deb rm -rf cf popd ) @@ -362,30 +335,30 @@ jobs: SIZE="$(BLOCKSIZE=1000 du $root/out/cf-cli_linux_x86-64 | cut -f 1)" pushd cli-ci/ci/installers/deb - mkdir -p cf/usr/bin cf/usr/share/doc/${CF_VERSION}-cli/ cf/DEBIAN cf/usr/share/bash-completion/completions + mkdir -p cf/usr/bin cf/usr/share/doc/cf${VERSION_MAJOR}-cli/ cf/DEBIAN cf/usr/share/bash-completion/completions cp copyright_preamble cf/DEBIAN/copyright sed 's/^$/ ./' $root/LICENSE >> cf/DEBIAN/copyright cat copyright_comment_header >> cf/DEBIAN/copyright sed 's/^$/ ./' ../../license/3RD-PARTY-LICENSES >> cf/DEBIAN/copyright - cp cf/DEBIAN/copyright cf/usr/share/doc/${CF_VERSION}-cli/copyright + cp cf/DEBIAN/copyright cf/usr/share/doc/cf${VERSION_MAJOR}-cli/copyright - cp ../../license/NOTICE cf/usr/share/doc/${CF_VERSION}-cli - cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf/usr/share/doc/${CF_VERSION}-cli/LICENSE + cp ../../license/NOTICE cf/usr/share/doc/cf${VERSION_MAJOR}-cli + cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf/usr/share/doc/cf${VERSION_MAJOR}-cli/LICENSE - cp control_v${MAJOR_VERSION}.template cf/DEBIAN/control + cp control_v${VERSION_MAJOR}.template cf/DEBIAN/control echo "Installed-Size: ${SIZE}" >> cf/DEBIAN/control - echo "Version: ${BUILD_VERSION}" >> cf/DEBIAN/control + echo "Version: ${VERSION_BUILD}" >> cf/DEBIAN/control echo "Architecture: amd64" >> cf/DEBIAN/control - cp ../completion/${CF_VERSION} cf/usr/share/bash-completion/completions/${CF_VERSION} + cp ../completion/cf${VERSION_MAJOR} cf/usr/share/bash-completion/completions/cf${VERSION_MAJOR} - cp $root/out/cf-cli_linux_x86-64 cf/usr/bin/${CF_VERSION} - ln -frs cf/usr/bin/${CF_VERSION} cf/usr/bin/cf + cp $root/out/cf-cli_linux_x86-64 cf/usr/bin/cf${VERSION_MAJOR} + ln -frs cf/usr/bin/cf${VERSION_MAJOR} cf/usr/bin/cf - fakeroot dpkg --build cf ${CF_VERSION}-cli-installer_${BUILD_VERSION}_x86-64.deb - mv ${CF_VERSION}-cli-installer_${BUILD_VERSION}_x86-64.deb $root/packaged-deb + fakeroot dpkg --build cf cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_x86-64.deb + mv cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_x86-64.deb $root/packaged-deb popd ) @@ -409,19 +382,16 @@ jobs: name: Build macOS needs: - setup + env: + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} runs-on: macos-latest environment: ${{ needs.setup.outputs.secrets-environment }} - env: - MAJOR_VERSION: ${{ needs.setup.outputs.major-version }} steps: - - name: Setup - run: | - echo "BUILD_VERSION=${{ needs.setup.outputs.build-version }}" >> $GITHUB_ENV - - name: Get Build Version id: get_build_version - run: echo "BUILD_VERSION $BUILD_VERSION" + run: echo "VERSION_BUILD $VERSION_BUILD" - name: Checkout cli uses: actions/checkout@v2 @@ -488,7 +458,7 @@ jobs: - name: Build macOS Binaries run: | make out/cf-cli_osx - make out/cf-cli_osx_arm + make out/cf-cli_macosarm - name: Store macOS Binaries uses: actions/upload-artifact@v2 @@ -497,29 +467,63 @@ jobs: name: cf-cli-macos-binaries path: out/cf-cli* - - name: Build macOS Installer + - name: Build macOS x86 Installer run: | set -ex set -o pipefail root=$PWD - CF_VERSION=cf${MAJOR_VERSION} mkdir -pv $root/packaged echo "Building OS X installer" ( SIZE="$(BLOCKSIZE=1000 du $root/out/cf-cli_osx | cut -f 1)" - pushd cli-ci/ci/installers/osx_v${MAJOR_VERSION} - sed -i -e "s/VERSION/${BUILD_VERSION}/g" Distribution + pushd cli-ci/ci/installers/osx_v${VERSION_MAJOR} + sed -i -e "s/VERSION/${VERSION_BUILD}/g" Distribution + sed -i -e "s/SIZE/${SIZE}/g" Distribution + mkdir -p cf-cli/usr/local/bin cf-cli/usr/local/share/doc/cf${VERSION_MAJOR}-cli + + cp $root/out/cf-cli_osx cf-cli/usr/local/bin/cf${VERSION_MAJOR} + gln -frs cf-cli/usr/local/bin/cf${VERSION_MAJOR} cf-cli/usr/local/bin/cf + cp ../../license/NOTICE cf-cli/usr/local/share/doc/cf${VERSION_MAJOR}-cli + cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf-cli/usr/local/share/doc/cf${VERSION_MAJOR}-cli/LICENSE + chmod -R go-w cf-cli + pushd cf-cli + find usr | cpio -o --format=odc | gzip -c > ../Payload + popd + + $root/bomutils/build/bin/ls4mkbom cf-cli | sed 's/1000\/1000/0\/80/' > bom_list + mkbom -i bom_list Bom + mv Bom Payload com.cloudfoundry.cf${VERSION_MAJOR}-cli.pkg + xar -c --compression none -f cf${VERSION_MAJOR}-cli-installer_osx.pkg com.cloudfoundry.cf${VERSION_MAJOR}-cli.pkg Distribution + mv cf${VERSION_MAJOR}-cli-installer_osx.pkg $root/packaged/cf${VERSION_MAJOR}-cli-installer_osx.pkg + popd + ) + + - name: Build macOS ARM Installer + run: | + set -ex + set -o pipefail + + root=$PWD + + mkdir -pv $root/packaged + + echo "Build macOS ARM Installer" + ( + SIZE="$(BLOCKSIZE=1000 du $root/out/cf-cli_macosarm | cut -f 1)" + + pushd cli-ci/ci/installers/osx_v${VERSION_MAJOR} + sed -i -e "s/VERSION/${VERSION_BUILD}/g" Distribution sed -i -e "s/SIZE/${SIZE}/g" Distribution - mkdir -p cf-cli/usr/local/bin cf-cli/usr/local/share/doc/${CF_VERSION}-cli + mkdir -p cf-cli/usr/local/bin cf-cli/usr/local/share/doc/cf${VERSION_MAJOR}-cli - cp $root/out/cf-cli_osx cf-cli/usr/local/bin/${CF_VERSION} - gln -frs cf-cli/usr/local/bin/${CF_VERSION} cf-cli/usr/local/bin/cf - cp ../../license/NOTICE cf-cli/usr/local/share/doc/${CF_VERSION}-cli - cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf-cli/usr/local/share/doc/${CF_VERSION}-cli/LICENSE + cp $root/out/cf-cli_macosarm cf-cli/usr/local/bin/cf${VERSION_MAJOR} + gln -frs cf-cli/usr/local/bin/cf${VERSION_MAJOR} cf-cli/usr/local/bin/cf + cp ../../license/NOTICE cf-cli/usr/local/share/doc/cf${VERSION_MAJOR}-cli + cp ../../license/LICENSE-WITH-3RD-PARTY-LICENSES cf-cli/usr/local/share/doc/cf${VERSION_MAJOR}-cli/LICENSE chmod -R go-w cf-cli pushd cf-cli find usr | cpio -o --format=odc | gzip -c > ../Payload @@ -527,21 +531,18 @@ jobs: $root/bomutils/build/bin/ls4mkbom cf-cli | sed 's/1000\/1000/0\/80/' > bom_list mkbom -i bom_list Bom - mv Bom Payload com.cloudfoundry.${CF_VERSION}-cli.pkg - xar -c --compression none -f ${CF_VERSION}-cli-installer_osx.pkg com.cloudfoundry.${CF_VERSION}-cli.pkg Distribution - mv ${CF_VERSION}-cli-installer_osx.pkg $root/packaged/${CF_VERSION}-cli-installer_osx.pkg + mv Bom Payload com.cloudfoundry.cf${VERSION_MAJOR}-cli.pkg + xar -c --compression none -f cf${VERSION_MAJOR}-cli-installer_macosarm.pkg com.cloudfoundry.cf${VERSION_MAJOR}-cli.pkg Distribution + mv cf${VERSION_MAJOR}-cli-installer_macosarm.pkg $root/packaged/cf${VERSION_MAJOR}-cli-installer_macosarm.pkg popd ) - name: Load macos key env: - SIGNING_TEST_CA_MAC: ${{ secrets.SIGNING_TEST_CA_MAC }} - SIGNING_KEYCHAIN_PASSPHRASE: ${{ secrets.SIGNING_KEYCHAIN_PASSPHRASE }} - - SIGNING_KEY_MAC: ${{ secrets.SIGNING_KEY_MAC }} - SIGNING_KEY_MAC_ID: ${{ secrets.SIGNING_KEY_MAC_ID }} + # SIGNING_TEST_CA_MAC: ${{ secrets.SIGNING_TEST_CA_MAC }} + SIGNING_KEY_MAC_ID: ${{ secrets.SIGNING_KEY_MAC_ID }} SIGNING_KEY_MAC_PASSPHRASE: ${{ secrets.SIGNING_KEY_MAC_PASSPHRASE }} - SIGNING_KEY_MAC_PFX: ${{ secrets.SIGNING_KEY_MAC_PFX }} + SIGNING_KEY_MAC_PFX: ${{ secrets.SIGNING_KEY_MAC_PFX }} run: | echo -n "$SIGNING_KEY_MAC_PFX" | base64 --decode > mac-signing-key.p12 @@ -576,26 +577,35 @@ jobs: - name: Sign macOS env: - MAJOR_VERSION: ${{ needs.setup.outputs.major-version }} - SIGNING_KEY_MAC_ID: ${{ secrets.SIGNING_KEY_MAC_ID }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} + SIGNING_KEY_MAC_ID: ${{ secrets.SIGNING_KEY_MAC_ID }} SIGNING_KEY_MAC_PASSPHRASE: ${{ secrets.SIGNING_KEY_MAC_PASSPHRASE }} run: | root=$PWD - mkdir -pv signed-osx-installer + mkdir -pv signed-macos-installer + #TODO: DEV shim + # cp \ productsign --timestamp \ --sign "$SIGNING_KEY_MAC_ID" \ - "$root/packaged/cf${MAJOR_VERSION}-cli-installer_osx.pkg" \ - "signed-osx-installer/cf${MAJOR_VERSION}-cli-installer_${BUILD_VERSION}_osx.pkg" + "$root/packaged/cf${VERSION_MAJOR}-cli-installer_osx.pkg" \ + "signed-macos-installer/cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_osx.pkg" + + #TODO: DEV shim + # cp \ + productsign --timestamp \ + --sign "$SIGNING_KEY_MAC_ID" \ + "$root/packaged/cf${VERSION_MAJOR}-cli-installer_macosarm.pkg" \ + "signed-macos-installer/cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_macosarm.pkg" - name: Store macOS Signed Packages uses: actions/upload-artifact@v2 with: if-no-files-found: error name: cf-cli-macos-packages - path: signed-osx-installer/*.pkg + path: signed-macos-installer/*.pkg build-windows: @@ -604,12 +614,12 @@ jobs: - setup runs-on: windows-2019 environment: ${{ needs.setup.outputs.secrets-environment }} - env: - MAJOR_VERSION: ${{ needs.setup.outputs.major-version }} - CF_VERSION: cf${MAJOR_VERSION} defaults: run: shell: pwsh + env: + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Checkout uses: actions/checkout@v2 @@ -633,20 +643,20 @@ jobs: make out/cf-cli_winx64.exe - name: write windows cert - run: | - $pass = convertto-securestring -string "$env:SIGNING_KEY_WINDOWS_PASSPHRASE" -asplaintext - [convert]::frombase64string($env:SIGNING_KEY_WINDOWS_PFX) | set-content -path $env:runner_temp\cert.pfx -asbytestream - env: - SIGNING_KEY_WINDOWS_PFX: ${{ secrets.SIGNING_KEY_WINDOWS_PFX }} SIGNING_KEY_WINDOWS_PASSPHRASE: ${{ secrets.SIGNING_KEY_WINDOWS_PASSPHRASE }} + SIGNING_KEY_WINDOWS_PFX: ${{ secrets.SIGNING_KEY_WINDOWS_PFX }} + run: | + $pass = convertto-securestring -string "${env:SIGNING_KEY_WINDOWS_PASSPHRASE}" -asplaintext + [convert]::frombase64string(${env:SIGNING_KEY_WINDOWS_PFX}) | set-content -path $env:runner_temp\cert.pfx -asbytestream + - name: Sign windows binaries + env: + SIGNING_KEY_WINDOWS_PASSPHRASE: ${{ secrets.SIGNING_KEY_WINDOWS_PASSPHRASE }} run: | .\.github\win\sign-windows-binary.ps1 -BinaryFilePath out\cf-cli_win32.exe .\.github\win\sign-windows-binary.ps1 -BinaryFilePath out\cf-cli_winx64.exe - env: - SIGNING_KEY_WINDOWS_PASSPHRASE: ${{ secrets.SIGNING_KEY_WINDOWS_PASSPHRASE }} - name: View binary signatures run: | @@ -688,56 +698,52 @@ jobs: - name: Run innosetup run: | - mkdir "$env:RUNNER_TEMP\winx64" - & .\.github\win\run-innosetup.ps1 -InnoSetupConfig ".github\win\windows-installer-v${env:MAJOR_VERSION}-x64.iss" -CfBinary "out\cf-cli_winx64.exe" -InstallerOutput "$env:RUNNER_TEMP\winx64\${env:CF_VERSION}_installer.exe" - mkdir "$env:RUNNER_TEMP\win32" - & .\.github\win\run-innosetup.ps1 -InnoSetupConfig ".github\win\windows-installer-v${env:MAJOR_VERSION}-x86.iss" -CfBinary "out\cf-cli_win32.exe" -InstallerOutput "$env:RUNNER_TEMP\win32\${env:CF_VERSION}_installer.exe" - env: - MAJOR_VERSION: ${{ needs.setup.outputs.major-version }} - CF_VERSION: cf${{ needs.setup.outputs.major-version }} + mkdir "${env:RUNNER_TEMP}\winx64" + .\.github\win\run-innosetup.ps1 -InnoSetupConfig ".github\win\windows-installer-v${env:VERSION_MAJOR}-x64.iss" -CfBinary "out\cf-cli_winx64.exe" -InstallerOutput "${env:RUNNER_TEMP}\winx64\cf${env:VERSION_MAJOR}_installer.exe" + mkdir "${env:RUNNER_TEMP}\win32" + .\.github\win\run-innosetup.ps1 -InnoSetupConfig ".github\win\windows-installer-v${env:VERSION_MAJOR}-x86.iss" -CfBinary "out\cf-cli_win32.exe" -InstallerOutput "${env:RUNNER_TEMP}\win32\cf${env:VERSION_MAJOR}_installer.exe" + - name: Sign windows installer - run: | - .\.github\win\sign-windows-binary.ps1 -BinaryFilePath "$env:RUNNER_TEMP\winx64\${env:CF_VERSION}_installer.exe" - .\.github\win\sign-windows-binary.ps1 -BinaryFilePath "$env:RUNNER_TEMP\win32\${env:CF_VERSION}_installer.exe" env: SIGNING_KEY_WINDOWS_PASSPHRASE: ${{ secrets.SIGNING_KEY_WINDOWS_PASSPHRASE }} - CF_VERSION: cf${{ needs.setup.outputs.major-version }} + run: | + .\.github\win\sign-windows-binary.ps1 -BinaryFilePath "${env:RUNNER_TEMP}\winx64\cf${env:VERSION_MAJOR}_installer.exe" + .\.github\win\sign-windows-binary.ps1 -BinaryFilePath "${env:RUNNER_TEMP}\win32\cf${env:VERSION_MAJOR}_installer.exe" - name: View installer signature run: | - Get-AuthenticodeSignature -Verbose -ErrorAction Stop "$env:RUNNER_TEMP\winx64\${env:CF_VERSION}_installer.exe" - Get-AuthenticodeSignature -Verbose -ErrorAction Stop "$env:RUNNER_TEMP\win32\${env:CF_VERSION}_installer.exe" - env: - CF_VERSION: cf${{ needs.setup.outputs.major-version }} + Get-AuthenticodeSignature -Verbose -ErrorAction Stop "${env:RUNNER_TEMP}\winx64\cf${env:VERSION_MAJOR}_installer.exe" + Get-AuthenticodeSignature -Verbose -ErrorAction Stop "${env:RUNNER_TEMP}\win32\cf${env:VERSION_MAJOR}_installer.exe" - name: Arrange files for upload # note the -Path flag takes comma-delimited args run: | - Copy-Item -Destination "$env:RUNNER_TEMP\winx64" -Path .github\win\LICENSE,.github\win\NOTICE - Copy-Item -Destination "$env:RUNNER_TEMP\win32" -Path .github\win\LICENSE,.github\win\NOTICE + Copy-Item -Destination "${env:RUNNER_TEMP}\winx64" -Path .github\win\LICENSE,.github\win\NOTICE + Copy-Item -Destination "${env:RUNNER_TEMP}\win32" -Path .github\win\LICENSE,.github\win\NOTICE - name: Zip windows artifact run: | # strip leading v to go from tag -> semver $installer_release_version="$(cat BUILD_VERSION)".Replace("v", "") - pushd "$env:RUNNER_TEMP\winx64" - $installer_zip_filename="$env:RUNNER_TEMP\${env:CF_VERSION}-cli-installer_${installer_release_version}_winx64.zip" + pushd "${env:RUNNER_TEMP}\winx64" + $installer_zip_filename="${env:RUNNER_TEMP}\cf${env:VERSION_MAJOR}-cli-installer_${installer_release_version}_winx64.zip" Compress-Archive -DestinationPath "$installer_zip_filename" -Path * popd - pushd "$env:RUNNER_TEMP\win32" - $installer_zip_filename="$env:RUNNER_TEMP\${env:CF_VERSION}-cli-installer_${installer_release_version}_win32.zip" + pushd "${env:RUNNER_TEMP}\win32" + $installer_zip_filename="${env:RUNNER_TEMP}\cf${env:VERSION_MAJOR}-cli-installer_${installer_release_version}_win32.zip" Compress-Archive -DestinationPath "$installer_zip_filename" -Path * popd - Get-ChildItem "$env:RUNNER_TEMP" - env: - CF_VERSION: cf${{ needs.setup.outputs.major-version }} + Get-ChildItem "${env:RUNNER_TEMP}" + + # - name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 - name: Save installer and dist files as a GitHub Action Artifact uses: actions/upload-artifact@v2 with: name: cf-cli-windows-packages if-no-files-found: error - path: ${{ runner.temp }}/cf${{ needs.setup.outputs.major-version }}-cli-installer*win*.zip + path: ${{ runner.temp }}/cf${{ needs.setup.outputs.version-major }}-cli-installer*win*.zip ################################# ######## Release Section ######## @@ -745,33 +751,41 @@ jobs: s3-upload: name: Upload Artifacts to S3 bucket - if: ${{ github.ref_type == 'tag' }} runs-on: ubuntu-latest needs: - - setup - - test-rpm-package - - test-deb-package - - test-macos - - test-windows - environment: DEV + - setup + - test-rpm-package + - test-deb-package + - test-macos + - test-windows + environment: ${{ needs.setup.outputs.secrets-environment }} permissions: actions: read contents: read + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_S3_BUCKET: ${{ needs.setup.outputs.aws-s3-bucket }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: + - name: Checkout cli uses: actions/checkout@v2 - - name: get semver version - # set environment var for subsequent steps. see: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable - run: | - INSTALLER_RELEASE_VERSION=$(cat BUILD_VERSION | tr -d v) - echo "INSTALLER_RELEASE_VERSION=${INSTALLER_RELEASE_VERSION}" >> $GITHUB_ENV + + # - name: get semver version + # # set environment var for subsequent steps. see: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + # run: | + # VERSION_BUILD=$(cat BUILD_VERSION | tr -d v) + # echo "VERSION_BUILD=${VERSION_BUILD}" >> $GITHUB_ENV - name: Download signed artifacts uses: actions/download-artifact@v2 with: path: signed # download all artifacts to 'signed/' - - name: Archive linux binaries for upload + - name: Archive nix binaries for upload run: | prepare_legal() { @@ -780,58 +794,62 @@ jobs: } prepare_artifacts() { - chmod +x ${CF_VERSION} - ln -s ${CF_VERSION} cf + chmod +x cf${VERSION_MAJOR} + ln -s cf${VERSION_MAJOR} cf prepare_legal } prepare_win_artifacts() { - cp ${CF_VERSION}.exe cf.exe + cp cf${VERSION_MAJOR}.exe cf.exe prepare_legal } pushd signed mkdir linux_i686 linux_x86-64 - mv cf-cli-linux-binaries/cf-cli_linux_i686 linux_i686/${CF_VERSION} - mv cf-cli-linux-binaries/cf-cli_linux_x86-64 linux_x86-64/${CF_VERSION} + mv cf-cli-linux-binaries/cf-cli_linux_i686 linux_i686/cf${VERSION_MAJOR} + mv cf-cli-linux-binaries/cf-cli_linux_x86-64 linux_x86-64/cf${VERSION_MAJOR} pushd linux_i686 prepare_artifacts - tar -cvzf ${CF_VERSION}-cli_${INSTALLER_RELEASE_VERSION}_linux_i686.tgz * + tar -cvzf cf${VERSION_MAJOR}-cli_${VERSION_BUILD}_linux_i686.tgz * popd pushd linux_x86-64 prepare_artifacts - tar -cvzf ${CF_VERSION}-cli_${INSTALLER_RELEASE_VERSION}_linux_x86-64.tgz * + tar -cvzf cf${VERSION_MAJOR}-cli_${VERSION_BUILD}_linux_x86-64.tgz * popd mkdir osx - mv cf-cli-macos-binaries/cf-cli_osx osx/${CF_VERSION} + mv cf-cli-macos-binaries/cf-cli_osx osx/cf${VERSION_MAJOR} pushd osx prepare_artifacts - tar -cvzf ${CF_VERSION}-cli_${INSTALLER_RELEASE_VERSION}_osx.tgz * + tar -cvzf cf${VERSION_MAJOR}-cli_${VERSION_BUILD}_osx.tgz * + popd + + mkdir macosarm + mv cf-cli-macos-binaries/cf-cli_macosarm macosarm/cf${VERSION_MAJOR} + pushd macosarm + prepare_artifacts + tar -cvzf cf${VERSION_MAJOR}-cli_${VERSION_BUILD}_macosarm.tgz * popd mkdir win32 winx64 - mv cf-cli-windows-binaries/cf-cli_win32.exe win32/${CF_VERSION}.exe - mv cf-cli-windows-binaries/cf-cli_winx64.exe winx64/${CF_VERSION}.exe + mv cf-cli-windows-binaries/cf-cli_win32.exe win32/cf${VERSION_MAJOR}.exe + mv cf-cli-windows-binaries/cf-cli_winx64.exe winx64/cf${VERSION_MAJOR}.exe pushd win32 prepare_win_artifacts # -y flag avoids the default behavior of derefencing the link, so we archive the symlink as-is - zip -y ${CF_VERSION}-cli_${INSTALLER_RELEASE_VERSION}_win32.zip * + zip -y cf${VERSION_MAJOR}-cli_${VERSION_BUILD}_win32.zip * popd pushd winx64 prepare_win_artifacts # -y flag avoids the default behavior of derefencing the link, so we archive the symlink as-is - zip -y ${CF_VERSION}-cli_${INSTALLER_RELEASE_VERSION}_winx64.zip * + zip -y cf${VERSION_MAJOR}-cli_${VERSION_BUILD}_winx64.zip * popd popd - env: - INSTALLER_RELEASE_VERSION: ${{ env.INSTALLER_RELEASE_VERSION }} - CF_VERSION: cf${{ needs.setup.outputs.major-version }} - name: Rearrange artifacts before upload run: | mkdir upload - cp -t upload \ + cp -v -t upload \ signed/cf-cli-linux-rpm-packages/cf*rpm \ signed/cf-cli-linux-deb-packages/cf*deb \ signed/cf-cli-macos-packages/cf*pkg \ @@ -839,51 +857,41 @@ jobs: signed/linux_i686/*tgz \ signed/linux_x86-64/*tgz \ signed/osx/*tgz \ + signed/macosarm/*tgz \ signed/win32/*zip \ signed/winx64/*zip - name: Upload installers to CLAW S3 bucket - if: ${{ github.ref_type == 'tag' }} # forked for security considerations uses: pivotalsoftware/s3-sync-action@v0.5.2 #pinned to no-default-region change env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} - SOURCE_DIR: upload - DEST_DIR: "releases/v${{ env.INSTALLER_RELEASE_VERSION }}" + DEST_DIR: "releases/v${{ env.VERSION_BUILD }}" + SOURCE_DIR: upload - name: list S3 for human verification uses: docker://amazon/aws-cli:latest with: - args: s3 ls "${{ env.AWS_S3_BUCKET }}/releases/v${{ env.INSTALLER_RELEASE_VERSION }}/" - env: - AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - INSTALLER_RELEASE_VERSION: ${{ env.INSTALLER_RELEASE_VERSION }} + args: s3 ls "${{ env.AWS_S3_BUCKET }}/releases/v${{ env.VERSION_BUILD }}/" - name: Instruct human to update CLAW run: | echo "Please go to https://github.com/cloudfoundry/CLAW/blob/develop/claw-variables.yml and add the following line to the file:" echo - echo "- ${INSTALLER_RELEASE_VERSION}" - - + echo "- ${VERSION_BUILD}" github-release-draft: name: Create GitHub Release Draft - if: ${{ github.ref_type == 'tag' }} runs-on: ubuntu-latest - environment: DEV permissions: actions: read contents: write needs: - - test-rpm-package - - test-deb-package - - test-macos - - test-windows + - setup + - test-rpm-package + - test-deb-package + - test-macos + - test-windows + environment: ${{ needs.setup.outputs.secrets-environment }} steps: - name: Download signed artifacts uses: actions/download-artifact@v2 @@ -893,18 +901,120 @@ jobs: - name: Create draft release uses: pivotalsoftware/action-gh-release@v1 with: - draft: true - repository: ${{ secrets.GIT_RELEASE_TARGET_REPO }} # repo to draft a release under, in <user>/<repo> format - token: ${{ secrets.GIT_REPO_ACCESS_TOKEN }} # only needed when pushing to a repo other than 'self' + draft: true + name: "DRAFT v${{ env.VERSION_BUILD }}" + # tag_name: "v${{ env.VERSION_BUILD }}" + repository: ${{ secrets.GIT_RELEASE_TARGET_REPO }} # repo to draft a release under, in <user>/<repo> format + token: ${{ secrets.GIT_REPO_ACCESS_TOKEN }} # only needed when pushing to a repo other than 'self' fail_on_unmatched_files: true - files: | - signed/cf-cli-linux-binaries/cf-cli* - signed/cf-cli-linux-deb-packages/cf*deb - signed/cf-cli-linux-rpm-packages/cf*rpm - signed/cf-cli-macos-binaries/cf-cli* - signed/cf-cli-macos-packages/cf*pkg - signed/cf-cli-windows-binaries/cf-cli*.exe - signed/cf-cli-windows-packages/*.zip + # files: | + # signed/cf-cli-linux-binaries/cf-cli* + # signed/cf-cli-linux-deb-packages/cf*deb + # signed/cf-cli-linux-rpm-packages/cf*rpm + # signed/cf-cli-macos-binaries/cf-cli* + # signed/cf-cli-macos-packages/cf*pkg + # signed/cf-cli-windows-binaries/cf-cli*.exe + # signed/cf-cli-windows-packages/*.zip + + test-rpm-package: + name: Test RPM Artifacts + needs: + - setup + - build-linux + runs-on: ubuntu-latest + container: + image: fedora + steps: + + - name: Download Signed Linux Packages + uses: actions/download-artifact@v2 + with: + name: cf-cli-linux-rpm-packages + + - name: Display structure of downloaded files + run: ls -R + + - name: Test RPMs + run: | + rpm -q --qf 'FN:\t%{FILENAMES}\nNAME:\t%{NAME}\nPGP:\t%{SIGPGP:pgpsig}\nGPG:\t%{SIGGPG:pgpsig}\n' -p *.rpm + + test-deb-package: + name: Test Debian Artifacts + needs: + - setup + - build-linux + runs-on: ubuntu-latest + container: + image: ubuntu + steps: + + - name: Download Signed Linux Packages + uses: actions/download-artifact@v2 + with: + name: cf-cli-linux-deb-packages + + - name: Display structure of downloaded files + run: | + ls -R + ls *.deb | xargs -n1 dpkg --info + + test-macos: + name: Test macOS Artifacts + needs: + - setup + - build-macos + runs-on: macos-latest + steps: + + - name: Download Signed macOS Packages + uses: actions/download-artifact@v2 + with: + name: cf-cli-macos-packages + + - name: Inspect macOS packages + run: | + ls -R + #TODO: DEV shim + pkgutil --check-signature * + + test-windows: + name: Test Windows Artifacts + needs: + - setup + - build-windows + runs-on: windows-latest + defaults: + run: + shell: pwsh + env: + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} + steps: + + - name: Download Signed Windows Binaries + uses: actions/download-artifact@v2 + with: + name: cf-cli-windows-binaries + + - name: Inspect Windows packages + run: | + Get-AuthenticodeSignature -Verbose -ErrorAction Stop .\cf-cli_win32.exe + Get-AuthenticodeSignature -Verbose -ErrorAction Stop .\cf-cli_winx64.exe + + - name: Download Signed Windows Binaries + uses: actions/download-artifact@v2 + with: + name: cf-cli-windows-packages + + - name: Inspect Windows files + run: | + Get-ChildItem -Force + + - name: View installer signature + run: | + Expand-Archive -DestinationPath winx64 -Path cf${env:VERSION_MAJOR}-cli-installer_*_winx64.zip + Expand-Archive -DestinationPath win32 -Path cf${env:VERSION_MAJOR}-cli-installer_*_win32.zip + Get-AuthenticodeSignature -Verbose -ErrorAction Stop ".\winx64\cf${env:VERSION_MAJOR}_installer.exe" + Get-AuthenticodeSignature -Verbose -ErrorAction Stop ".\win32\cf${env:VERSION_MAJOR}_installer.exe" # vim: set sw=2 ts=2 sts=2 et tw=78 foldlevel=2 fdm=indent nospell: diff --git a/.github/workflows/update-repos.yml b/.github/workflows/update-repos.yml index c12e52459a3..99e774bb5a8 100644 --- a/.github/workflows/update-repos.yml +++ b/.github/workflows/update-repos.yml @@ -2,12 +2,6 @@ name: Update Repositories on: workflow_dispatch: - inputs: - build_version: - description: 'Build Version' - required: true - default: '8.3.0' - type: string permissions: contents: write @@ -17,35 +11,65 @@ defaults: shell: bash jobs: - shared-values: + setup: name: Setup runs-on: ubuntu-latest if: ${{ github.action_repository != 'cloudfoundry/cli' }} outputs: - build-version: ${{ steps.set-build-version.outputs.build-version }} secrets-environment: ${{ steps.set-secrets-environment.outputs.secrets-environment }} - steps: + version-build: ${{ steps.parse-semver.outputs.version-build }} + version-major: ${{ steps.parse-semver.outputs.version-major }} + version-minor: ${{ steps.parse-semver.outputs.version-minor }} + version-patch: ${{ steps.parse-semver.outputs.version-patch }} + claw-url: ${{ steps.set-claw-url.outputs.claw-url }} + + steps: - name: Set environment id: set-secrets-environment run: echo "::set-output name=secrets-environment::PROD" + - name: Set CLAW URL + id: set-claw-url + run: echo "::set-output name=claw-url::https://packages.cloudfoundry.org" + - name: Checkout cli uses: actions/checkout@v2 - - name: Set build version - id: set-build-version + - name: Parse semver + id: parse-semver run: | - version=$(cat BUILD_VERSION) - echo "::set-output name=build-version::$version" + VERSION=$(cat BUILD_VERSION) + VERSION="${VERSION#[vV]}" + + VERSION_MINOR="${VERSION#*.}" + VERSION_MINOR="${VERSION_MINOR%.*}" + + echo "::set-output name=version-build::${VERSION}" + echo "::set-output name=version-major::${VERSION%%\.*}" + echo "::set-output name=version-minor::${VERSION_MINOR}" + echo "::set-output name=version-patch::${VERSION##*.}" + + echo "VERSION_BUILD=${VERSION}" >> ${GITHUB_ENV} + + - name: Test if CLAW serve this version + env: + CLAW_URL: ${{ steps.set-claw-url.outputs.claw-url }} + run: > + set -vx + + curl --head "${CLAW_URL}/stable?release=linux64-binary&version=${VERSION_BUILD}&source=test" 2>&1 | + grep --quiet --regexp 'HTTP.*302' update-homebrew: name: Update Homebrew Repository runs-on: ubuntu-latest - needs: shared-values - environment: ${{ needs.shared-values.outputs.secrets-environment }} + needs: setup + environment: ${{ needs.setup.outputs.secrets-environment }} env: - BUILD_VERSION: ${{ needs.shared-values.outputs.build-version }} + CLAW_URL: ${{ needs.setup.outputs.claw-url }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Checkout cli-ci @@ -64,70 +88,82 @@ jobs: ssh-key: ${{ secrets.GIT_DEPLOY_HOMEBREW_TAP }} - name: Setup - run: | - mkdir cf8-cli-osx-tarball cf8-cli-linux-tarball + run: > + mkdir + cf-cli-osx-tarball + cf-cli-macosarm-tarball + cf-cli-linux-tarball - name: Calculate checksums run: | set -x - curl -L "https://packages.cloudfoundry.org/stable?release=macosx64-binary&version=${BUILD_VERSION}&source=github-rel" \ - > cf8-cli-osx-tarball/cf8-cli_${BUILD_VERSION}_osx.tgz + curl -L "${CLAW_URL}/stable?release=macosx64-binary&version=${VERSION_BUILD}&source=github-rel" \ + > cf-cli-osx-tarball/cf-cli_osx.tgz - # Because CLAW always returns 200 we have to check if we got archive - file cf8-cli-osx-tarball/cf8-cli_${BUILD_VERSION}_osx.tgz | grep -q gzip || exit 1 + curl -L "${CLAW_URL}/stable?release=macosarm-binary&version=${VERSION_BUILD}&source=github-rel" \ + > cf-cli-macosarm-tarball/cf-cli_macosarm.tgz - curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${BUILD_VERSION}&source=github-rel" \ - > cf8-cli-linux-tarball/cf8-cli_${BUILD_VERSION}_linux64.tgz + curl -L "${CLAW_URL}/stable?release=linux64-binary&version=${VERSION_BUILD}&source=github-rel" \ + > cf-cli-linux-tarball/cf-cli_linux64.tgz # Because CLAW always returns 200 we have to check if we got archive - file cf8-cli-linux-tarball/cf8-cli_${BUILD_VERSION}_linux64.tgz | grep -q gzip || exit 1 + file cf-cli-osx-tarball/cf-cli_osx.tgz | grep -q gzip || exit 1 + file cf-cli-macosarm-tarball/cf-cli_macosarm.tgz | grep -q gzip || exit 1 + file cf-cli-linux-tarball/cf-cli_linux64.tgz | grep -q gzip || exit 1 - pushd cf8-cli-osx-tarball - CLI_OSX_SHA256=$(shasum -a 256 cf8-cli_*_osx.tgz | cut -d ' ' -f 1) + pushd cf-cli-osx-tarball + CLI_OSX_SHA256=$(shasum -a 256 cf-cli_osx.tgz | cut -d ' ' -f 1) popd - pushd cf8-cli-linux-tarball - CLI_LINUX_64_SHA256=$(shasum -a 256 cf8-cli_*_linux64.tgz | cut -d ' ' -f 1) + pushd cf-cli-macosarm-tarball + CLI_MACOSARM_SHA256=$(shasum -a 256 cf-cli_macosarm.tgz | cut -d ' ' -f 1) popd - echo "CLI_OSX_SHA256=${CLI_OSX_SHA256}" >> $GITHUB_ENV - echo "CLI_LINUX_64_SHA256=${CLI_LINUX_64_SHA256}" >> $GITHUB_ENV + pushd cf-cli-linux-tarball + CLI_LINUX_64_SHA256=$(shasum -a 256 cf-cli_linux64.tgz | cut -d ' ' -f 1) + popd + + echo "CLI_OSX_SHA256=${CLI_OSX_SHA256}" >> ${GITHUB_ENV} + echo "CLI_MACOSARM_SHA256=${CLI_MACOSARM_SHA256}" >> ${GITHUB_ENV} + echo "CLI_LINUX_64_SHA256=${CLI_LINUX_64_SHA256}" >> ${GITHUB_ENV} - name: Generate Homebrew formula file run: | set -ex pushd homebrew-tap - cat <<EOF > cf-cli@8.rb - require 'formula' - - class CfCliAT8 < Formula - homepage 'https://code.cloudfoundry.org/cli' - version '${BUILD_VERSION}' + cat <<EOF > cf-cli@${VERSION_MAJOR}.rb + class CfCliAT${VERSION_MAJOR} < Formula + desc "Cloud Foundry CLI" + homepage "https://code.cloudfoundry.org/cli" + version "${VERSION_BUILD}" if OS.mac? - url 'https://packages.cloudfoundry.org/homebrew/cf8-${BUILD_VERSION}.tgz' - sha256 '${CLI_OSX_SHA256}' + if Hardware::CPU.arm? + url "${CLAW_URL}/homebrew?arch=macosarm&version=${VERSION_BUILD}" + sha256 "${CLI_MACOSARM_SHA256}" + elsif + url "${CLAW_URL}/homebrew?arch=macosx64&version=${VERSION_BUILD}" + sha256 "${CLI_OSX_SHA256}" + end elsif OS.linux? - url 'https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${BUILD_VERSION}&source=homebrew' - sha256 '${CLI_LINUX_64_SHA256}' + url "${CLAW_URL}/stable?release=linux64-binary&version=${VERSION_BUILD}&source=homebrew" + sha256 "${CLI_LINUX_64_SHA256}" end - depends_on :arch => :x86_64 - def install - bin.install 'cf8' - bin.install_symlink 'cf8' => 'cf' - (bash_completion/"cf8-cli").write <<-completion - $(cat ../cli-ci/ci/installers/completion/cf8) + bin.install "cf${VERSION_MAJOR}" + bin.install_symlink "cf${VERSION_MAJOR}" => "cf" + (bash_completion/"cf${VERSION_MAJOR}-cli").write <<-completion + $(cat ../cli-ci/ci/installers/completion/cf${VERSION_MAJOR}) completion - doc.install 'LICENSE' - doc.install 'NOTICE' + doc.install "LICENSE" + doc.install "NOTICE" end test do - system "#{bin}/cf8" + system "#{bin}/cf${VERSION_MAJOR}" end end EOF @@ -137,20 +173,20 @@ jobs: - name: Commit new homebrew formula run: | pushd homebrew-tap - git add cf-cli@8.rb + git add cf-cli@${VERSION_MAJOR}.rb if ! [ -z "$(git status --porcelain)"]; then git config user.name github-actions git config user.email github-actions@github.com - git commit -m "Release CF CLI $BUILD_VERSION" + git commit -m "Release CF CLI ${VERSION_BUILD}" else echo "no new version to commit" fi git push - echo "::group::cf-cli@8.rb" - cat cf-cli@8.rb + echo "::group::cf-cli@${VERSION_MAJOR}.rb" + cat cf-cli@${VERSION_MAJOR}.rb echo "::endgroup::" echo "::group::git show" @@ -163,47 +199,51 @@ jobs: name: Test Homebrew Repository runs-on: macos-latest needs: - - shared-values + - setup - update-homebrew - environment: ${{ needs.shared-values.outputs.secrets-environment }} + environment: ${{ needs.setup.outputs.secrets-environment }} env: - BUILD_VERSION: ${{ needs.shared-values.outputs.build-version }} + CLAW_URL: ${{ needs.setup.outputs.claw-url }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Install CF CLI via Homebrew run: | set -evx - brew install cloudfoundry/tap/cf-cli@8 - installed_cf_version=$(cf8 version) + brew install cloudfoundry/tap/cf-cli@${VERSION_MAJOR} + installed_cf_version=$(cf${VERSION_MAJOR} version) cf_location=$(which cf) echo $cf_location echo $installed_cf_version - echo $BUILD_VERSION + echo ${VERSION_BUILD} codesign --verify $cf_location || echo --- - cf -v | grep "$BUILD_VERSION" + cf -v | grep "${VERSION_BUILD}" update-deb: name: Update Debian Repository runs-on: ubuntu-latest - needs: shared-values - environment: ${{ needs.shared-values.outputs.secrets-environment }} + needs: setup + environment: ${{ needs.setup.outputs.secrets-environment }} env: - BUILD_VERSION: ${{ needs.shared-values.outputs.build-version }} - AWS_BUCKET_NAME: cf-cli-debian-repo - AWS_DEFAULT_REGION: us-west-2 - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_BUCKET_NAME: cf-cli-debian-repo + AWS_DEFAULT_REGION: us-west-2 AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + CLAW_URL: ${{ needs.setup.outputs.claw-url }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Setup run: | - echo "BUILD_VERSION: $BUILD_VERSION" - echo "Environment: $ENVIRONMENT" + echo "VERSION_BUILD: ${VERSION_BUILD}" + echo "Environment: ${ENVIRONMENT}" - name: Checkout uses: actions/checkout@v2 @@ -217,10 +257,10 @@ jobs: - name: Load GPG key env: - SIGNING_KEY_GPG: ${{ secrets.SIGNING_KEY_GPG }} + SIGNING_KEY_GPG: ${{ secrets.SIGNING_KEY_GPG }} SIGNING_KEY_GPG_PASSPHRASE: ${{ secrets.SIGNING_KEY_GPG_PASSPHRASE }} run: | - echo -n "$SIGNING_KEY_GPG" | base64 --decode | gpg --no-tty --batch --pinentry-mode loopback --import + echo -n "${SIGNING_KEY_GPG}" | base64 --decode | gpg --no-tty --batch --pinentry-mode loopback --import - name: View GPG keys run: gpg --list-keys @@ -230,10 +270,10 @@ jobs: echo "Configure GPG" # mkdir gpg-dir - # export GNUPGHOME=$PWD/gpg-dir - # chmod 700 $GNUPGHOME + # export GNUPGHOME=${PWD}/gpg-dir + # chmod 700 ${GNUPGHOME} # TODO: restore - # trap "rm -rf $GNUPGHOME" 0 + # trap "rm -rf ${GNUPGHOME}" 0 cat >> ~/gpg.conf <<EOF personal-digest-preferences SHA256 @@ -244,12 +284,12 @@ jobs: - name: Download New Debian Packages From CLAW run: | mkdir installers - curl -L "https://cli.run.pivotal.io/stable?release=debian32&version=${BUILD_VERSION}&source=github-rel" > installers/cf8-cli-installer_${BUILD_VERSION}_i686.deb - curl -L "https://cli.run.pivotal.io/stable?release=debian64&version=${BUILD_VERSION}&source=github-rel" > installers/cf8-cli-installer_${BUILD_VERSION}_x86-64.deb + curl -L "${CLAW_URL}/stable?release=debian32&version=${VERSION_BUILD}&source=github-rel" > installers/cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_i686.deb + curl -L "${CLAW_URL}/stable?release=debian64&version=${VERSION_BUILD}&source=github-rel" > installers/cf${VERSION_MAJOR}-cli-installer_${VERSION_BUILD}_x86-64.deb - name: Update Debian Repository env: - DEBIAN_FRONTEND: noninteractive + DEBIAN_FRONTEND: noninteractive SIGNING_KEY_GPG_ID: ${{ secrets.SIGNING_KEY_GPG_ID }} run: | deb-s3 upload installers/*.deb \ @@ -261,11 +301,13 @@ jobs: name: Test Debian Repository runs-on: ubuntu-latest needs: - - shared-values + - setup - update-deb - environment: ${{ needs.shared-values.outputs.secrets-environment }} + environment: ${{ needs.setup.outputs.secrets-environment }} env: - BUILD_VERSION: ${{ needs.shared-values.outputs.build-version }} + CLAW_URL: ${{ needs.setup.outputs.claw-url }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Install CF CLI via apt @@ -275,45 +317,48 @@ jobs: sudo apt update sudo apt install -y wget gnupg - wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - - echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + wget -q -O - ${CLAW_URL}/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb ${CLAW_URL}/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list sudo apt update - sudo apt install -y cf8-cli + sudo apt install -y cf${VERSION_MAJOR}-cli which cf set -x cf -v - cf8 -v + cf${VERSION_MAJOR} -v - cf -v | grep "$BUILD_VERSION" + cf -v | grep "${VERSION_BUILD}" update-rpm: name: Update RPM Repository runs-on: ubuntu-18.04 - environment: ${{ needs.shared-values.outputs.secrets-environment }} - needs: shared-values + environment: ${{ needs.setup.outputs.secrets-environment }} + needs: setup env: - AWS_DEFAULT_REGION: us-east-1 - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_DEFAULT_REGION: us-east-1 AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + CLAW_URL: ${{ needs.setup.outputs.claw-url }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Setup env: - BUILD_VERSION: ${{ github.event.inputs.build_version }} - ENVIRONMENT: ${{ github.event.inputs.environment }} + ENVIRONMENT: ${{ github.event.inputs.environment }} + VERSION_BUILD: ${{ github.event.inputs.build_version }} run: | - echo "BUILD_VERSION: $BUILD_VERSION" - echo "Environment: $ENVIRONMENT" + echo "VERSION_BUILD: ${VERSION_BUILD}" + echo "Environment: ${ENVIRONMENT}" # TODO: fix backup # - name: Download current RPM repodata # env: - # AWS_DEFAULT_REGION: us-east-1 - # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + # AWS_DEFAULT_REGION: us-east-1 # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # uses: docker://amazon/aws-cli:latest # with: @@ -387,30 +432,32 @@ jobs: test-rpm-repo: name: Test RPM Repository needs: - - shared-values + - setup - update-rpm runs-on: ubuntu-latest container: image: fedora - environment: ${{ needs.shared-values.outputs.secrets-environment }} + environment: ${{ needs.setup.outputs.secrets-environment }} env: - BUILD_VERSION: ${{ needs.shared-values.outputs.build-version }} + CLAW_URL: ${{ needs.setup.outputs.claw-url }} + VERSION_BUILD: ${{ needs.setup.outputs.version-build }} + VERSION_MAJOR: ${{ needs.setup.outputs.version-major }} steps: - name: Configure Custom CF Repository run: | curl -sL -o /etc/yum.repos.d/cloudfoundry-cli.repo \ - https://packages.cloudfoundry.org/fedora/cloudfoundry-cli.repo + ${CLAW_URL}/fedora/cloudfoundry-cli.repo - - name: Install cf8-cli package - run: dnf install -y cf8-cli + - name: Install cf cli package + run: dnf install -y cf${VERSION_MAJOR}-cli - name: Print CF CLI Versions run: | cf -v - cf8 -v + cf${VERSION_MAJOR} -v - name: Test Version Match - run: cf -v | grep -q "$BUILD_VERSION" + run: cf -v | grep -q "${VERSION_BUILD}" # vim: set sw=2 ts=2 sts=2 et tw=78 foldlevel=2 fdm=indent nospell: diff --git a/Makefile b/Makefile index 3572db616d6..7e4100089ef 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,10 @@ out/cf-cli_osx: $(GOSRC) GOARCH=amd64 GOOS=darwin go build \ -a -ldflags "$(LD_FLAGS)" -o out/cf-cli_osx . +out/cf-cli_macosarm: $(GOSRC) + GOARCH=arm64 GOOS=darwin go build \ + -a -ldflags "$(LD_FLAGS)" -o out/cf-cli_macosarm . + out/cf-cli_win32.exe: $(GOSRC) rsrc.syso GOARCH=386 GOOS=windows go build -tags="forceposix" -o out/cf-cli_win32.exe -ldflags "$(LD_FLAGS)" . rm rsrc.syso diff --git a/bin/generate-release-notes b/bin/generate-release-notes index 0d7d8649681..1246d0af5cc 100755 --- a/bin/generate-release-notes +++ b/bin/generate-release-notes @@ -9,22 +9,22 @@ Package Manager Installation Installers ---------- -- Debian [64 bit](https://packages.cloudfoundry.org/stable?release=debian64&version=$VERSION&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=debian32&version=$VERSION&source=github-rel) (deb) -- Redhat [64 bit](https://packages.cloudfoundry.org/stable?release=redhat64&version=$VERSION&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=redhat32&version=$VERSION&source=github-rel) (rpm) -- Mac OS X [64 bit](https://packages.cloudfoundry.org/stable?release=macosx64&version=$VERSION&source=github-rel) (pkg) -- Windows [64 bit](https://packages.cloudfoundry.org/stable?release=windows64&version=$VERSION&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=windows32&version=$VERSION&source=github-rel) (zip) +- Debian [64 bit](https://packages.cloudfoundry.org/stable?release=debian64&version=${VERSION}&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=debian32&version=${VERSION}&source=github-rel) (deb) +- Redhat [64 bit](https://packages.cloudfoundry.org/stable?release=redhat64&version=${VERSION}&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=redhat32&version=${VERSION}&source=github-rel) (rpm) +- macOS [64 bit](https://packages.cloudfoundry.org/stable?release=macosx64&version=${VERSION}&source=github-rel) / [arm](https://packages.cloudfoundry.org/stable?release=macosarm&version=${VERSION}&source=github-rel) (pkg) +- Windows [64 bit](https://packages.cloudfoundry.org/stable?release=windows64&version=${VERSION}&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=windows32&version=${VERSION}&source=github-rel) (zip) Binaries -------- -- Linux [64 bit](https://packages.cloudfoundry.org/stable?release=linux64-binary&version=$VERSION&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=linux32-binary&version=$VERSION&source=github-rel) (tgz) -- Mac OS X [64 bit](https://packages.cloudfoundry.org/stable?release=macosx64-binary&version=$VERSION&source=github-rel) (tgz) -- Windows [64 bit](https://packages.cloudfoundry.org/stable?release=windows64-exe&version=$VERSION&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=windows32-exe&version=$VERSION&source=github-rel) (zip) +- Linux [64 bit](https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${VERSION}&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=linux32-binary&version=${VERSION}&source=github-rel) (tgz) +- macOS [64 bit](https://packages.cloudfoundry.org/stable?release=macosx64-binary&version=${VERSION}&source=github-rel) / [arm](https://packages.cloudfoundry.org/stable?release=macosarm-binary&version=${VERSION}&source=github-rel) (tgz) +- Windows [64 bit](https://packages.cloudfoundry.org/stable?release=windows64-exe&version=${VERSION}&source=github-rel) / [32 bit](https://packages.cloudfoundry.org/stable?release=windows32-exe&version=${VERSION}&source=github-rel) (zip) Docker -------- \`\`\`shell -docker pull cloudfoundry/cli:$VERSION +docker pull cloudfoundry/cli:${VERSION} \`\`\` Change Log