Repository: cli/cli
Pull request #7385 (2023-04-30 13:10:22)

Include auto-merge information in gh pr view
_This PR is part of a series for issue #7309, adding auto-merge status support to PR commands_

If a PR has auto-merge enabled, the human-readable output shows this in an additional line in the metadata section:

```
Auto-merge: enabled by [userid], using [merge method] • [when]
```

The line is not added when auto-merge is not enabled.

The raw version alawys includes a line; either

```
auto-merge: disabled
```

or

```
auto-merge: enabled ([userid], [merge method])
```

Note: this PR includes the commit from #7384 as this is a direct dependency on the GraphQL query data being available.

mjpieters: Note: this PR includes the commit from #7384 as this is a direct dependency on the GraphQL query data being available.

Patch:
diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
index c011fc408..8d1cb2169 100644
--- a/.github/workflows/deployment.yml
+++ b/.github/workflows/deployment.yml
@@ -1,7 +1,11 @@
 name: Deployment
 
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref_name }}
+  cancel-in-progress: true
+
 permissions:
-  contents: read
+  contents: write
 
 on:
   workflow_dispatch:
@@ -9,13 +13,59 @@ on:
       tag_name:
         required: true
         type: string
+      environment:
+        default: production
+        type: environment
       go_version:
         default: "1.19"
         type: string
+      platforms:
+        default: "linux,macos,windows"
+        type: string
+      release:
+        description: "Whether to create a GitHub Release"
+        type: boolean
+        default: true
 
 jobs:
   linux:
     runs-on: ubuntu-latest
+    environment: ${{ inputs.environment }}
+    if: contains(inputs.platforms, 'linux')
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+      - name: Set up Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: ${{ inputs.go_version }}
+      - name: Install GoReleaser
+        uses: goreleaser/goreleaser-action@v4
+        with:
+          version: "~1.17.1"
+          install-only: true
+      - name: Build release binaries
+        env:
+          TAG_NAME: ${{ inputs.tag_name }}
+        run: script/release --local "$TAG_NAME" --platform linux
+      - name: Generate web manual pages
+        run: |
+          go run ./cmd/gen-docs --website --doc-path dist/manual
+          tar -czvf dist/manual.tar.gz -C dist -- manual
+      - uses: actions/upload-artifact@v3
+        with:
+          name: linux
+          if-no-files-found: error
+          retention-days: 7
+          path: |
+            dist/*.tar.gz
+            dist/*.rpm
+            dist/*.deb
+  
+  macos:
+    runs-on: macos-latest
+    environment: ${{ inputs.environment }}
+    if: contains(inputs.platforms, 'macos')
     steps:
       - name: Checkout
         uses: actions/checkout@v3
@@ -23,3 +73,272 @@ jobs:
         uses: actions/setup-go@v4
         with:
           go-version: ${{ inputs.go_version }}
+      - name: Configure macOS signing
+        if: inputs.environment == 'production'
+        env:
+          APPLE_DEVELOPER_ID: ${{ vars.APPLE_DEVELOPER_ID }}
+          APPLE_APPLICATION_CERT: ${{ secrets.APPLE_APPLICATION_CERT }}
+          APPLE_APPLICATION_CERT_PASSWORD: ${{ secrets.APPLE_APPLICATION_CERT_PASSWORD }}
+        run: |
+          keychain="$RUNNER_TEMP/buildagent.keychain"
+          keychain_password="password1"
+
+          security create-keychain -p "$keychain_password" "$keychain"
+          security default-keychain -s "$keychain"
+          security unlock-keychain -p "$keychain_password" "$keychain"
+
+          base64 -D <<<"$APPLE_APPLICATION_CERT" > "$RUNNER_TEMP/cert.p12"
+          security import "$RUNNER_TEMP/cert.p12" -k "$keychain" -P "$APPLE_APPLICATION_CERT_PASSWORD" -T /usr/bin/codesign
+          security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k "$keychain_password" "$keychain"
+          rm "$RUNNER_TEMP/cert.p12"
+      - name: Install GoReleaser
+        uses: goreleaser/goreleaser-action@v4
+        with:
+          version: "~1.17.1"
+          install-only: true
+      - name: Build release binaries
+        env:
+          TAG_NAME: ${{ inputs.tag_name }}
+          APPLE_DEVELOPER_ID: ${{ vars.APPLE_DEVELOPER_ID }}
+        run: script/release --local "$TAG_NAME" --platform macos
+      - name: Notarize macOS archives
+        if: inputs.environment == 'production'
+        env:
+          APPLE_ID: ${{ vars.APPLE_ID }}
+          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
+          APPLE_DEVELOPER_ID: ${{ vars.APPLE_DEVELOPER_ID }}
+        run: |
+          shopt -s failglob
+          script/sign dist/gh_*_macOS_*.zip
+      - uses: actions/upload-artifact@v3
+        with:
+          name: macos
+          if-no-files-found: error
+          retention-days: 7
+          path: |
+            dist/*.tar.gz
+            dist/*.zip
+  
+  windows:
+    runs-on: windows-latest
+    environment: ${{ inputs.environment }}
+    if: contains(inputs.platforms, 'windows')
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+      - name: Set up Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: ${{ inputs.go_version }}
+      - name: Obtain signing certificate
+        id: obtain_cert
+        if: inputs.environment == 'production'
+        shell: bash
+        run: |
+          base64 -d <<<"$CERT_CONTENTS" > ./cert.pfx
+          printf "cert-file=%s\n" ".\\cert.pfx" >> $GITHUB_OUTPUT
+        env:
+          CERT_CONTENTS: ${{ secrets.WINDOWS_CERT_PFX }}
+      - name: Install GoReleaser
+        uses: goreleaser/goreleaser-action@v4
+        with:
+          version: "~1.17.1"
+          install-only: true
+      - name: Build release binaries
+        shell: bash
+        env:
+          TAG_NAME: ${{ inputs.tag_name }}
+          CERT_FILE: ${{ steps.obtain_cert.outputs.cert-file }}
+          CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
+        run: script/release --local "$TAG_NAME" --platform windows
+      - name: Set up MSBuild
+        id: setupmsbuild
+        uses: microsoft/setup-msbuild@v1.3.1
+      - name: Build MSI
+        shell: bash
+        env:
+          MSBUILD_PATH: ${{ steps.setupmsbuild.outputs.msbuildPath }}
+        run: |
+          for ZIP_FILE in dist/gh_*_windows_*.zip; do
+            MSI_NAME="$(basename "$ZIP_FILE" ".zip")"
+            MSI_VERSION="$(cut -d_ -f2 <<<"$MSI_NAME" | cut -d- -f1)"
+            case "$MSI_NAME" in
+            *_386 )
+              source_dir="$PWD/dist/windows_windows_386"
+              platform="x86"
+              ;;
+            *_amd64 )
+              source_dir="$PWD/dist/windows_windows_amd64_v1"
+              platform="x64"
+              ;;
+            *_arm64 )
+              echo "skipping building MSI for arm64 because WiX 3.11 doesn't support it: https://github.com/wixtoolset/issues/issues/6141" >&2
+              continue
+              #source_dir="$PWD/dist/windows_windows_arm64"
+              #platform="arm64"
+              ;;
+            * )
+              printf "unsupported architecture: %s\n" "$MSI_NAME" >&2
+              exit 1
+              ;;
+            esac
+            "${MSBUILD_PATH}\MSBuild.exe" ./build/windows/gh.wixproj -p:SourceDir="$source_dir" -p:OutputPath="$PWD/dist" -p:OutputName="$MSI_NAME" -p:ProductVersion="${MSI_VERSION#v}" -p:Platform="$platform"
+          done
+      - name: Sign MSI
+        if: inputs.environment == 'production'
+        shell: pwsh
+        run: |
+          Get-ChildItem -Path .\dist -Filter *.msi | ForEach-Object {
+              .\script\sign $_.FullName
+          }
+        env:
+          CERT_FILE: ${{ steps.obtain_cert.outputs.cert-file }}
+          CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
+      - uses: actions/upload-artifact@v3
+        with:
+          name: windows
+          if-no-files-found: error
+          retention-days: 7
+          path: |
+            dist/*.zip
+            dist/*.msi
+
+  release:
+    runs-on: ubuntu-latest
+    needs: [linux, macos, windows]
+    if: inputs.release
+    steps:
+      - name: Checkout cli/cli
+        uses: actions/checkout@v3
+      - name: Merge built artifacts
+        uses: actions/download-artifact@v3
+      - name: Checkout documentation site
+        uses: actions/checkout@v3
+        with:
+          repository: github/cli.github.com
+          path: site
+          fetch-depth: 0
+          ssh-key: ${{ secrets.SITE_SSH_KEY }}
+      - name: Update site man pages
+        env:
+          GIT_COMMITTER_NAME: cli automation
+          GIT_AUTHOR_NAME: cli automation
+          GIT_COMMITTER_EMAIL: noreply@github.com
+          GIT_AUTHOR_EMAIL: noreply@github.com
+          TAG_NAME: ${{ inputs.tag_name }}
+        run: |
+          git -C site rm 'manual/gh*.md' 2>/dev/null || true
+          tar -xzvf linux/manual.tar.gz -C site
+          git -C site add 'manual/gh*.md'
+          sed -i.bak -E "s/(assign version = )\".+\"/\1\"${TAG_NAME#v}\"/" site/index.html
+          rm -f site/index.html.bak
+          git -C site add index.html
+          git -C site diff --quiet --cached || git -C site commit -m "gh ${TAG_NAME#v}"
+      - name: Prepare release assets
+        env:
+          TAG_NAME: ${{ inputs.tag_name }}
+        run: |
+          shopt -s failglob
+          rm -rf dist
+          mkdir dist
+          mv -v {linux,macos,windows}/gh_* dist/
+      - name: Install packaging dependencies
+        run: sudo apt-get install -y rpm reprepro
+      - name: Set up GPG
+        if: inputs.environment == 'production'
+        env:
+          GPG_PUBKEY: ${{ secrets.GPG_PUBKEY }}
+          GPG_KEY: ${{ secrets.GPG_KEY }}
+          GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+          GPG_KEYGRIP: ${{ secrets.GPG_KEYGRIP }}
+        run: |
+          base64 -d <<<"$GPG_PUBKEY" | gpg --import --no-tty --batch --yes
+          base64 -d <<<"$GPG_KEY" | gpg --import --no-tty --batch --yes
+          echo "allow-preset-passphrase" > ~/.gnupg/gpg-agent.conf
+          gpg-connect-agent RELOADAGENT /bye
+          /usr/lib/gnupg2/gpg-preset-passphrase --preset "$GPG_KEYGRIP" <<<"$GPG_PASSPHRASE"
+      - name: Sign RPMs
+        if: inputs.environment == 'production'
+        run: |
+          cp script/rpmmacros ~/.rpmmacros
+          rpmsign --addsign dist/*.rpm
+      - name: Run createrepo
+        env:
+          GPG_SIGN: ${{ inputs.environment == 'production' }}
+        run: |
+          mkdir -p site/packages/rpm
+          cp dist/*.rpm site/packages/rpm/
+          ./script/createrepo.sh
+          cp -r dist/repodata site/packages/rpm/
+          pushd site/packages/rpm
+          [ "$GPG_SIGN" = "false" ] || gpg --yes --detach-sign --armor repodata/repomd.xml
+          popd
+      - name: Run reprepro
+        env:
+          GPG_SIGN: ${{ inputs.environment == 'production' }}
+          # We are no longer adding to the distribution list.
+          # All apt distributions should use "stable" according to our install documentation.
+          # In the future we will remove legacy distributions listed here.
+          RELEASES: "cosmic eoan disco groovy focal stable oldstable testing sid unstable buster bullseye stretch jessie bionic trusty precise xenial hirsute impish kali-rolling"
+        run: |
+          mkdir -p upload
+          [ "$GPG_SIGN" = "true" ] || sed -i.bak '/^SignWith:/d' script/distributions
+          for release in $RELEASES; do
+            for file in dist/*.deb; do
+              reprepro --confdir="+b/script" includedeb "$release" "$file"
+            done
+          done
+          cp -a dists/ pool/ upload/
+          mkdir -p site/packages
+          cp -a upload/* site/packages/
+      - name: Create the release
+        env:
+          DO_PUBLISH: ${{ inputs.environment == 'production' }}
+          TAG_NAME: ${{ inputs.tag_name }}
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          shopt -s failglob
+          pushd dist
+          shasum -a 256 gh_* > checksums.txt
+          mv checksums.txt gh_${TAG_NAME#v}_checksums.txt
+          popd
+          release_args=(
+            "$TAG_NAME"
+            --title "GitHub CLI ${TAG_NAME#v}"
+            --target "$GITHUB_SHA"
+            --generate-notes
+          )
+          if [[ $TAG_NAME == *-* ]]; then
+            release_args+=( --prerelease )
+          else
+            release_args+=( --discussion-category "General" )
+          fi
+          action="echo"
+          [ "$DO_PUBLISH" = "false" ] || action="command"
+          script/label-assets dist/gh_* | xargs $action gh release create "${release_args[@]}" -- 
+      - name: Publish site
+        env:
+          DO_PUBLISH: ${{ inputs.environment == 'production' && !contains(inputs.tag_name, '-') }}
+          TAG_NAME: ${{ inputs.tag_name }}
+          GIT_COMMITTER_NAME: cli automation
+          GIT_AUTHOR_NAME: cli automation
+          GIT_COMMITTER_EMAIL: noreply@github.com
+          GIT_AUTHOR_EMAIL: noreply@github.com
+        working-directory: ./site
+        run: |
+          git add packages
+          git commit -m "Add rpm and deb packages for $TAG_NAME"
+          if [ "$DO_PUBLISH" = "true" ]; then
+            git push
+          else
+            git log --oneline @{upstream}..
+            git diff --name-status @{upstream}..
+          fi
+      - name: Bump homebrew-core formula
+        uses: mislav/bump-homebrew-formula-action@v2
+        if: inputs.environment == 'production' && !contains(inputs.tag_name, '-')
+        with:
+          formula-name: gh
+          tag-name: ${{ inputs.tag_name }}
+        env:
+          COMMITTER_TOKEN: ${{ secrets.UPLOAD_GITHUB_TOKEN }}
diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml
deleted file mode 100644
index 7ed0c3fa9..000000000
--- a/.github/workflows/releases.yml
+++ /dev/null
@@ -1,222 +0,0 @@
-name: goreleaser
-
-on:
-  push:
-    tags:
-      - "v*"
-
-permissions:
-  contents: write  # publishing releases
-  repository-projects: write  # move cards between columns
-
-jobs:
-  goreleaser:
-    runs-on: ubuntu-20.04
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v3
-        with:
-          fetch-depth: 0
-      - name: Set up Go 1.19
-        uses: actions/setup-go@v4
-        with:
-          go-version: 1.19
-      - name: Generate changelog
-        id: changelog
-        run: |
-          echo "tag-name=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
-          gh api repos/$GITHUB_REPOSITORY/releases/generate-notes \
-            -f tag_name="${GITHUB_REF#refs/tags/}" \
-            -f target_commitish=trunk \
-            -q .body > CHANGELOG.md
-        env:
-          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
-      - name: Install osslsigncode
-        run: sudo apt-get install -y osslsigncode
-      - name: Obtain signing cert
-        run: |
-          cert="$(mktemp -t cert.XXX)"
-          base64 -d <<<"$CERT_CONTENTS" > "$cert"
-          echo "CERT_FILE=$cert" >> $GITHUB_ENV
-        env:
-          CERT_CONTENTS: ${{ secrets.WINDOWS_CERT_PFX }}
-      - name: Run GoReleaser
-        uses: goreleaser/goreleaser-action@v4
-        with:
-          version: "~1.13.1"
-          args: release --release-notes=CHANGELOG.md
-        env:
-          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
-          GORELEASER_CURRENT_TAG: ${{steps.changelog.outputs.tag-name}}
-          CERT_PASSWORD: ${{secrets.WINDOWS_CERT_PASSWORD}}
-      - name: Checkout documentation site
-        uses: actions/checkout@v3
-        with:
-          repository: github/cli.github.com
-          path: site
-          fetch-depth: 0
-          ssh-key: ${{secrets.SITE_SSH_KEY}}
-      - name: Update site man pages
-        env:
-          GIT_COMMITTER_NAME: cli automation
-          GIT_AUTHOR_NAME: cli automation
-          GIT_COMMITTER_EMAIL: noreply@github.com
-          GIT_AUTHOR_EMAIL: noreply@github.com
-        run: make site-bump
-      - name: Move project cards
-        continue-on-error: true
-        env:
-          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
-          PENDING_COLUMN: 8189733
-          DONE_COLUMN: 7110130
-        run: |
-          api() { gh api -H 'accept: application/vnd.github.inertia-preview+json' "$@"; }
-          api-write() { [[ $GITHUB_REF == *-* ]] && echo "skipping: api $*" || api "$@"; }
-          cards=$(api --paginate projects/columns/$PENDING_COLUMN/cards | jq ".[].id")
-          for card in $cards; do
-            api-write --silent projects/columns/cards/$card/moves -f position=top -F column_id=$DONE_COLUMN
-          done
-          echo "moved ${#cards[@]} cards to the Done column"
-      - name: Install packaging dependencies
-        run: sudo apt-get install -y rpm reprepro
-      - name: Set up GPG
-        run: |
-          echo "${{secrets.GPG_PUBKEY}}" | base64 -d | gpg --import --no-tty --batch --yes
-          echo "${{secrets.GPG_KEY}}" | base64 -d | gpg --import --no-tty --batch --yes
-          echo "allow-preset-passphrase" > ~/.gnupg/gpg-agent.conf
-          gpg-connect-agent RELOADAGENT /bye
-          echo "${{secrets.GPG_PASSPHRASE}}" | /usr/lib/gnupg2/gpg-preset-passphrase --preset "${{secrets.GPG_KEYGRIP}}"
-      - name: Sign RPMs
-        run: |
-          cp script/rpmmacros ~/.rpmmacros
-          rpmsign --addsign dist/*.rpm
-      - name: Run createrepo
-        run: |
-          mkdir -p site/packages/rpm
-          cp dist/*.rpm site/packages/rpm/
-          ./script/createrepo.sh
-          cp -r dist/repodata site/packages/rpm/
-          pushd site/packages/rpm
-          gpg --yes --detach-sign --armor repodata/repomd.xml
-          popd
-      - name: Run reprepro
-        env:
-          # We are no longer adding to the distribution list.
-          # All apt distributions should use "stable" according to our install documentation.
-          # In the future we will remove legacy distributions listed here.
-          RELEASES: "cosmic eoan disco groovy focal stable oldstable testing sid unstable buster bullseye stretch jessie bionic trusty precise xenial hirsute impish kali-rolling"
-        run: |
-          mkdir -p upload
-          for release in $RELEASES; do
-            for file in dist/*.deb; do
-              reprepro --confdir="+b/script" includedeb "$release" "$file"
-            done
-          done
-          cp -a dists/ pool/ upload/
-          mkdir -p site/packages
-          cp -a upload/* site/packages/
-      - name: Publish site
-        env:
-          GIT_COMMITTER_NAME: cli automation
-          GIT_AUTHOR_NAME: cli automation
-          GIT_COMMITTER_EMAIL: noreply@github.com
-          GIT_AUTHOR_EMAIL: noreply@github.com
-        working-directory: ./site
-        run: |
-          git add packages
-          git commit -m "Add rpm and deb packages for ${GITHUB_REF#refs/tags/}"
-          if [[ $GITHUB_REF == *-* ]]; then
-            git log --oneline @{upstream}..
-            git diff --name-status @{upstream}..
-          else
-            git push
-          fi
-
-  msi:
-    needs: goreleaser
-    runs-on: windows-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v3
-      - name: Download gh.exe
-        id: download_exe
-        shell: bash
-        run: |
-          hub release download "${GITHUB_REF#refs/tags/}" -i '*windows_amd64*.zip'
-          printf "zip=%s\n" *.zip >> $GITHUB_OUTPUT
-          unzip -o *.zip && rm -v *.zip
-        env:
-          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
-      - name: Prepare PATH
-        id: setupmsbuild
-        uses: microsoft/setup-msbuild@v1.3.1
-      - name: Build MSI
-        id: buildmsi
-        shell: bash
-        env:
-          ZIP_FILE: ${{ steps.download_exe.outputs.zip }}
-          MSBUILD_PATH: ${{ steps.setupmsbuild.outputs.msbuildPath }}
-        run: |
-          name="$(basename "$ZIP_FILE" ".zip")"
-          version="$(echo -e ${GITHUB_REF#refs/tags/v} | sed s/-.*$//)"
-          "${MSBUILD_PATH}\MSBuild.exe" ./build/windows/gh.wixproj -p:SourceDir="$PWD" -p:OutputPath="$PWD" -p:OutputName="$name" -p:ProductVersion="$version"
-      - name: Obtain signing cert
-        id: obtain_cert
-        shell: bash
-        run: |
-          base64 -d <<<"$CERT_CONTENTS" > ./cert.pfx
-          printf "cert-file=%s\n" ".\\cert.pfx" >> $GITHUB_OUTPUT
-        env:
-          CERT_CONTENTS: ${{ secrets.WINDOWS_CERT_PFX }}
-      - name: Sign MSI
-        env:
-          CERT_FILE: ${{ steps.obtain_cert.outputs.cert-file }}
-          EXE_FILE: ${{ steps.buildmsi.outputs.msi }}
-          CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
-        run: .\script\signtool sign /d "GitHub CLI" /f $env:CERT_FILE /p $env:CERT_PASSWORD /fd sha256 /tr http://timestamp.digicert.com /v $env:EXE_FILE
-      - name: Upload MSI
-        shell: bash
-        run: |
-          tag_name="${GITHUB_REF#refs/tags/}"
-          hub release edit "$tag_name" -m "" -a "$MSI_FILE"
-          release_url="$(gh api repos/:owner/:repo/releases -q ".[]|select(.tag_name==\"${tag_name}\")|.url")"
-          publish_args=( -F draft=false )
-          if [[ $GITHUB_REF != *-* ]]; then
-            publish_args+=( -f discussion_category_name="$DISCUSSION_CATEGORY" )
-          fi
-          gh api -X PATCH "$release_url" "${publish_args[@]}"
-        env:
-          MSI_FILE: ${{ steps.buildmsi.outputs.msi }}
-          DISCUSSION_CATEGORY: General
-          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
-      - name: Bump homebrew-core formula
-        uses: mislav/bump-homebrew-formula-action@v2
-        if: "!contains(github.ref, '-')" # skip prereleases
-        with:
-          formula-name: gh
-        env:
-          COMMITTER_TOKEN: ${{ secrets.UPLOAD_GITHUB_TOKEN }}
-      - name: Checkout scoop bucket
-        uses: actions/checkout@v3
-        with:
-          repository: cli/scoop-gh
-          path: scoop-gh
-          fetch-depth: 0
-          token: ${{secrets.UPLOAD_GITHUB_TOKEN}}
-      - name: Bump scoop bucket
-        shell: bash
-        run: |
-          hub release download "${GITHUB_REF#refs/tags/}" -i '*_checksums.txt'
-          script/scoop-gen "${GITHUB_REF#refs/tags/}" ./scoop-gh/gh.json < *_checksums.txt
-          git -C ./scoop-gh commit -m "gh ${GITHUB_REF#refs/tags/}" gh.json
-          if [[ $GITHUB_REF == *-* ]]; then
-            git -C ./scoop-gh show -m
-          else
-            git -C ./scoop-gh push
-          fi
-        env:
-          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
-          GIT_COMMITTER_NAME: cli automation
-          GIT_AUTHOR_NAME: cli automation
-          GIT_COMMITTER_EMAIL: noreply@github.com
-          GIT_AUTHOR_EMAIL: noreply@github.com
diff --git a/.gitignore b/.gitignore
index c9a58ce00..b1e8e1fa8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,9 @@
 /site
 .github/**/node_modules
 /CHANGELOG.md
+/.goreleaser.generated.yml
 /script/build
+/script/build.exe
 
 # VS Code
 .vscode
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 26277d3c5..f441156e3 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -7,57 +7,76 @@ release:
 
 before:
   hooks:
-    - go mod tidy
-    - make manpages GH_VERSION={{.Version}}
-    - make completions
+    - >-
+      {{ if eq .Runtime.Goos "windows" }}echo{{ end }} make manpages GH_VERSION={{.Version}}
+    - >-
+      {{ if ne .Runtime.Goos "linux" }}echo{{ end }} make completions
 
 builds:
-  - <<: &build_defaults
-      binary: bin/gh
-      main: ./cmd/gh
-      ldflags:
-        - -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
-    id: macos
+  - id: macos #build:macos
     goos: [darwin]
     goarch: [amd64, arm64]
+    hooks:
+      post:
+        - cmd: ./script/sign '{{ .Path }}'
+          output: true
+    binary: bin/gh
+    main: ./cmd/gh
+    ldflags:
+      - -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
 
-  - <<: *build_defaults
-    id: linux
+  - id: linux #build:linux
     goos: [linux]
     goarch: [386, arm, amd64, arm64]
     env:
       - CGO_ENABLED=0
+    binary: bin/gh
+    main: ./cmd/gh
+    ldflags:
+      - -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
 
-  - <<: *build_defaults
-    id: windows
+  - id: windows #build:windows
     goos: [windows]
     goarch: [386, amd64, arm64]
     hooks:
       post:
-        - cmd: ./script/sign-windows-executable.sh '{{ .Path }}'
-          output: false
+        - cmd: >-
+            {{ if eq .Runtime.Goos "windows" }}.\script\sign{{ else }}./script/sign{{ end }} '{{ .Path }}'
+          output: true
+    binary: bin/gh
+    main: ./cmd/gh
+    ldflags:
+      - -s -w -X github.com/cli/cli/v2/internal/build.Version={{.Version}} -X github.com/cli/cli/v2/internal/build.Date={{time "2006-01-02"}}
 
 archives:
-  - id: nix
-    builds: [macos, linux]
-    <<: &archive_defaults
-      name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
+  - id: linux-archive
+    builds: [linux]
+    name_template: "gh_{{ .Version }}_linux_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
     wrap_in_directory: true
-    replacements:
-      darwin: macOS
     format: tar.gz
+    rlcp: true
+    files:
+      - LICENSE
+      - ./share/man/man1/gh*.1
+  - id: macos-archive
+    builds: [macos]
+    name_template: "gh_{{ .Version }}_macOS_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
+    wrap_in_directory: true
+    format: zip
+    rlcp: true
     files:
       - LICENSE
       - ./share/man/man1/gh*.1
-  - id: windows
+  - id: windows-archive
     builds: [windows]
-    <<: *archive_defaults
+    name_template: "gh_{{ .Version }}_windows_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
     wrap_in_directory: false
     format: zip
+    rlcp: true
     files:
       - LICENSE
 
-nfpms:
+nfpms: #build:linux
   - license: MIT
     maintainer: GitHub
     homepage: https://github.com/cli/cli
diff --git a/Makefile b/Makefile
index ab1e28a01..7dac0d290 100644
--- a/Makefile
+++ b/Makefile
@@ -6,33 +6,37 @@ CGO_LDFLAGS ?= $(filter -g -L% -l% -O%,${LDFLAGS})
 export CGO_LDFLAGS
 
 EXE =
-ifeq ($(GOOS),windows)
+ifeq ($(shell go env GOOS),windows)
 EXE = .exe
 endif
 
 ## The following tasks delegate to `script/build.go` so they can be run cross-platform.
 
 .PHONY: bin/gh$(EXE)
-bin/gh$(EXE): script/build
-	@script/build $@
+bin/gh$(EXE): script/build$(EXE)
+	@script/build$(EXE) $@
 
-script/build: script/build.go
+script/build$(EXE): script/build.go
+ifeq ($(EXE),)
 	GOOS= GOARCH= GOARM= GOFLAGS= CGO_ENABLED= go build -o $@ $<
+else
+	go build -o $@ $<
+endif
 
 .PHONY: clean
-clean: script/build
-	@script/build $@
+clean: script/build$(EXE)
+	@$< $@
 
 .PHONY: manpages
-manpages: script/build
-	@script/build $@
+manpages: script/build$(EXE)
+	@$< $@
 
 .PHONY: completions
-completions:
+completions: bin/gh$(EXE)
 	mkdir -p ./share/bash-completion/completions ./share/fish/vendor_completions.d ./share/zsh/site-functions
-	go run ./cmd/gh completion -s bash > ./share/bash-completion/completions/gh
-	go run ./cmd/gh completion -s fish > ./share/fish/vendor_completions.d/gh.fish
-	go run ./cmd/gh completion -s zsh > ./share/zsh/site-functions/_gh
+	bin/gh$(EXE) completion -s bash > ./share/bash-completion/completions/gh
+	bin/gh$(EXE) completion -s fish > ./share/fish/vendor_completions.d/gh.fish
+	bin/gh$(EXE) completion -s zsh > ./share/zsh/site-functions/_gh
 
 # just a convenience task around `go test`
 .PHONY: test
diff --git a/build/windows/gh.wixproj b/build/windows/gh.wixproj
index aa72d4da3..6c1e971d0 100644
--- a/build/windows/gh.wixproj
+++ b/build/windows/gh.wixproj
@@ -30,9 +30,5 @@
         <WixExtension Include="WixUIExtension"/>
         <WixExtension Include="WixUtilExtension"/>
     </ItemGroup>
-    <Target Name="SetStepOutput" AfterTargets="Build" Condition="'$(GITHUB_ACTIONS)' != ''">
-        <!-- Make sure the correct target path is always set as the step output -->
-        <Message Importance="high" Text="::set-output name=msi::$(TargetPath)"/>
-    </Target>
     <Import Project="$(WixTargetsPath)"/>
 </Project>
diff --git a/docs/releasing.md b/docs/releasing.md
index e762d845e..d9b01ea41 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -1,36 +1,40 @@
 # Releasing
 
-Our build system automatically compiles and attaches cross-platform binaries to any git tag named `vX.Y.Z`. The changelog is [generated from git commit log](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes).
-
-Users who run official builds of `gh` on their machines will get notified about the new version within a 24 hour period.
-
-To test out the build system, publish a prerelease tag with a name such as `vX.Y.Z-pre.0` or `vX.Y.Z-rc.1`. Note that such a release will still be public, but it will be marked as a "prerelease", meaning that it will never show up as a "latest" release nor trigger upgrade notifications for users.
+To initiate a new production deployment:
+```sh
+script/release vX.Y.Z
+```
+See `script/release --help` for more information.
+
+> **Note:**  
+> Every production release will request an approval by the select few people before it can proceed.
+
+What this does is:
+- Builds Linux binaries on Ubuntu;
+- Builds and signs Windows binaries on Windows;
+- Builds, signs, and notarizes macOS binaries on macOS;
+- Uploads all release artifacts to a new GitHub Release;
+- A new git tag `vX.Y.Z` is created in the remote repository;
+- The changelog is [generated from the list of merged pull requests](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes);
+- Updates cli.github.com with the contents of the new release;
+- Updates our Homebrew formula in the homebrew-core repo.
+
+To test out the build system while avoiding creating an actual release:
+```sh
+script/release --staging vX.Y.Z --branch patch-1 -p macos
+```
+The build artifacts will be available via `gh run download <RUN> -n macos`.
 
 ## General guidelines
 
 * Features to be released should be reviewed and approved at least one day prior to the release.
 * Feature releases should bump up the minor version number.
+* Breaking releases should bump up the major version number. These should generally be rare.
 
-## Tagging a new release
-
-1. `git tag v1.2.3 && git push origin v1.2.3`
-2. Wait several minutes for builds to run: <https://github.com/cli/cli/actions>
-3. Verify release is displayed and has correct assets: <https://github.com/cli/cli/releases>
-4. Scan generated release notes and optionally add a human touch by grouping items under topic sections
-5. Verify the marketing site was updated: <https://cli.github.com>
-6. (Optional) Delete any pre-releases related to this release
-
-A successful build will result in changes across several repositories:
-* <https://github.com/github/cli.github.com>
-* <https://github.com/Homebrew/homebrew-core/pulls>
-* <https://github.com/cli/scoop-gh>
-
-If the build fails, there is not a clean way to re-run it. The easiest way would be to start over by deleting the partial release on GitHub and re-publishing the tag. Note that this might be disruptive to users or tooling that were already notified about an upgrade. If a functional release and its binaries are already out there, it might be better to try to manually fix up only the specific workflow tasks that failed. Use your best judgement depending on the failure type.
-
-## Release locally for debugging
+## Test the build system locally
 
 A local release can be created for testing without creating anything official on the release page.
 
 1. Make sure GoReleaser is installed: `brew install goreleaser`
-2. `goreleaser --skip-validate --skip-publish --rm-dist`
+2. `script/release --local`
 3. Find the built products under `dist/`.
diff --git a/pkg/cmd/pr/status/fixtures/prStatus.json b/pkg/cmd/pr/status/fixtures/prStatus.json
index a226663a3..d5cfb265a 100644
--- a/pkg/cmd/pr/status/fixtures/prStatus.json
+++ b/pkg/cmd/pr/status/fixtures/prStatus.json
@@ -15,7 +15,8 @@
               "headRepositoryOwner": {
                 "login": "OWNER"
               },
-              "isCrossRepository": false
+              "isCrossRepository": false,
+              "autoMergeRequest": null
             }
           }
         ]
@@ -31,7 +32,8 @@
             "state": "OPEN",
             "url": "https://github.com/cli/cli/pull/8",
             "headRefName": "strawberries",
-            "isDraft": false
+            "isDraft": false,
+            "autoMergeRequest": null
           }
         }
       ]
@@ -46,7 +48,17 @@
             "state": "OPEN",
             "url": "https://github.com/cli/cli/pull/9",
             "headRefName": "apples",
-            "isDraft": false
+            "isDraft": false,
+            "autoMergeRequest": {
+              "authorEmail": null,
+              "commitBody": null,
+              "commitHeadline": null,
+              "mergeMethod": "SQUASH",
+              "enabledAt": "2020-08-27T19:00:12Z",
+              "enabledBy": {
+                "login": "hubot"
+              }
+            }
           } }, {
           "node": {
             "number": 11,
@@ -54,7 +66,8 @@
             "state": "OPEN",
             "url": "https://github.com/cli/cli/pull/1",
             "headRefName": "figs",
-            "isDraft": true
+            "isDraft": true,
+            "autoMergeRequest": null
           }
         }
       ]
diff --git a/pkg/cmd/pr/status/http.go b/pkg/cmd/pr/status/http.go
index e0b2145e9..49bdb147c 100644
--- a/pkg/cmd/pr/status/http.go
+++ b/pkg/cmd/pr/status/http.go
@@ -191,7 +191,7 @@ func pullRequestFragment(hostname string, conflictStatus bool) (string, error) {
 	fields := []string{
 		"number", "title", "state", "url", "isDraft", "isCrossRepository",
 		"headRefName", "headRepositoryOwner", "mergeStateStatus",
-		"statusCheckRollup", "requiresStrictStatusChecks",
+		"statusCheckRollup", "requiresStrictStatusChecks", "autoMergeRequest",
 	}
 
 	if conflictStatus {
diff --git a/pkg/cmd/pr/status/status.go b/pkg/cmd/pr/status/status.go
index c2d4271d0..fa517bada 100644
--- a/pkg/cmd/pr/status/status.go
+++ b/pkg/cmd/pr/status/status.go
@@ -284,6 +284,10 @@ func printPrs(io *iostreams.IOStreams, totalCount int, prs ...api.PullRequest) {
 				}
 			}
 
+			if pr.AutoMergeRequest != nil {
+				fmt.Fprintf(w, " %s", cs.Green("✓ Auto-merge enabled"))
+			}
+
 		} else {
 			fmt.Fprintf(w, " - %s", shared.StateTitleWithColor(cs, pr))
 		}
diff --git a/pkg/cmd/pr/view/fixtures/prViewPreviewWithAutoMergeEnabled.json b/pkg/cmd/pr/view/fixtures/prViewPreviewWithAutoMergeEnabled.json
new file mode 100644
index 000000000..000dc440e
--- /dev/null
+++ b/pkg/cmd/pr/view/fixtures/prViewPreviewWithAutoMergeEnabled.json
@@ -0,0 +1,55 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "number": 12,
+        "title": "Blueberries are from a fork",
+        "state": "OPEN",
+        "body": "**blueberries taste good**",
+        "url": "https://github.com/OWNER/REPO/pull/12",
+        "author": {
+          "login": "nobody"
+        },
+        "autoMergeRequest": {
+          "authorEmail": null,
+          "commitBody": null,
+          "commitHeadline": null,
+          "mergeMethod": "SQUASH",
+          "enabledAt": "2020-08-27T19:00:12Z",
+          "enabledBy": {
+            "login": "hubot"
+          }
+        },
+        "additions": 100,
+        "deletions": 10,
+        "reviewRequests": {
+            "nodes": [],
+            "totalcount": 0
+        },
+        "assignees": {
+          "nodes": [],
+          "totalcount": 0
+        },
+        "labels": {
+          "nodes": [],
+          "totalcount": 0
+        },
+        "projectcards": {
+          "nodes": [],
+          "totalcount": 0
+        },
+        "milestone": {},
+        "commits": {
+          "totalCount": 12
+        },
+        "baseRefName": "master",
+        "headRefName": "blueberries",
+        "headRepositoryOwner": {
+          "login": "hubot"
+        },
+        "isCrossRepository": true,
+        "isDraft": false
+      }
+    }
+  }
+}
diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go
index 10300a23c..71bda0649 100644
--- a/pkg/cmd/pr/view/view.go
+++ b/pkg/cmd/pr/view/view.go
@@ -77,7 +77,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
 }
 
 var defaultFields = []string{
-	"url", "number", "title", "state", "body", "author",
+	"url", "number", "title", "state", "body", "author", "autoMergeRequest",
 	"isDraft", "maintainerCanModify", "mergeable", "additions", "deletions", "commitsCount",
 	"baseRefName", "headRefName", "headRepositoryOwner", "headRepository", "isCrossRepository",
 	"reviewRequests", "reviews", "assignees", "labels", "projectCards", "milestone",
@@ -157,6 +157,15 @@ func printRawPrPreview(io *iostreams.IOStreams, pr *api.PullRequest) error {
 	fmt.Fprintf(out, "url:\t%s\n", pr.URL)
 	fmt.Fprintf(out, "additions:\t%s\n", cs.Green(strconv.Itoa(pr.Additions)))
 	fmt.Fprintf(out, "deletions:\t%s\n", cs.Red(strconv.Itoa(pr.Deletions)))
+	var autoMerge string
+	if pr.AutoMergeRequest == nil {
+		autoMerge = "disabled"
+	} else {
+		autoMerge = fmt.Sprintf("enabled\t%s\t%s",
+			pr.AutoMergeRequest.EnabledBy.Login,
+			strings.ToLower(pr.AutoMergeRequest.MergeMethod))
+	}
+	fmt.Fprintf(out, "auto-merge:\t%s\n", autoMerge)
 
 	fmt.Fprintln(out, "--")
 	fmt.Fprintln(out, pr.Body)
@@ -223,6 +232,29 @@ func printHumanPrPreview(opts *ViewOptions, pr *api.PullRequest) error {
 		fmt.Fprintln(out, pr.Milestone.Title)
 	}
 
+	// Auto-Merge status
+	autoMerge := pr.AutoMergeRequest
+	if autoMerge != nil {
+		var mergeMethod string
+		switch autoMerge.MergeMethod {
+		case "MERGE":
+			mergeMethod = "a merge commit"
+		case "REBASE":
+			mergeMethod = "rebase and merge"
+		case "SQUASH":
+			mergeMethod = "squash and merge"
+		default:
+			mergeMethod = fmt.Sprintf("an unknown merge method (%s)", autoMerge.MergeMethod)
+		}
+		fmt.Fprintf(out,
+			"%s %s by %s, using %s\n",
+			cs.Bold("Auto-merge:"),
+			cs.Green("enabled"),
+			autoMerge.EnabledBy.Login,
+			mergeMethod,
+		)
+	}
+
 	// Body
 	var md string
 	var err error
diff --git a/script/label-assets b/script/label-assets
new file mode 100755
index 000000000..8c06c4b91
--- /dev/null
+++ b/script/label-assets
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -e
+
+if [ $# -eq 0 ]; then
+  echo "usage: script/label-assets dist/gh_*" >&2
+  exit 1
+fi
+
+for asset; do
+  label="$(basename "$asset")"
+  label="${label%.*}"
+  label="${label%.tar}"
+  label="GitHub CLI $(tr '_' ' ' <<<"${label#gh_}")"
+  case "$asset" in
+  *.msi ) label="${label} installer" ;;
+  *.deb ) label="${label} deb" ;;
+  *.rpm ) label="${label} RPM" ;;
+  esac
+  printf "%s#%s\n" "$asset" "$label"
+done
\ No newline at end of file
diff --git a/script/release b/script/release
new file mode 100755
index 000000000..774a8c1dd
--- /dev/null
+++ b/script/release
@@ -0,0 +1,115 @@
+#!/bin/bash
+set -e
+
+print_help() {
+    cat <<EOF
+To tag a new release:
+  script/release [--staging] <tag-name> [--platform {linux|macos|windows}] [--branch <branch>]
+
+To build staging binaries from the current branch:
+  script/release --current [--platform {linux|macos|windows}]
+
+To build binaries locally with goreleaser:
+  script/release --local --platform {linux|macos|windows}
+EOF
+}
+
+if [ $# -eq 0 ]; then
+  print_help >&2
+  exit 1
+fi
+
+tag_name=""
+is_local=""
+do_push=""
+platform=""
+branch="trunk"
+deploy_env="production"
+
+while [ $# -gt 0 ]; do
+  case "$1" in
+  -h | --help )
+    print_help
+    exit 0
+    ;;
+  -b | --branch )
+    branch="$2"
+    shift 2
+    ;;
+  -p | --platform )
+    platform="$2"
+    shift 2
+    ;;
+  --local )
+    is_local=1
+    shift 1
+    ;;
+  --staging )
+    deploy_env="staging"
+    shift 1
+    ;;
+  --current )
+    deploy_env="staging"
+    tag_name="$(git describe --tags --abbrev=0)"
+    branch="$(git rev-parse --symbolic-full-name '@{upstream}' 2>/dev/null || git branch --show-current)"
+    branch="${branch#refs/remotes/*/}"
+    do_push=1
+    shift 1
+    ;;
+  -* )
+    printf "unrecognized flag: %s\n" "$1" >&2
+    exit 1
+    ;;
+  * )
+    tag_name="$1"
+    shift 1
+    ;;
+  esac
+done
+
+announce() {
+  local tmpdir="${TMPDIR:-/tmp}"
+  echo "$*" | sed "s:${tmpdir%/}:\$TMPDIR:"
+  "$@"
+}
+
+trigger_deployment() {
+  announce gh workflow -R cli/cli run deployment.yml --ref "$branch" -f tag_name="$tag_name" -f environment="$deploy_env"
+}
+
+build_local() {
+  local goreleaser_config=".goreleaser.yml"
+  case "$platform" in
+  linux )
+    sed '/#build:windows/,/^$/d; /#build:macos/,/^$/d' .goreleaser.yml >.goreleaser.generated.yml
+    goreleaser_config=".goreleaser.generated.yml"
+    ;;
+  macos )
+    sed '/#build:windows/,/^$/d; /#build:linux/,/^$/d' .goreleaser.yml >.goreleaser.generated.yml
+    goreleaser_config=".goreleaser.generated.yml"
+    ;;
+  windows )
+    sed '/#build:linux/,/^$/d; /#build:macos/,/^$/d' .goreleaser.yml >.goreleaser.generated.yml
+    goreleaser_config=".goreleaser.generated.yml"
+    ;;
+  esac
+  [ -z "$tag_name" ] || export GORELEASER_CURRENT_TAG="$tag_name"
+  announce goreleaser release -f "$goreleaser_config" --clean --skip-validate --skip-publish --release-notes="$(mktemp)"
+}
+
+if [ -n "$is_local" ]; then
+  build_local
+else
+  if [ -n "$do_push" ]; then
+    if ! git diff --quiet || ! git diff --cached --quiet; then
+      echo "refusing to continue due to uncomitted local changes" >&2
+      exit 1
+    fi
+    announce git push
+  fi
+  trigger_deployment
+  if [ "$deploy_env" = "production" ]; then
+    echo
+    echo "Go to Slack to manually approve this production deployment."
+  fi
+fi
diff --git a/script/scoop-gen b/script/scoop-gen
deleted file mode 100755
index 2e4adf647..000000000
--- a/script/scoop-gen
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-# Usage: cat checksums.txt | script/scoop-gen <version> <json-file>
-set -e
-
-tagname="${1?}"
-jsonfile="${2?}"
-
-jq_args=(
-  --arg version "${tagname#v}"
-  --arg urlprefix "https://github.com/cli/cli/releases/download/$tagname/"
-  $(cat | awk '
-    /windows_386/ {
-      printf "--arg win32hash %s\n", $1
-      printf "--arg win32file %s\n", $2
-    }
-    /windows_amd64/ {
-      printf "--arg win64hash %s\n", $1
-      printf "--arg win64file %s\n", $2
-    }
-  ')
-)
-
-jq '
-  .version = $version |
-  .architecture."32bit".url  = $urlprefix + $win32file |
-  .architecture."32bit".hash = $win32hash |
-  .architecture."64bit".url  = $urlprefix + $win64file |
-  .architecture."64bit".hash = $win64hash
-' "${jq_args[@]}" --indent 4 "$jsonfile" > "$jsonfile"~
-
-mv "$jsonfile"{~,}
diff --git a/script/sign b/script/sign
new file mode 100755
index 000000000..1630a06b5
--- /dev/null
+++ b/script/sign
@@ -0,0 +1,65 @@
+#!/bin/bash
+# usage: script/sign <file>
+#
+# Signs macOS binaries using codesign, notarizes macOS zip archives using notarytool, and signs
+# Windows EXE and MSI files using osslsigncode.
+#
+set -e
+
+sign_windows() {
+  if [ -z "$CERT_FILE" ]; then
+    echo "skipping Windows code-signing; CERT_FILE not set" >&2
+    return 0
+  fi
+
+  if [ ! -f "$CERT_FILE" ]; then
+    echo "error Windows code-signing; file '$CERT_FILE' not found" >&2
+    return 1
+  fi
+
+  if [ -z "$CERT_PASSWORD" ]; then
+    echo "error Windows code-signing; no value for CERT_PASSWORD" >&2
+    return 1
+  fi
+
+  osslsigncode sign -n "GitHub CLI" -t http://timestamp.digicert.com \
+    -pkcs12 "$CERT_FILE" -readpass <(printf "%s" "$CERT_PASSWORD") -h sha256 \
+    -in "$1" -out "$1"~
+
+  mv "$1"~ "$1"
+}
+
+sign_macos() {
+  if [ -z "$APPLE_DEVELOPER_ID" ]; then
+    echo "skipping macOS code-signing; APPLE_DEVELOPER_ID not set" >&2
+    return 0
+  fi
+
+  if [[ $1 == *.zip ]]; then
+    xcrun notarytool submit "$1" --apple-id "${APPLE_ID?}" --team-id "${APPLE_DEVELOPER_ID?}" --password "${APPLE_ID_PASSWORD?}"
+  else
+    codesign --timestamp --options=runtime -s "${APPLE_DEVELOPER_ID?}" -v "$1"
+  fi
+}
+
+if [ $# -eq 0 ]; then
+  echo "usage: script/sign <file>" >&2
+  exit 1
+fi
+
+platform="$(uname -s)"
+
+for input_file; do
+  case "$input_file" in
+  *.exe | *.msi )
+    sign_windows "$input_file"
+    ;;
+  * )
+    if [ "$platform" = "Darwin" ]; then
+      sign_macos "$input_file"
+    else
+      printf "warning: don't know how to sign %s on %s\n" "$1", "$platform" >&2
+    fi
+    ;;
+  esac
+done
\ No newline at end of file
diff --git a/script/sign-windows-executable.sh b/script/sign-windows-executable.sh
deleted file mode 100755
index d89e6dbe4..000000000
--- a/script/sign-windows-executable.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-set -e
-
-EXE="$1"
-
-if [ -z "$CERT_FILE" ]; then
-  echo "skipping Windows code-signing; CERT_FILE not set" >&2
-  exit 0
-fi
-
-if [ ! -f "$CERT_FILE" ]; then
-  echo "error Windows code-signing; file '$CERT_FILE' not found" >&2
-  exit 1
-fi
-
-if [ -z "$CERT_PASSWORD" ]; then
-  echo "error Windows code-signing; no value for CERT_PASSWORD" >&2
-  exit 1
-fi
-
-osslsigncode sign -n "GitHub CLI" -t http://timestamp.digicert.com \
-  -pkcs12 "$CERT_FILE" -readpass <(printf "%s" "$CERT_PASSWORD") -h sha256 \
-  -in "$EXE" -out "$EXE"~
-
-mv "$EXE"~ "$EXE"
diff --git a/script/sign.bat b/script/sign.bat
new file mode 100644
index 000000000..4fbe8a7af
--- /dev/null
+++ b/script/sign.bat
@@ -0,0 +1,8 @@
+@echo off
+
+if "%CERT_FILE%" == "" (
+    echo skipping Windows code-signing; CERT_FILE not set
+    exit /b
+)
+
+.\script\signtool sign /d "GitHub CLI" /f "%CERT_FILE%" /p "%CERT_PASSWORD%" /fd sha256 /tr http://timestamp.digicert.com /v "%1"
\ No newline at end of file
diff --git a/script/sign.ps1 b/script/sign.ps1
deleted file mode 100644
index 336e0204c..000000000
--- a/script/sign.ps1
+++ /dev/null
@@ -1,12 +0,0 @@
-param (
-    [string]$Certificate = $(throw "-Certificate is required."),
-    [string]$Executable = $(throw "-Executable is required.")
-)
-
-Set-StrictMode -Version Latest
-$ErrorActionPreference = "Stop"
-
-$ProgramName = "GitHub CLI"
-$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
-
-& $scriptPath\signtool.exe sign /d $ProgramName /f $Certificate /p $env:CERT_PASSWORD /fd sha256 /tr http://timestamp.digicert.com /v $Executable


Test patch:
diff --git a/pkg/cmd/pr/status/status_test.go b/pkg/cmd/pr/status/status_test.go
index f94822e47..1b39ddb24 100644
--- a/pkg/cmd/pr/status/status_test.go
+++ b/pkg/cmd/pr/status/status_test.go
@@ -91,7 +91,7 @@ func TestPRStatus(t *testing.T) {
 
 	expectedPrs := []*regexp.Regexp{
 		regexp.MustCompile(`#8.*\[strawberries\]`),
-		regexp.MustCompile(`#9.*\[apples\]`),
+		regexp.MustCompile(`#9.*\[apples\].*✓ Auto-merge enabled`),
 		regexp.MustCompile(`#10.*\[blueberries\]`),
 		regexp.MustCompile(`#11.*\[figs\]`),
 	}
diff --git a/pkg/cmd/pr/view/view_test.go b/pkg/cmd/pr/view/view_test.go
index 166289050..91b77cac8 100644
--- a/pkg/cmd/pr/view/view_test.go
+++ b/pkg/cmd/pr/view/view_test.go
@@ -314,6 +314,25 @@ func TestPRView_Preview_nontty(t *testing.T) {
 				`\*\*blueberries taste good\*\*`,
 			},
 		},
+		"PR with auto-merge enabled": {
+			branch: "master",
+			args:   "12",
+			fixtures: map[string]string{
+				"PullRequestByNumber": "./fixtures/prViewPreviewWithAutoMergeEnabled.json",
+			},
+			expectedOutputs: []string{
+				`title:\tBlueberries are from a fork\n`,
+				`state:\tOPEN\n`,
+				`author:\tnobody\n`,
+				`labels:\t\n`,
+				`assignees:\t\n`,
+				`projects:\t\n`,
+				`milestone:\t\n`,
+				`additions:\t100\n`,
+				`deletions:\t10\n`,
+				`auto-merge:\tenabled\thubot\tsquash\n`,
+			},
+		},
 	}
 
 	for name, tc := range tests {
@@ -504,6 +523,20 @@ func TestPRView_Preview(t *testing.T) {
 				`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
 			},
 		},
+		"PR with auto-merge enabled": {
+			branch: "master",
+			args:   "12",
+			fixtures: map[string]string{
+				"PullRequestByNumber": "./fixtures/prViewPreviewWithAutoMergeEnabled.json",
+			},
+			expectedOutputs: []string{
+				`Blueberries are from a fork #12\n`,
+				`Open.*nobody wants to merge 12 commits into master from blueberries . about X years ago`,
+				`Auto-merge:.*enabled.* by hubot, using squash and merge`,
+				`blueberries taste good`,
+				`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
+			},
+		},
 	}
 
 	for name, tc := range tests {