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
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Run unit tests
run: |
mkdir -p .cache
GOCACHE=$(pwd)/.cache go test ./...
78 changes: 78 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Release

on:
push:
tags:
- 'v*'

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Run unit tests
run: |
mkdir -p .cache
GOCACHE=$(pwd)/.cache go test ./...

build:
needs: test
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64

steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Build ${{ matrix.goos }}-${{ matrix.goarch }} binary
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: |
mkdir -p bin
go build -ldflags "-s -w -X github.com/strandnerd/tunn/version.Version=${GITHUB_REF_NAME}" -o bin/tunn-${{ matrix.goos }}-${{ matrix.goarch }} .

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: tunn-${{ matrix.goos }}-${{ matrix.goarch }}
path: bin/tunn-${{ matrix.goos }}-${{ matrix.goarch }}

publish:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/download-artifact@v4
with:
path: dist

- name: Publish release
uses: softprops/action-gh-release@v1
with:
files: dist/**
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30 changes: 25 additions & 5 deletions cli/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const (
CommandStart Command = iota
CommandStatus
CommandStop
CommandVersion
)

// Options captures parsed CLI arguments.
Expand All @@ -23,10 +24,12 @@ type Options struct {
}

var (
errStatusWithDetach = errors.New("status command cannot be used with --detach")
errStatusWithArgs = errors.New("status command does not accept tunnel names")
errStopWithDetach = errors.New("stop command cannot be used with --detach")
errStopWithArgs = errors.New("stop command does not accept tunnel names")
errStatusWithDetach = errors.New("status command cannot be used with --detach")
errStatusWithArgs = errors.New("status command does not accept tunnel names")
errStopWithDetach = errors.New("stop command cannot be used with --detach")
errStopWithArgs = errors.New("stop command does not accept tunnel names")
errVersionWithDetach = errors.New("version command cannot be used with --detach")
errVersionWithArgs = errors.New("version command does not accept additional arguments")
)

// Parse inspects the provided arguments and produces structured options.
Expand All @@ -43,6 +46,9 @@ func Parse(args []string) (*Options, error) {
if opts.Command == CommandStop {
return nil, errStopWithDetach
}
if opts.Command == CommandVersion {
return nil, errVersionWithDetach
}
opts.Detach = true
case "--internal-daemon":
opts.InternalDaemon = true
Expand All @@ -68,8 +74,19 @@ func Parse(args []string) (*Options, error) {
return nil, errStopWithArgs
}
opts.Command = CommandStop
case "version":
if opts.Command != CommandStart {
return nil, fmt.Errorf("duplicate command")
}
if opts.Detach {
return nil, errVersionWithDetach
}
if len(opts.TunnelNames) > 0 {
return nil, errVersionWithArgs
}
opts.Command = CommandVersion
case "-h", "--help":
return nil, fmt.Errorf("usage: tunn [--detach|-d] [tunnel ...]\n tunn status")
return nil, fmt.Errorf("usage: tunn [--detach|-d] [tunnel ...]\n tunn status\n tunn version")
default:
if len(arg) > 0 && arg[0] == '-' {
return nil, fmt.Errorf("unknown flag: %s", arg)
Expand All @@ -80,6 +97,9 @@ func Parse(args []string) (*Options, error) {
if opts.Command == CommandStop {
return nil, errStopWithArgs
}
if opts.Command == CommandVersion {
return nil, errVersionWithArgs
}
opts.TunnelNames = append(opts.TunnelNames, arg)
}
}
Expand Down
15 changes: 15 additions & 0 deletions cli/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func TestParse(t *testing.T) {
input: []string{"status"},
want: Options{Command: CommandStatus},
},
{
name: "version",
input: []string{"version"},
want: Options{Command: CommandVersion},
},
{
name: "status with detach",
input: []string{"status", "--detach"},
Expand Down Expand Up @@ -64,6 +69,16 @@ func TestParse(t *testing.T) {
input: []string{"stop", "db"},
wantError: errStopWithArgs.Error(),
},
{
name: "version with detach",
input: []string{"version", "--detach"},
wantError: errVersionWithDetach.Error(),
},
{
name: "version with args",
input: []string{"version", "extra"},
wantError: errVersionWithArgs.Error(),
},
{
name: "unknown flag",
input: []string{"--unknown"},
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/strandnerd/tunn/output"
"github.com/strandnerd/tunn/status"
"github.com/strandnerd/tunn/tunnel"
"github.com/strandnerd/tunn/version"
)

const daemonPreviewDuration = 2 * time.Second
Expand Down Expand Up @@ -56,6 +57,9 @@ func run() error {
return runDaemonCommand(paths, opts.TunnelNames)
}
return runStartCommand(paths, opts)
case cli.CommandVersion:
fmt.Println(version.String())
return nil
default:
return fmt.Errorf("unknown command")
}
Expand Down
9 changes: 9 additions & 0 deletions version/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package version

// Version mirrors the build's semantic version; overridden via ldflags at release time.
var Version = "dev"

// String returns the current version string.
func String() string {
return Version
}