diff --git a/.github/workflows/scripts.yml b/.github/workflows/scripts.yml new file mode 100644 index 0000000000000000000000000000000000000000..47df18d6f17ab74fef491d8fcf97b87ce71ec3cf --- /dev/null +++ b/.github/workflows/scripts.yml @@ -0,0 +1,27 @@ +name: Script unit tests + +on: + push: + branches: + - main + paths: + - "installer.sh" + pull_request: + branches: + - main + +jobs: + test: + name: Run script unit tests + runs-on: ubuntu-latest + # This runs on Alpine to make sure we're testing with actual sh. + container: "alpine:3.14" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Install bats + run: apk add bats + + - name: Run script unit tests + run: ./ci/dev/test-scripts.sh diff --git a/ci/dev/test-scripts.sh b/ci/dev/test-scripts.sh new file mode 100755 index 0000000000000000000000000000000000000000..ebe1efa8ed2257152631ece0ae1966aadf4e83c7 --- /dev/null +++ b/ci/dev/test-scripts.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +main() { + cd "$(dirname "$0")/../.." + bats ./test/scripts +} + +main "$@" diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index d4bf6f07a10b55014475281b5d7d2ec8a8bd1163..691c9f1f2ac14ef68f3af43be8ec5b2416081776 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -44,6 +44,8 @@ Here is what is needed: - Get this by running `apt-get install -y build-essential` - `rsync` and `unzip` - Used for code-server releases +- `bats` + - Used to run script unit tests ## Creating pull requests diff --git a/package.json b/package.json index 51be2d53fa57811e628466d28d5f8caf8f4a19d5..fca00efdf0d32f2b2ab7c6d07cdd4bb47864ff7c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:e2e": "./ci/dev/test-e2e.sh", "test:standalone-release": "./ci/build/test-standalone-release.sh", "test:unit": "./ci/dev/test-unit.sh", + "test:scripts": "./ci/dev/test-scripts.sh", "package": "./ci/build/build-packages.sh", "postinstall": "./ci/dev/postinstall.sh", "update:vscode": "./ci/dev/update-vscode.sh", diff --git a/test/scripts/install.bats b/test/scripts/install.bats new file mode 100644 index 0000000000000000000000000000000000000000..651236b7a8dc89930ba8414c3d7600c04595bc84 --- /dev/null +++ b/test/scripts/install.bats @@ -0,0 +1,163 @@ +#!/usr/bin/env bats + +SCRIPT_NAME="install.sh" +SCRIPT="$BATS_TEST_DIRNAME/../../$SCRIPT_NAME" + +# Override version so it doesn't have to curl and to avoid caching in case the +# user already has the latest version installed. +export VERSION="9999.99.9" + +function should-use-deb() { + DISTRO=$1 ARCH=$2 OS=linux run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Installing v$VERSION of the $2 deb package from GitHub." ] + [ "${lines[-5]}" = "deb package has been installed." ] +} + +function should-use-rpm() { + DISTRO=$1 ARCH=$2 OS=linux run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Installing v$VERSION of the $2 rpm package from GitHub." ] + [ "${lines[-5]}" = "rpm package has been installed." ] +} + +function should-fallback-npm() { + YARN_PATH=true DISTRO=$1 ARCH=$2 OS=linux run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "No standalone releases for $2." ] + [ "${lines[2]}" = "Falling back to installation from npm." ] + [ "${lines[3]}" = "Installing latest from npm." ] + [ "${lines[-5]}" = "npm package has been installed." ] +} + +function should-use-npm() { + YARN_PATH=true DISTRO=$1 ARCH=$2 OS=linux run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Installing latest from npm." ] + [ "${lines[-5]}" = "npm package has been installed." ] +} + +function should-use-aur() { + DISTRO=$1 ARCH=$2 OS=linux run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Installing latest from the AUR." ] + [ "${lines[-5]}" = "AUR package has been installed." ] +} + +function should-fallback-npm-brew() { + YARN_PATH=true BREW_PATH= OS=macos ARCH=$1 run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Homebrew not installed." ] + [ "${lines[2]}" = "Falling back to standalone installation." ] + [ "${lines[3]}" = "No standalone releases for $1." ] + [ "${lines[4]}" = "Falling back to installation from npm." ] + [ "${lines[5]}" = "Installing latest from npm." ] + [ "${lines[-5]}" = "npm package has been installed." ] +} + +function should-use-brew() { + BREW_PATH=true OS=macos ARCH=$1 run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Installing latest from Homebrew." ] + [ "${lines[-3]}" = "Brew release has been installed." ] +} + +function should-use-standalone() { + DISTRO=$1 ARCH=$2 OS=$3 run "$SCRIPT" --method standalone --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Installing v$VERSION of the $2 release from GitHub." ] + [[ "${lines[-5]}" = "Standalone release has been installed"* ]] +} + +@test "$SCRIPT_NAME: usage with --help" { + run "$SCRIPT" --help + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Installs code-server." ] + [[ "${lines[-1]}" = "More installation docs are at"* ]] +} + +# These use the deb but fall back to npm for unsupported architectures. +@test "$SCRIPT_NAME: debian arm64" { + should-use-deb "debian" "arm64" +} +@test "$SCRIPT_NAME: debian amd64" { + should-use-deb "debian" "amd64" +} +@test "$SCRIPT_NAME: debian i386" { + should-fallback-npm "debian" "i386" +} + +# These use the rpm but fall back to npm for unsupported architectures. +@test "$SCRIPT_NAME: fedora arm64" { + should-use-rpm "fedora" "arm64" +} +@test "$SCRIPT_NAME: fedora amd64" { + should-use-rpm "fedora" "amd64" +} +@test "$SCRIPT_NAME: fedora i386" { + should-fallback-npm "fedora" "i386" +} + +# These always use npm regardless of the architecture. +@test "$SCRIPT_NAME: alpine arm64" { + should-use-npm "alpine" "arm64" +} +@test "$SCRIPT_NAME: alpine amd64" { + should-use-npm "alpine" "amd64" +} +@test "$SCRIPT_NAME: alpine i386" { + should-use-npm "alpine" "i386" +} + +@test "$SCRIPT_NAME: freebsd arm64" { + should-use-npm "freebsd" "arm64" +} +@test "$SCRIPT_NAME: freebsd amd64" { + should-use-npm "freebsd" "amd64" +} +@test "$SCRIPT_NAME: freebsd i386" { + should-use-npm "freebsd" "i386" +} + +# Arch Linux uses AUR but falls back to npm for unsuppported architectures. +@test "$SCRIPT_NAME: arch arm64" { + should-use-aur "arch" "arm64" +} +@test "$SCRIPT_NAME: arch amd64" { + should-use-aur "arch" "amd64" +} +@test "$SCRIPT_NAME: arch i386" { + should-fallback-npm "arch" "i386" +} + +# macOS use homebrew but falls back to standalone when brew is unavailable then +# to npm for unsupported architectures. +@test "$SCRIPT_NAME: macos arm64 (no brew)" { + should-fallback-npm-brew "arm64" +} +@test "$SCRIPT_NAME: macos amd64 (no brew)" { + BREW_PATH= OS=macos ARCH=amd64 run "$SCRIPT" --dry-run + [ "$status" -eq 0 ] + [ "${lines[1]}" = "Homebrew not installed." ] + [ "${lines[2]}" = "Falling back to standalone installation." ] + [ "${lines[3]}" = "Installing v$VERSION of the amd64 release from GitHub." ] + [[ "${lines[-5]}" = "Standalone release has been installed"* ]] +} +@test "$SCRIPT_NAME: macos i386 (no brew)" { + should-fallback-npm-brew "i386" +} + +@test "$SCRIPT_NAME: macos arm64 (brew)" { + should-use-brew "arm64" +} +@test "$SCRIPT_NAME: macos amd64 (brew)" { + should-use-brew "amd64" +} +@test "$SCRIPT_NAME: macos i386 (brew)" { + should-use-brew "i386" +} + +# Force standalone. +@test "$SCRIPT_NAME: debian amd64 --method standalone" { + should-use-standalone "debian" "amd64" "linux" +}