Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.0] - 2026-02-13

### Added
- `install.sh` script for downloading and installing standalone binaries with SHA-256 verification

## [0.2.5] - 2026-02-13

### Changed
Expand Down Expand Up @@ -53,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Multi-stage Docker build
- Automated vendor upgrade workflow

[0.3.0]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.2.5...v0.3.0
[0.2.5]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.2.2...v0.2.3
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

Granular change detection for Rush monorepos. Analyzes code changes at the AST level to determine which library exports are affected by a pull request, then propagates taint through the workspace dependency graph to identify which e2e test targets need to run.

## Install

```bash
# latest version
curl -fsSL https://raw.githubusercontent.com/gooddata/gooddata-goodchanges/master/install.sh | sh

# specific version
curl -fsSL https://raw.githubusercontent.com/gooddata/gooddata-goodchanges/master/install.sh | sh -s v0.2.5

# custom install directory
curl -fsSL https://raw.githubusercontent.com/gooddata/gooddata-goodchanges/master/install.sh | BINDIR=~/.local/bin sh
```

Or run locally:

```bash
BINDIR=~/.local/bin ./install.sh v0.2.5
```

## How it works

1. Finds the merge base commit (comparison point)
Expand Down Expand Up @@ -219,6 +238,7 @@ internal/
rush.go # Rush config, dependency graph, project configs
tsparse/
tsparse.go # TypeScript parser (imports, exports, symbols)
install.sh # Standalone binary installer
vendor-tsgo.sh # Vendor script for typescript-go
TSGO_COMMIT # Pinned typescript-go commit hash
Dockerfile # Multi-stage Docker build
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.5
0.3.0
142 changes: 142 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/bin/sh
set -e

OWNER="gooddata"
REPO="gooddata-goodchanges"
BINARY="goodchanges"
BINDIR="${BINDIR:-/usr/local/bin}"

usage() {
cat <<EOF
Usage: $0 [version]

Install ${BINARY} binary.

[version] Tag to install (e.g. v0.2.5). Defaults to latest release.

Environment:
BINDIR Installation directory (default: /usr/local/bin)

Examples:
$0 # install latest
$0 v0.2.5 # install specific version
BINDIR=~/.local/bin $0
EOF
exit 2
}

case "${1:-}" in
-h|--help) usage ;;
esac

# --- detect os/arch ---

detect_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
darwin|linux) ;;
mingw*|msys*|cygwin*) os="windows" ;;
*) echo "error: unsupported OS: $os" >&2; exit 1 ;;
esac
echo "$os"
}

detect_arch() {
arch=$(uname -m)
case "$arch" in
x86_64|amd64) arch="amd64" ;;
arm64|aarch64) arch="arm64" ;;
*) echo "error: unsupported architecture: $arch" >&2; exit 1 ;;
esac
echo "$arch"
}

# --- resolve version ---

resolve_version() {
if [ -n "${1:-}" ]; then
echo "$1"
return
fi
# GitHub redirects /releases/latest to the latest tag; grab it from the JSON response
tag=$(curl -sSL "https://api.github.com/repos/${OWNER}/${REPO}/releases/latest" \
| grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"//;s/".*//')
if [ -z "$tag" ]; then
echo "error: could not determine latest release" >&2
exit 1
fi
echo "$tag"
}

# --- download helpers ---

download() {
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "$1" "$2"
elif command -v wget >/dev/null 2>&1; then
wget -qO "$1" "$2"
else
echo "error: curl or wget required" >&2
exit 1
fi
}

verify_checksum() {
target="$1"
checksum_file="$2"
want=$(cut -d ' ' -f 1 < "$checksum_file")
if command -v sha256sum >/dev/null 2>&1; then
got=$(sha256sum "$target" | cut -d ' ' -f 1)
elif command -v shasum >/dev/null 2>&1; then
got=$(shasum -a 256 "$target" | cut -d ' ' -f 1)
else
echo "error: no sha256 tool found (need sha256sum or shasum), cannot verify download" >&2
exit 1
fi
if [ "$want" != "$got" ]; then
echo "error: checksum mismatch" >&2
echo " expected: $want" >&2
echo " got: $got" >&2
exit 1
fi
}

# --- main ---

OS=$(detect_os)
ARCH=$(detect_arch)
VERSION=$(resolve_version "${1:-}")

case "$OS" in
windows) ext="zip" ;;
*) ext="tar.gz" ;;
esac

asset="${BINARY}-${OS}-${ARCH}.${ext}"
base_url="https://github.com/${OWNER}/${REPO}/releases/download/${VERSION}"

tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

echo "Installing ${BINARY} ${VERSION} (${OS}/${ARCH})..."

download "${tmpdir}/${asset}" "${base_url}/${asset}"
download "${tmpdir}/${asset}.sha256" "${base_url}/${asset}.sha256"
verify_checksum "${tmpdir}/${asset}" "${tmpdir}/${asset}.sha256"

# extract
case "$ext" in
tar.gz) tar -xzf "${tmpdir}/${asset}" -C "$tmpdir" ;;
zip) unzip -oq "${tmpdir}/${asset}" -d "$tmpdir" ;;
esac

# install
mkdir -p "$BINDIR" 2>/dev/null || sudo mkdir -p "$BINDIR"
srcname="${BINARY}-${OS}-${ARCH}"
binexe="${BINARY}"
[ "$OS" = "windows" ] && srcname="${srcname}.exe" && binexe="${binexe}.exe"

mv "${tmpdir}/${srcname}" "${BINDIR}/${binexe}" 2>/dev/null \
|| sudo mv "${tmpdir}/${srcname}" "${BINDIR}/${binexe}"

echo "Installed ${BINDIR}/${binexe}"