diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5badfd5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + - run: go vet ./... + - run: go test -race ./... + + # Builds every release target without publishing, so a broken + # .goreleaser.yaml shows up here instead of on a tag. + release-snapshot: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + - uses: goreleaser/goreleaser-action@v7 + with: + version: "~> v2" + args: release --snapshot --clean diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fc2fcde --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release + +on: + push: + tags: ["v*"] + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # the changelog needs the previous tag + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + - run: go test ./... + - uses: goreleaser/goreleaser-action@v7 + with: + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..2d7776c --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,41 @@ +# Release builds: push a vX.Y.Z tag and .github/workflows/release.yml runs +# `goreleaser release`. Check changes locally with +# `goreleaser release --snapshot --clean`. +version: 2 + +project_name: funky + +builds: + - main: ./cmd/funky + binary: funky + env: + # Static binaries: the keychain libraries need no cgo on any OS. + - CGO_ENABLED=0 + goos: [darwin, linux, windows] + goarch: [amd64, arm64] + flags: [-trimpath] + ldflags: + - -s -w -X github.com/funkyhq/cli/cmd.version={{ .Version }} + +archives: + # No version in the names, so releases/latest/download/ always + # points at the newest build (for install scripts and docs). + - name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" + formats: [tar.gz] + format_overrides: + - goos: windows + formats: [zip] + +checksum: + name_template: checksums.txt + +changelog: + use: github + filters: + exclude: + - "^docs:" + - "^test:" + +release: + # A tag like v0.2.0-rc.1 is published as a pre-release. + prerelease: auto diff --git a/README.md b/README.md index e922fb0..9b74284 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,20 @@ Manage [Funky Cloud](https://platform.funky.dev) agents as code. Describe each agent in a YAML file, then `funky apply` creates or updates it — like Terraform, for agent configs. +## Install + +Download a binary for macOS, Linux or Windows from the +[latest release](https://github.com/funkyhq/cli/releases/latest), or, with Go installed: + ```bash -go install github.com/funkyhq/cli@latest # installs as `cli`; or `go build -o funky .` +go install github.com/funkyhq/cli/cmd/funky@latest +``` +`funky --version` shows the installed version. + +## Quick start + +```bash funky login # opens your browser: sign in, pick a project, approve this machine funky init # creates agents/ $EDITOR agents/researcher.yaml @@ -63,7 +74,20 @@ precedence over a login. ```bash go test ./... +go run ./cmd/funky --help ``` `FUNKY_API_URL`, `FUNKY_CONSOLE_URL` and `FUNKY_PLATFORM_URL` point the CLI at non-production services; `FUNKY_CONFIG_DIR` moves where the login is saved. + +## Releasing + +Push a version tag; the release workflow builds every platform with +[GoReleaser](https://goreleaser.com) and publishes a GitHub release: + +```bash +git tag v0.1.0 && git push origin v0.1.0 +``` + +A tag with a suffix (`v0.2.0-rc.1`) becomes a pre-release. To try the build +locally: `goreleaser release --snapshot --clean` (output in `dist/`). diff --git a/main.go b/cmd/funky/main.go similarity index 61% rename from main.go rename to cmd/funky/main.go index 737e1fb..26e9cc3 100644 --- a/main.go +++ b/cmd/funky/main.go @@ -1,7 +1,4 @@ -/* -Copyright © 2026 NAME HERE - -*/ +// Command funky manages Funky agents as code. package main import "github.com/funkyhq/cli/cmd" diff --git a/cmd/root.go b/cmd/root.go index 6e43832..a422e0c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "runtime/debug" "strings" "github.com/spf13/cobra" @@ -26,9 +27,25 @@ infrastructure: describe each agent in agents/.yaml, then run In CI, set FUNKY_API_KEY to a key from https://platform.funky.dev/api-keys instead of logging in; it takes precedence over a login.`, + Version: buildVersion(), SilenceUsage: true, } +// version is set by release builds (-ldflags "-X ...cmd.version=1.2.3"). +var version = "" + +// buildVersion is the release version, or the module version `go install` +// records, or "dev" for a local build. +func buildVersion() string { + if version != "" { + return version + } + if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "" && info.Main.Version != "(devel)" { + return strings.TrimPrefix(info.Main.Version, "v") + } + return "dev" +} + // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() {