Skip to content

Commit 51369f2

Browse files
committed
release: add Mac OSX installer build
- include `scalar` - build signed .dmg & .pkg for target OS version 10.6 - upload artifacts to workflow Update 2022-08-11: .dmg and .pkg are now signed
1 parent 7729533 commit 51369f2

File tree

9 files changed

+672
-0
lines changed

9 files changed

+672
-0
lines changed

.github/macos-installer/Makefile

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
ARCH := x86_64
11+
ARCH_CODE := x86_64
12+
ARCH_FLAGS_x86_64 := -arch x86_64
13+
14+
CFLAGS := $(TARGET_FLAGS) $(ARCH_FLAGS_${ARCH_CODE})
15+
LDFLAGS := $(TARGET_FLAGS) $(ARCH_FLAGS_${ARCH_CODE})
16+
17+
PREFIX := /usr/local
18+
GIT_PREFIX := $(PREFIX)/git
19+
20+
BUILD_CODE := intel-$(ARCH_CODE)
21+
BUILD_DIR := $(GITHUB_WORKSPACE)/payload
22+
DESTDIR := $(PWD)/stage/git-$(BUILD_CODE)-$(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+
.PHONY: image pkg payload
28+
29+
.SECONDARY:
30+
31+
$(DESTDIR)$(GIT_PREFIX)/VERSION-$(VERSION)-$(BUILD_CODE):
32+
rm -f $(BUILD_DIR)/git-$(VERSION)/osx-installed*
33+
mkdir -p $(DESTDIR)$(GIT_PREFIX)
34+
touch $@
35+
36+
$(BUILD_DIR)/git-$(VERSION)/osx-built-keychain:
37+
cd $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain; $(SUBMAKE) CFLAGS="$(CFLAGS) -g -O2 -Wall"
38+
touch $@
39+
40+
$(BUILD_DIR)/git-$(VERSION)/osx-built:
41+
[ -d $(DESTDIR)$(GIT_PREFIX) ] && $(SUDO) rm -rf $(DESTDIR) || echo ok
42+
cd $(BUILD_DIR)/git-$(VERSION); $(SUBMAKE) -j $(CORES) all strip
43+
touch $@
44+
45+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-bin: $(BUILD_DIR)/git-$(VERSION)/osx-built $(BUILD_DIR)/git-$(VERSION)/osx-built-keychain
46+
cd $(BUILD_DIR)/git-$(VERSION); $(SUBMAKE) install
47+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain $(DESTDIR)$(GIT_PREFIX)/bin/git-credential-osxkeychain
48+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/contrib/completion
49+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-completion.bash $(DESTDIR)$(GIT_PREFIX)/contrib/completion/
50+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-completion.zsh $(DESTDIR)$(GIT_PREFIX)/contrib/completion/
51+
cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-prompt.sh $(DESTDIR)$(GIT_PREFIX)/contrib/completion/
52+
# This is needed for Git-Gui, GitK
53+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl
54+
[ ! -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
55+
touch $@
56+
57+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-man: $(BUILD_DIR)/git-$(VERSION)/osx-installed-bin
58+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/share/man
59+
cp -R $(GITHUB_WORKSPACE)/manpages/ $(DESTDIR)$(GIT_PREFIX)/share/man
60+
touch $@
61+
62+
$(BUILD_DIR)/git-$(VERSION)/osx-built-subtree:
63+
cd $(BUILD_DIR)/git-$(VERSION)/contrib/subtree; $(SUBMAKE) XML_CATALOG_FILES="$(XML_CATALOG_FILES)" all git-subtree.1
64+
touch $@
65+
66+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-subtree: $(BUILD_DIR)/git-$(VERSION)/osx-built-subtree
67+
mkdir -p $(DESTDIR)
68+
cd $(BUILD_DIR)/git-$(VERSION)/contrib/subtree; $(SUBMAKE) XML_CATALOG_FILES="$(XML_CATALOG_FILES)" install install-man
69+
touch $@
70+
71+
$(BUILD_DIR)/git-$(VERSION)/osx-installed-assets: $(BUILD_DIR)/git-$(VERSION)/osx-installed-bin
72+
mkdir -p $(DESTDIR)$(GIT_PREFIX)/etc
73+
cat assets/etc/gitconfig.osxkeychain >> $(DESTDIR)$(GIT_PREFIX)/etc/gitconfig
74+
cp assets/uninstall.sh $(DESTDIR)$(GIT_PREFIX)/uninstall.sh
75+
sh -c "echo .DS_Store >> $(DESTDIR)$(GIT_PREFIX)/share/git-core/templates/info/exclude"
76+
77+
symlinks:
78+
mkdir -p $(ARTIFACTDIR)$(PREFIX)/bin
79+
cd $(ARTIFACTDIR)$(PREFIX)/bin; find ../git/bin -type f -exec ln -sf {} \;
80+
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
81+
ruby ../scripts/symlink-git-hardlinks.rb $(ARTIFACTDIR)
82+
touch $@
83+
84+
$(BUILD_DIR)/git-$(VERSION)/osx-installed: $(DESTDIR)$(GIT_PREFIX)/VERSION-$(VERSION)-$(BUILD_CODE) $(BUILD_DIR)/git-$(VERSION)/osx-installed-man $(BUILD_DIR)/git-$(VERSION)/osx-installed-assets $(BUILD_DIR)/git-$(VERSION)/osx-installed-subtree
85+
find $(DESTDIR)$(GIT_PREFIX) -type d -exec chmod ugo+rx {} \;
86+
find $(DESTDIR)$(GIT_PREFIX) -type f -exec chmod ugo+r {} \;
87+
touch $@
88+
89+
$(BUILD_DIR)/git-$(VERSION)/osx-built-assert-$(ARCH_CODE): $(BUILD_DIR)/git-$(VERSION)/osx-built
90+
ifeq ("$(ARCH_CODE)", "universal")
91+
File $(BUILD_DIR)/git-$(VERSION)/git
92+
File $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain
93+
else
94+
[ "$$(File $(BUILD_DIR)/git-$(VERSION)/git | cut -f 5 -d' ')" == "$(ARCH_CODE)" ]
95+
[ "$$(File $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain | cut -f 5 -d' ')" == "$(ARCH_CODE)" ]
96+
endif
97+
touch $@
98+
99+
disk-image/VERSION-$(VERSION)-$(ARCH_CODE):
100+
rm -f disk-image/*.pkg disk-image/VERSION-* disk-image/.DS_Store
101+
mkdir disk-image
102+
touch "$@"
103+
104+
disk-image/git-$(VERSION)-$(BUILD_CODE).pkg: disk-image/VERSION-$(VERSION)-$(ARCH_CODE) symlinks
105+
pkgbuild --identifier com.git.pkg --version $(VERSION) --root $(ARTIFACTDIR)$(PREFIX) --scripts assets/scripts --install-location $(PREFIX) --component-plist ./assets/git-components.plist disk-image/git-$(VERSION)-$(BUILD_CODE).pkg
106+
107+
git-%-$(BUILD_CODE).dmg:
108+
hdiutil create git-$(VERSION)-$(BUILD_CODE).uncompressed.dmg -fs HFS+ -srcfolder disk-image -volname "Git $(VERSION) Intel $(ARCH)" -ov
109+
hdiutil convert -format UDZO -o $@ git-$(VERSION)-$(BUILD_CODE).uncompressed.dmg
110+
rm -f git-$(VERSION)-$(BUILD_CODE).uncompressed.dmg
111+
112+
payload: $(BUILD_DIR)/git-$(VERSION)/osx-installed $(BUILD_DIR)/git-$(VERSION)/osx-built-assert-$(ARCH_CODE)
113+
114+
pkg: disk-image/git-$(VERSION)-$(BUILD_CODE).pkg
115+
116+
image: git-$(VERSION)-$(BUILD_CODE).dmg
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[credential]
2+
helper = osxkeychain
Lines changed: 18 additions & 0 deletions
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>
Lines changed: 62 additions & 0 deletions
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
Lines changed: 34 additions & 0 deletions
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
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import argparse
2+
import json
3+
import os
4+
import glob
5+
import pprint
6+
import subprocess
7+
import sys
8+
import re
9+
10+
parser = argparse.ArgumentParser(description='Sign binaries for macOS')
11+
parser.add_argument('path', help='Path to file for signing')
12+
parser.add_argument('keycode', help='Platform-specific key code for signing')
13+
parser.add_argument('opcode', help='Platform-specific operation code for signing')
14+
# Setting nargs=argparse.REMAINDER allows us to pass in params that begin with `--`
15+
parser.add_argument('--params', nargs=argparse.REMAINDER, help='Parameters for signing')
16+
args = parser.parse_args()
17+
18+
esrp_tool = os.path.join("esrp", "tools", "EsrpClient.exe")
19+
20+
aad_id = os.environ['AZURE_AAD_ID'].strip()
21+
workspace = os.environ['GITHUB_WORKSPACE'].strip()
22+
23+
source_location = args.path
24+
files = glob.glob(os.path.join(source_location, "*"))
25+
26+
print("Found files:")
27+
pprint.pp(files)
28+
29+
auth_json = {
30+
"Version": "1.0.0",
31+
"AuthenticationType": "AAD_CERT",
32+
"TenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
33+
"ClientId": f"{aad_id}",
34+
"AuthCert": {
35+
"SubjectName": f"CN={aad_id}.microsoft.com",
36+
"StoreLocation": "LocalMachine",
37+
"StoreName": "My",
38+
"SendX5c" : "true"
39+
},
40+
"RequestSigningCert": {
41+
"SubjectName": f"CN={aad_id}",
42+
"StoreLocation": "LocalMachine",
43+
"StoreName": "My"
44+
}
45+
}
46+
47+
input_json = {
48+
"Version": "1.0.0",
49+
"SignBatches": [
50+
{
51+
"SourceLocationType": "UNC",
52+
"SourceRootDirectory": source_location,
53+
"DestinationLocationType": "UNC",
54+
"DestinationRootDirectory": workspace,
55+
"SignRequestFiles": [],
56+
"SigningInfo": {
57+
"Operations": [
58+
{
59+
"KeyCode": f"{args.keycode}",
60+
"OperationCode": f"{args.opcode}",
61+
"Parameters": {},
62+
"ToolName": "sign",
63+
"ToolVersion": "1.0",
64+
}
65+
]
66+
}
67+
}
68+
]
69+
}
70+
71+
# add files to sign
72+
for f in files:
73+
name = os.path.basename(f)
74+
input_json["SignBatches"][0]["SignRequestFiles"].append(
75+
{
76+
"SourceLocation": name,
77+
"DestinationLocation": os.path.join("signed", name),
78+
}
79+
)
80+
81+
# add parameters to input.json (e.g. enabling the hardened runtime for macOS)
82+
if args.params is not None:
83+
i = 0
84+
while i < len(args.params):
85+
input_json["SignBatches"][0]["SigningInfo"]["Operations"][0]["Parameters"][args.params[i]] = args.params[i + 1]
86+
i += 2
87+
88+
policy_json = {
89+
"Version": "1.0.0",
90+
"Intent": "production release",
91+
"ContentType": "binary",
92+
}
93+
94+
configs = [
95+
("auth.json", auth_json),
96+
("input.json", input_json),
97+
("policy.json", policy_json),
98+
]
99+
100+
for filename, data in configs:
101+
with open(filename, 'w') as fp:
102+
json.dump(data, fp)
103+
104+
# Run ESRP Client
105+
esrp_out = "esrp_out.json"
106+
result = subprocess.run(
107+
[esrp_tool, "sign",
108+
"-a", "auth.json",
109+
"-i", "input.json",
110+
"-p", "policy.json",
111+
"-o", esrp_out,
112+
"-l", "Verbose"],
113+
capture_output=True,
114+
text=True,
115+
cwd=workspace)
116+
117+
# Scrub log before printing
118+
log = re.sub(r'^.+Uploading.*to\s*destinationUrl\s*(.+?),.+$',
119+
'***',
120+
result.stdout,
121+
flags=re.IGNORECASE|re.MULTILINE)
122+
print(log)
123+
124+
if result.returncode != 0:
125+
print("Failed to run ESRPClient.exe")
126+
sys.exit(1)
127+
128+
if os.path.isfile(esrp_out):
129+
print("ESRP output json:")
130+
with open(esrp_out, 'r') as fp:
131+
pprint.pp(json.load(fp))
132+
133+
for file in files:
134+
if os.path.isfile(os.path.join("signed", file)):
135+
print(f"Success!\nSigned {file}")

.github/scripts/set-up-esrp.ps1

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Install ESRP client
2+
az storage blob download --file esrp.zip --auth-mode login --account-name esrpsigningstorage --container signing-resources --name microsoft.esrpclient.1.2.76.nupkg
3+
Expand-Archive -Path esrp.zip -DestinationPath .\esrp
4+
5+
# Install certificates
6+
az keyvault secret download --vault-name "$env:AZURE_VAULT" --name "$env:AUTH_CERT" --file out.pfx
7+
certutil -f -importpfx out.pfx
8+
Remove-Item out.pfx
9+
10+
az keyvault secret download --vault-name "$env:AZURE_VAULT" --name "$env:REQUEST_SIGNING_CERT" --file out.pfx
11+
certutil -f -importpfx out.pfx
12+
Remove-Item out.pfx

0 commit comments

Comments
 (0)