build-git-installers #214
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: build-git-installers | |
on: | |
push: | |
tags: | |
- 'v[0-9]*vfs*' # matches "v<number><any characters>vfs<any characters>" | |
permissions: | |
id-token: write # required for Azure login via OIDC | |
jobs: | |
# Check prerequisites for the workflow | |
prereqs: | |
runs-on: ubuntu-latest | |
environment: release | |
outputs: | |
tag_name: ${{ steps.tag.outputs.name }} # The full name of the tag, e.g. v2.32.0.vfs.0.0 | |
tag_version: ${{ steps.tag.outputs.version }} # The version number (without preceding "v"), e.g. 2.32.0.vfs.0.0 | |
steps: | |
- name: Validate tag | |
run: | | |
echo "$GITHUB_REF" | | |
grep -E '^refs/tags/v2\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.vfs\.0\.(0|[1-9][0-9]*)(\.rc[0-9])?$' || { | |
echo "::error::${GITHUB_REF#refs/tags/} is not of the form v2.<X>.<Y>.vfs.0.<W>[.rc<N>]" >&2 | |
exit 1 | |
} | |
- name: Determine tag to build | |
run: | | |
echo "name=${GITHUB_REF#refs/tags/}" >>$GITHUB_OUTPUT | |
echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT | |
id: tag | |
- name: Clone git | |
uses: actions/checkout@v4 | |
- name: Validate the tag identified with trigger | |
run: | | |
die () { | |
echo "::error::$*" >&2 | |
exit 1 | |
} | |
# `actions/checkout` only downloads the peeled tag (i.e. the commit) | |
git fetch origin +$GITHUB_REF:$GITHUB_REF | |
# Verify that the tag is annotated | |
test $(git cat-file -t "$GITHUB_REF") == "tag" || die "Tag ${{ steps.tag.outputs.name }} is not annotated" | |
# Verify tag follows rules in GIT-VERSION-GEN (i.e., matches the specified "DEF_VER" in | |
# GIT-VERSION-FILE) and matches tag determined from trigger | |
make GIT-VERSION-FILE | |
test "${{ steps.tag.outputs.version }}" == "$(sed -n 's/^GIT_VERSION = //p'< GIT-VERSION-FILE)" || die "GIT-VERSION-FILE tag does not match ${{ steps.tag.outputs.name }}" | |
# End check prerequisites for the workflow | |
# Build Windows installers (x86_64 & aarch64; installer & portable) | |
windows_pkg: | |
environment: release | |
needs: prereqs | |
strategy: | |
fail-fast: false | |
matrix: | |
arch: | |
- name: x86_64 | |
artifact: pkg-x86_64 | |
toolchain: x86_64 | |
mingwprefix: /mingw64 | |
runner: windows-2019 | |
- name: aarch64 | |
artifact: pkg-aarch64 | |
toolchain: clang-aarch64 | |
mingwprefix: /clangarm64 | |
runner: ['self-hosted', '1ES.Pool=github-arm64-pool'] | |
runs-on: ${{ matrix.arch.runner }} | |
env: | |
GPG_OPTIONS: "--batch --yes --no-tty --list-options no-show-photos --verify-options no-show-photos --pinentry-mode loopback" | |
HOME: "${{github.workspace}}\\home" | |
USERPROFILE: "${{github.workspace}}\\home" | |
steps: | |
- name: Configure user | |
shell: bash | |
run: | |
USER_NAME="${{github.actor}}" && | |
USER_EMAIL="${{github.actor}}@users.noreply.github.com" && | |
mkdir -p "$HOME" && | |
git config --global user.name "$USER_NAME" && | |
git config --global user.email "$USER_EMAIL" && | |
echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >>$GITHUB_ENV | |
- uses: git-for-windows/setup-git-for-windows-sdk@v1 | |
with: | |
flavor: build-installers | |
architecture: ${{ matrix.arch.name }} | |
- name: Clone build-extra | |
shell: bash | |
run: | | |
git clone --filter=blob:none --single-branch -b main https://github.com/git-for-windows/build-extra /usr/src/build-extra | |
- name: Clone git | |
shell: bash | |
run: | | |
# Since we cannot directly clone a specified tag (as we would a branch with `git clone -b <branch name>`), | |
# this clone has to be done manually (via init->fetch->reset). | |
tag_name="${{ needs.prereqs.outputs.tag_name }}" && | |
git -c init.defaultBranch=main init && | |
git remote add -f origin https://github.com/git-for-windows/git && | |
git fetch "https://github.com/${{github.repository}}" refs/tags/${tag_name}:refs/tags/${tag_name} && | |
git reset --hard ${tag_name} | |
- name: Prepare home directory for code-signing | |
env: | |
CODESIGN_P12: ${{secrets.CODESIGN_P12}} | |
CODESIGN_PASS: ${{secrets.CODESIGN_PASS}} | |
if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != '' | |
shell: bash | |
run: | | |
cd home && | |
mkdir -p .sig && | |
echo -n "$CODESIGN_P12" | tr % '\n' | base64 -d >.sig/codesign.p12 && | |
echo -n "$CODESIGN_PASS" >.sig/codesign.pass | |
git config --global alias.signtool '!sh "/usr/src/build-extra/signtool.sh"' | |
- name: Prepare home directory for GPG signing | |
if: env.GPGKEY != '' | |
shell: bash | |
run: | | |
# This section ensures that the identity for the GPG key matches the git user identity, otherwise | |
# signing will fail | |
echo '${{secrets.PRIVGPGKEY}}' | tr % '\n' | gpg $GPG_OPTIONS --import && | |
info="$(gpg --list-keys --with-colons "${GPGKEY%% *}" | cut -d : -f 1,10 | sed -n '/^uid/{s|uid:||p;q}')" && | |
git config --global user.name "${info% <*}" && | |
git config --global user.email "<${info#*<}" | |
env: | |
GPGKEY: ${{secrets.GPGKEY}} | |
- name: Build mingw-w64-${{matrix.arch.toolchain}}-git | |
env: | |
GPGKEY: "${{secrets.GPGKEY}}" | |
shell: bash | |
run: | | |
set -x | |
# Make sure that there is a `/usr/bin/git` that can be used by `makepkg-mingw` | |
printf '#!/bin/sh\n\nexec ${{matrix.arch.mingwprefix}}/bin/git.exe "$@"\n' >/usr/bin/git && | |
sh -x /usr/src/build-extra/please.sh build-mingw-w64-git --only-${{matrix.arch.name}} --build-src-pkg -o artifacts HEAD && | |
if test -n "$GPGKEY" | |
then | |
for tar in artifacts/*.tar* | |
do | |
/usr/src/build-extra/gnupg-with-gpgkey.sh --detach-sign --no-armor $tar | |
done | |
fi && | |
b=$PWD/artifacts && | |
version=${{ needs.prereqs.outputs.tag_name }} && | |
(cd /usr/src/MINGW-packages/mingw-w64-git && | |
cp PKGBUILD.$version PKGBUILD && | |
git commit -s -m "mingw-w64-git: new version ($version)" PKGBUILD && | |
git bundle create "$b"/MINGW-packages.bundle origin/main..main) | |
- name: Publish mingw-w64-${{matrix.arch.toolchain}}-git | |
uses: actions/upload-artifact@v4 | |
with: | |
name: "${{ matrix.arch.artifact }}" | |
path: artifacts | |
windows_artifacts: | |
environment: release | |
needs: [prereqs, windows_pkg] | |
env: | |
HOME: "${{github.workspace}}\\home" | |
strategy: | |
fail-fast: false | |
matrix: | |
arch: | |
- name: x86_64 | |
artifact: pkg-x86_64 | |
toolchain: x86_64 | |
runner: windows-2019 | |
- name: aarch64 | |
artifact: pkg-aarch64 | |
toolchain: clang-aarch64 | |
runner: ['self-hosted', '1ES.Pool=github-arm64-pool'] | |
type: | |
- name: installer | |
fileprefix: Git | |
- name: portable | |
fileprefix: PortableGit | |
runs-on: ${{ matrix.arch.runner }} | |
steps: | |
- name: Download ${{ matrix.arch.artifact }} | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ matrix.arch.artifact }} | |
path: ${{ matrix.arch.artifact }} | |
- uses: git-for-windows/setup-git-for-windows-sdk@v1 | |
with: | |
flavor: build-installers | |
architecture: ${{ matrix.arch.name }} | |
- name: Clone build-extra | |
shell: bash | |
run: | | |
git clone --filter=blob:none --single-branch -b main https://github.com/git-for-windows/build-extra /usr/src/build-extra | |
- name: Prepare home directory for code-signing | |
env: | |
CODESIGN_P12: ${{secrets.CODESIGN_P12}} | |
CODESIGN_PASS: ${{secrets.CODESIGN_PASS}} | |
if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != '' | |
shell: bash | |
run: | | |
mkdir -p home/.sig && | |
echo -n "$CODESIGN_P12" | tr % '\n' | base64 -d >home/.sig/codesign.p12 && | |
echo -n "$CODESIGN_PASS" >home/.sig/codesign.pass && | |
git config --global alias.signtool '!sh "/usr/src/build-extra/signtool.sh"' | |
- name: Retarget auto-update to microsoft/git | |
shell: bash | |
run: | | |
set -x | |
b=/usr/src/build-extra && | |
filename=$b/git-update-git-for-windows.config | |
tr % '\t' >$filename <<-\EOF && | |
[update] | |
%fromFork = microsoft/git | |
EOF | |
sed -i -e '/^#include "file-list.iss"/a\ | |
Source: {#SourcePath}\\..\\git-update-git-for-windows.config; DestDir: {app}\\mingw64\\bin; Flags: replacesameversion; AfterInstall: DeleteFromVirtualStore' \ | |
-e '/^Type: dirifempty; Name: {app}\\{#MINGW_BITNESS}$/i\ | |
Type: files; Name: {app}\\{#MINGW_BITNESS}\\bin\\git-update-git-for-windows.config\ | |
Type: dirifempty; Name: {app}\\{#MINGW_BITNESS}\\bin' \ | |
$b/installer/install.iss | |
- name: Set alerts to continue until upgrade is taken | |
shell: bash | |
run: | | |
set -x | |
b=/mingw64/bin && | |
sed -i -e '6 a use_recently_seen=no' \ | |
$b/git-update-git-for-windows | |
- name: Set the installer Publisher to the Git Client team | |
shell: bash | |
run: | | |
b=/usr/src/build-extra && | |
sed -i -e 's/^\(AppPublisher=\).*/\1The Git Client Team at Microsoft/' $b/installer/install.iss | |
- name: Let the installer configure Visual Studio to use the installed Git | |
shell: bash | |
run: | | |
set -x | |
b=/usr/src/build-extra && | |
sed -i -e '/^ *InstallAutoUpdater();$/a\ | |
CustomPostInstall();' \ | |
-e '/^ *UninstallAutoUpdater();$/a\ | |
CustomPostUninstall();' \ | |
$b/installer/install.iss && | |
cat >>$b/installer/helpers.inc.iss <<\EOF | |
procedure CustomPostInstall(); | |
begin | |
if not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\15.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or | |
not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\16.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or | |
not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\17.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or | |
not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\18.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or | |
not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\19.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or | |
not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\20.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) then | |
LogError('Could not register TeamFoundation\GitSourceControl'); | |
end; | |
procedure CustomPostUninstall(); | |
begin | |
if not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\15.0\TeamFoundation\GitSourceControl','GitPath') or | |
not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\16.0\TeamFoundation\GitSourceControl','GitPath') or | |
not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\17.0\TeamFoundation\GitSourceControl','GitPath') or | |
not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\18.0\TeamFoundation\GitSourceControl','GitPath') or | |
not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\19.0\TeamFoundation\GitSourceControl','GitPath') or | |
not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\20.0\TeamFoundation\GitSourceControl','GitPath') then | |
LogError('Could not register TeamFoundation\GitSourceControl'); | |
end; | |
EOF | |
- name: Enable Scalar/C and the auto-updater in the installer by default | |
shell: bash | |
run: | | |
set -x | |
b=/usr/src/build-extra && | |
sed -i -e "/ChosenOptions:=''/a\\ | |
if (ExpandConstant('{param:components|/}')='/') then begin\n\ | |
WizardSelectComponents('autoupdate');\n\ | |
#ifdef WITH_SCALAR\n\ | |
WizardSelectComponents('scalar');\n\ | |
#endif\n\ | |
end;" $b/installer/install.iss | |
- name: Build ${{matrix.type.name}} (${{matrix.arch.name}}) | |
shell: bash | |
run: | | |
set -x | |
# Copy the PDB archive to the directory where `--include-pdbs` expects it | |
b=/usr/src/build-extra && | |
mkdir -p $b/cached-source-packages && | |
cp ${{matrix.arch.artifact}}/*-pdb* $b/cached-source-packages/ && | |
# Build the installer, embedding PDBs | |
eval $b/please.sh make_installers_from_mingw_w64_git --include-pdbs \ | |
--version=${{ needs.prereqs.outputs.tag_version }} \ | |
-o artifacts --${{matrix.type.name}} \ | |
--pkg=${{matrix.arch.artifact}}/mingw-w64-${{matrix.arch.toolchain}}-git-[0-9]*.tar.xz \ | |
--pkg=${{matrix.arch.artifact}}/mingw-w64-${{matrix.arch.toolchain}}-git-doc-html-[0-9]*.tar.xz && | |
if test portable = '${{matrix.type.name}}' && test -n "$(git config alias.signtool)" | |
then | |
git signtool artifacts/PortableGit-*.exe | |
fi && | |
openssl dgst -sha256 artifacts/${{matrix.type.fileprefix}}-*.exe | sed "s/.* //" >artifacts/sha-256.txt | |
- name: Verify that .exe files are code-signed | |
if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != '' | |
shell: bash | |
run: | | |
PATH=$PATH:"/c/Program Files (x86)/Windows Kits/10/App Certification Kit/" \ | |
signtool verify //pa artifacts/${{matrix.type.fileprefix}}-*.exe | |
- name: Publish ${{matrix.type.name}}-${{matrix.arch.name}} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: win-${{matrix.type.name}}-${{matrix.arch.name}} | |
path: artifacts | |
# End build Windows installers | |
# Build and sign Mac OSX installers & upload artifacts | |
create-macos-artifacts: | |
runs-on: macos-latest-xl-arm64 | |
needs: prereqs | |
env: | |
VERSION: "${{ needs.prereqs.outputs.tag_version }}" | |
environment: release | |
steps: | |
- name: Check out repository | |
uses: actions/checkout@v4 | |
with: | |
path: 'git' | |
- name: Install Git dependencies | |
run: | | |
set -ex | |
# Install x86_64 packages | |
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
arch -x86_64 /usr/local/bin/brew install gettext | |
# Install arm64 packages | |
brew install automake asciidoc xmlto docbook | |
brew link --force gettext | |
# Make universal gettext library | |
lipo -create -output libintl.a /usr/local/opt/gettext/lib/libintl.a /opt/homebrew/opt/gettext/lib/libintl.a | |
- name: Set up signing/notarization infrastructure | |
env: | |
A1: ${{ secrets.APPLICATION_CERTIFICATE_BASE64 }} | |
A2: ${{ secrets.APPLICATION_CERTIFICATE_PASSWORD }} | |
I1: ${{ secrets.INSTALLER_CERTIFICATE_BASE64 }} | |
I2: ${{ secrets.INSTALLER_CERTIFICATE_PASSWORD }} | |
N1: ${{ secrets.APPLE_TEAM_ID }} | |
N2: ${{ secrets.APPLE_DEVELOPER_ID }} | |
N3: ${{ secrets.APPLE_DEVELOPER_PASSWORD }} | |
N4: ${{ secrets.APPLE_KEYCHAIN_PROFILE }} | |
run: | | |
echo "Setting up signing certificates" | |
security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain | |
security default-keychain -s $RUNNER_TEMP/buildagent.keychain | |
security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain | |
# Prevent re-locking | |
security set-keychain-settings $RUNNER_TEMP/buildagent.keychain | |
echo "$A1" | base64 -D > $RUNNER_TEMP/cert.p12 | |
security import $RUNNER_TEMP/cert.p12 \ | |
-k $RUNNER_TEMP/buildagent.keychain \ | |
-P "$A2" \ | |
-T /usr/bin/codesign | |
security set-key-partition-list \ | |
-S apple-tool:,apple:,codesign: \ | |
-s -k pwd \ | |
$RUNNER_TEMP/buildagent.keychain | |
echo "$I1" | base64 -D > $RUNNER_TEMP/cert.p12 | |
security import $RUNNER_TEMP/cert.p12 \ | |
-k $RUNNER_TEMP/buildagent.keychain \ | |
-P "$I2" \ | |
-T /usr/bin/pkgbuild | |
security set-key-partition-list \ | |
-S apple-tool:,apple:,pkgbuild: \ | |
-s -k pwd \ | |
$RUNNER_TEMP/buildagent.keychain | |
echo "Setting up notarytool" | |
xcrun notarytool store-credentials \ | |
--team-id "$N1" \ | |
--apple-id "$N2" \ | |
--password "$N3" \ | |
"$N4" | |
- name: Build, sign, and notarize artifacts | |
env: | |
A3: ${{ secrets.APPLE_APPLICATION_SIGNING_IDENTITY }} | |
I3: ${{ secrets.APPLE_INSTALLER_SIGNING_IDENTITY }} | |
N4: ${{ secrets.APPLE_KEYCHAIN_PROFILE }} | |
run: | | |
die () { | |
echo "$*" >&2 | |
exit 1 | |
} | |
# Trace execution, stop on error | |
set -ex | |
# Write to "version" file to force match with trigger payload version | |
echo "${{ needs.prereqs.outputs.tag_version }}" >>git/version | |
# Configure universal build | |
cat >git/config.mak <<EOF | |
# Create universal binaries. HOST_CPU is a bit of a lie and only | |
# used in 'git version --build-options'. We'll fix that in code. | |
HOST_CPU = universal | |
BASIC_CFLAGS += -arch arm64 -arch x86_64 | |
EOF | |
# Configure the Git build to pick up gettext | |
homebrew_prefix="$(brew --prefix)" | |
cat >>git/config.mak <<EOF | |
CFLAGS = -I$homebrew_prefix/include -I/usr/local/opt/gettext/include | |
LDFLAGS = -L"$(pwd)" | |
EOF | |
# Configure the Git to use the OS supplied libcurl. | |
cat >>git/config.mak <<EOF | |
CURL_LDFLAGS := -lcurl | |
CURL_CONFIG := /usr/bin/true | |
EOF | |
# Avoid even building the dashed built-ins; Those should be hard-linked | |
# copies of the `git` executable but would end up as actual copies instead, | |
# bloating the size of the `.dmg` indecently. | |
echo 'SKIP_DASHED_BUILT_INS = YabbaDabbaDoo' >>git/config.mak | |
# To make use of the catalogs... | |
export XML_CATALOG_FILES=$homebrew_prefix/etc/xml/catalog | |
make -C git -j$(sysctl -n hw.physicalcpu) GIT-VERSION-FILE dist dist-doc | |
export GIT_BUILT_FROM_COMMIT=$(gunzip -c git/git-$VERSION.tar.gz | git get-tar-commit-id) || | |
die "Could not determine commit for build" | |
# Extract tarballs | |
mkdir payload manpages | |
tar -xvf git/git-$VERSION.tar.gz -C payload | |
tar -xvf git/git-manpages-$VERSION.tar.gz -C manpages | |
# Lay out payload | |
cp git/config.mak payload/git-$VERSION/config.mak | |
make -C git/.github/macos-installer V=1 payload | |
# Codesign payload | |
cp -R stage/git-universal-$VERSION/ \ | |
git/.github/macos-installer/build-artifacts | |
make -C git/.github/macos-installer V=1 codesign \ | |
APPLE_APP_IDENTITY="$A3" || die "Creating signed payload failed" | |
# Build and sign pkg | |
make -C git/.github/macos-installer V=1 pkg \ | |
APPLE_INSTALLER_IDENTITY="$I3" \ | |
|| die "Creating signed pkg failed" | |
# Notarize pkg | |
make -C git/.github/macos-installer V=1 notarize \ | |
APPLE_INSTALLER_IDENTITY="$I3" APPLE_KEYCHAIN_PROFILE="$N4" \ | |
|| die "Creating signed and notarized pkg failed" | |
# Create DMG | |
make -C git/.github/macos-installer V=1 image || die "Creating DMG failed" | |
# Move all artifacts into top-level directory | |
mv git/.github/macos-installer/disk-image/*.pkg git/.github/macos-installer/ | |
- name: Upload artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: macos-artifacts | |
path: | | |
git/.github/macos-installer/*.dmg | |
git/.github/macos-installer/*.pkg | |
# End build and sign Mac OSX installers | |
# Build and sign Debian package | |
create-linux-artifacts: | |
runs-on: ubuntu-latest | |
needs: prereqs | |
environment: release | |
steps: | |
- name: Install git dependencies | |
run: | | |
set -ex | |
sudo apt-get update -q | |
sudo apt-get install -y -q --no-install-recommends gettext libcurl4-gnutls-dev libpcre3-dev asciidoc xmlto | |
- name: Clone git | |
uses: actions/checkout@v4 | |
with: | |
path: git | |
- name: Build and create Debian package | |
run: | | |
set -ex | |
die () { | |
echo "$*" >&2 | |
exit 1 | |
} | |
echo "${{ needs.prereqs.outputs.tag_version }}" >>git/version | |
make -C git GIT-VERSION-FILE | |
VERSION="${{ needs.prereqs.outputs.tag_version }}" | |
ARCH="$(dpkg-architecture -q DEB_HOST_ARCH)" | |
if test -z "$ARCH"; then | |
die "Could not determine host architecture!" | |
fi | |
PKGNAME="microsoft-git_$VERSION" | |
PKGDIR="$(dirname $(pwd))/$PKGNAME" | |
rm -rf "$PKGDIR" | |
mkdir -p "$PKGDIR" | |
DESTDIR="$PKGDIR" make -C git -j5 V=1 DEVELOPER=1 \ | |
USE_LIBPCRE=1 \ | |
NO_CROSS_DIRECTORY_HARDLINKS=1 \ | |
ASCIIDOC8=1 ASCIIDOC_NO_ROFF=1 \ | |
ASCIIDOC='TZ=UTC asciidoc' \ | |
prefix=/usr/local \ | |
gitexecdir=/usr/local/lib/git-core \ | |
libexecdir=/usr/local/lib/git-core \ | |
htmldir=/usr/local/share/doc/git/html \ | |
install install-doc install-html | |
cd .. | |
mkdir "$PKGNAME/DEBIAN" | |
# Based on https://packages.ubuntu.com/xenial/vcs/git | |
cat >"$PKGNAME/DEBIAN/control" <<EOF | |
Package: microsoft-git | |
Version: $VERSION | |
Section: vcs | |
Priority: optional | |
Architecture: $ARCH | |
Depends: libcurl3-gnutls, liberror-perl, libexpat1, libpcre2-8-0, perl, perl-modules, zlib1g | |
Maintainer: GitClient <[email protected]> | |
Description: Git client built from the https://github.com/microsoft/git repository, | |
specialized in supporting monorepo scenarios. Includes the Scalar CLI. | |
EOF | |
dpkg-deb -Zxz --build "$PKGNAME" | |
# Move Debian package for later artifact upload | |
mv "$PKGNAME.deb" "$GITHUB_WORKSPACE" | |
- name: Log into Azure | |
uses: azure/login@v2 | |
with: | |
client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
- name: Prepare for GPG signing | |
env: | |
AZURE_VAULT: ${{ secrets.AZURE_VAULT }} | |
GPG_KEY_SECRET_NAME: ${{ secrets.GPG_KEY_SECRET_NAME }} | |
GPG_PASSPHRASE_SECRET_NAME: ${{ secrets.GPG_PASSPHRASE_SECRET_NAME }} | |
GPG_KEYGRIP_SECRET_NAME: ${{ secrets.GPG_KEYGRIP_SECRET_NAME }} | |
run: | | |
# Install debsigs | |
sudo apt install debsigs | |
# Download GPG key, passphrase, and keygrip from Azure Key Vault | |
key=$(az keyvault secret show --name $GPG_KEY_SECRET_NAME --vault-name $AZURE_VAULT --query "value") | |
passphrase=$(az keyvault secret show --name $GPG_PASSPHRASE_SECRET_NAME --vault-name $AZURE_VAULT --query "value") | |
keygrip=$(az keyvault secret show --name $GPG_KEYGRIP_SECRET_NAME --vault-name $AZURE_VAULT --query "value") | |
# Remove quotes from downloaded values | |
key=$(sed -e 's/^"//' -e 's/"$//' <<<"$key") | |
passphrase=$(sed -e 's/^"//' -e 's/"$//' <<<"$passphrase") | |
keygrip=$(sed -e 's/^"//' -e 's/"$//' <<<"$keygrip") | |
# Import GPG key | |
echo "$key" | base64 -d | gpg --import --no-tty --batch --yes | |
# Configure GPG | |
echo "allow-preset-passphrase" > ~/.gnupg/gpg-agent.conf | |
gpg-connect-agent RELOADAGENT /bye | |
/usr/lib/gnupg2/gpg-preset-passphrase --preset "$keygrip" <<<"$passphrase" | |
- name: Sign Debian package | |
run: | | |
# Sign Debian package | |
version="${{ needs.prereqs.outputs.tag_version }}" | |
debsigs --sign=origin --verify --check microsoft-git_"$version".deb | |
- name: Upload artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: linux-artifacts | |
path: | | |
*.deb | |
# End build and sign Debian package | |
# Validate installers | |
validate-installers: | |
name: Validate installers | |
strategy: | |
matrix: | |
component: | |
- os: ubuntu-latest | |
artifact: linux-artifacts | |
command: git | |
- os: macos-latest-xl-arm64 | |
artifact: macos-artifacts | |
command: git | |
- os: macos-latest | |
artifact: macos-artifacts | |
command: git | |
- os: windows-latest | |
artifact: win-installer-x86_64 | |
command: $PROGRAMFILES\Git\cmd\git.exe | |
- os: ['self-hosted', '1ES.Pool=github-arm64-pool'] | |
artifact: win-installer-aarch64 | |
command: $PROGRAMFILES\Git\cmd\git.exe | |
runs-on: ${{ matrix.component.os }} | |
needs: [prereqs, windows_artifacts, create-macos-artifacts, create-linux-artifacts] | |
steps: | |
- name: Download artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ matrix.component.artifact }} | |
- name: Install Windows | |
if: contains(matrix.component.artifact, 'win-installer') | |
shell: pwsh | |
run: | | |
$exePath = Get-ChildItem -Path ./*.exe | %{$_.FullName} | |
Start-Process -Wait -FilePath "$exePath" -ArgumentList "/SILENT /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /ALLOWDOWNGRADE=1" | |
- name: Install Linux | |
if: contains(matrix.component.artifact, 'linux') | |
run: | | |
debpath=$(find ./*.deb) | |
sudo apt install $debpath | |
- name: Install macOS | |
if: contains(matrix.component.artifact, 'macos') | |
run: | | |
# avoid letting Homebrew's `git` in `/opt/homebrew/bin` override `/usr/local/bin/git` | |
arch="$(uname -m)" | |
test arm64 != "$arch" || | |
brew uninstall git | |
pkgpath=$(find ./*universal*.pkg) | |
sudo installer -pkg $pkgpath -target / | |
- name: Validate | |
shell: bash | |
run: | | |
"${{ matrix.component.command }}" --version | sed 's/git version //' >actual | |
echo ${{ needs.prereqs.outputs.tag_version }} >expect | |
cmp expect actual || exit 1 | |
- name: Validate universal binary CPU architecture | |
if: contains(matrix.component.os, 'macos') | |
shell: bash | |
run: | | |
set -ex | |
git version --build-options >actual | |
cat actual | |
grep "cpu: $(uname -m)" actual | |
# End validate installers | |
create-github-release: | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
id-token: write # required for Azure login via OIDC | |
needs: | |
- validate-installers | |
- create-linux-artifacts | |
- create-macos-artifacts | |
- windows_artifacts | |
- prereqs | |
env: | |
AZURE_VAULT: ${{ secrets.AZURE_VAULT }} | |
GPG_PUBLIC_KEY_SECRET_NAME: ${{ secrets.GPG_PUBLIC_KEY_SECRET_NAME }} | |
environment: release | |
if: | | |
success() || | |
(needs.create-linux-artifacts.result == 'skipped' && | |
needs.create-macos-artifacts.result == 'success' && | |
needs.windows_artifacts.result == 'success') | |
steps: | |
- name: Download Windows portable (x86_64) | |
uses: actions/download-artifact@v4 | |
with: | |
name: win-portable-x86_64 | |
path: win-portable-x86_64 | |
- name: Download Windows portable (aarch64) | |
uses: actions/download-artifact@v4 | |
with: | |
name: win-portable-aarch64 | |
path: win-portable-aarch64 | |
- name: Download Windows installer (x86_64) | |
uses: actions/download-artifact@v4 | |
with: | |
name: win-installer-x86_64 | |
path: win-installer-x86_64 | |
- name: Download Windows installer (aarch64) | |
uses: actions/download-artifact@v4 | |
with: | |
name: win-installer-aarch64 | |
path: win-installer-aarch64 | |
- name: Download macOS artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
name: macos-artifacts | |
path: macos-artifacts | |
- name: Download Debian package | |
uses: actions/download-artifact@v4 | |
with: | |
name: linux-artifacts | |
path: deb-package | |
- name: Log into Azure | |
uses: azure/login@v2 | |
with: | |
client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
- name: Download GPG public key signature file | |
run: | | |
az keyvault secret show --name "$GPG_PUBLIC_KEY_SECRET_NAME" \ | |
--vault-name "$AZURE_VAULT" --query "value" \ | |
| sed -e 's/^"//' -e 's/"$//' | base64 -d >msft-git-public.asc | |
mv msft-git-public.asc deb-package | |
- uses: actions/github-script@v6 | |
with: | |
script: | | |
const fs = require('fs'); | |
const path = require('path'); | |
var releaseMetadata = { | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}; | |
// Create the release | |
var tagName = "${{ needs.prereqs.outputs.tag_name }}"; | |
var createdRelease = await github.rest.repos.createRelease({ | |
...releaseMetadata, | |
draft: true, | |
tag_name: tagName, | |
name: tagName | |
}); | |
releaseMetadata.release_id = createdRelease.data.id; | |
// Uploads contents of directory to the release created above | |
async function uploadDirectoryToRelease(directory, includeExtensions=[]) { | |
return fs.promises.readdir(directory) | |
.then(async(files) => Promise.all( | |
files.filter(file => { | |
return includeExtensions.length==0 || includeExtensions.includes(path.extname(file).toLowerCase()); | |
}) | |
.map(async (file) => { | |
var filePath = path.join(directory, file); | |
github.rest.repos.uploadReleaseAsset({ | |
...releaseMetadata, | |
name: file, | |
headers: { | |
"content-length": (await fs.promises.stat(filePath)).size | |
}, | |
data: fs.createReadStream(filePath) | |
}); | |
})) | |
); | |
} | |
await Promise.all([ | |
// Upload Windows x86_64 artifacts | |
uploadDirectoryToRelease('win-installer-x86_64', ['.exe']), | |
uploadDirectoryToRelease('win-portable-x86_64', ['.exe']), | |
// Upload Windows aarch64 artifacts | |
uploadDirectoryToRelease('win-installer-aarch64', ['.exe']), | |
uploadDirectoryToRelease('win-portable-aarch64', ['.exe']), | |
// Upload Mac artifacts | |
uploadDirectoryToRelease('macos-artifacts'), | |
// Upload Ubuntu artifacts | |
uploadDirectoryToRelease('deb-package') | |
]); |