Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macOS pkg installer to deployment (#7554) #7555

Merged
merged 22 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment on lines +111 to +115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How have you been testing script/pkgmacos locally?

I was wondering how large the new .pkg files were, so tried building it locally. I thought about trying to build this locally only to realize we probably want to add a new target to Makefile as this involves multiple steps.

Here's how far I've got so far before realizing I should simply ask 😅

andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ ./script/pkgmacos 
To build a universal pkg for macOS:
  script/pkgmacos <tag-name>

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.
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ ./script/pkgmacos v0.0.0
build_pkg:7: no matches found: ./share/man/man1/gh*.1
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ vim Makefile 
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ make
go build -trimpath -ldflags "-X github.com/cli/cli/v2/internal/build.Date=2024-05-23 -X github.com/cli/cli/v2/internal/build.Version=v2.49.2-50-g9454d5e7 " -o bin/gh ./cmd/gh
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ ./script/pkgmacos v0.0.0
build_pkg:7: no matches found: ./share/man/man1/gh*.1
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ vim Makefile 
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ make manpages
go run ./cmd/gen-docs --man-page --doc-path ./share/man/man1/
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ ./script/pkgmacos v0.0.0

Copy link
Contributor Author

@paulober paulober May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerning the pkg size, its about 24MB:
-rw-r--r--@ 1 paulober staff 24M May 24 14:10 gh_v2.99.8_macOS_universal.pkg

To test it locally:

make manpages
make completions
./script/release --local "v2.99.8" --platform macos
./script/pkgmacos v2.99.8

And for the Makefile target. Maybe i can read into it. But as of now i know nothing about the syntax within a Makefile (only cmake).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take that back, it wasn't that difficult. You can now build a pkg using make macospkg VERSION=v2.99.8. (goreleaser required)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is really going to help the team, thank you for saving us future time 🫶

- 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
Expand All @@ -116,7 +129,8 @@ jobs:
path: |
dist/*.tar.gz
dist/*.zip

dist/*.pkg

windows:
runs-on: windows-latest
environment: ${{ inputs.environment }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
/.goreleaser.generated.yml
/script/build
/script/build.exe
/pkg_payload
/build/macOS/resources

# VS Code
.vscode
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines +96 to +103
Copy link
Contributor

@andyfeller andyfeller May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking with and without VERSION ❤️

andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ make macospkg
go run ./cmd/gen-docs --man-page --doc-path ./share/man/man1/
go build -trimpath -ldflags "-X github.com/cli/cli/v2/internal/build.Date=2024-05-24 -X github.com/cli/cli/v2/internal/build.Version=v2.49.2-64-g4db87793 " -o bin/gh ./cmd/gh
mkdir -p ./share/bash-completion/completions ./share/fish/vendor_completions.d ./share/zsh/site-functions
bin/gh completion -s bash > ./share/bash-completion/completions/gh
bin/gh completion -s fish > ./share/fish/vendor_completions.d/gh.fish
bin/gh completion -s zsh > ./share/zsh/site-functions/_gh
Makefile:100: *** VERSION is not set. Use `make macospkg VERSION=vX.Y.Z`.  Stop.
andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer›$ make macospkg VERSION=v0.0.0
go run ./cmd/gen-docs --man-page --doc-path ./share/man/man1/
build.go: `bin/gh` is up to date.
mkdir -p ./share/bash-completion/completions ./share/fish/vendor_completions.d ./share/zsh/site-functions
bin/gh completion -s bash > ./share/bash-completion/completions/gh
bin/gh completion -s fish > ./share/fish/vendor_completions.d/gh.fish
bin/gh completion -s zsh > ./share/zsh/site-functions/_gh
./script/release --local "v0.0.0" --platform macos
goreleaser release -f .goreleaser.generated.yml --clean --skip-validate --skip-publish --release-notes=$TMPDIR/tmp.6ZAch4nuYr
  • starting release...
  • loading                                          path=.goreleaser.generated.yml
  • DEPRECATED: --skip-publish was deprecated in favor of --skip=publish, check https://goreleaser.com/deprecations#-skip for more details
  • DEPRECATED: --skip-validate was deprecated in favor of --skip=validate, check https://goreleaser.com/deprecations#-skip for more details
  • skipping announce, publish and validate...
  • loading environment variables
  • getting and validating git state
    • couldn't find any tags before "v0.0.0"
    • git state                                      commit=4db87793cd2315a18a33a9b56e6234b594b8b16a branch=feature-macos-pkg-installer current_tag=v0.0.0 previous_tag=<unknown> dirty=false
    • pipe skipped                                   reason=validation is disabled
  • parsing tag
  • setting defaults
    • DEPRECATED:  archives.rlcp  should not be used anymore, check https://goreleaser.com/deprecations#archivesrlcp for more info
    • DEPRECATED:  archives.rlcp  should not be used anymore, check https://goreleaser.com/deprecations#archivesrlcp for more info
    • DEPRECATED:  archives.rlcp  should not be used anymore, check https://goreleaser.com/deprecations#archivesrlcp for more info
  • running before hooks
    • running                                        hook= make manpages GH_VERSION=0.0.0
    • running                                        hook=echo make completions
    • took: 2s
  • checking distribution directory
    • cleaning dist
  • loading go mod information
  • build prerequisites
  • writing effective config file
    • writing                                        config=dist/config.yaml
  • building binaries
    • building                                       binary=dist/macos_darwin_arm64/bin/gh
    • building                                       binary=dist/macos_darwin_amd64_v1/bin/gh
    • running hook                                   hook=./script/sign '/Users/andyfeller/Documents/workspace/cli/cli/dist/macos_darwin_arm64/bin/gh'
skipping macOS code-signing; APPLE_DEVELOPER_ID not set
    • running hook                                   hook=./script/sign '/Users/andyfeller/Documents/workspace/cli/cli/dist/macos_darwin_amd64_v1/bin/gh'
skipping macOS code-signing; APPLE_DEVELOPER_ID not set
    • took: 9s
  • generating changelog
    • loaded "/var/folders/y5/q89b76cj7ws55x3trcyllf_80000gn/T/tmp.6ZAch4nuYr", but it is empty
  • archives
    • creating                                       archive=dist/gh_0.0.0_macOS_amd64.zip
    • creating                                       archive=dist/gh_0.0.0_macOS_arm64.zip
    • took: 3s
  • calculating checksums
  • storing release metadata
    • writing                                        file=dist/artifacts.json
    • writing                                        file=dist/metadata.json
  • you are using deprecated options, check the output above for details
  • release succeeded after 14s
  • thanks for using goreleaser!
./script/pkgmacos v0.0.0
pkgbuild: Inferring bundle components from contents of pkg_payload
pkgbuild: Wrote package to ./dist/com.github.cli.pkg
skipping macOS pkg code-signing; APPLE_DEVELOPER_INSTALLER_ID not set
productbuild: Wrote product to ./dist/gh_v0.0.0_macOS_universal.pkg

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
33 changes: 33 additions & 0 deletions build/macOS/distribution.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<installer-gui-script minSpecVersion="2">
<title>GitHub CLI</title>
<license file="LICENSE" mime-type="text/plain"/>
<options hostArchitectures="arm64,x86_64" customize="never" require-scripts="true" allow-external-scripts="false"/>
<domains enable_localSystem="true"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should GitHub CLI be system-wide, per-user, or can be anywhere?

GitHub Desktop zips up the application and on start up will ask the user if they want to move it from Downloads (in my case) to Applications.

Screenshot of GitHub Desktop startup, asking if user wants to relocate it to Applications directory

Reference

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williammartin : would appreciate your 2 cents here as brew installs packages such that any user on the workstation can add /opt/homebrew to their path where the configuration is per user.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd be inclined to say /usr/local/bin. When I look at the contents of my /usr/local/bin it feels like a familiar idea. Homebrew seems to have a variety of reasons relating to Apple Silicon to use /opt/homebrew.

I would anticipate the vast majority of users:

  • Don't feel the need to use amd64 and arm versions of gh
  • Have a single user account on their personal machine

So I think I would suggest we sidestep that mess and install into /usr/local/bin, knowing that it's on the path already.

What do you think?

<installation-check script="installCheck();"/>
<script>
function installCheck() {
// this check is redundant, but it produces a user friendly error message
// compared to a disabled install button caused by allowed-os-versions
if (!(system.compareVersions(system.version.ProductVersion, '12') &gt;= 0)) {
my.result.title = 'Unable to install';
my.result.message = 'GitHub CLI requires macOS 12 or later.';
my.result.type = 'Fatal';
return false;
}
return true;
}
</script>
<allowed-os-versions>
<os-version min="12.0" />
</allowed-os-versions>

<choices-outline>
<line choice="gh-cli"/>
</choices-outline>
<choice id="gh-cli" title="GitHub CLI (universal)">
<pkg-ref id="com.github.cli.pkg"/>
</choice>

<pkg-ref id="com.github.cli.pkg" auth="root">#com.github.cli.pkg</pkg-ref>
</installer-gui-script>
129 changes: 129 additions & 0 deletions script/pkgmacos
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/bin/zsh
set -e

print_help() {
cat <<EOF
To build a universal pkg for macOS:
script/pkgmacos <tag-name>

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
}
Comment on lines +1 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulober : Is this something you've written wholesale or is this based on any existing OSS package script we should reference?

Copy link
Contributor Author

@paulober paulober May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote this but i looked up some of your style in the other build files (like the print_help) while learning your code-base. I did mention the developer environment variable after i found out how the build stuff for macOS targets work to make it easier for others to use this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the insight, just to be clear this was pure curiosity! I am always learning by looking at variations on the same approach, like listening to covers of a specific song to appreciate nuances.


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}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulober : just a heads up, I pushed up a small change to strip the leading v so the resulting artifact matched the others 👍

andyfeller@Andys-MBP:cli/cli ‹feature-macos-pkg-installer*›$ ll dist 
total 103056
-rw-r--r--  1 andyfeller  staff   1.2K May 24 15:09 artifacts.json
-rw-r--r--  1 andyfeller  staff   4.0K May 24 15:09 config.yaml
-rw-r--r--  1 andyfeller  staff   182B May 24 15:09 gh_0.0.0_checksums.txt
-rw-r--r--  1 andyfeller  staff    13M May 24 15:09 gh_0.0.0_macOS_amd64.zip
-rw-r--r--  1 andyfeller  staff    12M May 24 15:09 gh_0.0.0_macOS_arm64.zip
-rw-r--r--  1 andyfeller  staff    24M May 24 15:09 gh_0.0.0_macOS_universal.pkg
drwxr-xr-x  3 andyfeller  staff    96B May 24 15:09 macos_darwin_amd64_v1
drwxr-xr-x  3 andyfeller  staff    96B May 24 15:09 macos_darwin_arm64
-rw-r--r--  1 andyfeller  staff   211B May 24 15:09 metadata.json

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
Loading