Skip to content

Commit 2331626

Browse files
vdyedscho
authored andcommitted
Merge pull request #399 from vdye/feature/build-installers
Implement workflow to create GitHub release with attached `git` installers
2 parents f24fa85 + a7616bf commit 2331626

File tree

11 files changed

+1190
-0
lines changed

11 files changed

+1190
-0
lines changed

.github/macos-installer/Makefile

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
SHELL := /bin/bash
2+
SUDO := sudo
3+
C_INCLUDE_PATH := /usr/include
4+
CPLUS_INCLUDE_PATH := /usr/include
5+
LD_LIBRARY_PATH := /usr/lib
6+
7+
OSX_VERSION := $(shell sw_vers -productVersion)
8+
TARGET_FLAGS := -mmacosx-version-min=$(OSX_VERSION) -DMACOSX_DEPLOYMENT_TARGET=$(OSX_VERSION)
9+
10+
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
11+
12+
ARCH_UNIV := universal
13+
ARCH_FLAGS := -arch x86_64 -arch arm64
14+
15+
CFLAGS := $(TARGET_FLAGS) $(ARCH_FLAGS)
16+
LDFLAGS := $(TARGET_FLAGS) $(ARCH_FLAGS)
17+
18+
PREFIX := /usr/local
19+
GIT_PREFIX := $(PREFIX)/git
20+
21+
BUILD_DIR := $(GITHUB_WORKSPACE)/payload
22+
DESTDIR := $(PWD)/stage/git-$(ARCH_UNIV)-$(VERSION)
23+
ARTIFACTDIR := build-artifacts
24+
SUBMAKE := $(MAKE) C_INCLUDE_PATH="$(C_INCLUDE_PATH)" CPLUS_INCLUDE_PATH="$(CPLUS_INCLUDE_PATH)" LD_LIBRARY_PATH="$(LD_LIBRARY_PATH)" TARGET_FLAGS="$(TARGET_FLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" NO_GETTEXT=1 NO_DARWIN_PORTS=1 prefix=$(GIT_PREFIX) DESTDIR=$(DESTDIR)
25+
CORES := $(shell bash -c "sysctl hw.ncpu | awk '{print \$$2}'")
26+
27+
# Guard against environment variables
28+
APPLE_APP_IDENTITY =
29+
APPLE_INSTALLER_IDENTITY =
30+
APPLE_KEYCHAIN_PROFILE =
31+
32+
.PHONY: image pkg payload codesign notarize
33+
34+
.SECONDARY:
35+
36+
$(DESTDIR)$(GIT_PREFIX)/VERSION-$(VERSION)-$(ARCH_UNIV):
37+
rm -f $(BUILD_DIR)/git-$(VERSION)/osx-installed*
38+
mkdir -p $(DESTDIR)$(GIT_PREFIX)
39+
touch $@
40+
41+
$(BUILD_DIR)/git-$(VERSION)/osx-built-keychain:
42+
cd $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain; $(SUBMAKE) CFLAGS="$(CFLAGS) -g -O2 -Wall"
43+
touch $@
44+
45+
$(BUILD_DIR)/git-$(VERSION)/osx-built:
46+
[ -d $(DESTDIR)$(GIT_PREFIX) ] && $(SUDO) rm -rf $(DESTDIR) || echo ok
47+
cd $(BUILD_DIR)/git-$(VERSION); $(SUBMAKE) -j $(CORES) all strip
48+
echo "================"
49+
echo "Dumping Linkage"
50+
cd $(BUILD_DIR)/git-$(VERSION); ./git version
51+
echo "===="
52+
cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git
53+
echo "===="
54+
cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-http-fetch
55+
echo "===="
56+
cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-http-push
57+
echo "===="
58+
cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-remote-http
59+
echo "===="
60+
cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-gvfs-helper
61+
echo "================"
62+
touch $@
63+
64+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-bin: $(BUILD_DIR)/git-$(VERSION)/osx-built $(BUILD_DIR)/git-$(VERSION)/osx-built-keychain
65+
cd $(BUILD_DIR)/git-$(VERSION); $(SUBMAKE) install
66+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain $(DESTDIR)$(GIT_PREFIX)/bin/git-credential-osxkeychain
67+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/contrib/completion
68+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-completion.bash $(DESTDIR)$(GIT_PREFIX)/contrib/completion/
69+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-completion.zsh $(DESTDIR)$(GIT_PREFIX)/contrib/completion/
70+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-prompt.sh $(DESTDIR)$(GIT_PREFIX)/contrib/completion/
71+
# This is needed for Git-Gui, GitK
72+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl
73+
[ ! -f $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl/Error.pm ] && cp $(BUILD_DIR)/git-$(VERSION)/perl/private-Error.pm $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl/Error.pm || echo done
74+
touch $@
75+
76+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-man: $(BUILD_DIR)/git-$(VERSION)/osx-installed-bin
77+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/share/man
78+
cp -R $(GITHUB_WORKSPACE)/manpages/ $(DESTDIR)$(GIT_PREFIX)/share/man
79+
touch $@
80+
81+
$(BUILD_DIR)/git-$(VERSION)/osx-built-subtree:
82+
$(SUBMAKE) -C $(BUILD_DIR)/git-$(VERSION)/Documentation asciidoc.conf
83+
cd $(BUILD_DIR)/git-$(VERSION)/contrib/subtree; $(SUBMAKE) XML_CATALOG_FILES="$(XML_CATALOG_FILES)" all git-subtree.1
84+
touch $@
85+
86+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-subtree: $(BUILD_DIR)/git-$(VERSION)/osx-built-subtree
87+
mkdir -p $(DESTDIR)
88+
cd $(BUILD_DIR)/git-$(VERSION)/contrib/subtree; $(SUBMAKE) XML_CATALOG_FILES="$(XML_CATALOG_FILES)" install install-man
89+
touch $@
90+
91+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-assets: $(BUILD_DIR)/git-$(VERSION)/osx-installed-bin
92+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/etc
93+
cat assets/etc/gitconfig.osxkeychain >> $(DESTDIR)$(GIT_PREFIX)/etc/gitconfig
94+
cp assets/uninstall.sh $(DESTDIR)$(GIT_PREFIX)/uninstall.sh
95+
sh -c "echo .DS_Store >> $(DESTDIR)$(GIT_PREFIX)/share/git-core/templates/info/exclude"
96+
97+
symlinks:
98+
mkdir -p $(ARTIFACTDIR)$(PREFIX)/bin
99+
cd $(ARTIFACTDIR)$(PREFIX)/bin; find ../git/bin -type f -exec ln -sf {} \;
100+
for man in man1 man3 man5 man7; do mkdir -p $(ARTIFACTDIR)$(PREFIX)/share/man/$$man; (cd $(ARTIFACTDIR)$(PREFIX)/share/man/$$man; ln -sf ../../../git/share/man/$$man/* ./); done
101+
ruby ../scripts/symlink-git-hardlinks.rb $(ARTIFACTDIR)
102+
touch $@
103+
104+
$(BUILD_DIR)/git-$(VERSION)/osx-installed: $(DESTDIR)$(GIT_PREFIX)/VERSION-$(VERSION)-$(ARCH_UNIV) $(BUILD_DIR)/git-$(VERSION)/osx-installed-man $(BUILD_DIR)/git-$(VERSION)/osx-installed-assets $(BUILD_DIR)/git-$(VERSION)/osx-installed-subtree
105+
find $(DESTDIR)$(GIT_PREFIX) -type d -exec chmod ugo+rx {} \;
106+
find $(DESTDIR)$(GIT_PREFIX) -type f -exec chmod ugo+r {} \;
107+
touch $@
108+
109+
$(BUILD_DIR)/git-$(VERSION)/osx-built-assert-$(ARCH_UNIV): $(BUILD_DIR)/git-$(VERSION)/osx-built
110+
File $(BUILD_DIR)/git-$(VERSION)/git
111+
File $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain
112+
touch $@
113+
114+
disk-image/VERSION-$(VERSION)-$(ARCH_UNIV):
115+
rm -f disk-image/*.pkg disk-image/VERSION-* disk-image/.DS_Store
116+
mkdir disk-image
117+
touch "$@"
118+
119+
pkg_cmd := pkgbuild --identifier com.git.pkg --version $(VERSION) \
120+
--root $(ARTIFACTDIR)$(PREFIX) --scripts assets/scripts \
121+
--install-location $(PREFIX) --component-plist ./assets/git-components.plist
122+
123+
ifdef APPLE_INSTALLER_IDENTITY
124+
pkg_cmd += --sign "$(APPLE_INSTALLER_IDENTITY)"
125+
endif
126+
127+
pkg_cmd += disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg
128+
disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg: disk-image/VERSION-$(VERSION)-$(ARCH_UNIV) symlinks
129+
$(pkg_cmd)
130+
131+
git-%-$(ARCH_UNIV).dmg:
132+
hdiutil create git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg -fs HFS+ -srcfolder disk-image -volname "Git $(VERSION) $(ARCH_UNIV)" -ov 2>&1 | tee err || { \
133+
grep "Resource busy" err && \
134+
sleep 5 && \
135+
hdiutil create git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg -fs HFS+ -srcfolder disk-image -volname "Git $(VERSION) $(ARCH_UNIV)" -ov; }
136+
hdiutil convert -format UDZO -o $@ git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg
137+
rm -f git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg
138+
139+
payload: $(BUILD_DIR)/git-$(VERSION)/osx-installed $(BUILD_DIR)/git-$(VERSION)/osx-built-assert-$(ARCH_UNIV)
140+
141+
pkg: disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg
142+
143+
image: git-$(VERSION)-$(ARCH_UNIV).dmg
144+
145+
ifdef APPLE_APP_IDENTITY
146+
codesign:
147+
@$(CURDIR)/../scripts/codesign.sh --payload="build-artifacts/usr/local/git" \
148+
--identity="$(APPLE_APP_IDENTITY)" \
149+
--entitlements="$(CURDIR)/entitlements.xml"
150+
endif
151+
152+
# Notarization can only happen if the package is fully signed
153+
ifdef APPLE_KEYCHAIN_PROFILE
154+
notarize:
155+
@$(CURDIR)/../scripts/notarize.sh \
156+
--package="disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg" \
157+
--keychain-profile="$(APPLE_KEYCHAIN_PROFILE)"
158+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[credential]
2+
helper = osxkeychain
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<array>
5+
<dict>
6+
<key>BundleHasStrictIdentifier</key>
7+
<true/>
8+
<key>BundleIsRelocatable</key>
9+
<false/>
10+
<key>BundleIsVersionChecked</key>
11+
<true/>
12+
<key>BundleOverwriteAction</key>
13+
<string>upgrade</string>
14+
<key>RootRelativeBundlePath</key>
15+
<string>git/share/git-gui/lib/Git Gui.app</string>
16+
</dict>
17+
</array>
18+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
INSTALL_DST="$2"
3+
SCALAR_C_CMD="$INSTALL_DST/git/bin/scalar"
4+
SCALAR_DOTNET_CMD="/usr/local/scalar/scalar"
5+
SCALAR_UNINSTALL_SCRIPT="/usr/local/scalar/uninstall_scalar.sh"
6+
7+
function cleanupScalar()
8+
{
9+
echo "checking whether Scalar was installed"
10+
if [ ! -f "$SCALAR_C_CMD" ]; then
11+
echo "Scalar not installed; exiting..."
12+
return 0
13+
fi
14+
echo "Scalar is installed!"
15+
16+
echo "looking for Scalar.NET"
17+
if [ ! -f "$SCALAR_DOTNET_CMD" ]; then
18+
echo "Scalar.NET not found; exiting..."
19+
return 0
20+
fi
21+
echo "Scalar.NET found!"
22+
23+
currentUser=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
24+
25+
# Re-register Scalar.NET repositories with the newly-installed Scalar
26+
for repo in $($SCALAR_DOTNET_CMD list); do
27+
(
28+
PATH="$INSTALL_DST/git/bin:$PATH"
29+
sudo -u "$currentUser" scalar register $repo || \
30+
echo "warning: skipping re-registration of $repo"
31+
)
32+
done
33+
34+
# Uninstall Scalar.NET
35+
echo "removing Scalar.NET"
36+
37+
# Add /usr/local/bin to path - default install location of Homebrew
38+
PATH="/usr/local/bin:$PATH"
39+
if (sudo -u "$currentUser" brew list --cask scalar); then
40+
# Remove from Homebrew
41+
sudo -u "$currentUser" brew remove --cask scalar || echo "warning: Scalar.NET uninstall via Homebrew completed with code $?"
42+
echo "Scalar.NET uninstalled via Homebrew!"
43+
elif (sudo -u "$currentUser" brew list --cask scalar-azrepos); then
44+
sudo -u "$currentUser" brew remove --cask scalar-azrepos || echo "warning: Scalar.NET with GVFS uninstall via Homebrew completed with code $?"
45+
echo "Scalar.NET with GVFS uninstalled via Homebrew!"
46+
elif [ -f $SCALAR_UNINSTALL_SCRIPT ]; then
47+
# If not installed with Homebrew, manually remove package
48+
sudo -S sh $SCALAR_UNINSTALL_SCRIPT || echo "warning: Scalar.NET uninstall completed with code $?"
49+
echo "Scalar.NET uninstalled!"
50+
else
51+
echo "warning: Scalar.NET uninstall script not found"
52+
fi
53+
54+
# Re-create the Scalar symlink, in case it was removed by the Scalar.NET uninstall operation
55+
mkdir -p $INSTALL_DST/bin
56+
/bin/ln -Fs "$SCALAR_C_CMD" "$INSTALL_DST/bin/scalar"
57+
}
58+
59+
# Run Scalar cleanup (will exit if not applicable)
60+
cleanupScalar
61+
62+
exit 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash -e
2+
if [ ! -r "/usr/local/git" ]; then
3+
echo "Git doesn't appear to be installed via this installer. Aborting"
4+
exit 1
5+
fi
6+
7+
if [ "$1" != "--yes" ]; then
8+
echo "This will uninstall git by removing /usr/local/git/, and symlinks"
9+
printf "Type 'yes' if you are sure you wish to continue: "
10+
read response
11+
else
12+
response="yes"
13+
fi
14+
15+
if [ "$response" == "yes" ]; then
16+
# remove all of the symlinks we've created
17+
pkgutil --files com.git.pkg | grep bin | while read f; do
18+
if [ -L /usr/local/$f ]; then
19+
sudo rm /usr/local/$f
20+
fi
21+
done
22+
23+
# forget receipts.
24+
pkgutil --packages | grep com.git.pkg | xargs -I {} sudo pkgutil --forget {}
25+
echo "Uninstalled"
26+
27+
# The guts all go here.
28+
sudo rm -rf /usr/local/git/
29+
else
30+
echo "Aborted"
31+
exit 1
32+
fi
33+
34+
exit 0
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.cs.allow-jit</key>
6+
<true/>
7+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
8+
<true/>
9+
<key>com.apple.security.cs.disable-library-validation</key>
10+
<true/>
11+
</dict>
12+
</plist>

.github/scripts/codesign.sh

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/bash
2+
3+
sign_directory () {
4+
(
5+
cd "$1"
6+
for f in *
7+
do
8+
macho=$(file --mime $f | grep mach)
9+
# Runtime sign dylibs and Mach-O binaries
10+
if [[ $f == *.dylib ]] || [ ! -z "$macho" ];
11+
then
12+
echo "Runtime Signing $f"
13+
codesign -s "$IDENTITY" $f --timestamp --force --options=runtime --entitlements $ENTITLEMENTS_FILE
14+
elif [ -d "$f" ];
15+
then
16+
echo "Signing files in subdirectory $f"
17+
sign_directory "$f"
18+
19+
else
20+
echo "Signing $f"
21+
codesign -s "$IDENTITY" $f --timestamp --force
22+
fi
23+
done
24+
)
25+
}
26+
27+
for i in "$@"
28+
do
29+
case "$i" in
30+
--payload=*)
31+
SIGN_DIR="${i#*=}"
32+
shift # past argument=value
33+
;;
34+
--identity=*)
35+
IDENTITY="${i#*=}"
36+
shift # past argument=value
37+
;;
38+
--entitlements=*)
39+
ENTITLEMENTS_FILE="${i#*=}"
40+
shift # past argument=value
41+
;;
42+
*)
43+
die "unknown option '$i'"
44+
;;
45+
esac
46+
done
47+
48+
if [ -z "$SIGN_DIR" ]; then
49+
echo "error: missing directory argument"
50+
exit 1
51+
elif [ -z "$IDENTITY" ]; then
52+
echo "error: missing signing identity argument"
53+
exit 1
54+
elif [ -z "$ENTITLEMENTS_FILE" ]; then
55+
echo "error: missing entitlements file argument"
56+
exit 1
57+
fi
58+
59+
echo "======== INPUTS ========"
60+
echo "Directory: $SIGN_DIR"
61+
echo "Signing identity: $IDENTITY"
62+
echo "Entitlements: $ENTITLEMENTS_FILE"
63+
echo "======== END INPUTS ========"
64+
65+
sign_directory "$SIGN_DIR"

.github/scripts/notarize.sh

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
3+
for i in "$@"
4+
do
5+
case "$i" in
6+
--package=*)
7+
PACKAGE="${i#*=}"
8+
shift # past argument=value
9+
;;
10+
--keychain-profile=*)
11+
KEYCHAIN_PROFILE="${i#*=}"
12+
shift # past argument=value
13+
;;
14+
*)
15+
die "unknown option '$i'"
16+
;;
17+
esac
18+
done
19+
20+
if [ -z "$PACKAGE" ]; then
21+
echo "error: missing package argument"
22+
exit 1
23+
elif [ -z "$KEYCHAIN_PROFILE" ]; then
24+
echo "error: missing keychain profile argument"
25+
exit 1
26+
fi
27+
28+
# Exit as soon as any line fails
29+
set -e
30+
31+
# Send the notarization request
32+
xcrun notarytool submit -v "$PACKAGE" -p "$KEYCHAIN_PROFILE" --wait
33+
34+
# Staple the notarization ticket (to allow offline installation)
35+
xcrun stapler staple -v "$PACKAGE"

0 commit comments

Comments
 (0)