diff --git a/.github/workflows/lint-docs.yml b/.github/workflows/lint-docs.yml index c1a09a92d..fe7d53095 100644 --- a/.github/workflows/lint-docs.yml +++ b/.github/workflows/lint-docs.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - - uses: DavidAnson/markdownlint-cli2-action@e3969ef4ed874458f4b66d4631f78fff7717012c + - uses: DavidAnson/markdownlint-cli2-action@d57f8bd57670b9c1deedf71219dd494614ff3335 with: globs: | "**/*.md" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aea67dbe5..70ff1d6a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -378,29 +378,28 @@ jobs: - name: Lay out run: | mkdir -p linux-build/deb linux-build/tar - mv out/linux/Packaging.Linux/deb/Release/*.deb linux-build/deb - mv out/linux/Packaging.Linux/tar/Release/*.tar.gz linux-build/tar + mv out/linux/Packaging.Linux/Release/deb/*.deb linux-build/deb + mv out/linux/Packaging.Linux/Release/tar/*.tar.gz linux-build/tar - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: linux-build + name: tmp.linux-build path: | linux-build linux-sign: - name: Sign Debian package + name: Sign Linux tarball and Debian package + needs: linux-build # ESRP service requires signing to run on Windows runs-on: windows-latest - needs: linux-build steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - name: Download artifacts uses: actions/download-artifact@v3 with: - name: linux-build - path: artifacts + name: tmp.linux-build - uses: azure/login@v1 with: @@ -422,14 +421,197 @@ jobs: LINUX_KEY_CODE: ${{ secrets.LINUX_KEY_CODE }} LINUX_OP_CODE: ${{ secrets.LINUX_OPERATION_CODE }} run: | - python .github/run_esrp_signing.py artifacts/deb $env:LINUX_KEY_CODE $env:LINUX_OP_CODE + python .github/run_esrp_signing.py deb $env:LINUX_KEY_CODE $env:LINUX_OP_CODE + python .github/run_esrp_signing.py tar $env:LINUX_KEY_CODE $env:LINUX_OP_CODE - - name: Upload signed Debian package + - name: Upload signed tarball and Debian package uses: actions/upload-artifact@v3 with: name: linux-sign path: | signed + +# ================================ +# .NET Tool +# ================================ + dotnet-tool-build: + name: Build .NET tool + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Indicate full history so Nerdbank.GitVersioning works. + + - name: Setup .NET + uses: actions/setup-dotnet@v3.0.3 + with: + dotnet-version: 6.0.201 + + - uses: dotnet/nbgv@master + with: + setCommonVars: true + + - name: Build .NET tool + run: | + src/shared/DotnetTool/layout.sh --configuration=Release + + - name: Upload .NET tool artifacts + uses: actions/upload-artifact@v3 + with: + name: tmp.dotnet-tool-build + path: | + out/shared/DotnetTool/nupkg/Release + + dotnet-tool-payload-sign: + name: Sign .NET tool payload + # ESRP service requires signing to run on Windows + runs-on: windows-latest + needs: dotnet-tool-build + steps: + - name: Check out repository + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + + - name: Download payload + uses: actions/download-artifact@v3 + with: + name: tmp.dotnet-tool-build + + - name: Zip unsigned payload + shell: pwsh + run: | + Compress-Archive -Path payload payload/payload.zip + cd payload + Get-ChildItem -Exclude payload.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: | + .github\set_up_esrp.ps1 + + - name: Run ESRP client + shell: pwsh + env: + AZURE_AAD_ID: ${{ secrets.AZURE_AAD_ID }} + NUGET_KEY_CODE: ${{ secrets.NUGET_KEY_CODE }} + NUGET_OPERATION_CODE: ${{ secrets.NUGET_OPERATION_CODE }} + run: | + python .github\run_esrp_signing.py payload ` + $env:NUGET_KEY_CODE $env:NUGET_OPERATION_CODE + + - name: Lay out signed payload, images, and symbols + shell: bash + run: | + mkdir dotnet-tool-payload-sign + rm -rf payload + mv images payload.sym -t dotnet-tool-payload-sign + unzip signed/payload.zip -d dotnet-tool-payload-sign + + - name: Upload signed payload + uses: actions/upload-artifact@v3 + with: + name: dotnet-tool-payload-sign + path: | + dotnet-tool-payload-sign + + dotnet-tool-pack: + name: Package .NET tool + runs-on: ubuntu-latest + needs: dotnet-tool-payload-sign + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Indicate full history so Nerdbank.GitVersioning works. + + - name: Download signed payload + uses: actions/download-artifact@v3 + with: + name: dotnet-tool-payload-sign + path: signed + + - name: Setup .NET + uses: actions/setup-dotnet@v3.0.3 + with: + dotnet-version: 6.0.201 + + - uses: dotnet/nbgv@master + with: + setCommonVars: true + + - name: Package tool + run: | + src/shared/DotnetTool/pack.sh --configuration=Release \ + --version=$GitBuildVersionSimple --publish-dir=$(pwd)/signed + + - name: Upload unsigned package + uses: actions/upload-artifact@v3 + with: + name: tmp.dotnet-tool-package-unsigned + path: | + out/shared/DotnetTool/nupkg/Release/*.nupkg + + dotnet-tool-sign: + name: Sign .NET tool package + # ESRP service requires signing to run on Windows + runs-on: windows-latest + needs: dotnet-tool-pack + steps: + - name: Check out repository + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + + - name: Download unsigned package + uses: actions/download-artifact@v3 + with: + name: tmp.dotnet-tool-package-unsigned + path: nupkg + + - name: Zip unsigned package + shell: pwsh + run: | + Compress-Archive -Path nupkg/*.nupkg nupkg/gcm-nupkg.zip + cd nupkg + Get-ChildItem -Exclude gcm-nupkg.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: | + .github\set_up_esrp.ps1 + + - name: Sign package + shell: pwsh + env: + AZURE_AAD_ID: ${{ secrets.AZURE_AAD_ID }} + NUGET_KEY_CODE: ${{ secrets.NUGET_KEY_CODE }} + NUGET_OPERATION_CODE: ${{ secrets.NUGET_OPERATION_CODE }} + run: | + python .github\run_esrp_signing.py nupkg $env:NUGET_KEY_CODE $env:NUGET_OPERATION_CODE + + - name: Unzip signed package + shell: pwsh + run: | + Expand-Archive -LiteralPath signed\gcm-nupkg.zip -DestinationPath .\signed -Force + Remove-Item signed\gcm-nupkg.zip -Force + + - name: Publish signed package + uses: actions/upload-artifact@v3 + with: + name: dotnet-tool-sign + path: signed/*.nupkg # ================================ # Validate @@ -452,8 +634,11 @@ jobs: # Windows due to its placement on the PATH. For this reason, we use # the full path to our installation to validate the Windows version. command: "$PROGRAMFILES (x86)/Git Credential Manager/git-credential-manager.exe" + - os: ubuntu-latest + artifact: dotnet-tool-sign + command: git-credential-manager runs-on: ${{ matrix.component.os }} - needs: [ osx-sign, win-sign, linux-sign ] + needs: [ osx-sign, win-sign, linux-sign, dotnet-tool-sign ] steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: @@ -479,7 +664,7 @@ jobs: } - name: Install Linux - if: contains(matrix.component.os, 'ubuntu') + if: contains(matrix.component.os, 'ubuntu') && contains(matrix.component.artifact, 'linux') run: | debpath=$(find ./*.deb) sudo apt install $debpath @@ -491,6 +676,13 @@ jobs: # Only validate x64, given arm64 agents are not available pkgpath=$(find ./*.pkg) sudo installer -pkg $pkgpath -target / + + - name: Install .NET tool + if: contains(matrix.component.os, 'ubuntu') && contains(matrix.component.artifact, 'dotnet-tool') + run: | + nupkgpath=$(find ./*.nupkg) + dotnet tool install -g --add-source $(dirname "$nupkgpath") git-credential-manager + "${{ matrix.component.command }}" configure - name: Validate shell: bash @@ -596,33 +788,12 @@ jobs: // Upload Linux artifacts uploadDirectoryToRelease('linux-sign'), - uploadDirectoryToRelease('linux-build/tar') - ]); - create-dotnet-tool: - name: Publish dotnet tool - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 # Indicate full history so Nerdbank.GitVersioning works. - - - name: Setup .NET - uses: actions/setup-dotnet@v3.0.3 - with: - dotnet-version: 6.0.201 - - - uses: dotnet/nbgv@master - with: - setCommonVars: true - - - name: Package tool - run: | - src/shared/DotnetTool/pack-tool.sh \ - --version=$GitBuildVersionSimple \ - --configuration=Release + // Upload .NET tool package + uploadDirectoryToRelease('dotnet-tool-sign'), + ]); - - name: Publish tool - run: | - dotnet nuget push ./out/shared/DotnetTool/nupkg/Release/*.nupkg \ - --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json + - name: Publish .NET tool to nuget.org + run: | + dotnet nuget push dotnet-tool-sign/signed/*.nupkg \ + --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json diff --git a/docs/install.md b/docs/install.md index 4a023315e..6233eb3c9 100644 --- a/docs/install.md +++ b/docs/install.md @@ -84,7 +84,7 @@ git-credential-manager configure ```shell git-credential-manager unconfigure -sudo dpkg -r gcmcore +sudo dpkg -r gcm ``` --- diff --git a/src/linux/Packaging.Linux/build.sh b/src/linux/Packaging.Linux/build.sh index 4c186a509..c407fd892 100755 --- a/src/linux/Packaging.Linux/build.sh +++ b/src/linux/Packaging.Linux/build.sh @@ -4,21 +4,16 @@ die () { exit 1 } -make_absolute () { - case "$1" in - /*) - echo "$1" - ;; - *) - echo "$PWD/$1" - ;; - esac -} - -##################################################################### -# Building -##################################################################### echo "Building Packaging.Linux..." + +# Directories +THISDIR="$( cd "$(dirname "$0")" ; pwd -P )" +ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )" +SRC="$ROOT/src" +OUT="$ROOT/out" +INSTALLER_SRC="$SRC/linux/Packaging.Linux" +INSTALLER_OUT="$OUT/linux/Packaging.Linux" + # Parse script arguments for i in "$@" do @@ -32,7 +27,7 @@ case "$i" in shift # past argument=value ;; --install-from-source=*) - INSTALL_FROM_SOURCE=${i#*=} + INSTALL_FROM_SOURCE="${i#*=}" shift # past argument=value ;; *) @@ -41,208 +36,49 @@ case "$i" in esac done -# Directories -THISDIR="$( cd "$(dirname "$0")" ; pwd -P )" -ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )" -SRC="$ROOT/src" -OUT="$ROOT/out" -GCM_SRC="$SRC/shared/Git-Credential-Manager" -GCM_UI_SRC="$SRC/shared/Git-Credential-Manager.UI.Avalonia" -BITBUCKET_UI_SRC="$SRC/shared/Atlassian.Bitbucket.UI.Avalonia" -GITHUB_UI_SRC="$SRC/shared/GitHub.UI.Avalonia" -GITLAB_UI_SRC="$SRC/shared/GitLab.UI.Avalonia" -PROJ_OUT="$OUT/linux/Packaging.Linux" - -# Build parameters -FRAMEWORK=net6.0 -RUNTIME=linux-x64 - # Perform pre-execution checks CONFIGURATION="${CONFIGURATION:=Debug}" if [ -z "$VERSION" ]; then die "--version was not set" fi -if [ $INSTALL_FROM_SOURCE = false ]; then - ARCH="`dpkg-architecture -q DEB_HOST_ARCH`" - if test -z "$ARCH"; then - die "Could not determine host architecture!" - fi -fi - -# Outputs -PAYLOAD="$PROJ_OUT/payload/$CONFIGURATION" -SYMBOLOUT="$PROJ_OUT/payload.sym/$CONFIGURATION" +OUTDIR="$INSTALLER_OUT/$CONFIGURATION" +PAYLOAD="$OUTDIR/payload" -if [ $INSTALL_FROM_SOURCE = false ]; then - TAROUT="$PROJ_OUT/tar/$CONFIGURATION" - TARBALL="$TAROUT/gcm-linux_$ARCH.$VERSION.tar.gz" - SYMTARBALL="$TAROUT/gcm-linux_$ARCH.$VERSION-symbols.tar.gz" +# Lay out payload +"$INSTALLER_SRC/layout.sh" --configuration="$CONFIGURATION" || exit 1 - DEBOUT="$PROJ_OUT/deb/$CONFIGURATION" - DEBROOT="$DEBOUT/root" - DEBPKG="$DEBOUT/gcm-linux_$ARCH.$VERSION.deb" -else +if [ $INSTALL_FROM_SOURCE = true ]; then INSTALL_LOCATION="/usr/local" -fi - -# Cleanup payload directory -if [ -d "$PAYLOAD" ]; then - echo "Cleaning existing payload directory '$PAYLOAD'..." - rm -rf "$PAYLOAD" -fi - -# Cleanup symbol directory -if [ -d "$SYMBOLOUT" ]; then - echo "Cleaning existing symbols directory '$SYMBOLOUT'..." - rm -rf "$SYMBOLOUT" -fi - -# Ensure directories exists -mkdir -p "$PAYLOAD" "$SYMBOLOUT" - -if [ $INSTALL_FROM_SOURCE = false ]; then - mkdir -p "$DEBROOT" -else mkdir -p "$INSTALL_LOCATION" -fi - -if [ -z "$DOTNET_ROOT" ]; then - DOTNET_ROOT="$(dirname $(which dotnet))" -fi - -# Publish core application executables -echo "Publishing core application..." -$DOTNET_ROOT/dotnet publish "$GCM_SRC" \ - --configuration="$CONFIGURATION" \ - --framework="$FRAMEWORK" \ - --runtime="$RUNTIME" \ - --self-contained=true \ - -p:PublishSingleFile=true \ - --output="$(make_absolute "$PAYLOAD")" || exit 1 - -echo "Publishing core UI helper..." -$DOTNET_ROOT/dotnet publish "$GCM_UI_SRC" \ - --configuration="$CONFIGURATION" \ - --framework="$FRAMEWORK" \ - --runtime="$RUNTIME" \ - --self-contained=true \ - -p:PublishSingleFile=true \ - --output="$(make_absolute "$PAYLOAD")" || exit 1 - -echo "Publishing Bitbucket UI helper..." -$DOTNET_ROOT/dotnet publish "$BITBUCKET_UI_SRC" \ - --configuration="$CONFIGURATION" \ - --framework="$FRAMEWORK" \ - --runtime="$RUNTIME" \ - --self-contained=true \ - -p:PublishSingleFile=true \ - --output="$(make_absolute "$PAYLOAD")" || exit 1 - -echo "Publishing GitHub UI helper..." -$DOTNET_ROOT/dotnet publish "$GITHUB_UI_SRC" \ - --configuration="$CONFIGURATION" \ - --framework="$FRAMEWORK" \ - --runtime="$RUNTIME" \ - --self-contained=true \ - -p:PublishSingleFile=true \ - --output="$(make_absolute "$PAYLOAD")" || exit 1 - -echo "Publishing GitLab UI helper..." -$DOTNET_ROOT/dotnet publish "$GITLAB_UI_SRC" \ - --configuration="$CONFIGURATION" \ - --framework="$FRAMEWORK" \ - --runtime="$RUNTIME" \ - --self-contained=true \ - -p:PublishSingleFile=true \ - --output="$(make_absolute "$PAYLOAD")" || exit 1 - -# Collect symbols -echo "Collecting managed symbols..." -mv "$PAYLOAD"/*.pdb "$SYMBOLOUT" || exit 1 - -echo "Build complete." - -##################################################################### -# PACKING AND INSTALLING -##################################################################### -# Set full read, write, execute permissions for owner and just read and execute permissions for group and other -echo "Setting file permissions..." -/bin/chmod -R 755 "$PAYLOAD" || exit 1 -if [ $INSTALL_FROM_SOURCE = false ]; then - echo "Packing Packaging.Linux..." - # Cleanup any old archive files - if [ -e "$TAROUT" ]; then - echo "Deleteing old archive '$TAROUT'..." - rm "$TAROUT" - fi - - # Ensure the parent directory for the archive exists - mkdir -p "$TAROUT" || exit 1 - - # Build binaries tarball - echo "Building binaries tarball..." - pushd "$PAYLOAD" - tar -czvf "$TARBALL" * || exit 1 - popd - - # Build symbols tarball - echo "Building symbols tarball..." - pushd "$SYMBOLOUT" - tar -czvf "$SYMTARBALL" * || exit 1 - popd - - # Build .deb - INSTALL_TO="$DEBROOT/usr/local/share/gcm-core/" - LINK_TO="$DEBROOT/usr/local/bin/" - mkdir -p "$DEBROOT/DEBIAN" "$INSTALL_TO" "$LINK_TO" || exit 1 - -# make the debian control file -# this is purposefully not indented, see -# https://stackoverflow.com/questions/9349616/bash-eof-in-if-statement -# for details -cat >"$DEBROOT/DEBIAN/control" < -Description: Cross Platform Git Credential Manager command line utility. - GCM supports authentication with a number of Git hosting providers - including GitHub, BitBucket, and Azure DevOps. - For more information see https://aka.ms/gcm -EOF -else echo "Installing..." # Install directories INSTALL_TO="$INSTALL_LOCATION/share/gcm-core/" LINK_TO="$INSTALL_LOCATION/bin/" - MESSAGE="Install complete." -fi -mkdir -p "$INSTALL_TO" "$LINK_TO" + mkdir -p "$INSTALL_TO" "$LINK_TO" -# Copy all binaries and shared libraries to target installation location -cp -R "$PAYLOAD"/* "$INSTALL_TO" || exit 1 + # Copy all binaries and shared libraries to target installation location + cp -R "$PAYLOAD"/* "$INSTALL_TO" || exit 1 -# Create symlink -if [ ! -f "$LINK_TO/git-credential-manager" ]; then - ln -s -r "$INSTALL_TO/git-credential-manager" \ - "$LINK_TO/git-credential-manager" || exit 1 -fi + # Create symlink + if [ ! -f "$LINK_TO/git-credential-manager" ]; then + ln -s -r "$INSTALL_TO/git-credential-manager" \ + "$LINK_TO/git-credential-manager" || exit 1 + fi -# Create legacy symlink with older name -if [ ! -f "$LINK_TO/git-credential-manager-core" ]; then - ln -s -r "$INSTALL_TO/git-credential-manager" \ - "$LINK_TO/git-credential-manager-core" || exit 1 -fi + # Create legacy symlink with older name + if [ ! -f "$LINK_TO/git-credential-manager-core" ]; then + ln -s -r "$INSTALL_TO/git-credential-manager" \ + "$LINK_TO/git-credential-manager-core" || exit 1 + fi -if [ $INSTALL_FROM_SOURCE = false ]; then - dpkg-deb --build "$DEBROOT" "$DEBPKG" || exit 1 + echo "Install complete." +else + # Pack + "$INSTALLER_SRC/pack.sh" --configuration="$CONFIGURATION" --payload="$PAYLOAD" --version="$VERSION" || exit 1 fi -echo $MESSAGE +echo "Build of Packaging.Linux complete." diff --git a/src/linux/Packaging.Linux/layout.sh b/src/linux/Packaging.Linux/layout.sh new file mode 100755 index 000000000..0f2c8ab6a --- /dev/null +++ b/src/linux/Packaging.Linux/layout.sh @@ -0,0 +1,124 @@ +#!/bin/bash +die () { + echo "$*" >&2 + exit 1 +} + +make_absolute () { + case "$1" in + /*) + echo "$1" + ;; + *) + echo "$PWD/$1" + ;; + esac +} + +# Parse script arguments +for i in "$@" +do +case "$i" in + --configuration=*) + CONFIGURATION="${i#*=}" + shift # past argument=value + ;; + *) + # unknown option + ;; +esac +done + +# Directories +THISDIR="$( cd "$(dirname "$0")" ; pwd -P )" +ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )" +SRC="$ROOT/src" +OUT="$ROOT/out" +GCM_SRC="$SRC/shared/Git-Credential-Manager" +GCM_UI_SRC="$SRC/shared/Git-Credential-Manager.UI.Avalonia" +BITBUCKET_UI_SRC="$SRC/shared/Atlassian.Bitbucket.UI.Avalonia" +GITHUB_UI_SRC="$SRC/shared/GitHub.UI.Avalonia" +GITLAB_UI_SRC="$SRC/shared/GitLab.UI.Avalonia" +PROJ_OUT="$OUT/linux/Packaging.Linux" + +# Build parameters +FRAMEWORK=net6.0 +RUNTIME=linux-x64 + +# Perform pre-execution checks +CONFIGURATION="${CONFIGURATION:=Debug}" + +# Outputs +PAYLOAD="$PROJ_OUT/$CONFIGURATION/payload" +SYMBOLOUT="$PROJ_OUT/$CONFIGURATION/payload.sym" + +# Cleanup payload directory +if [ -d "$PAYLOAD" ]; then + echo "Cleaning existing payload directory '$PAYLOAD'..." + rm -rf "$PAYLOAD" +fi + +# Cleanup symbol directory +if [ -d "$SYMBOLOUT" ]; then + echo "Cleaning existing symbols directory '$SYMBOLOUT'..." + rm -rf "$SYMBOLOUT" +fi + +# Ensure directories exists +mkdir -p "$PAYLOAD" "$SYMBOLOUT" + +if [ -z "$DOTNET_ROOT" ]; then + DOTNET_ROOT="$(dirname $(which dotnet))" +fi + +# Publish core application executables +echo "Publishing core application..." +$DOTNET_ROOT/dotnet publish "$GCM_SRC" \ + --configuration="$CONFIGURATION" \ + --framework="$FRAMEWORK" \ + --runtime="$RUNTIME" \ + --self-contained \ + -p:PublishSingleFile=true \ + --output="$(make_absolute "$PAYLOAD")" || exit 1 + +echo "Publishing core UI helper..." +$DOTNET_ROOT/dotnet publish "$GCM_UI_SRC" \ + --configuration="$CONFIGURATION" \ + --framework="$FRAMEWORK" \ + --runtime="$RUNTIME" \ + --self-contained \ + -p:PublishSingleFile=true \ + --output="$(make_absolute "$PAYLOAD")" || exit 1 + +echo "Publishing Bitbucket UI helper..." +$DOTNET_ROOT/dotnet publish "$BITBUCKET_UI_SRC" \ + --configuration="$CONFIGURATION" \ + --framework="$FRAMEWORK" \ + --runtime="$RUNTIME" \ + --self-contained \ + -p:PublishSingleFile=true \ + --output="$(make_absolute "$PAYLOAD")" || exit 1 + +echo "Publishing GitHub UI helper..." +$DOTNET_ROOT/dotnet publish "$GITHUB_UI_SRC" \ + --configuration="$CONFIGURATION" \ + --framework="$FRAMEWORK" \ + --runtime="$RUNTIME" \ + --self-contained \ + -p:PublishSingleFile=true \ + --output="$(make_absolute "$PAYLOAD")" || exit 1 + +echo "Publishing GitLab UI helper..." +$DOTNET_ROOT/dotnet publish "$GITLAB_UI_SRC" \ + --configuration="$CONFIGURATION" \ + --framework="$FRAMEWORK" \ + --runtime="$RUNTIME" \ + --self-contained=true \ + -p:PublishSingleFile=true \ + --output="$(make_absolute "$PAYLOAD")" || exit 1 + +# Collect symbols +echo "Collecting managed symbols..." +mv "$PAYLOAD"/*.pdb "$SYMBOLOUT" || exit 1 + +echo "Build complete." diff --git a/src/linux/Packaging.Linux/pack.sh b/src/linux/Packaging.Linux/pack.sh new file mode 100755 index 000000000..c853af72d --- /dev/null +++ b/src/linux/Packaging.Linux/pack.sh @@ -0,0 +1,136 @@ +#!/bin/bash +die () { + echo "$*" >&2 + exit 1 +} + +# Directories +THISDIR="$( cd "$(dirname "$0")" ; pwd -P )" +ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )" +SRC="$ROOT/src" +OUT="$ROOT/out" +PROJ_OUT="$OUT/linux/Packaging.Linux" +INSTALLER_SRC="$SRC/osx/Installer.Mac" + +# Product information +IDENTIFIER="com.microsoft.gitcredentialmanager" +INSTALL_LOCATION="/usr/local/share/gcm-core" + +# Parse script arguments +for i in "$@" +do +case "$i" in + --version=*) + VERSION="${i#*=}" + shift # past argument=value + ;; + --payload=*) + PAYLOAD="${i#*=}" + shift # past argument=value + ;; + --configuration=*) + CONFIGURATION="${i#*=}" + shift # past argument=value + ;; + *) + # unknown option + ;; +esac +done + +# Perform pre-execution checks +CONFIGURATION="${CONFIGURATION:=Debug}" +if [ -z "$VERSION" ]; then + die "--version was not set" +fi +if [ -z "$PAYLOAD" ]; then + die "--payload was not set" +elif [ ! -d "$PAYLOAD" ]; then + die "Could not find '$PAYLOAD'. Did you run layout.sh first?" +fi + +ARCH="`dpkg-architecture -q DEB_HOST_ARCH`" + +TAROUT="$PROJ_OUT/$CONFIGURATION/tar/" +TARBALL="$TAROUT/gcm-linux_$ARCH.$VERSION.tar.gz" +SYMTARBALL="$TAROUT/gcm-linux_$ARCH.$VERSION-symbols.tar.gz" + +DEBOUT="$PROJ_OUT/$CONFIGURATION/deb" +DEBROOT="$DEBOUT/root" +DEBPKG="$DEBOUT/gcm-linux_$ARCH.$VERSION.deb" +mkdir -p "$DEBROOT" + +if test -z "$ARCH"; then + die "Could not determine host architecture!" +fi + +# Set full read, write, execute permissions for owner and just read and execute permissions for group and other +echo "Setting file permissions..." +/bin/chmod -R 755 "$PAYLOAD" || exit 1 + +echo "Packing Packaging.Linux..." + +# Cleanup any old archive files +if [ -e "$TAROUT" ]; then + echo "Deleteing old archive '$TAROUT'..." + rm "$TAROUT" +fi + +# Ensure the parent directory for the archive exists +mkdir -p "$TAROUT" || exit 1 + +# Build binaries tarball +echo "Building binaries tarball..." +pushd "$PAYLOAD" +tar -czvf "$TARBALL" * || exit 1 +popd + +# Build symbols tarball +echo "Building symbols tarball..." +pushd "$SYMBOLOUT" +tar -czvf "$SYMTARBALL" * || exit 1 +popd + +# Build .deb +INSTALL_TO="$DEBROOT/usr/local/share/gcm-core/" +LINK_TO="$DEBROOT/usr/local/bin/" +mkdir -p "$DEBROOT/DEBIAN" "$INSTALL_TO" "$LINK_TO" || exit 1 + +# make the debian control file +# this is purposefully not indented, see +# https://stackoverflow.com/questions/9349616/bash-eof-in-if-statement +# for details +cat >"$DEBROOT/DEBIAN/control" < +Description: Cross Platform Git Credential Manager command line utility. + GCM supports authentication with a number of Git hosting providers + including GitHub, BitBucket, and Azure DevOps. + For more information see https://aka.ms/gcm +EOF + +mkdir -p "$INSTALL_TO" "$LINK_TO" + +# Copy all binaries and shared libraries to target installation location +cp -R "$PAYLOAD"/* "$INSTALL_TO" || exit 1 + +# Create symlink +if [ ! -f "$LINK_TO/git-credential-manager" ]; then + ln -s -r "$INSTALL_TO/git-credential-manager" \ + "$LINK_TO/git-credential-manager" || exit 1 +fi + +# Create legacy symlink with older name +if [ ! -f "$LINK_TO/git-credential-manager-core" ]; then + ln -s -r "$INSTALL_TO/git-credential-manager" \ + "$LINK_TO/git-credential-manager-core" || exit 1 +fi + +dpkg-deb --build "$DEBROOT" "$DEBPKG" || exit 1 + +echo $MESSAGE diff --git a/src/shared/Core/ApplicationBase.cs b/src/shared/Core/ApplicationBase.cs index fd78596a4..c2e1c05b6 100644 --- a/src/shared/Core/ApplicationBase.cs +++ b/src/shared/Core/ApplicationBase.cs @@ -91,7 +91,7 @@ public static string GetEntryApplicationPath() public static string GetInstallationDirectory() { - return Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]); + return AppContext.BaseDirectory; } /// diff --git a/src/shared/Core/Authentication/AuthenticationBase.cs b/src/shared/Core/Authentication/AuthenticationBase.cs index 6b2bd9511..65d38e002 100644 --- a/src/shared/Core/Authentication/AuthenticationBase.cs +++ b/src/shared/Core/Authentication/AuthenticationBase.cs @@ -140,6 +140,11 @@ protected bool TryFindHelperCommand(string envar, string configName, string defa Context.Trace.WriteLine($"UI helper override specified: '{helperName}'."); } + else if (string.IsNullOrWhiteSpace(defaultValue)) + { + Context.Trace.WriteLine("No default UI supplied."); + return false; + } else { Context.Trace.WriteLine($"Using default UI helper: '{defaultValue}'."); diff --git a/src/shared/Core/Interop/Windows/WindowsEnvironment.cs b/src/shared/Core/Interop/Windows/WindowsEnvironment.cs index c438582c9..b85979d66 100644 --- a/src/shared/Core/Interop/Windows/WindowsEnvironment.cs +++ b/src/shared/Core/Interop/Windows/WindowsEnvironment.cs @@ -22,7 +22,10 @@ internal WindowsEnvironment(IFileSystem fileSystem, IReadOnlyDictionary&2 - exit 1 -} - make_absolute () { case "$1" in /*) @@ -26,20 +21,12 @@ case "$i" in CONFIGURATION="${i#*=}" shift # past argument=value ;; - --version=*) - VERSION="${i#*=}" - shift # past argument=value - ;; *) # unknown option ;; esac done -if [ -z "$VERSION" ]; then - die "--version was not set" -fi - # Directories THISDIR="$( cd "$(dirname "$0")" ; pwd -P )" ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )" @@ -126,15 +113,3 @@ echo "Copying images..." cp "$SRC/$DOTNET_TOOL/icon.png" "$IMGOUT" || exit 1 echo "Build complete." - -##################################################################### -# Pack dotnet tool -##################################################################### -echo "Creating dotnet tool package..." - -dotnet pack "$SRC/$DOTNET_TOOL/DotnetTool.csproj" \ - /p:Configuration="$CONFIGURATION" \ - /p:PackageVersion="$VERSION" \ - /p:PublishDir="$OUTDIR/" - -echo "Dotnet tool pack complete." diff --git a/src/shared/DotnetTool/pack.sh b/src/shared/DotnetTool/pack.sh new file mode 100755 index 000000000..5b2eaf8dc --- /dev/null +++ b/src/shared/DotnetTool/pack.sh @@ -0,0 +1,52 @@ +#!/bin/bash +die () { + echo "$*" >&2 + exit 1 +} + +# Parse script arguments +for i in "$@" +do +case "$i" in + --configuration=*) + CONFIGURATION="${i#*=}" + shift # past argument=value + ;; + --version=*) + VERSION="${i#*=}" + shift # past argument=value + ;; + --publish-dir=*) + PUBLISH_DIR="${i#*=}" + shift # past argument=value + ;; + *) + # unknown option + ;; +esac +done + +CONFIGURATION="${CONFIGURATION:=Debug}" +if [ -z "$VERSION" ]; then + die "--version was not set" +fi + +# Directories +THISDIR="$( cd "$(dirname "$0")" ; pwd -P )" +ROOT="$( cd "$THISDIR"/../../.. ; pwd -P )" +SRC="$ROOT/src" +OUT="$ROOT/out" +DOTNET_TOOL="shared/DotnetTool" + +if [ -z "$PUBLISH_DIR" ]; then + PUBLISH_DIR="$OUT/$DOTNET_TOOL/nupkg/$CONFIGURATION" +fi + +echo "Creating dotnet tool package..." + +dotnet pack "$SRC/$DOTNET_TOOL/DotnetTool.csproj" \ + /p:Configuration="$CONFIGURATION" \ + /p:PackageVersion="$VERSION" \ + /p:PublishDir="$PUBLISH_DIR/" + +echo "Dotnet tool pack complete." diff --git a/src/shared/Git-Credential-Manager/Program.cs b/src/shared/Git-Credential-Manager/Program.cs index 5e463b504..14f20d13a 100644 --- a/src/shared/Git-Credential-Manager/Program.cs +++ b/src/shared/Git-Credential-Manager/Program.cs @@ -43,16 +43,24 @@ public static void Main(string[] args) // // On UNIX systems we do the same check, except instead of a copy we use a symlink. // - string oldName = PlatformUtils.IsWindows() - ? "git-credential-manager-core.exe" - : "git-credential-manager-core"; - if (appPath?.EndsWith(oldName, StringComparison.OrdinalIgnoreCase) ?? false) + if (!string.IsNullOrWhiteSpace(appPath)) { - context.Streams.Error.WriteLine( - "warning: git-credential-manager-core was renamed to git-credential-manager"); - context.Streams.Error.WriteLine( - $"warning: see {Constants.HelpUrls.GcmExecRename} for more information"); + // Trim any (.exe) file extension if we're on Windows + // Note that in some circumstances (like being called by Git when config is set + // to just `helper = manager-core`) we don't always have ".exe" at the end. + if (PlatformUtils.IsWindows() && appPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + { + appPath = appPath.Substring(0, appPath.Length - 4); + } + + if (appPath.EndsWith("git-credential-manager-core", StringComparison.OrdinalIgnoreCase)) + { + context.Streams.Error.WriteLine( + "warning: git-credential-manager-core was renamed to git-credential-manager"); + context.Streams.Error.WriteLine( + $"warning: see {Constants.HelpUrls.GcmExecRename} for more information"); + } } // Register all supported host providers at the normal priority.