From c1e029b7076c107be3d0d670fe2d44ebb48634c8 Mon Sep 17 00:00:00 2001 From: Lessley Dennington Date: Tue, 1 Mar 2022 14:59:00 -0800 Subject: [PATCH 1/4] Add install from source option to build.sh Update build.sh to support install from source scenario. --- .../Packaging.Linux/Packaging.Linux.csproj | 8 +- src/linux/Packaging.Linux/build.sh | 117 +++++++++++------- 2 files changed, 80 insertions(+), 45 deletions(-) diff --git a/src/linux/Packaging.Linux/Packaging.Linux.csproj b/src/linux/Packaging.Linux/Packaging.Linux.csproj index 87f7cc1c9..9b906e9a8 100644 --- a/src/linux/Packaging.Linux/Packaging.Linux.csproj +++ b/src/linux/Packaging.Linux/Packaging.Linux.csproj @@ -7,6 +7,10 @@ false + + false + + @@ -19,8 +23,8 @@ - - + + diff --git a/src/linux/Packaging.Linux/build.sh b/src/linux/Packaging.Linux/build.sh index 4cb9e37af..ba06f028f 100755 --- a/src/linux/Packaging.Linux/build.sh +++ b/src/linux/Packaging.Linux/build.sh @@ -19,7 +19,6 @@ make_absolute () { # Building ##################################################################### echo "Building Packaging.Linux..." - # Parse script arguments for i in "$@" do @@ -32,6 +31,10 @@ case "$i" in VERSION="${i#*=}" shift # past argument=value ;; + --install-from-source=*) + INSTALL_FROM_SOURCE=${i#*=} + shift # past argument=value + ;; *) # unknown option ;; @@ -59,22 +62,28 @@ if [ -z "$VERSION" ]; then die "--version was not set" fi -ARCH="`dpkg-architecture -q DEB_HOST_ARCH`" -if test -z "$ARCH"; then - die "Could not determine host architecture!" +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" -TAROUT="$PROJ_OUT/tar/$CONFIGURATION" -TARBALL="$TAROUT/gcmcore-linux_$ARCH.$VERSION.tar.gz" -SYMTARBALL="$TAROUT/symbols-linux_$ARCH.$VERSION.tar.gz" +if [ $INSTALL_FROM_SOURCE = false ]; then + TAROUT="$PROJ_OUT/tar/$CONFIGURATION" + TARBALL="$TAROUT/gcmcore-linux_$ARCH.$VERSION.tar.gz" + SYMTARBALL="$TAROUT/symbols-linux_$ARCH.$VERSION.tar.gz" -DEBOUT="$PROJ_OUT/deb/$CONFIGURATION" -DEBROOT="$DEBOUT/root" -DEBPKG="$DEBOUT/gcmcore-linux_$ARCH.$VERSION.deb" + DEBOUT="$PROJ_OUT/deb/$CONFIGURATION" + DEBROOT="$DEBOUT/root" + DEBPKG="$DEBOUT/gcmcore-linux_$ARCH.$VERSION.deb" +else + INSTALL_LOCATION="/usr/local" +fi # Cleanup payload directory if [ -d "$PAYLOAD" ]; then @@ -89,7 +98,13 @@ if [ -d "$SYMBOLOUT" ]; then fi # Ensure directories exists -mkdir -p "$PAYLOAD" "$SYMBOLOUT" "$DEBROOT" +mkdir -p "$PAYLOAD" "$SYMBOLOUT" + +if [ $INSTALL_FROM_SOURCE = false ]; then + mkdir -p "$DEBROOT" +else + mkdir -p "$INSTALL_LOCATION" +fi # Publish core application executables echo "Publishing core application..." @@ -135,40 +150,44 @@ mv "$PAYLOAD"/*.pdb "$SYMBOLOUT" || exit 1 echo "Build complete." ##################################################################### -# PACKING +# PACKING AND INSTALLING ##################################################################### -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 - # 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 -# 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 +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/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" < Date: Tue, 1 Mar 2022 15:05:46 -0800 Subject: [PATCH 2/4] Add install from source script Add script to help users automatically install from source on supported distributions. --- .../Packaging.Linux/install-from-source.sh | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100755 src/linux/Packaging.Linux/install-from-source.sh diff --git a/src/linux/Packaging.Linux/install-from-source.sh b/src/linux/Packaging.Linux/install-from-source.sh new file mode 100755 index 000000000..fb94261d3 --- /dev/null +++ b/src/linux/Packaging.Linux/install-from-source.sh @@ -0,0 +1,183 @@ +#!/bin/sh + +# halt execution immediately on failure +# note there are some scenarios in which this will not exit; +# see https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html +# for additional details +set -e + +is_ci= +for i in "$@"; do + case "$i" in + -y) + is_ci=true + shift # past argument=value + ;; + esac +done + +# in non-ci scenarios, advertise what we will be doing and +# give user the option to exit +if [ -z $is_ci ]; then + echo "This script will download, compile, and install Git Credential Manager to: + + /usr/local/bin + +Git Credential Manager is licensed under the MIT License: https://aka.ms/gcm/license" + + while true; do + read -p "Do you want to continue? [Y/n] " yn + case $yn in + [Yy]*|"") + break + ;; + [Nn]*) + exit + ;; + *) + echo "Please answer yes or no." + ;; + esac + done +fi + +install_shared_packages() { + pkg_manager=$1 + install_verb=$2 + + local shared_packages="git curl" + for package in $shared_packages; do + # ensure we don't stomp on existing installations + if [ ! -z $(which $package) ]; then + continue + fi + + if [ $pkg_manager = apk ]; then + $sudo_cmd $pkg_manager $install_verb $package + else + $sudo_cmd $pkg_manager $install_verb $package -y + fi + done +} + +ensure_dotnet_installed() { + if [ -z "$(verify_existing_dotnet_installation)" ]; then + curl -LO https://dot.net/v1/dotnet-install.sh + chmod +x ./dotnet-install.sh + bash -c "./dotnet-install.sh" + + # since we have to run the dotnet install script with bash, dotnet isn't added + # to the process PATH, so we manually add it here + cd ~ + export DOTNET_ROOT=$(pwd)/.dotnet + add_to_PATH $DOTNET_ROOT + fi +} + +verify_existing_dotnet_installation() { + # get initial pieces of installed sdk version(s) + sdks=$(dotnet --list-sdks | cut -c 1-3) + + # if we have a supported version installed, return + supported_dotnet_versions="6.0 5.0" + for v in $supported_dotnet_versions; do + if [ $(echo $sdks | grep "$v") ]; then + echo $sdks + fi + done +} + +add_to_PATH () { + for directory; do + if [ ! -d "$directory" ]; then + continue; # skip nonexistent directory + fi + case ":$PATH:" in + *":$directory:"*) + break + ;; + *) + export PATH=$PATH:$directory + ;; + esac + done +} + +sudo_cmd= + +# if the user isn't root, we need to use `sudo` for certain commands +# (e.g. installing packages) +if [ -z "$sudo_cmd" ]; then + if [ `id -u` != 0 ]; then + sudo_cmd=sudo + fi +fi + +eval "$(sed -n 's/^ID=/distribution=/p' /etc/os-release)" +eval "$(sed -n 's/^VERSION_ID=/version=/p' /etc/os-release | tr -d '"')" +case "$distribution" in + debian | ubuntu) + $sudo_cmd apt update + install_shared_packages apt install + + # add dotnet package repository/signing key + $sudo_cmd apt update && $sudo_cmd apt install wget -y + curl -LO https://packages.microsoft.com/config/"$distribution"/"$version"/packages-microsoft-prod.deb + $sudo_cmd dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + # proactively install tzdata to prevent prompts + export DEBIAN_FRONTEND=noninteractive + $sudo_cmd apt install -y --no-install-recommends tzdata + + # install dotnet packages and dependencies if needed + if [ -z "$(verify_existing_dotnet_installation)" ]; then + $sudo_cmd apt update + $sudo_cmd apt install apt-transport-https -y + $sudo_cmd apt update + $sudo_cmd apt install dotnet-sdk-5.0 dpkg-dev -y + fi + ;; + linuxmint) + $sudo_cmd apt update + install_shared_packages apt install + + # install dotnet packages and dependencies + $sudo_cmd apt install libc6 libgcc1 libgssapi-krb5-2 libssl1.1 libstdc++6 zlib1g libicu66 -y + ensure_dotnet_installed + ;; + fedora | centos | rhel) + $sudo_cmd dnf update -y + install_shared_packages dnf install + + # install dotnet/gcm dependencies + $sudo_cmd dnf install krb5-libs libicu openssl-libs zlib findutils which bash -y + + ensure_dotnet_installed + ;; + alpine) + $sudo_cmd apk update + install_shared_packages apk add + + # install dotnet/gcm dependencies + $sudo_cmd apk add icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib which bash coreutils gcompat + + ensure_dotnet_installed + ;; + *) + echo "ERROR: Unsupported Linux distribution: $distribution" + exit + ;; +esac + +# detect if the script is part of a full source checkout or standalone instead +script_path="$(cd "$(dirname "$0")" && pwd)" +toplevel_path="${script_path%/src/linux/Packaging.Linux}" +if [ "z$script_path" = "z$toplevel_path" ] || [ ! -f "$toplevel_path/Git-Credential-Manager.sln" ]; then + toplevel_path="$PWD/git-credential-manager" + test -d "$toplevel_path" || git clone https://github.com/GitCredentialManager/git-credential-manager +fi + +cd "$toplevel_path" +$sudo_cmd dotnet build ./src/linux/Packaging.Linux/Packaging.Linux.csproj -c Release -p:InstallFromSource=true +add_to_PATH "/usr/local/bin" \ No newline at end of file From 6fb14f9bea021fa1ea643ddcb5c4abf92a424d96 Mon Sep 17 00:00:00 2001 From: Lessley Dennington Date: Tue, 1 Mar 2022 15:06:47 -0800 Subject: [PATCH 3/4] Add validation workflow Add new GitHub actions workflow to validate install from source on supported distributions. --- .../validate-install-from-source.yml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/validate-install-from-source.yml diff --git a/.github/workflows/validate-install-from-source.yml b/.github/workflows/validate-install-from-source.yml new file mode 100644 index 000000000..23e61aa89 --- /dev/null +++ b/.github/workflows/validate-install-from-source.yml @@ -0,0 +1,33 @@ +name: validate-install-from-source + +on: + push: + branches: + - main + +jobs: + docker: + name: ${{matrix.vector.image}} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + vector: + - image: ubuntu + - image: debian + - image: linuxmintd/mint20-amd64 + - image: fedora + - image: centos + - image: redhat/ubi8 + - image: alpine + container: ${{matrix.vector.image}} + steps: + - uses: actions/checkout@v1 + - run: | + if [ ${{matrix.vector.image}} == "centos" ]; then + sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-* + sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-* + fi + + sh "${GITHUB_WORKSPACE}/src/linux/Packaging.Linux/install-from-source.sh" -y + git-credential-manager-core --help || exit 1 \ No newline at end of file From b2a0717f72003a472e7d3942a72bfb87ae790c73 Mon Sep 17 00:00:00 2001 From: Lessley Dennington Date: Tue, 1 Mar 2022 15:30:20 -0800 Subject: [PATCH 4/4] Add install from source instructions Update README.md with instructions for downloading and running the install from source script. --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index dc193e01b..0536d8364 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,32 @@ sudo /usr/local/share/gcm-core/uninstall.sh ### Linux +#### Experimental: install from source helper script + +If you would like to help dogfood our new install from source helper script, +run the following: + +1. To ensure `curl` is installed: + +```shell +curl --version +``` + +If `curl` is not installed, please use your distribution's package manager +to install it. + +0. To download and run the script: + +```shell +curl -LO https://raw.githubusercontent.com/GitCredentialManager/git-credential-manager/main/src/linux/Packaging.Linux/install-from-source.sh && +sh ./install-from-source.sh && +git-credential-manager-core configure +``` + +__Note:__ You will be prompted to enter your credentials so that the script +can download GCM's dependencies using your distribution's package +manager. + #### Ubuntu/Debian distributions Download the latest [.deb package](https://github.com/GitCredentialManager/git-credential-manager/releases/latest), and run the following: