diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
index 51059a669..9655d19bf 100644
--- a/.github/workflows/deployment.yml
+++ b/.github/workflows/deployment.yml
@@ -108,6 +108,19 @@ jobs:
run: |
shopt -s failglob
script/sign dist/gh_*_macOS_*.zip
+ - name: Build universal macOS pkg installer
+ if: inputs.environment != 'production'
+ env:
+ TAG_NAME: ${{ inputs.tag_name }}
+ run: script/pkgmacos "$TAG_NAME"
+ - name: Build & notarize universal macOS pkg installer
+ if: inputs.environment == 'production'
+ env:
+ TAG_NAME: ${{ inputs.tag_name }}
+ APPLE_DEVELOPER_INSTALLER_ID: ${{ vars.APPLE_DEVELOPER_INSTALLER_ID }}
+ run: |
+ shopt -s failglob
+ script/pkgmacos "$TAG_NAME"
- uses: actions/upload-artifact@v4
with:
name: macos
@@ -116,7 +129,8 @@ jobs:
path: |
dist/*.tar.gz
dist/*.zip
-
+ dist/*.pkg
+
windows:
runs-on: windows-latest
environment: ${{ inputs.environment }}
diff --git a/.gitignore b/.gitignore
index b1e8e1fa8..b2b66aaf7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,8 @@
/.goreleaser.generated.yml
/script/build
/script/build.exe
+/pkg_payload
+/build/macOS/resources
# VS Code
.vscode
diff --git a/Makefile b/Makefile
index 7dac0d290..e68b68939 100644
--- a/Makefile
+++ b/Makefile
@@ -93,3 +93,11 @@ uninstall:
rm -f ${DESTDIR}${datadir}/bash-completion/completions/gh
rm -f ${DESTDIR}${datadir}/fish/vendor_completions.d/gh.fish
rm -f ${DESTDIR}${datadir}/zsh/site-functions/_gh
+
+.PHONY: macospkg
+macospkg: manpages completions
+ifndef VERSION
+ $(error VERSION is not set. Use `make macospkg VERSION=vX.Y.Z`)
+endif
+ ./script/release --local "$(VERSION)" --platform macos
+ ./script/pkgmacos $(VERSION)
diff --git a/README.md b/README.md
index 6e0c7de6a..f637eabcf 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ If you are a hubber and are interested in shipping new commands for the CLI, che
### macOS
-`gh` is available via [Homebrew][], [MacPorts][], [Conda][], [Spack][], [Webi][], and as a downloadable binary from the [releases page][].
+`gh` is available via [Homebrew][], [MacPorts][], [Conda][], [Spack][], [Webi][], and as a downloadable binary including Mac OS installer `.pkg` from the [releases page][].
#### Homebrew
diff --git a/build/macOS/distribution.xml b/build/macOS/distribution.xml
new file mode 100644
index 000000000..dc93628f8
--- /dev/null
+++ b/build/macOS/distribution.xml
@@ -0,0 +1,33 @@
+
+
+ GitHub CLI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #com.github.cli.pkg
+
diff --git a/script/pkgmacos b/script/pkgmacos
new file mode 100755
index 000000000..a5c9134f1
--- /dev/null
+++ b/script/pkgmacos
@@ -0,0 +1,129 @@
+#!/bin/zsh
+set -e
+
+print_help() {
+ cat <
+
+To build and sign set APPLE_DEVELOPER_INSTALLER_ID environment variable before.
+For example, if you have a signing identity with the identifier
+"Developer ID Installer: Your Name (ABC123DEF)" set it in the variable.
+EOF
+}
+
+if [ $# -eq 0 ]; then
+ print_help >&2
+ exit 1
+fi
+
+tag_name=""
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h | --help )
+ print_help
+ exit 0
+ ;;
+ -* )
+ printf "unrecognized flag: %s\n" "$1" >&2
+ exit 1
+ ;;
+ * )
+ tag_name="${1#v}"
+ shift 1
+ ;;
+ esac
+done
+
+# check os requirements: is running macOS 12+ and pkgbuild + productbuild are available
+os_version=$(sw_vers -productVersion)
+major_version=${os_version%%.*}
+
+if (( major_version < 12 )); then
+ echo "This script requires macOS 12 or later. You are running macOS ${os_version}." >&2
+ exit 1
+fi
+
+if ! command -v pkgbuild &> /dev/null; then
+ echo "pkgbuild could not be found. Please install Xcode Command Line Tools." >&2
+ exit 1
+fi
+
+if ! command -v productbuild &> /dev/null; then
+ echo "productbuild could not be found. Please install Xcode Command Line Tools." >&2
+ exit 1
+fi
+# end of os requirements check
+
+# gh-binary paths
+bin_path="/bin/gh"
+arm64_bin="./dist/macos_darwin_arm64$bin_path"
+amd64_bin="./dist/macos_darwin_amd64_v1$bin_path"
+# payload paths
+payload_root="pkg_payload"
+payload_local_bin="${payload_root}/usr/local/bin"
+payload_zsh_site_functions="${payload_root}/usr/local/share/zsh/site-functions"
+payload_man1="${payload_root}/usr/local/share/man/man1"
+
+merge_binaries() {
+ lipo -create -output "${payload_local_bin}/gh" "$arm64_bin" "$amd64_bin"
+}
+
+build_pkg() {
+ # setup payload
+ mkdir -p "${payload_local_bin}"
+ mkdir -p "${payload_man1}"
+ mkdir -p "${payload_zsh_site_functions}"
+
+ # copy man pages
+ for file in ./share/man/man1/gh*.1; do
+ cp "$file" "${payload_man1}"
+ done
+ # Include only Zsh completions,
+ # the recommended/only option on macOS since Catalina for default shell.
+ cp "./share/zsh/site-functions/_gh" "${payload_zsh_site_functions}"
+
+ # merge binaries
+ merge_binaries
+
+ # build pkg
+ pkgbuild \
+ --root "$payload_root" \
+ --identifier "com.github.cli" \
+ --version "$tag_name" \
+ --install-location "/" \
+ "./dist/com.github.cli.pkg"
+
+ # setup resources
+ mkdir -p "./build/macOS/resources"
+ cp "LICENSE" "./build/macOS/resources"
+
+ PRODUCTBUILD_ARGS=()
+
+ # include signing if developer id is set
+ if [ -n "$APPLE_DEVELOPER_INSTALLER_ID" ]; then
+ PRODUCTBUILD_ARGS+=("--timestamp")
+ PRODUCTBUILD_ARGS+=("--sign")
+ PRODUCTBUILD_ARGS+=("${APPLE_DEVELOPER_INSTALLER_ID}")
+ else
+ echo "skipping macOS pkg code-signing; APPLE_DEVELOPER_INSTALLER_ID not set" >&2
+ fi
+
+ # build distribution
+ productbuild \
+ --distribution "./build/macOS/distribution.xml" \
+ --resources "./build/macOS/resources" \
+ --package-path "./dist" \
+ "${PRODUCTBUILD_ARGS[@]}" \
+ "./dist/gh_${tag_name}_macOS_universal.pkg"
+}
+
+cleanup() {
+ # remove temp installer so it does not get uploaded
+ rm -f "./dist/com.github.cli.pkg"
+}
+
+trap cleanup EXIT
+
+build_pkg