From a50368c73baa74f51e26d293d3307ade4441ad89 Mon Sep 17 00:00:00 2001 From: sheya Date: Wed, 1 Jul 2026 11:45:53 +0100 Subject: [PATCH 1/6] ci: add build, lint, test, release steps --- .github/workflows/ci.yml | 118 +++++++++++++++++++++++++++++++++------ .goreleaser.yaml | 35 ++++++++++++ README.md | 19 +++++++ 3 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 .goreleaser.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ff8000..2abbfb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,37 +3,119 @@ name: CI on: pull_request: push: + branches: + - "**" + tags: + - "v[0-9]+.[0-9]+.[0-9]+" permissions: contents: read jobs: - verify: + lint: runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.25 + cache: true + + - name: ShellCheck + uses: ludeeus/action-shellcheck@master + with: + scandir: ./scripts + + - name: Lint + uses: golangci/golangci-lint-action@v9 + with: + version: v2.12.2 + + test: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.25 + cache: true + + - name: Test + run: go test ./... + + scan: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.ref == 'refs/heads/main' || github.ref_type == 'tag' + needs: [lint, test] + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.25 + cache: true + + - name: Security (gosec) + uses: securego/gosec@master + with: + args: ./... + + - name: Vulnerability check (govulncheck) + uses: golang/govulncheck-action@v1 + with: + go-version-input: 1.25 + go-package: ./... + repo-checkout: "false" + + - name: Filesystem scan (trivy) + uses: aquasecurity/trivy-action@v0.36.0 + with: + cache: true + exit-code: 1 + ignore-unfixed: true + scan-type: fs + scan-ref: . + scanners: vuln,secret,misconfig + severity: HIGH,CRITICAL + + - name: Secret scan (gitleaks) + uses: gitleaks/gitleaks-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + release: + runs-on: ubuntu-latest + needs: [lint, test, scan] + if: github.ref_type == 'tag' + permissions: + contents: write + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go uses: actions/setup-go@v5 with: go-version: "1.25.11" cache: true - - name: Install ShellCheck - run: | - if command -v sudo >/dev/null 2>&1; then - apt_cmd="sudo apt-get" - else - apt_cmd="apt-get" - fi - $apt_cmd update - $apt_cmd install -y shellcheck - - - name: Bootstrap verification tools - run: | - ./scripts/bootstrap_tools.sh - echo "$PWD/.tools/bin" >> "$GITHUB_PATH" - - - name: Verify - run: .tools/bin/task verify + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..db13426 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,35 @@ +version: 2 + +project_name: hyperstack-agent + +builds: + - id: hyperstack-agent + main: ./cmd/agent + binary: hyperstack-agent + env: + - CGO_ENABLED=1 + goos: + - linux + goarch: + - amd64 + flags: + - -trimpath + ldflags: + - -s -w -X main.version={{.Version}} -X main.date={{.Date}} + +archives: + - id: default + # format: binary uploads the raw binary directly with no archive wrapper + format: binary + name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" + +checksum: + name_template: "checksums.txt" + algorithm: sha256 + +release: + draft: false + prerelease: auto + +changelog: + disable: true diff --git a/README.md b/README.md index 448a7f1..13b916a 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,25 @@ have `task` on your PATH, `task verify` works as well. `task release-agent-full` creates linux amd64, linux arm64, static linux amd64, and matching `.sha256` files. +## Publishing a Release + +Releases are created automatically by CI when a semver tag is pushed. The +`release` job runs after `lint`, `test`, and `scan` all pass, then uses +GoReleaser to build the binary and create the GitHub Release. + +```bash +git tag v1.2.3 +git push origin v1.2.3 +``` + +Tags must match `v[0-9]+.[0-9]+.[0-9]+` (e.g. `v1.2.3`). Tags with a +pre-release segment (e.g. `v1.2.3-alpha`) are published as pre-releases. + +Each release contains: + +- `hyperstack-agent_linux_amd64` — raw binary, no archive wrapper +- `checksums.txt` — SHA-256 checksum + ## License Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE). From 7331a99e61db8de2dc3e02981e1b9280db72ce85 Mon Sep 17 00:00:00 2001 From: sheya Date: Wed, 1 Jul 2026 11:54:52 +0100 Subject: [PATCH 2/6] ci: drop gitleaks, requires license --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2abbfb0..638eb85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,11 +88,6 @@ jobs: scanners: vuln,secret,misconfig severity: HIGH,CRITICAL - - name: Secret scan (gitleaks) - uses: gitleaks/gitleaks-action@v3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release: runs-on: ubuntu-latest needs: [lint, test, scan] From 5353734bfb0ad0831abef9d34f2ff987b9e706ce Mon Sep 17 00:00:00 2001 From: sheya Date: Wed, 1 Jul 2026 13:41:32 +0100 Subject: [PATCH 3/6] chore: clean dev tools --- Dockerfile | 5 +- Makefile | 62 ------------ README.md | 76 ++------------ Taskfile.yml | 75 -------------- cloud-init.yaml | 12 --- scripts/bootstrap_tools.sh | 148 --------------------------- scripts/build.sh | 37 ------- scripts/e2e.sh | 12 +-- scripts/install.sh | 113 --------------------- scripts/install_agent.sh | 200 ------------------------------------- scripts/serve.sh | 0 scripts/uninstall_agent.sh | 34 ------- 12 files changed, 16 insertions(+), 758 deletions(-) delete mode 100644 Makefile delete mode 100644 cloud-init.yaml delete mode 100755 scripts/bootstrap_tools.sh delete mode 100755 scripts/build.sh mode change 100644 => 100755 scripts/e2e.sh delete mode 100755 scripts/install.sh delete mode 100644 scripts/install_agent.sh mode change 100644 => 100755 scripts/serve.sh delete mode 100755 scripts/uninstall_agent.sh diff --git a/Dockerfile b/Dockerfile index 6dfc7ee..92e82e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ ENV AGENT_VERSION=${VERSION} \ AGENT_BUILD_DATE=${DATE} \ HYPERSTACK_HEALTH_ADDR=127.0.0.1:9100 -FROM runtime-base AS agent-host +FROM runtime-base AS dev USER root RUN apt-get update && apt-get install -y --no-install-recommends \ busybox \ @@ -40,7 +40,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts /scripts RUN chmod 0755 /scripts/*.sh USER hyperstack-agent -CMD ["/scripts/serve.sh"] + +CMD ["/usr/local/bin/hyperstack-agent"] FROM runtime-base AS agent USER hyperstack-agent diff --git a/Makefile b/Makefile deleted file mode 100644 index 454e1dc..0000000 --- a/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -.PHONY: tools build run clean test lint security govulncheck trivy-fs sbom shellcheck secrets verify build-linux-amd64 build-linux-arm64 static release-agent release-agent-full - -BINARY_NAME=hyperstack-agent -AMD64_BIN = bin/$(BINARY_NAME)-linux-amd64 -AMD64_SHA = bin/$(BINARY_NAME)-linux-amd64.sha256 -TOOL_BIN = $(CURDIR)/.tools/bin - -export PATH := $(TOOL_BIN):$(PATH) - -tools: - TOOL_BIN="$(TOOL_BIN)" ./scripts/bootstrap_tools.sh - -build: - GOOS=linux GOARCH=amd64 go build -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/agent - -build-linux-amd64: - GOOS=linux GOARCH=amd64 go build -o bin/$(BINARY_NAME)-linux-amd64 ./cmd/agent - -build-linux-arm64: - GOOS=linux GOARCH=arm64 go build -o bin/$(BINARY_NAME)-linux-arm64 ./cmd/agent - -run: - go run ./cmd/agent - -test: - go test ./... - -lint: tools - golangci-lint run ./... - -security: tools - gosec ./... - -govulncheck: tools - CGO_ENABLED=1 govulncheck ./... - -trivy-fs: tools - trivy fs --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1 . - -sbom: tools - trivy fs --format cyclonedx --output sbom.cdx.json . - -shellcheck: tools - shellcheck scripts/*.sh - -secrets: tools - gitleaks detect --source . --redact --no-banner - -verify: tools test shellcheck lint security govulncheck trivy-fs secrets - -clean: - rm -rf bin - -static: - CGO_ENABLED=0 go build -trimpath -a -ldflags '-s -w -extldflags "-static"' -o bin/$(BINARY_NAME)-static ./cmd/agent - -release-agent: build-linux-amd64 - @sha256sum "$(AMD64_BIN)" > "$(AMD64_SHA)" - @echo "Wrote $(AMD64_BIN) and $(AMD64_SHA)" - -release-agent-full: - ./scripts/build.sh diff --git a/README.md b/README.md index 13b916a..6509873 100644 --- a/README.md +++ b/README.md @@ -6,31 +6,13 @@ gateway. ## Install On A VM -Download the public install helper, review it if required by your change-control -process, then run it with the gateway URL. The helper downloads the agent binary -from the gateway `/download` endpoint, verifies the `Hyperstack-Agent-Digest` -SHA-256 header, installs the binary, and creates the systemd service. - -```bash -curl -fsSLO https://raw.githubusercontent.com/NexGenCloud/hyperstack-agent/main/scripts/install_agent.sh -chmod +x install_agent.sh -sudo ./install_agent.sh https://gateway.example.com -``` - -If the gateway requires a VM key to authorize release metadata or downloads, -provide `INFRAHUB_KEY` in the environment. The key is only sent as an HTTP -header and is not written to disk or systemd. +Installation is handled by the Hyperstack platform. ## Build -Install [Task](https://taskfile.dev/) before running project commands, or run -`make tools` to install pinned project tools into `.tools/bin`. Equivalent -`make` targets are also kept for environments that already standardize on Make. - ```bash -make tools -.tools/bin/task build-linux-amd64 -.tools/bin/task build-linux-arm64 +task build-linux-amd64 +task build-linux-arm64 ``` Build artifacts are written to `bin/`. @@ -38,30 +20,9 @@ Build artifacts are written to `bin/`. ## Run Locally ```bash -HYPERSTACK_URL="http://localhost:8000" .tools/bin/task run +HYPERSTACK_URL="http://localhost:8000" task run ``` -## Scripts - -The `scripts/` directory is part of the public release because it shows how the -agent is built, installed, tested, and removed. - -- `scripts/install_agent.sh`: VM install helper. Requires a gateway URL argument - or `GATEWAY_URL`; optionally sends `INFRAHUB_KEY` as a download authorization - header without persisting it. -- `scripts/install.sh`: advanced systemd installer for a local agent binary. - Requires `BINARY_SOURCE` and does not embed credentials. -- `scripts/uninstall_agent.sh`: removes the systemd service and agent runtime - files. -- `scripts/build.sh`: builds linux release artifacts and SHA-256 checksums. -- `scripts/serve.sh`: local Docker helper that serves `/download` and - `/version` for install/update tests. -- `scripts/e2e.sh`: local Docker end-to-end test helper. - -Do not commit `.env` files, real gateway URLs, private IPs, credentials, tokens, -or customer-specific metadata into this repository. Use placeholders such as -`gateway.example.com` in docs and test fixtures. - ## Configuration The agent is configured through environment variables: @@ -77,41 +38,18 @@ The agent is configured through environment variables: ## Task Targets ```bash -task tools # install pinned verification tools into .tools/bin task build-linux-amd64 # build linux/amd64 binary task build-linux-arm64 # build linux/arm64 binary task run # run agent locally task test # run Go tests -task lint # run Go lint checks -task security # run gosec checks -task govulncheck # run Go vulnerability checks -task trivy-fs # run filesystem vulnerability/misconfiguration scan -task sbom # generate a local CycloneDX SBOM -task secrets # run gitleaks secret scan -task verify # run tests, shellcheck, lint, gosec, govulncheck, Trivy, and gitleaks -task release-agent # build amd64 binary and checksum -task release-agent-full # build amd64, arm64, static amd64, and checksums +task static # build a static linux/amd64 binary +task clean # remove build artifacts ``` -The same target names are available through `make`, for example `make tools` and -`make verify`. The verification targets install and use pinned local tools from -`.tools/bin` instead of relying on globally installed binaries. If you already -have `task` on your PATH, `task verify` works as well. - -## Release Artifacts - -`task release-agent` creates: - -- `bin/hyperstack-agent-linux-amd64` -- `bin/hyperstack-agent-linux-amd64.sha256` - -`task release-agent-full` creates linux amd64, linux arm64, static linux amd64, -and matching `.sha256` files. - ## Publishing a Release Releases are created automatically by CI when a semver tag is pushed. The -`release` job runs after `lint`, `test`, and `scan` all pass, then uses +`release` job runs after `test`, `lint`, and `scan` all pass, then uses GoReleaser to build the binary and create the GitHub Release. ```bash diff --git a/Taskfile.yml b/Taskfile.yml index 3669345..318a865 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -2,17 +2,8 @@ version: "3" vars: BINARY_NAME: hyperstack-agent - AMD64_BIN: bin/{{.BINARY_NAME}}-linux-amd64 - AMD64_SHA: bin/{{.BINARY_NAME}}-linux-amd64.sha256 - TOOL_BIN: .tools/bin tasks: - tools: - desc: Install pinned local verification tools into .tools/bin. - run: once - cmds: - - ./scripts/bootstrap_tools.sh - build: desc: Build the linux/amd64 agent binary. cmds: @@ -38,60 +29,6 @@ tasks: cmds: - go test ./... - lint: - desc: Run Go lint checks. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" golangci-lint run ./... - - security: - desc: Run Go security checks. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" gosec ./... - - govulncheck: - desc: Run Go vulnerability checks. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" CGO_ENABLED=1 govulncheck ./... - - trivy-fs: - desc: Run filesystem vulnerability, secret, and misconfiguration scan. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" trivy fs --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1 . - - sbom: - desc: Generate a CycloneDX SBOM with Trivy. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" trivy fs --format cyclonedx --output sbom.cdx.json . - - shellcheck: - desc: Run shell script lint checks. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" shellcheck scripts/*.sh - - secrets: - desc: Run secret scanning. - deps: [tools] - cmds: - - PATH="{{.TOOL_BIN}}:$PATH" gitleaks detect --source . --redact --no-banner - - verify: - desc: Run all local verification checks. - deps: [tools] - cmds: - - task: test - - task: shellcheck - - task: lint - - task: security - - task: govulncheck - - task: trivy-fs - - task: secrets - clean: desc: Remove build artifacts. cmds: @@ -101,15 +38,3 @@ tasks: desc: Build a static linux/amd64 agent binary. cmds: - CGO_ENABLED=0 go build -trimpath -a -ldflags '-s -w -extldflags "-static"' -o bin/{{.BINARY_NAME}}-static ./cmd/agent - - release-agent: - desc: Build the linux/amd64 release artifact and checksum. - cmds: - - task: build-linux-amd64 - - sha256sum "{{.AMD64_BIN}}" > "{{.AMD64_SHA}}" - - echo "Wrote {{.AMD64_BIN}} and {{.AMD64_SHA}}" - - release-agent-full: - desc: Build all release artifacts and checksums. - cmds: - - ./scripts/build.sh diff --git a/cloud-init.yaml b/cloud-init.yaml deleted file mode 100644 index cbd9a97..0000000 --- a/cloud-init.yaml +++ /dev/null @@ -1,12 +0,0 @@ -#cloud-config -packages: - - curl - - coreutils - -runcmd: - - | - set -eu - GATEWAY_URL="https://gateway.example.com" - curl -fsSLo /tmp/install_agent.sh https://raw.githubusercontent.com/NexGenCloud/hyperstack-agent/main/scripts/install_agent.sh - chmod 0755 /tmp/install_agent.sh - /tmp/install_agent.sh "$GATEWAY_URL" diff --git a/scripts/bootstrap_tools.sh b/scripts/bootstrap_tools.sh deleted file mode 100755 index 6e551a1..0000000 --- a/scripts/bootstrap_tools.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -TOOL_BIN="${TOOL_BIN:-"$PWD/.tools/bin"}" - -TASK_VERSION="${TASK_VERSION:-v3.51.1}" -GOLANGCI_LINT_VERSION="${GOLANGCI_LINT_VERSION:-v2.12.2}" -GOSEC_VERSION="${GOSEC_VERSION:-v2.27.1}" -GOVULNCHECK_VERSION="${GOVULNCHECK_VERSION:-v1.4.0}" -GITLEAKS_VERSION="${GITLEAKS_VERSION:-8.30.1}" -TRIVY_VERSION="${TRIVY_VERSION:-0.71.2}" - -mkdir -p "$TOOL_BIN" - -cleanup_dirs=() -cleanup() { - local dir - for dir in "${cleanup_dirs[@]}"; do - rm -rf "$dir" - done -} -trap cleanup EXIT - -make_tmpdir() { - TMPDIR_RESULT="$(mktemp -d)" - cleanup_dirs+=("$TMPDIR_RESULT") -} - -need_cmd() { - local name="$1" - if ! command -v "$name" >/dev/null 2>&1; then - printf 'missing required command: %s\n' "$name" >&2 - exit 1 - fi -} - -fetch() { - local url="$1" - local output="$2" - - printf 'Downloading %s\n' "$url" - curl -fsSL "$url" -o "$output" -} - -go_install() { - local bin_name="$1" - local module="$2" - local version="$3" - local version_file="$TOOL_BIN/.${bin_name}.version" - - if [[ -x "$TOOL_BIN/$bin_name" ]] && [[ -f "$version_file" ]] && [[ "$(cat "$version_file")" == "$version" ]]; then - return - fi - - printf 'Installing %s@%s into %s\n' "$bin_name" "$version" "$TOOL_BIN" - GOBIN="$TOOL_BIN" go install "${module}@${version}" - printf '%s\n' "$version" >"$version_file" -} - -install_gitleaks() { - if "$TOOL_BIN/gitleaks" version 2>/dev/null | grep -Fq "$GITLEAKS_VERSION"; then - return - fi - - need_cmd curl - need_cmd tar - need_cmd sha256sum - - local tmpdir archive base_url - make_tmpdir - tmpdir="$TMPDIR_RESULT" - - archive="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" - base_url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}" - - printf 'Installing gitleaks %s into %s\n' "$GITLEAKS_VERSION" "$TOOL_BIN" - curl -fsSL "${base_url}/${archive}" -o "$tmpdir/${archive}" - curl -fsSL "${base_url}/gitleaks_${GITLEAKS_VERSION}_checksums.txt" -o "$tmpdir/checksums.txt" - (cd "$tmpdir" && grep " ${archive}$" checksums.txt | sha256sum -c -) - tar -xzf "$tmpdir/${archive}" -C "$tmpdir" - install -m 0755 "$tmpdir/gitleaks" "$TOOL_BIN/gitleaks" -} - -install_trivy() { - if "$TOOL_BIN/trivy" --version 2>/dev/null | grep -Fq "Version: ${TRIVY_VERSION}"; then - return - fi - if command -v trivy >/dev/null 2>&1; then - local existing_trivy - existing_trivy="$(command -v trivy)" - if [[ "$existing_trivy" != "$TOOL_BIN/trivy" ]] && "$existing_trivy" --version 2>/dev/null | grep -Fq "Version: ${TRIVY_VERSION}"; then - printf 'Copying trivy %s from PATH into %s\n' "$TRIVY_VERSION" "$TOOL_BIN" - install -m 0755 "$existing_trivy" "$TOOL_BIN/trivy" - return - fi - fi - - need_cmd curl - need_cmd tar - need_cmd sha256sum - - local tmpdir archive base_url - make_tmpdir - tmpdir="$TMPDIR_RESULT" - - archive="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" - base_url="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}" - - printf 'Installing trivy %s into %s\n' "$TRIVY_VERSION" "$TOOL_BIN" - fetch "${base_url}/${archive}" "$tmpdir/${archive}" - fetch "${base_url}/trivy_${TRIVY_VERSION}_checksums.txt" "$tmpdir/checksums.txt" - (cd "$tmpdir" && grep " ${archive}$" checksums.txt | sha256sum -c -) - tar -xzf "$tmpdir/${archive}" -C "$tmpdir" - install -m 0755 "$tmpdir/trivy" "$TOOL_BIN/trivy" -} - -install_shellcheck() { - if "$TOOL_BIN/shellcheck" --version >/dev/null 2>&1; then - return - fi - if command -v shellcheck >/dev/null 2>&1; then - printf 'Copying shellcheck from PATH into %s\n' "$TOOL_BIN" - install -m 0755 "$(command -v shellcheck)" "$TOOL_BIN/shellcheck" - return - fi - - cat >&2 <<'EOF' -shellcheck is required but was not found. - -Install it with your OS package manager, then rerun this command: - sudo apt-get install -y shellcheck - brew install shellcheck -EOF - exit 1 -} - -need_cmd go - -go_install task github.com/go-task/task/v3/cmd/task "$TASK_VERSION" -go_install golangci-lint github.com/golangci/golangci-lint/v2/cmd/golangci-lint "$GOLANGCI_LINT_VERSION" -go_install gosec github.com/securego/gosec/v2/cmd/gosec "$GOSEC_VERSION" -go_install govulncheck golang.org/x/vuln/cmd/govulncheck "$GOVULNCHECK_VERSION" - -install_shellcheck -install_gitleaks -install_trivy - -printf 'Tool bootstrap complete. Add this to PATH if needed: %s\n' "$TOOL_BIN" diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 215a400..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Build script for Hyperstack agent -# Supports: amd64, arm64; static builds; version embedding - -ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd) -BIN_DIR="$ROOT_DIR/bin" -BINARY_NAME="hyperstack-agent" - -mkdir -p "$BIN_DIR" - -VERSION=${VERSION:-$(git -C "$ROOT_DIR" describe --tags --always 2>/dev/null || echo "dev")} -DATE=${DATE:-$(date -u +%Y-%m-%dT%H:%M:%SZ)} - -ldflags="-s -w -X main.version=$VERSION -X main.date=$DATE" - -pushd "$ROOT_DIR" >/dev/null - -echo "Building for linux/amd64..." -GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "$ldflags" -o "$BIN_DIR/${BINARY_NAME}-linux-amd64" ./cmd/agent - -echo "Building for linux/arm64..." -GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "$ldflags" -o "$BIN_DIR/${BINARY_NAME}-linux-arm64" ./cmd/agent - -echo "Building static (amd64)..." -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -a -ldflags "$ldflags -extldflags '-static'" -o "$BIN_DIR/${BINARY_NAME}-static-linux-amd64" ./cmd/agent - -popd >/dev/null - -echo "Done. Artifacts in $BIN_DIR" - -for artifact in "$BIN_DIR"/"$BINARY_NAME"-*; do - [ -f "$artifact" ] || continue - sha256sum "$artifact" > "$artifact.sha256" - echo "Checksum written: $artifact.sha256" -done diff --git a/scripts/e2e.sh b/scripts/e2e.sh old mode 100644 new mode 100755 index 93e29bd..a59aafe --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -3,27 +3,27 @@ set -e HYPERSTACK_URL=${HYPERSTACK_URL:-http://gateway:8000} -SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +INSTALLER_URL="${HYPERSTACK_URL}/install?runtime=basic" # Wait for gateway to be ready max_retries=60 retry_interval=1 attempt=0 -while [ "$attempt" -lt "$max_retries" ]; do +while [ $attempt -lt $max_retries ]; do if curl -f -s "$HYPERSTACK_URL/ready" > /dev/null 2>&1; then echo "Gateway is ready" break fi attempt=$((attempt + 1)) echo "Waiting for gateway to be ready... (attempt $attempt/$max_retries)" - sleep "$retry_interval" + sleep $retry_interval done -if [ "$attempt" -eq "$max_retries" ]; then +if [ $attempt -eq $max_retries ]; then echo "Gateway failed to become ready after ${max_retries}s" exit 1 fi -echo "Installing agent from verified download metadata at $HYPERSTACK_URL" +echo "Downloading installer from $INSTALLER_URL" -ALLOW_INSECURE_HTTP=1 RUNTIME=basic "$SCRIPT_DIR/install_agent.sh" "$HYPERSTACK_URL" +curl -fSsl "$INSTALLER_URL" | bash diff --git a/scripts/install.sh b/scripts/install.sh deleted file mode 100755 index 09a8be2..0000000 --- a/scripts/install.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Install script for Hyperstack agent (systemd service) - -BINARY_SOURCE=${BINARY_SOURCE:-} -INSTALL_DIR=${INSTALL_DIR:-/opt/hyperstack-agent} -BIN_NAME=${BIN_NAME:-hyperstack-agent} -SERVICE_NAME=${SERVICE_NAME:-hyperstack-agent} -USER_NAME=${USER_NAME:-hyperstack-agent} -GROUP_NAME=${GROUP_NAME:-hyperstack-agent} - -die() { - echo "ERROR: $*" >&2 - exit 1 -} - -validate_name() { - local label=$1 - local value=$2 - case "$value" in - ""|*/*|*[[:space:]]*|*[^A-Za-z0-9_.@-]*) - die "$label contains unsafe characters: $value" - ;; - esac -} - -validate_path() { - local label=$1 - local value=$2 - case "$value" in - /*) ;; - *) die "$label must be an absolute path" ;; - esac - case "$value" in - *[[:space:]\"\'\\]*|*';'*|*'|'*|*'&'*|*'$'*|*'`'*) - die "$label contains unsafe characters: $value" - ;; - esac -} - -if [[ -z "${BINARY_SOURCE}" ]]; then - die "BINARY_SOURCE is required (path to agent binary)" -fi - -if [[ ${EUID:-$(id -u)} -ne 0 ]]; then - die "please run as root" -fi - -validate_path "BINARY_SOURCE" "$BINARY_SOURCE" -validate_path "INSTALL_DIR" "$INSTALL_DIR" -validate_name "BIN_NAME" "$BIN_NAME" -validate_name "SERVICE_NAME" "$SERVICE_NAME" -validate_name "USER_NAME" "$USER_NAME" -validate_name "GROUP_NAME" "$GROUP_NAME" - -[[ -f "$BINARY_SOURCE" ]] || die "BINARY_SOURCE does not exist or is not a file: $BINARY_SOURCE" - -getent group "$GROUP_NAME" >/dev/null 2>&1 || groupadd --system "$GROUP_NAME" -id -u "$USER_NAME" &>/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin --gid "$GROUP_NAME" "$USER_NAME" -mkdir -p "$INSTALL_DIR/bin" "$INSTALL_DIR/logs" "$INSTALL_DIR/buffer" -install -m 0755 -o root -g root "$BINARY_SOURCE" "$INSTALL_DIR/bin/$BIN_NAME" -chown root:root "$INSTALL_DIR" "$INSTALL_DIR/bin" -chown -R "$USER_NAME":"$GROUP_NAME" "$INSTALL_DIR/logs" "$INSTALL_DIR/buffer" - -# Create systemd service -SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.service" -cat > "$SERVICE_PATH" <&2 - exit 1 -} - -require_cmd() { - command -v "$1" >/dev/null 2>&1 || die "$1 is required" -} - -validate_gateway_url() { - case "$1" in - https://*) ;; - http://localhost:*|http://127.0.0.1:*|http://\[::1\]:*) ;; - http://*) - if [ "${ALLOW_INSECURE_HTTP:-}" = "1" ]; then - echo "WARNING: allowing insecure HTTP URL for local/test use: $1" >&2 - else - die "GATEWAY_URL must use https, except localhost test URLs" - fi - ;; - *) die "GATEWAY_URL must use https, except localhost test URLs" ;; - esac - case "$1" in - *[[:space:]\"\'\\]*|*';'*|*'|'*|*'&'*|*'$'*|*'`'*) - die "GATEWAY_URL contains unsafe characters" - ;; - esac -} - -header_value() { - local header_name=$1 - local header_file=$2 - awk -v key="$header_name" ' - BEGIN { key = tolower(key) } - { - line = $0 - sub(/\r$/, "", line) - pos = index(line, ":") - if (pos > 0 && tolower(substr(line, 1, pos - 1)) == key) { - value = substr(line, pos + 1) - sub(/^[ \t]+/, "", value) - } - } - END { print value } - ' "$header_file" -} - -resolve_download_url() { - local gateway_url=$1 - local location=$2 - - if [ -z "$location" ]; then - printf '%s/download' "$gateway_url" - return - fi - - case "$location" in - https://*|http://*) printf '%s' "$location" ;; - /*) printf '%s%s' "$gateway_url" "$location" ;; - *) die "download redirect Location is not an absolute URL or absolute path" ;; - esac -} - -install_systemd_service() { - getent group "$SERVICE_GROUP" >/dev/null 2>&1 || groupadd --system "$SERVICE_GROUP" - id -u "$SERVICE_USER" >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin --gid "$SERVICE_GROUP" "$SERVICE_USER" - - cat > "/etc/systemd/system/${SERVICE_NAME}.service" < format" ;; -esac -[[ "$expected_sha" =~ ^[A-Fa-f0-9]{64}$ ]] || die "release digest is not a valid SHA-256 value" - -binary_url=$(resolve_download_url "$GATEWAY_URL" "$location") -validate_gateway_url "$binary_url" - -echo "Downloading Hyperstack Agent ${version:-unknown} from ${binary_url}" -curl "${curl_args[@]}" -fL "$binary_url" -o "$tmp_binary" - -printf '%s %s\n' "$expected_sha" "$tmp_binary" | sha256sum -c - >/dev/null -install -m 0755 -o root -g root "$tmp_binary" "$AGENT_BIN_PATH" - -if [ "$RUNTIME" = "basic" ]; then - echo "Starting ${SERVICE_NAME} in basic runtime" - export HYPERSTACK_URL - exec "$AGENT_BIN_PATH" -fi - -install_systemd_service -echo "Installed ${SERVICE_NAME}. Check: systemctl status ${SERVICE_NAME}" diff --git a/scripts/serve.sh b/scripts/serve.sh old mode 100644 new mode 100755 diff --git a/scripts/uninstall_agent.sh b/scripts/uninstall_agent.sh deleted file mode 100755 index 13c384f..0000000 --- a/scripts/uninstall_agent.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SERVICE_NAME="hyperstack-agent" -INSTALL_ROOT="/opt/hyperstack-agent" -AGENT_ENV="${INSTALL_ROOT}/agent.env" -BUFFER_DIR="${INSTALL_ROOT}/buffer" -STATE_DIR="${INSTALL_ROOT}/state" -USER_NAME="hyperstack-agent" - -echo "Stopping and disabling ${SERVICE_NAME}..." -sudo systemctl stop "${SERVICE_NAME}" 2>/dev/null || true -sudo systemctl disable "${SERVICE_NAME}" 2>/dev/null || true - -echo "Removing systemd unit..." -sudo rm -f "/etc/systemd/system/${SERVICE_NAME}.service" -sudo systemctl daemon-reload - -echo "Removing agent environment file (if present)..." -sudo rm -f "${AGENT_ENV}" - -echo "Removing agent runtime directories..." -sudo rm -rf "${BUFFER_DIR}" "${STATE_DIR}" - -echo "Removing install directory ${INSTALL_ROOT}..." -sudo rm -rf "${INSTALL_ROOT}" - -# Optional: remove dedicated user if it exists -if id -u "${USER_NAME}" >/dev/null 2>&1; then - echo "Removing user ${USER_NAME}..." - sudo userdel -r "${USER_NAME}" 2>/dev/null || sudo userdel "${USER_NAME}" || true -fi - -echo "Cleanup complete." From 8cb8693a4516a99c9a0c91714740dd0b3785acb3 Mon Sep 17 00:00:00 2001 From: sheya Date: Wed, 1 Jul 2026 13:46:48 +0100 Subject: [PATCH 4/6] ci: support prerelease versions in tag pattern --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 638eb85..ac0271e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: branches: - "**" tags: - - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+\.[0-9]+\.[0-9]+(-[\w.]+)?" permissions: contents: read From 02610bfff814ac2c92d312c05c610931c0e39d7b Mon Sep 17 00:00:00 2001 From: sheya Date: Wed, 1 Jul 2026 13:51:11 +0100 Subject: [PATCH 5/6] fix: use single quotes in tag pattern --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac0271e..8a977f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: branches: - "**" tags: - - "v[0-9]+\.[0-9]+\.[0-9]+(-[\w.]+)?" + - 'v[0-9]+.[0-9]+.[0-9]+(-[\w.]+)?' permissions: contents: read From 8af7fddc90c967c80a2d363ddab8f945d183ff41 Mon Sep 17 00:00:00 2001 From: sheya Date: Wed, 1 Jul 2026 13:53:12 +0100 Subject: [PATCH 6/6] fix: split semver and prerelease tag patterns --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a977f1..2b9475d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,8 @@ on: branches: - "**" tags: - - 'v[0-9]+.[0-9]+.[0-9]+(-[\w.]+)?' + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-*" permissions: contents: read