build-git-installers #250
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 | |
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: Determine tag to build | |
run: | | |
echo "name=${GITHUB_REF#refs/tags/}" >>$GITHUB_OUTPUT | |
echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT | |
id: tag | |
# End check prerequisites for the workflow | |
# Build and sign Debian package | |
create-linux-unsigned-artifacts: | |
runs-on: ubuntu-latest | |
container: | |
image: ubuntu:16.04 # expanded security maintenance until 04/02/2026, according to https://endoflife.date/ubuntu | |
volumes: | |
# override /__e/node20 because GitHub Actions uses a version that requires too-recent glibc, see "Install dependencies" below | |
- /tmp:/__e/node20 | |
needs: prereqs | |
steps: | |
- name: Install dependencies | |
run: | | |
set -ex | |
apt-get update -q | |
apt-get install -y -q --no-install-recommends \ | |
build-essential \ | |
tcl tk gettext asciidoc xmlto \ | |
libcurl4-gnutls-dev libpcre2-dev zlib1g-dev libexpat-dev \ | |
curl ca-certificates | |
# Install a Node.js version that works in older Ubuntu containers (read: does not require very recent glibc) | |
NODE_VERSION=v20.18.1 && | |
NODE_URL=https://unofficial-builds.nodejs.org/download/release/$NODE_VERSION/node-$NODE_VERSION-linux-x64-glibc-217.tar.gz && | |
curl -Lo /tmp/node.tar.gz $NODE_URL && | |
tar -C /__e/node20 -x --strip-components=1 -f /tmp/node.tar.gz | |
- 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 \ | |
USE_CURL_FOR_IMAP_SEND=1 NO_OPENSSL=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: Upload artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: linux-unsigned-artifacts | |
path: | | |
*.deb | |
create-linux-artifacts: | |
runs-on: ubuntu-latest | |
needs: [prereqs, create-linux-unsigned-artifacts] | |
environment: release | |
steps: | |
- 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-get install -y 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" --output tsv)" | |
passphrase="$(az keyvault secret show --name "$GPG_PASSPHRASE_SECRET_NAME" --vault-name "$AZURE_VAULT" --query "value" --output tsv)" | |
keygrip="$(az keyvault secret show --name "$GPG_KEYGRIP_SECRET_NAME" --vault-name "$AZURE_VAULT" --query "value" --output tsv)" | |
# 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: Download artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
name: linux-unsigned-artifacts | |
- 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 | |
runs-on: ${{ matrix.component.os }} | |
needs: [prereqs, 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 |