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
105 changes: 105 additions & 0 deletions .github/workflows/cli-e2e-tests.yml
Comment thread
abelanger5 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: cli-e2e-tests

on:
pull_request:
paths:
- "cmd/hatchet-cli/cli/templates/**"
- "cmd/hatchet-cli/cli/quickstart.go"
- "cmd/hatchet-cli/cli/internal/templater/**"
- "cmd/hatchet-cli/cli/quickstart_e2e_test.go"
- ".github/workflows/test-templates.yml"
push:
branches:
- main
paths:
- "cmd/hatchet-cli/cli/templates/**"
- "cmd/hatchet-cli/cli/quickstart.go"
- "cmd/hatchet-cli/cli/internal/templater/**"
- "cmd/hatchet-cli/cli/quickstart_e2e_test.go"

jobs:
test-templates:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install Poetry
run: pipx install poetry

- name: Install uv
run: pip install uv

- name: Install pnpm
run: npm install -g pnpm

- name: Install Yarn
run: npm install -g yarn

- name: Install Bun
uses: oven-sh/setup-bun@v1

- name: Build CLI
run: |
cd cmd/hatchet-cli
go build -o hatchet-cli
sudo mv hatchet-cli /usr/local/bin/hatchet
hatchet --version

- name: Start Hatchet server
run: |
echo "Starting Hatchet server (this will wait until healthy)..."
hatchet server start --profile local --dashboard-port 8080

echo "Hatchet server is ready!"

- name: Verify local profile was created
run: |
hatchet profile list

- name: Run template E2E tests
run: |
cd cmd/hatchet-cli/cli
go test -tags e2e_cli -run TestQuickstartTemplates -v -timeout 15m
env:
# Ensure tests don't try to interact with stdin
CI: true

- name: Show server logs on failure
if: failure()
run: |
echo "=== Hatchet Container Logs ==="
docker logs hatchet-cli-hatchet-1 2>&1 || echo "Could not get hatchet container logs"
echo ""
echo "=== Postgres Container Logs ==="
docker logs hatchet-cli-postgres-1 2>&1 || echo "Could not get postgres container logs"
echo ""
echo "=== All Containers ==="
docker ps -a

- name: Cleanup
if: always()
run: |
# Stop Hatchet server (this will stop the containers)
hatchet server stop --profile local || true

# Cleanup any remaining docker containers
docker ps -q --filter "label=com.docker.compose.project=hatchet-cli" | xargs -r docker stop || true
docker ps -aq --filter "label=com.docker.compose.project=hatchet-cli" | xargs -r docker rm || true
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repos:
- id: trailing-whitespace
exclude: ^examples/
- id: check-yaml
exclude: ^examples/
exclude: (^examples/|^cmd/hatchet-cli/cli/templates/)
Comment thread
abelanger5 marked this conversation as resolved.
- repo: https://github.com/golangci/golangci-lint
rev: v2.5.0
hooks:
Expand Down
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,21 @@

### What is Hatchet?

Hatchet is a platform for running background tasks, built on top of Postgres. Instead of managing your own task queue or pub/sub system, you can use Hatchet to distribute your functions between a set of workers with minimal configuration or infrastructure.
Hatchet is a platform for running background tasks and durable workflows, built on top of Postgres. It bundles a durable task queue, observability, alerting, a dashboard, and a CLI into a single platform.

### Get started quickly

The fastest way to get started with a running Hatchet instance is to install the Hatchet CLI (on MacOS, Linux or WSL) - note that this requires [Docker](https://www.docker.com/get-started) installed locally to work:

```sh
curl -fsSL https://install.hatchet.run/install.sh | bash
hatchet --version
hatchet server start
```

You can also sign up on [Hatchet Cloud](https://cloud.onhatchet.run) to try it out! We recommend this even if you plan on self-hosting, so you can have a look at what a fully-deployed Hatchet platform looks like.

To view full documentation for self-hosting and using cloud, have a look at the [docs](https://docs.hatchet.run).

### When should I use Hatchet?

Expand Down Expand Up @@ -650,13 +664,6 @@ Hatchet supports Slack and email-based alerting for when your tasks fail. Alerts

</details>

### Quick Start

Hatchet is available as a cloud version or self-hosted. See the following docs to get up and running quickly:

- [Hatchet Cloud Quickstart](https://docs.hatchet.run/home/hatchet-cloud-quickstart)
- [Hatchet Self-Hosted](https://docs.hatchet.run/self-hosting)

### Documentation

The most up-to-date documentation can be found at https://docs.hatchet.run.
Expand Down
32 changes: 12 additions & 20 deletions cmd/hatchet-cli/.goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,7 @@ snapshot:
version_template: "{{ incpatch .Version }}-next"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
- "^ci:"
groups:
- title: Features
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 0
- title: "Bug fixes"
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 1
- title: Others
order: 999
disable: true

# Cross-platform signing and notarization with quill (works on any OS, including Linux)
# This will sign and notarize all macOS binaries
Expand Down Expand Up @@ -99,23 +84,30 @@ release:
prerelease: auto
mode: append
header: |
## Hatchet CLI {{ .Tag }} ({{ .Date }})
## Hatchet CLI {{ .Tag }}

Welcome to this new release of the Hatchet CLI!

For full documentation, visit [https://docs.hatchet.run/cli](https://docs.hatchet.run/cli)
footer: |
## Installation

### Homebrew (macOS/Linux)
### MacOS, Linux, WSL
```bash
curl -fsSL https://install.hatchet.run/install.sh | bash
```

### MacOS (Homebrew)
```bash
brew install hatchet-dev/hatchet/hatchet
brew install hatchet-dev/hatchet/hatchet --cask
```

### Manual Installation
Download the appropriate binary for your platform from the assets below.

### Verify Installation
```bash
hatchet version
hatchet --version
```
name_template: "{{.ProjectName}}-cli-{{.Version}}"
disable: false
Expand Down
30 changes: 30 additions & 0 deletions cmd/hatchet-cli/cli/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cli

import (
"github.com/rs/zerolog"

"github.com/hatchet-dev/hatchet/pkg/client"
profileconfig "github.com/hatchet-dev/hatchet/pkg/config/cli"
clientconfig "github.com/hatchet-dev/hatchet/pkg/config/client"
"github.com/hatchet-dev/hatchet/pkg/config/shared"
)

// NewClientFromProfile creates a new Hatchet client from a profile configuration.
// It properly handles TLS settings, host/port, and authentication based on the profile.
func NewClientFromProfile(profile *profileconfig.Profile, logger *zerolog.Logger) (client.Client, error) {
// Construct a ClientConfigFile from the profile
configFile := &clientconfig.ClientConfigFile{
TenantId: profile.TenantId,
Token: profile.Token,
HostPort: profile.GrpcHostPort,
ServerURL: profile.ApiServerURL,
TLS: clientconfig.ClientTLSConfigFile{
Base: shared.TLSConfigFile{
TLSStrategy: profile.TLSStrategy,
},
},
}

// Create client with the config file and logger
return client.NewFromConfigFile(configFile, client.WithLogger(logger))
}
6 changes: 3 additions & 3 deletions cmd/hatchet-cli/cli/internal/config/worker/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
)

type WorkerConfig struct {
Dev WorkerDevConfig `mapstructure:"dev" json:"dev,omitempty"`
Scripts []Script `mapstructure:"scripts" json:"scripts,omitempty"`
Dev WorkerDevConfig `mapstructure:"dev" json:"dev,omitempty"`
Triggers []Trigger `mapstructure:"triggers" json:"triggers,omitempty"`
}

type Script struct {
type Trigger struct {
// command to execute
Command string `mapstructure:"command" json:"command"`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ func displayImagePullProgress(reader io.Reader, imageName string) {

p := tea.NewProgram(m)

// Channel to signal when the goroutine has finished consuming the reader
done := make(chan struct{})

// Start parsing in background
go func() {
defer func() {
Expand All @@ -126,6 +129,7 @@ func displayImagePullProgress(reader io.Reader, imageName string) {
_ = r
}
p.Send(doneMsg{})
close(done) // Signal that we're done consuming the reader
}()

scanner := bufio.NewScanner(reader)
Expand Down Expand Up @@ -175,8 +179,13 @@ func displayImagePullProgress(reader io.Reader, imageName string) {

// Run the program (this blocks until done)
if _, err := p.Run(); err != nil {
// Fallback to simple message if TUI fails
// Fallback for non-TTY environments: wait for the goroutine to finish
// consuming the reader before returning to ensure the image pull completes
<-done
fmt.Fprintf(os.Stderr, "Pulled image %s\n", imageName)
} else {
// Even on success, wait for the goroutine to clean up
<-done
}
}

Expand Down
48 changes: 47 additions & 1 deletion cmd/hatchet-cli/cli/internal/templater/templater.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (

// Data holds the template data passed to each file.
type Data struct {
Name string
Name string
PackageManager string
}

// Process reads all files from the specified directory within the embedded filesystem,
Expand Down Expand Up @@ -69,6 +70,36 @@ func Process(fsys embed.FS, srcDir, dstDir string, data Data) error {
})
}

// ProcessMultiSource processes templates from multiple source directories (shared + package-manager-specific).
// It first processes files from the shared directory, then overlays package-manager-specific files.
// For languages that support multiple package managers (python, typescript), this function expects:
// - shared directory: templates/LANG/shared/
// - package manager directory: templates/LANG/PACKAGE_MANAGER/
//
// For languages with a single package manager (go), it falls back to the language root directory.
func ProcessMultiSource(fsys embed.FS, language, packageManager, dstDir string, data Data) error {
// For Go, use the old behavior (no shared directory)
if language == "go" {
return Process(fsys, "templates/go", dstDir, data)
}

// For Python and TypeScript, process shared + package-manager-specific
sharedDir := filepath.Join("templates", language, "shared")
pkgMgrDir := filepath.Join("templates", language, packageManager)

// Process shared files first
if err := Process(fsys, sharedDir, dstDir, data); err != nil {
return err
}

// Process package-manager-specific files (may overwrite shared files)
if err := Process(fsys, pkgMgrDir, dstDir, data); err != nil {
return err
}

return nil
}

// ProcessPostQuickstart reads and processes the POST_QUICKSTART.md file from the template directory.
// Returns the processed content as a string, or empty string if the file doesn't exist.
func ProcessPostQuickstart(fsys embed.FS, srcDir string, data Data) (string, error) {
Expand Down Expand Up @@ -101,3 +132,18 @@ func ProcessPostQuickstart(fsys embed.FS, srcDir string, data Data) (string, err

return buf.String(), nil
}

// ProcessPostQuickstartMultiSource reads and processes the POST_QUICKSTART.md file from the package-manager-specific directory.
// For languages with multiple package managers, it looks in templates/LANG/PACKAGE_MANAGER/.
// For Go, it looks in the templates/go/ directory.
// Returns the processed content as a string, or empty string if the file doesn't exist.
func ProcessPostQuickstartMultiSource(fsys embed.FS, language, packageManager string, data Data) (string, error) {
var srcDir string
if language == "go" {
srcDir = "templates/go"
} else {
srcDir = filepath.Join("templates", language, packageManager)
}

return ProcessPostQuickstart(fsys, srcDir, data)
}
Loading
Loading