diff --git a/.github/macos-installer/Makefile b/.github/macos-installer/Makefile index fa7cfe9eb77e4a..cba8bf5a890d1d 100644 --- a/.github/macos-installer/Makefile +++ b/.github/macos-installer/Makefile @@ -31,11 +31,16 @@ GIT_PREFIX := $(PREFIX)/git BUILD_CODE := $(ARCH_CODE) BUILD_DIR := $(GITHUB_WORKSPACE)/payload DESTDIR := $(PWD)/stage/git-$(BUILD_CODE)-$(VERSION) -ARTIFACTDIR := build_artifacts +ARTIFACTDIR := build-artifacts 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) GIT_BUILT_FROM_COMMIT="$(GIT_BUILT_FROM_COMMIT)" DESTDIR=$(DESTDIR) CORES := $(shell bash -c "sysctl hw.ncpu | awk '{print \$$2}'") -.PHONY: image pkg payload +# Guard against environment variables +APPLE_APP_IDENTITY = +APPLE_INSTALLER_IDENTITY = +APPLE_KEYCHAIN_PROFILE = + +.PHONY: image pkg payload codesign notarize .SECONDARY: @@ -112,8 +117,17 @@ disk-image/VERSION-$(VERSION)-$(ARCH_CODE): mkdir disk-image touch "$@" +pkg_cmd := pkgbuild --identifier com.git.pkg --version $(VERSION) \ + --root $(ARTIFACTDIR)$(PREFIX) --scripts assets/scripts \ + --install-location $(PREFIX) --component-plist ./assets/git-components.plist + +ifdef APPLE_INSTALLER_IDENTITY + pkg_cmd += --sign "$(APPLE_INSTALLER_IDENTITY)" +endif + +pkg_cmd += disk-image/git-$(VERSION)-$(BUILD_CODE).pkg disk-image/git-$(VERSION)-$(BUILD_CODE).pkg: disk-image/VERSION-$(VERSION)-$(ARCH_CODE) symlinks - 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 + $(pkg_cmd) git-%-$(BUILD_CODE).dmg: hdiutil create git-$(VERSION)-$(BUILD_CODE).uncompressed.dmg -fs HFS+ -srcfolder disk-image -volname "Git $(VERSION) $(CPU_VENDOR) $(ARCH)" -ov @@ -125,3 +139,18 @@ payload: $(BUILD_DIR)/git-$(VERSION)/osx-installed $(BUILD_DIR)/git-$(VERSION)/o pkg: disk-image/git-$(VERSION)-$(BUILD_CODE).pkg image: git-$(VERSION)-$(BUILD_CODE).dmg + +ifdef APPLE_APP_IDENTITY +codesign: + @$(CURDIR)/../scripts/codesign.sh --payload="build-artifacts/usr/local/git" \ + --identity="$(APPLE_APP_IDENTITY)" \ + --entitlements="$(CURDIR)/entitlements.xml" +endif + +# Notarization can only happen if the package is fully signed +ifdef APPLE_KEYCHAIN_PROFILE +notarize: + @$(CURDIR)/../scripts/notarize.sh \ + --package="disk-image/git-$(VERSION)-$(BUILD_CODE).pkg" \ + --keychain-profile="$(APPLE_KEYCHAIN_PROFILE)" +endif diff --git a/.github/macos-installer/entitlements.xml b/.github/macos-installer/entitlements.xml new file mode 100644 index 00000000000000..46f675661149b6 --- /dev/null +++ b/.github/macos-installer/entitlements.xml @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/.github/scripts/codesign.sh b/.github/scripts/codesign.sh new file mode 100755 index 00000000000000..076b29f93be45e --- /dev/null +++ b/.github/scripts/codesign.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +sign_directory () { + ( + cd "$1" + for f in * + do + macho=$(file --mime $f | grep mach) + # Runtime sign dylibs and Mach-O binaries + if [[ $f == *.dylib ]] || [ ! -z "$macho" ]; + then + echo "Runtime Signing $f" + codesign -s "$IDENTITY" $f --timestamp --force --options=runtime --entitlements $ENTITLEMENTS_FILE + elif [ -d "$f" ]; + then + echo "Signing files in subdirectory $f" + sign_directory "$f" + + else + echo "Signing $f" + codesign -s "$IDENTITY" $f --timestamp --force + fi + done + ) +} + +for i in "$@" +do +case "$i" in + --payload=*) + SIGN_DIR="${i#*=}" + shift # past argument=value + ;; + --identity=*) + IDENTITY="${i#*=}" + shift # past argument=value + ;; + --entitlements=*) + ENTITLEMENTS_FILE="${i#*=}" + shift # past argument=value + ;; + *) + die "unknown option '$i'" + ;; +esac +done + +if [ -z "$SIGN_DIR" ]; then + echo "error: missing directory argument" + exit 1 +elif [ -z "$IDENTITY" ]; then + echo "error: missing signing identity argument" + exit 1 +elif [ -z "$ENTITLEMENTS_FILE" ]; then + echo "error: missing entitlements file argument" + exit 1 +fi + +echo "======== INPUTS ========" +echo "Directory: $SIGN_DIR" +echo "Signing identity: $IDENTITY" +echo "Entitlements: $ENTITLEMENTS_FILE" +echo "======== END INPUTS ========" + +sign_directory "$SIGN_DIR" diff --git a/.github/scripts/notarize.sh b/.github/scripts/notarize.sh new file mode 100755 index 00000000000000..9315d688afbd49 --- /dev/null +++ b/.github/scripts/notarize.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +for i in "$@" +do +case "$i" in + --package=*) + PACKAGE="${i#*=}" + shift # past argument=value + ;; + --keychain-profile=*) + KEYCHAIN_PROFILE="${i#*=}" + shift # past argument=value + ;; + *) + die "unknown option '$i'" + ;; +esac +done + +if [ -z "$PACKAGE" ]; then + echo "error: missing package argument" + exit 1 +elif [ -z "$KEYCHAIN_PROFILE" ]; then + echo "error: missing keychain profile argument" + exit 1 +fi + +# Exit as soon as any line fails +set -e + +# Send the notarization request +xcrun notarytool submit -v "$PACKAGE" -p "$KEYCHAIN_PROFILE" --wait + +# Staple the notarization ticket (to allow offline installation) +xcrun stapler staple -v "$PACKAGE" diff --git a/.github/scripts/run-esrp-signing.py b/.github/scripts/run-esrp-signing.py deleted file mode 100644 index 725bf4580f5f1b..00000000000000 --- a/.github/scripts/run-esrp-signing.py +++ /dev/null @@ -1,135 +0,0 @@ -import argparse -import json -import os -import glob -import pprint -import subprocess -import sys -import re - -parser = argparse.ArgumentParser(description='Sign binaries for macOS') -parser.add_argument('path', help='Path to file for signing') -parser.add_argument('keycode', help='Platform-specific key code for signing') -parser.add_argument('opcode', help='Platform-specific operation code for signing') -# Setting nargs=argparse.REMAINDER allows us to pass in params that begin with `--` -parser.add_argument('--params', nargs=argparse.REMAINDER, help='Parameters for signing') -args = parser.parse_args() - -esrp_tool = os.path.join("esrp", "tools", "EsrpClient.exe") - -aad_id = os.environ['AZURE_AAD_ID'].strip() -workspace = os.environ['GITHUB_WORKSPACE'].strip() - -source_location = args.path -files = glob.glob(os.path.join(source_location, "*")) - -print("Found files:") -pprint.pp(files) - -auth_json = { - "Version": "1.0.0", - "AuthenticationType": "AAD_CERT", - "TenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", - "ClientId": f"{aad_id}", - "AuthCert": { - "SubjectName": f"CN={aad_id}.microsoft.com", - "StoreLocation": "LocalMachine", - "StoreName": "My", - "SendX5c" : "true" - }, - "RequestSigningCert": { - "SubjectName": f"CN={aad_id}", - "StoreLocation": "LocalMachine", - "StoreName": "My" - } -} - -input_json = { - "Version": "1.0.0", - "SignBatches": [ - { - "SourceLocationType": "UNC", - "SourceRootDirectory": source_location, - "DestinationLocationType": "UNC", - "DestinationRootDirectory": workspace, - "SignRequestFiles": [], - "SigningInfo": { - "Operations": [ - { - "KeyCode": f"{args.keycode}", - "OperationCode": f"{args.opcode}", - "Parameters": {}, - "ToolName": "sign", - "ToolVersion": "1.0", - } - ] - } - } - ] -} - -# add files to sign -for f in files: - name = os.path.basename(f) - input_json["SignBatches"][0]["SignRequestFiles"].append( - { - "SourceLocation": name, - "DestinationLocation": os.path.join("signed", name), - } - ) - -# add parameters to input.json (e.g. enabling the hardened runtime for macOS) -if args.params is not None: - i = 0 - while i < len(args.params): - input_json["SignBatches"][0]["SigningInfo"]["Operations"][0]["Parameters"][args.params[i]] = args.params[i + 1] - i += 2 - -policy_json = { - "Version": "1.0.0", - "Intent": "production release", - "ContentType": "binary", -} - -configs = [ - ("auth.json", auth_json), - ("input.json", input_json), - ("policy.json", policy_json), -] - -for filename, data in configs: - with open(filename, 'w') as fp: - json.dump(data, fp) - -# Run ESRP Client -esrp_out = "esrp_out.json" -result = subprocess.run( - [esrp_tool, "sign", - "-a", "auth.json", - "-i", "input.json", - "-p", "policy.json", - "-o", esrp_out, - "-l", "Verbose"], - capture_output=True, - text=True, - cwd=workspace) - -# Scrub log before printing -log = re.sub(r'^.+Uploading.*to\s*destinationUrl\s*(.+?),.+$', - '***', - result.stdout, - flags=re.IGNORECASE|re.MULTILINE) -print(log) - -if result.returncode != 0: - print("Failed to run ESRPClient.exe") - sys.exit(1) - -if os.path.isfile(esrp_out): - print("ESRP output json:") - with open(esrp_out, 'r') as fp: - pprint.pp(json.load(fp)) - -for file in files: - if os.path.isfile(os.path.join("signed", file)): - print(f"Success!\nSigned {file}") \ No newline at end of file diff --git a/.github/scripts/set-up-esrp.ps1 b/.github/scripts/set-up-esrp.ps1 deleted file mode 100644 index ca56266e33f553..00000000000000 --- a/.github/scripts/set-up-esrp.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -# Install ESRP client -az storage blob download --file esrp.zip --auth-mode login --account-name esrpsigningstorage --container signing-resources --name microsoft.esrpclient.1.2.76.nupkg -Expand-Archive -Path esrp.zip -DestinationPath .\esrp - -# Install certificates -az keyvault secret download --vault-name "$env:AZURE_VAULT" --name "$env:AUTH_CERT" --file out.pfx -certutil -f -importpfx out.pfx -Remove-Item out.pfx - -az keyvault secret download --vault-name "$env:AZURE_VAULT" --name "$env:REQUEST_SIGNING_CERT" --file out.pfx -certutil -f -importpfx out.pfx -Remove-Item out.pfx \ No newline at end of file diff --git a/.github/workflows/build-git-installers.yml b/.github/workflows/build-git-installers.yml index 8ac2bef0e85efc..05c9e97b011355 100644 --- a/.github/workflows/build-git-installers.yml +++ b/.github/workflows/build-git-installers.yml @@ -10,13 +10,9 @@ jobs: prereqs: runs-on: ubuntu-latest environment: release - env: - AZ_SUB: ${{ secrets.AZURE_SUBSCRIPTION }} - AZ_CREDS: ${{ secrets.AZURE_CREDENTIALS }} 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 - deb_signable: ${{ steps.deb.outputs.signable }} # Whether the credentials needed to sign the .deb package are available steps: - name: Validate tag run: | @@ -30,9 +26,6 @@ jobs: echo "name=${GITHUB_REF#refs/tags/}" >>$GITHUB_OUTPUT echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT id: tag - - name: Determine whether signing certificates are present - run: echo "signable=$([[ $AZ_SUB != '' && $AZ_CREDS != '' ]] && echo 'true' || echo 'false')" >>$GITHUB_OUTPUT - id: deb - name: Clone git uses: actions/checkout@v3 - name: Validate the tag identified with trigger @@ -309,7 +302,7 @@ jobs: # End build Windows installers # Build and sign Mac OSX installers & upload artifacts - osx_build: + create-macos-artifacts: strategy: matrix: arch: @@ -321,6 +314,7 @@ jobs: needs: prereqs env: VERSION: "${{ needs.prereqs.outputs.tag_version }}" + environment: release steps: - name: Check out repository uses: actions/checkout@v3 @@ -333,7 +327,56 @@ jobs: brew install automake asciidoc xmlto docbook brew link --force gettext - - name: Build payload + - 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 @@ -375,290 +418,55 @@ jobs: # Lay out payload make -C git/.github/macos-installer V=1 payload - # This step is necessary because we cannot use the $VERSION - # environment variable or the tag_version output from the prereqs - # job in the upload-artifact task. - mkdir -p build_artifacts - cp -R stage/git-${{ matrix.arch.name }}-$VERSION/ build_artifacts + # Codesign payload + cp -R stage/git-${{ matrix.arch.name }}-$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" - # We keep a list of executable files because their executable bits are - # removed when they are zipped, and we need to re-add. - find build_artifacts -type f -a -perm -u=x >executable-files.txt + # Build and sign pkg + make -C git/.github/macos-installer V=1 pkg \ + APPLE_INSTALLER_IDENTITY="$I3" \ + || die "Creating signed pkg failed" - - name: Upload macOS artifacts - uses: actions/upload-artifact@v3 - with: - name: tmp.osx-${{ matrix.arch.name }}-build - path: | - build_artifacts + # 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" - - name: Upload list of executable files - uses: actions/upload-artifact@v3 - with: - name: tmp.executable-files - path: | - executable-files.txt + # Create DMG + make -C git/.github/macos-installer V=1 image || die "Creating DMG failed" - osx_sign_payload: - # ESRP service requires signing to run on Windows - runs-on: windows-latest - environment: release - needs: osx_build - strategy: - matrix: - arch: [x86_64, arm64] - steps: - - name: Check out repository - uses: actions/checkout@v3 - with: - path: 'git' - - - name: Download unsigned build artifacts - uses: actions/download-artifact@v3 - with: - name: tmp.osx-${{ matrix.arch }}-build - path: build_artifacts - - - name: Zip unsigned build artifacts - shell: pwsh - run: | - Compress-Archive -Path build_artifacts build_artifacts/build_artifacts.zip - cd build_artifacts - Get-ChildItem -Exclude build_artifacts.zip | Remove-Item -Recurse -Force - - - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Set up ESRP client - shell: pwsh - env: - AZURE_VAULT: ${{ secrets.AZURE_VAULT }} - AUTH_CERT: ${{ secrets.AZURE_VAULT_AUTH_CERT_NAME }} - REQUEST_SIGNING_CERT: ${{ secrets.AZURE_VAULT_REQUEST_SIGNING_CERT_NAME }} - run: | - git\.github\scripts\set-up-esrp.ps1 - - - name: Run ESRP client - shell: pwsh - env: - AZURE_AAD_ID: ${{ secrets.AZURE_AAD_ID }} - APPLE_KEY_CODE: ${{ secrets.APPLE_KEY_CODE }} - APPLE_SIGNING_OP_CODE: ${{ secrets.APPLE_SIGNING_OPERATION_CODE }} - run: | - python git\.github\scripts\run-esrp-signing.py build_artifacts ` - $env:APPLE_KEY_CODE $env:APPLE_SIGNING_OP_CODE ` - --params 'Hardening' '--options=runtime' - - - name: Unzip signed build artifacts - shell: pwsh - run: | - Expand-Archive signed/build_artifacts.zip -DestinationPath signed - Remove-Item signed/build_artifacts.zip - - - name: Upload signed payload - uses: actions/upload-artifact@v3 - with: - name: osx-signed-${{ matrix.arch }}-payload - path: | - signed - - osx_pack: - strategy: - matrix: - arch: - - name: x86_64 - runner: macos-latest - - name: arm64 - runner: macos-latest-xl-arm64 - runs-on: ${{ matrix.arch.runner }} - needs: [prereqs, osx_sign_payload] - steps: - - name: Check out repository - uses: actions/checkout@v3 - with: - path: 'git' + # Move all artifacts into top-level directory + mv git/.github/macos-installer/disk-image/*.pkg git/.github/macos-installer/ - - name: Download signed artifacts - uses: actions/download-artifact@v3 - with: - name: osx-signed-${{ matrix.arch.name }}-payload - - - name: Download list of executable files - uses: actions/download-artifact@v3 - with: - name: tmp.executable-files - - - name: Build macOS pkg - env: - VERSION: "${{ needs.prereqs.outputs.tag_version }}" - run: | - die () { - echo "$*" >&2 - exit 1 - } - - set -ex - - # Install findutils to use gxargs below - brew install findutils - - # Configure the environment - export CURL_LDFLAGS=$(curl-config --libs) - - # Add executable bits and move build_artifacts into - # the same directory as Makefile (so that executable bits - # will be recognized). - gxargs -r -d '\n' chmod a+x &2 - exit 1 - } - - set -ex - - # Move disk-image into the same directory as Makefile - mv disk-image git/.github/macos-installer/ - - PATH=/usr/local/bin:$PATH \ - make -C git/.github/macos-installer V=1 image || die "Build failed" - - - name: Publish disk image - uses: actions/upload-artifact@v3 - with: - name: osx-${{ matrix.arch.name }}-dmg - path: git/.github/macos-installer/*.dmg + git/.github/macos-installer/*.dmg + git/.github/macos-installer/*.pkg # End build and sign Mac OSX installers - # Build & sign Ubuntu package - ubuntu_build: - runs-on: ubuntu-20.04 + # 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@v3 with: path: git - - name: Build and package .deb + + - name: Build and create Debian package run: | set -ex @@ -710,57 +518,56 @@ jobs: specialized in supporting monorepo scenarios. Includes the Scalar CLI. EOF - dpkg-deb --build "$PKGNAME" + dpkg-deb -Zxz --build "$PKGNAME" + # Move Debian package for later artifact upload + mv "$PKGNAME.deb" "$GITHUB_WORKSPACE" - mkdir $GITHUB_WORKSPACE/artifacts - mv "$PKGNAME.deb" $GITHUB_WORKSPACE/artifacts/ - - name: Publish unsigned .deb package - uses: actions/upload-artifact@v3 - with: - name: deb-package-unsigned - path: artifacts/ - ubuntu_sign-artifacts: - runs-on: windows-latest # Must be run on Windows due to ESRP executable OS compatibility - environment: release - needs: [ubuntu_build, prereqs] - if: needs.prereqs.outputs.deb_signable == 'true' - env: - ARTIFACTS_DIR: artifacts - steps: - - name: Clone repository - uses: actions/checkout@v3 - with: - path: 'git' - - name: Download unsigned packages - uses: actions/download-artifact@v3 - with: - name: deb-package-unsigned - path: unsigned - - uses: azure/login@v1 + - name: Log into Azure + uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - - name: Set up ESRP client - shell: pwsh + + - name: Prepare for GPG signing env: AZURE_VAULT: ${{ secrets.AZURE_VAULT }} - AUTH_CERT: ${{ secrets.AZURE_VAULT_AUTH_CERT_NAME }} - REQUEST_SIGNING_CERT: ${{ secrets.AZURE_VAULT_REQUEST_SIGNING_CERT_NAME }} + 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: | - git\.github\scripts\set-up-esrp.ps1 - - name: Sign package - shell: pwsh - env: - AZURE_AAD_ID: ${{ secrets.AZURE_AAD_ID }} - LINUX_KEY_CODE: ${{ secrets.LINUX_KEY_CODE }} - LINUX_OP_CODE: ${{ secrets.LINUX_OPERATION_CODE }} + # 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: | - python git\.github\scripts\run-esrp-signing.py unsigned $env:LINUX_KEY_CODE $env:LINUX_OP_CODE - - name: Upload signed artifact + # 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@v3 with: - name: deb-package-signed - path: signed - # End build & sign Ubuntu package + name: linux-artifacts + path: | + *.deb + # End build and sign Debian package # Validate installers validate-installers: @@ -769,19 +576,19 @@ jobs: matrix: component: - os: ubuntu-latest - artifact: deb-package-signed + artifact: linux-artifacts command: git - os: macos-latest-xl-arm64 - artifact: osx-signed-arm64-pkg + artifact: macos-artifacts command: git - os: macos-latest - artifact: osx-signed-x86_64-pkg + artifact: macos-artifacts command: git - os: windows-latest artifact: win-installer-x86_64 command: $PROGRAMFILES\Git\cmd\git.exe runs-on: ${{ matrix.component.os }} - needs: [prereqs, windows_artifacts, osx_publish_dmg, ubuntu_sign-artifacts] + needs: [prereqs, windows_artifacts, create-macos-artifacts, create-linux-artifacts] steps: - name: Download artifacts uses: actions/download-artifact@v3 @@ -805,10 +612,11 @@ jobs: if: contains(matrix.component.os, 'macos') run: | # avoid letting Homebrew's `git` in `/opt/homebrew/bin` override `/usr/local/bin/git` - test arm64 != "$(uname -m)" || + arch="$(uname -m)" + test arm64 != "$arch" || brew uninstall git - pkgpath=$(find ./*.pkg) + pkgpath=$(find ./*$arch*.pkg) sudo installer -pkg $pkgpath -target / - name: Validate @@ -822,10 +630,14 @@ jobs: create-github-release: runs-on: ubuntu-latest needs: [validate-installers] + env: + AZURE_VAULT: ${{ secrets.AZURE_VAULT }} + GPG_PUBLIC_KEY_SECRET_NAME: ${{ secrets.GPG_PUBLIC_KEY_SECRET_NAME }} + environment: release if: | success() || - (needs.ubuntu_sign-artifacts.result == 'skipped' && - needs.osx_publish_dmg.result == 'success' && + (needs.create-linux-artifacts.result == 'skipped' && + needs.create-macos-artifacts.result == 'success' && needs.windows_artifacts.result == 'success') steps: - name: Download Windows portable installer @@ -833,43 +645,37 @@ jobs: with: name: win-portable-x86_64 path: win-portable-x86_64 + - name: Download Windows x86_64 installer uses: actions/download-artifact@v3 with: name: win-installer-x86_64 path: win-installer-x86_64 - - name: Download Mac x86_64 dmg - uses: actions/download-artifact@v3 - with: - name: osx-x86_64-dmg - path: osx-dmg - - name: Download Mac ARM64 dmg - uses: actions/download-artifact@v3 - with: - name: osx-arm64-dmg - path: osx-dmg - - name: Download Mac x86_64 pkg - uses: actions/download-artifact@v3 - with: - name: osx-signed-x86_64-pkg - path: osx-pkg - - name: Download Mac ARM64 pkg + + - name: Download macOS artifacts uses: actions/download-artifact@v3 with: - name: osx-signed-arm64-pkg - path: osx-pkg - - name: Download Ubuntu package (signed) - if: needs.prereqs.outputs.deb_signable == 'true' + name: macos-artifacts + path: macos-artifacts + + - name: Download Debian package uses: actions/download-artifact@v3 with: - name: deb-package-signed + name: linux-artifacts path: deb-package - - name: Download Ubuntu package (unsigned) - if: needs.prereqs.outputs.deb_signable != 'true' - uses: actions/download-artifact@v3 + + - name: Log into Azure + uses: azure/login@v1 with: - name: deb-package-unsigned - path: deb-package + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - 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: | @@ -918,8 +724,7 @@ jobs: uploadDirectoryToRelease('win-portable-x86_64', ['.exe']), // Upload Mac artifacts - uploadDirectoryToRelease('osx-dmg'), - uploadDirectoryToRelease('osx-pkg'), + uploadDirectoryToRelease('macos-artifacts'), // Upload Ubuntu artifacts uploadDirectoryToRelease('deb-package') diff --git a/README.md b/README.md index fe82df58415570..cb7be1a108914f 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,59 @@ Or you can run the `git update-microsoft-git` command, which will run those brew ## Linux ### Ubuntu/Debian distributions -On newer distributions*, you can download the most recent Debian package from -the [releases page](https://github.com/microsoft/git/releases/latest) (or -using a tool such as `wget`) then run: +On newer distributions*, you can install using the most recent Debian package. +To download and validate the signature of this package, run the following: ```shell -sudo dpkg -i +# Install needed packages +apt-get install -y curl debsig-verify + +# Download public key signature file +curl -s https://api.github.com/repos/microsoft/git/releases/latest \ +| grep -E 'browser_download_url.*msft-git-public.asc' \ +| cut -d : -f 2,3 \ +| tr -d \" \ +| xargs -I 'url' curl -L -o msft-git-public.asc 'url' + +# De-armor public key signature file +gpg --output msft-git-public.gpg --dearmor msft-git-public.asc + +# Note that the fingerprint of this key is "B8F12E25441124E1", which you can +# determine by running: +gpg --show-keys msft-git-public.asc | head -n 2 | tail -n 1 | tail -c 17 + +# Copy de-armored public key to debsig keyring folder +mkdir /usr/share/debsig/keyrings/B8F12E25441124E1 +mv msft-git-public.gpg /usr/share/debsig/keyrings/B8F12E25441124E1/ + +# Create an appropriate policy file +mkdir /etc/debsig/policies/B8F12E25441124E1 +cat > /etc/debsig/policies/B8F12E25441124E1/generic.pol << EOL + + + + + + + + + + + +EOL + +# Download Debian package +curl -s https://api.github.com/repos/microsoft/git/releases/latest \ +| grep "browser_download_url.*deb" \ +| cut -d : -f 2,3 \ +| tr -d \" \ +| xargs -I 'url' curl -L -o msft-git.deb 'url' + +# Verify +debsig-verify msft-git.deb + +# Install +sudo dpkg -i msft-git.deb ``` Double-check that you have the right version by running these commands,