From 3427e8a58ce471adadac46118dd593a5870c9ef8 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sat, 23 May 2026 12:49:08 +0100 Subject: [PATCH] feat: goreleaser config, install.sh, FUNDING, README install + sponsor --- .github/FUNDING.yml | 15 +++++ .goreleaser.yaml | 85 +++++++++++++++++++++++ README.md | 32 ++++++++- install.sh | 159 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .goreleaser.yaml create mode 100755 install.sh diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..0e1b38f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: lukaszraczylo +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +thanks_dev: # Replace with a single thanks.dev username +custom: https://monzo.me/lukaszraczylo diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..da5316f --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,85 @@ +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - id: harness-sync + main: ./cmd/harness-sync + binary: harness-sync + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + ldflags: + - -s -w + - -X main.version={{.Version}} + +archives: + - id: harness-sync + formats: [tar.gz] + name_template: "harness-sync-{{ .Os }}-{{ .Arch }}" + format_overrides: + - goos: windows + formats: [zip] + files: + - LICENSE + - README.md + +checksum: + name_template: "harness-sync-checksums.txt" + algorithm: sha256 + +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' + - '^Merge' + - '^WIP' + - '^chore: ' + - '^Update go.mod' + +release: + github: + owner: lukaszraczylo + name: harness-sync + name_template: "version {{.Version}}" + draft: false + prerelease: auto + +homebrew_casks: + - name: harness-sync + repository: + owner: lukaszraczylo + name: homebrew-taps + token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" + directory: Casks + homepage: https://github.com/lukaszraczylo/harness-sync + description: "Sync skills, agents, MCP, and LLM endpoints across multiple LLM harnesses" + license: MIT + hooks: + post: + install: | + if OS.mac? + system_command "/usr/bin/xattr", + args: ["-dr", "com.apple.quarantine", "#{staged_path}/harness-sync"] + end + +signs: + - cmd: cosign + signature: "${artifact}.sigstore.json" + args: + - sign-blob + - "--bundle=${signature}" + - "${artifact}" + - "--yes" + artifacts: checksum + output: true diff --git a/README.md b/README.md index d01efbc..099de2f 100644 --- a/README.md +++ b/README.md @@ -67,13 +67,31 @@ One canonical tree → seven harness-native configs, each with the correct keys. ## Install +**One-line installer (recommended):** + +```bash +curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/harness-sync/main/install.sh | bash +``` + +Picks the right release artefact for your OS/arch, verifies the SHA-256, drops the binary in `~/.local/bin` (override with `INSTALL_DIR=/usr/local/bin`). + +**Homebrew (macOS / Linux):** + +```bash +brew install lukaszraczylo/taps/harness-sync +``` + +**From source:** + ```bash git clone https://github.com/lukaszraczylo/harness-sync cd harness-sync make build && make install # installs to ~/.local/bin/harness-sync ``` -Requires Go 1.22+ and `git` on `$PATH`. +Requires Go 1.24+ and `git` on `$PATH`. + +**Self-update:** once installed, `harness-sync update` pulls the latest release into the same directory as the running binary (`--dry-run` to preview). --- @@ -154,6 +172,9 @@ harness-sync profile use switch active profile --apply reapply automatically after switching harness-sync rollback [n] git revert last N apply commits harness-sync adapter list print registered adapters +harness-sync update reinstall the latest release in place + --dry-run print the install command instead + --install-dir DIR override target directory ``` Every command accepts `--root ` to point at a non-default canonical tree. @@ -270,9 +291,16 @@ yourself), keychain integration beyond env-var substitution. --- +## Sponsor + +If harness-sync saves you time, consider sponsoring continued work: + +- GitHub Sponsors → [@lukaszraczylo](https://github.com/sponsors/lukaszraczylo) +- One-off coffee → [monzo.me/lukaszraczylo](https://monzo.me/lukaszraczylo) + ## License -Same as the surrounding repository. +MIT — see [LICENSE](LICENSE).

harness-sync diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..2f8e5b2 --- /dev/null +++ b/install.sh @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +# harness-sync installer +# Usage: +# curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/harness-sync/main/install.sh | bash +# Optional: +# INSTALL_DIR=/usr/local/bin curl ... | bash +# VERSION=v1.2.3 curl ... | bash + +set -euo pipefail + +REPO="lukaszraczylo/harness-sync" +BINARY_NAME="harness-sync" +INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}" +VERSION="${VERSION:-}" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { printf '%b[INFO]%b %s\n' "$GREEN" "$NC" "$1"; } +warn() { printf '%b[WARN]%b %s\n' "$YELLOW" "$NC" "$1"; } +error() { printf '%b[ERROR]%b %s\n' "$RED" "$NC" "$1" >&2; } + +detect_platform() { + local os arch + case "$(uname -s)" in + Linux*) os="linux" ;; + Darwin*) os="darwin" ;; + MINGW*|MSYS*|CYGWIN*) + error "Use WSL or download the Windows zip from the releases page." + exit 1 + ;; + *) + error "Unsupported OS: $(uname -s)" + exit 1 + ;; + esac + case "$(uname -m)" in + x86_64|amd64) arch="amd64" ;; + arm64|aarch64) arch="arm64" ;; + *) + error "Unsupported architecture: $(uname -m)" + exit 1 + ;; + esac + echo "${os}-${arch}" +} + +check_deps() { + local missing=() + for cmd in curl tar; do + command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd") + done + if [ ${#missing[@]} -ne 0 ]; then + error "Missing required commands: ${missing[*]}" + exit 1 + fi +} + +resolve_version() { + if [ -n "$VERSION" ]; then + echo "$VERSION" + return + fi + local tag + tag=$(curl -sSfL "https://api.github.com/repos/${REPO}/releases/latest" \ + | grep '"tag_name"' \ + | head -1 \ + | cut -d'"' -f4) + if [ -z "$tag" ]; then + error "Could not resolve latest release tag from GitHub." + exit 1 + fi + echo "$tag" +} + +download_and_extract() { + local version="$1" platform="$2" tmpdir="$3" + local archive="harness-sync-${platform}.tar.gz" + local url="https://github.com/${REPO}/releases/download/${version}/${archive}" + local checksums_url="https://github.com/${REPO}/releases/download/${version}/harness-sync-checksums.txt" + + info "Downloading ${archive} from ${version}..." + if ! curl -sSfL "$url" -o "$tmpdir/$archive"; then + error "Failed to download $url" + exit 1 + fi + + info "Verifying checksum..." + if curl -sSfL "$checksums_url" -o "$tmpdir/checksums.txt"; then + local expected actual + expected=$(grep " $archive\$" "$tmpdir/checksums.txt" | awk '{print $1}' || true) + if [ -n "$expected" ]; then + if command -v sha256sum >/dev/null 2>&1; then + actual=$(sha256sum "$tmpdir/$archive" | awk '{print $1}') + elif command -v shasum >/dev/null 2>&1; then + actual=$(shasum -a 256 "$tmpdir/$archive" | awk '{print $1}') + fi + if [ -n "${actual:-}" ] && [ "$expected" != "$actual" ]; then + error "Checksum mismatch (expected $expected, got $actual)" + exit 1 + fi + else + warn "Checksum entry for $archive not found, skipping verification." + fi + else + warn "Checksums file unavailable, skipping verification." + fi + + tar -xzf "$tmpdir/$archive" -C "$tmpdir" + if [ ! -f "$tmpdir/$BINARY_NAME" ]; then + error "Archive did not contain a $BINARY_NAME binary." + exit 1 + fi +} + +install_binary() { + local tmpdir="$1" + + if ! mkdir -p "$INSTALL_DIR" 2>/dev/null; then + warn "Cannot create $INSTALL_DIR; retrying with sudo..." + sudo mkdir -p "$INSTALL_DIR" + fi + + local dest="$INSTALL_DIR/$BINARY_NAME" + if [ -w "$INSTALL_DIR" ]; then + install -m 0755 "$tmpdir/$BINARY_NAME" "$dest" + else + sudo install -m 0755 "$tmpdir/$BINARY_NAME" "$dest" + fi + + info "Installed $dest" + + case ":$PATH:" in + *":$INSTALL_DIR:"*) ;; + *) + warn "$INSTALL_DIR is not on your PATH. Add this to your shell rc:" + printf ' export PATH="%s:$PATH"\n' "$INSTALL_DIR" + ;; + esac +} + +main() { + check_deps + local platform version tmpdir + platform=$(detect_platform) + version=$(resolve_version) + info "harness-sync $version → $INSTALL_DIR ($platform)" + + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' EXIT + download_and_extract "$version" "$platform" "$tmpdir" + install_binary "$tmpdir" + + info "Done. Run: $BINARY_NAME --help" +} + +main "$@"