Skip to content

Commit 5dd5430

Browse files
authored
feat(cli): update quickstarts with package manager support, e2e tests for quickstarts (#2801)
- Adds options for Python and Typescript package manager support - uv, pip, poetry, npm, pnpm, yarn, and bun. Also adds e2e tests to ensure that all of these quickstarts successfully can start a worker. - Replaces the scripts with triggers in the CLI - Adds the hatchet trigger command
1 parent f73e67e commit 5dd5430

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2054
-342
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
name: cli-e2e-tests
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "cmd/hatchet-cli/cli/templates/**"
7+
- "cmd/hatchet-cli/cli/quickstart.go"
8+
- "cmd/hatchet-cli/cli/internal/templater/**"
9+
- "cmd/hatchet-cli/cli/quickstart_e2e_test.go"
10+
- ".github/workflows/test-templates.yml"
11+
push:
12+
branches:
13+
- main
14+
paths:
15+
- "cmd/hatchet-cli/cli/templates/**"
16+
- "cmd/hatchet-cli/cli/quickstart.go"
17+
- "cmd/hatchet-cli/cli/internal/templater/**"
18+
- "cmd/hatchet-cli/cli/quickstart_e2e_test.go"
19+
20+
jobs:
21+
test-templates:
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 15
24+
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@v4
28+
29+
- name: Set up Go
30+
uses: actions/setup-go@v5
31+
with:
32+
go-version: "1.25"
33+
34+
- name: Set up Python
35+
uses: actions/setup-python@v5
36+
with:
37+
python-version: "3.11"
38+
39+
- name: Set up Node.js
40+
uses: actions/setup-node@v4
41+
with:
42+
node-version: "20"
43+
44+
- name: Install Poetry
45+
run: pipx install poetry
46+
47+
- name: Install uv
48+
run: pip install uv
49+
50+
- name: Install pnpm
51+
run: npm install -g pnpm
52+
53+
- name: Install Yarn
54+
run: npm install -g yarn
55+
56+
- name: Install Bun
57+
uses: oven-sh/setup-bun@v1
58+
59+
- name: Build CLI
60+
run: |
61+
cd cmd/hatchet-cli
62+
go build -o hatchet-cli
63+
sudo mv hatchet-cli /usr/local/bin/hatchet
64+
hatchet --version
65+
66+
- name: Start Hatchet server
67+
run: |
68+
echo "Starting Hatchet server (this will wait until healthy)..."
69+
hatchet server start --profile local --dashboard-port 8080
70+
71+
echo "Hatchet server is ready!"
72+
73+
- name: Verify local profile was created
74+
run: |
75+
hatchet profile list
76+
77+
- name: Run template E2E tests
78+
run: |
79+
cd cmd/hatchet-cli/cli
80+
go test -tags e2e_cli -run TestQuickstartTemplates -v -timeout 15m
81+
env:
82+
# Ensure tests don't try to interact with stdin
83+
CI: true
84+
85+
- name: Show server logs on failure
86+
if: failure()
87+
run: |
88+
echo "=== Hatchet Container Logs ==="
89+
docker logs hatchet-cli-hatchet-1 2>&1 || echo "Could not get hatchet container logs"
90+
echo ""
91+
echo "=== Postgres Container Logs ==="
92+
docker logs hatchet-cli-postgres-1 2>&1 || echo "Could not get postgres container logs"
93+
echo ""
94+
echo "=== All Containers ==="
95+
docker ps -a
96+
97+
- name: Cleanup
98+
if: always()
99+
run: |
100+
# Stop Hatchet server (this will stop the containers)
101+
hatchet server stop --profile local || true
102+
103+
# Cleanup any remaining docker containers
104+
docker ps -q --filter "label=com.docker.compose.project=hatchet-cli" | xargs -r docker stop || true
105+
docker ps -aq --filter "label=com.docker.compose.project=hatchet-cli" | xargs -r docker rm || true

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repos:
1212
- id: trailing-whitespace
1313
exclude: ^examples/
1414
- id: check-yaml
15-
exclude: ^examples/
15+
exclude: (^examples/|^cmd/hatchet-cli/cli/templates/)
1616
- repo: https://github.com/golangci/golangci-lint
1717
rev: v2.5.0
1818
hooks:

README.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,21 @@
2828

2929
### What is Hatchet?
3030

31-
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.
31+
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.
32+
33+
### Get started quickly
34+
35+
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:
36+
37+
```sh
38+
curl -fsSL https://install.hatchet.run/install.sh | bash
39+
hatchet --version
40+
hatchet server start
41+
```
42+
43+
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.
44+
45+
To view full documentation for self-hosting and using cloud, have a look at the [docs](https://docs.hatchet.run).
3246

3347
### When should I use Hatchet?
3448

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

651665
</details>
652666

653-
### Quick Start
654-
655-
Hatchet is available as a cloud version or self-hosted. See the following docs to get up and running quickly:
656-
657-
- [Hatchet Cloud Quickstart](https://docs.hatchet.run/home/hatchet-cloud-quickstart)
658-
- [Hatchet Self-Hosted](https://docs.hatchet.run/self-hosting)
659-
660667
### Documentation
661668

662669
The most up-to-date documentation can be found at https://docs.hatchet.run.

cmd/hatchet-cli/.goreleaser.yml

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,7 @@ snapshot:
5454
version_template: "{{ incpatch .Version }}-next"
5555

5656
changelog:
57-
sort: asc
58-
filters:
59-
exclude:
60-
- "^docs:"
61-
- "^test:"
62-
- "^chore:"
63-
- "^ci:"
64-
groups:
65-
- title: Features
66-
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
67-
order: 0
68-
- title: "Bug fixes"
69-
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
70-
order: 1
71-
- title: Others
72-
order: 999
57+
disable: true
7358

7459
# Cross-platform signing and notarization with quill (works on any OS, including Linux)
7560
# This will sign and notarize all macOS binaries
@@ -99,23 +84,30 @@ release:
9984
prerelease: auto
10085
mode: append
10186
header: |
102-
## Hatchet CLI {{ .Tag }} ({{ .Date }})
87+
## Hatchet CLI {{ .Tag }}
10388
10489
Welcome to this new release of the Hatchet CLI!
90+
91+
For full documentation, visit [https://docs.hatchet.run/cli](https://docs.hatchet.run/cli)
10592
footer: |
10693
## Installation
10794
108-
### Homebrew (macOS/Linux)
95+
### MacOS, Linux, WSL
96+
```bash
97+
curl -fsSL https://install.hatchet.run/install.sh | bash
98+
```
99+
100+
### MacOS (Homebrew)
109101
```bash
110-
brew install hatchet-dev/hatchet/hatchet
102+
brew install hatchet-dev/hatchet/hatchet --cask
111103
```
112104
113105
### Manual Installation
114106
Download the appropriate binary for your platform from the assets below.
115107
116108
### Verify Installation
117109
```bash
118-
hatchet version
110+
hatchet --version
119111
```
120112
name_template: "{{.ProjectName}}-cli-{{.Version}}"
121113
disable: false

cmd/hatchet-cli/cli/client.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cli
2+
3+
import (
4+
"github.com/rs/zerolog"
5+
6+
"github.com/hatchet-dev/hatchet/pkg/client"
7+
profileconfig "github.com/hatchet-dev/hatchet/pkg/config/cli"
8+
clientconfig "github.com/hatchet-dev/hatchet/pkg/config/client"
9+
"github.com/hatchet-dev/hatchet/pkg/config/shared"
10+
)
11+
12+
// NewClientFromProfile creates a new Hatchet client from a profile configuration.
13+
// It properly handles TLS settings, host/port, and authentication based on the profile.
14+
func NewClientFromProfile(profile *profileconfig.Profile, logger *zerolog.Logger) (client.Client, error) {
15+
// Construct a ClientConfigFile from the profile
16+
configFile := &clientconfig.ClientConfigFile{
17+
TenantId: profile.TenantId,
18+
Token: profile.Token,
19+
HostPort: profile.GrpcHostPort,
20+
ServerURL: profile.ApiServerURL,
21+
TLS: clientconfig.ClientTLSConfigFile{
22+
Base: shared.TLSConfigFile{
23+
TLSStrategy: profile.TLSStrategy,
24+
},
25+
},
26+
}
27+
28+
// Create client with the config file and logger
29+
return client.NewFromConfigFile(configFile, client.WithLogger(logger))
30+
}

cmd/hatchet-cli/cli/internal/config/worker/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import (
99
)
1010

1111
type WorkerConfig struct {
12-
Dev WorkerDevConfig `mapstructure:"dev" json:"dev,omitempty"`
13-
Scripts []Script `mapstructure:"scripts" json:"scripts,omitempty"`
12+
Dev WorkerDevConfig `mapstructure:"dev" json:"dev,omitempty"`
13+
Triggers []Trigger `mapstructure:"triggers" json:"triggers,omitempty"`
1414
}
1515

16-
type Script struct {
16+
type Trigger struct {
1717
// command to execute
1818
Command string `mapstructure:"command" json:"command"`
1919

cmd/hatchet-cli/cli/internal/drivers/docker/image_pull_progress.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ func displayImagePullProgress(reader io.Reader, imageName string) {
118118

119119
p := tea.NewProgram(m)
120120

121+
// Channel to signal when the goroutine has finished consuming the reader
122+
done := make(chan struct{})
123+
121124
// Start parsing in background
122125
go func() {
123126
defer func() {
@@ -126,6 +129,7 @@ func displayImagePullProgress(reader io.Reader, imageName string) {
126129
_ = r
127130
}
128131
p.Send(doneMsg{})
132+
close(done) // Signal that we're done consuming the reader
129133
}()
130134

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

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

cmd/hatchet-cli/cli/internal/templater/templater.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212

1313
// Data holds the template data passed to each file.
1414
type Data struct {
15-
Name string
15+
Name string
16+
PackageManager string
1617
}
1718

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

73+
// ProcessMultiSource processes templates from multiple source directories (shared + package-manager-specific).
74+
// It first processes files from the shared directory, then overlays package-manager-specific files.
75+
// For languages that support multiple package managers (python, typescript), this function expects:
76+
// - shared directory: templates/LANG/shared/
77+
// - package manager directory: templates/LANG/PACKAGE_MANAGER/
78+
//
79+
// For languages with a single package manager (go), it falls back to the language root directory.
80+
func ProcessMultiSource(fsys embed.FS, language, packageManager, dstDir string, data Data) error {
81+
// For Go, use the old behavior (no shared directory)
82+
if language == "go" {
83+
return Process(fsys, "templates/go", dstDir, data)
84+
}
85+
86+
// For Python and TypeScript, process shared + package-manager-specific
87+
sharedDir := filepath.Join("templates", language, "shared")
88+
pkgMgrDir := filepath.Join("templates", language, packageManager)
89+
90+
// Process shared files first
91+
if err := Process(fsys, sharedDir, dstDir, data); err != nil {
92+
return err
93+
}
94+
95+
// Process package-manager-specific files (may overwrite shared files)
96+
if err := Process(fsys, pkgMgrDir, dstDir, data); err != nil {
97+
return err
98+
}
99+
100+
return nil
101+
}
102+
72103
// ProcessPostQuickstart reads and processes the POST_QUICKSTART.md file from the template directory.
73104
// Returns the processed content as a string, or empty string if the file doesn't exist.
74105
func ProcessPostQuickstart(fsys embed.FS, srcDir string, data Data) (string, error) {
@@ -101,3 +132,18 @@ func ProcessPostQuickstart(fsys embed.FS, srcDir string, data Data) (string, err
101132

102133
return buf.String(), nil
103134
}
135+
136+
// ProcessPostQuickstartMultiSource reads and processes the POST_QUICKSTART.md file from the package-manager-specific directory.
137+
// For languages with multiple package managers, it looks in templates/LANG/PACKAGE_MANAGER/.
138+
// For Go, it looks in the templates/go/ directory.
139+
// Returns the processed content as a string, or empty string if the file doesn't exist.
140+
func ProcessPostQuickstartMultiSource(fsys embed.FS, language, packageManager string, data Data) (string, error) {
141+
var srcDir string
142+
if language == "go" {
143+
srcDir = "templates/go"
144+
} else {
145+
srcDir = filepath.Join("templates", language, packageManager)
146+
}
147+
148+
return ProcessPostQuickstart(fsys, srcDir, data)
149+
}

0 commit comments

Comments
 (0)