Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
381 lines (316 loc) · 19.2 KB
/
Copy pathMakefile
File metadata and controls
381 lines (316 loc) · 19.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# Parakeet Makefile
# Variables
BIN_DIR := ./bin
BINARY_NAME := $(BIN_DIR)/parakeet
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
LDFLAGS := -ldflags "-s -w -X main.Version=$(VERSION) -X main.Commit=$(COMMIT) -X main.BuildDate=$(BUILD_DATE)"
# Go parameters
GOCMD := go
GOBUILD := $(GOCMD) build
GOTEST := $(GOCMD) test
GOMOD := $(GOCMD) mod
GOFMT := gofmt
GOVET := $(GOCMD) vet
# Docker parameters
DOCKER_IMAGE := ghcr.io/achetronic/parakeet
DOCKER_TAG ?= $(VERSION)
# Directories
MODELS_DIR := ./models
.PHONY: all build clean test fmt vet lint run help
.PHONY: docker-build-int8 docker-build-fp32 docker-build-cuda docker-run-int8 docker-run-fp32 docker-run-cuda docker-push
.PHONY: crane
.PHONY: model-download model-download-asr model-download-asr-int8 model-download-asr-fp32 model-download-vad model-download-diarization model-download-diarization-int8 model-download-diarization-fp32
.PHONY: model-export-asr-venv model-export-asr model-export-diarization-venv model-export-diarization model-build-oci-images model-push-oci-images
.PHONY: release release-linux release-darwin release-windows
.PHONY: deps-onnxruntime
# Auto-detect ONNX Runtime library path
ONNXRUNTIME_LIB ?= $(shell \
if [ -f /usr/lib/libonnxruntime.so ]; then echo /usr/lib/libonnxruntime.so; \
elif [ -f /usr/local/lib/libonnxruntime.so ]; then echo /usr/local/lib/libonnxruntime.so; \
elif [ -f /opt/onnxruntime/lib/libonnxruntime.so ]; then echo /opt/onnxruntime/lib/libonnxruntime.so; \
elif ls /usr/lib/x86_64-linux-gnu/libonnxruntime.so* 1>/dev/null 2>&1; then ls /usr/lib/x86_64-linux-gnu/libonnxruntime.so* 2>/dev/null | head -1; \
elif ls /usr/lib/aarch64-linux-gnu/libonnxruntime.so* 1>/dev/null 2>&1; then ls /usr/lib/aarch64-linux-gnu/libonnxruntime.so* 2>/dev/null | head -1; \
fi)
# Default target
all: build
$(BIN_DIR):
@mkdir -p $(BIN_DIR)
################################################################################
## Build targets
################################################################################
build: $(BIN_DIR) ## Build the binary
$(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME) .
# NOTE: Static build (CGO_ENABLED=0) is not possible because onnxruntime_go requires CGO
################################################################################
## Development targets
################################################################################
run: build ## Build and run the server
@if [ -z "$(ONNXRUNTIME_LIB)" ]; then \
echo "Error: ONNX Runtime library not found. Run 'make deps-onnxruntime' to install it."; \
exit 1; \
fi
ONNXRUNTIME_LIB=$(ONNXRUNTIME_LIB) ./$(BINARY_NAME) -log-level=debug
run-dev: build ## Run with custom port for development
@if [ -z "$(ONNXRUNTIME_LIB)" ]; then \
echo "Error: ONNX Runtime library not found. Run 'make deps-onnxruntime' to install it."; \
exit 1; \
fi
ONNXRUNTIME_LIB=$(ONNXRUNTIME_LIB) ./$(BINARY_NAME) -port 5092 -models $(MODELS_DIR) -log-level=debug
clean: ## Remove build artifacts
rm -rf $(BIN_DIR)
################################################################################
## Code quality targets
################################################################################
fmt: ## Format source code
$(GOFMT) -s -w .
vet: ## Run go vet
$(GOVET) ./...
lint: vet fmt ## Run all linters
test: ## Run tests
$(GOTEST) -v ./...
test-coverage: ## Run tests with coverage
$(GOTEST) -v -coverprofile=coverage.out ./...
$(GOCMD) tool cover -html=coverage.out -o coverage.html
################################################################################
## Dependency targets
################################################################################
deps: ## Download dependencies
$(GOMOD) download
deps-tidy: ## Tidy dependencies
$(GOMOD) tidy
deps-verify: ## Verify dependencies
$(GOMOD) verify
ONNXRUNTIME_VERSION ?= 1.25.1
deps-onnxruntime: ## Install ONNX Runtime library
@echo "Installing ONNX Runtime $(ONNXRUNTIME_VERSION)..."
@ARCH=$$(uname -m); \
if [ "$$ARCH" = "x86_64" ]; then ARCH_NAME="x64"; \
elif [ "$$ARCH" = "aarch64" ]; then ARCH_NAME="aarch64"; \
else echo "Unsupported architecture: $$ARCH"; exit 1; fi; \
curl -L -o /tmp/onnxruntime.tgz "https://github.com/microsoft/onnxruntime/releases/download/v$(ONNXRUNTIME_VERSION)/onnxruntime-linux-$$ARCH_NAME-$(ONNXRUNTIME_VERSION).tgz" && \
tar -xzf /tmp/onnxruntime.tgz -C /tmp && \
sudo cp -a /tmp/onnxruntime-linux-$$ARCH_NAME-$(ONNXRUNTIME_VERSION)/lib/*.so* /usr/local/lib/ && \
sudo ldconfig && \
rm -rf /tmp/onnxruntime.tgz /tmp/onnxruntime-linux-$$ARCH_NAME-$(ONNXRUNTIME_VERSION) && \
echo "ONNX Runtime installed to /usr/local/lib/"
################################################################################
## Model targets
################################################################################
# Full model lifecycle in three stages: train (export assets), package (build
# + push OCI images), and download (fetch prebuilt assets).
### Training (export ONNX assets from checkpoints)
# One venv per family: their transformers requirements conflict.
EXPORT_PYTHON ?= export/venv/bin/python
EXPORT_DIARIZATION_PYTHON ?= export/venv-diarization/bin/python
# Torch index for the export venv. cu126 matches a local GPU machine; CI
# overrides it to the CPU wheel (export runs on CPU).
PYTORCH_INDEX_URL ?= https://download.pytorch.org/whl/cu126
model-export-asr-venv: ## Create the export venv with ASR dependencies
@uv venv --python 3.12 export/venv
@uv pip install --python export/venv/bin/python torch torchaudio --index-url $(PYTORCH_INDEX_URL)
@uv pip install --python export/venv/bin/python -r export/asr/requirements.txt
model-export-asr: ## Regenerate ASR ONNX assets from the NVIDIA checkpoint
@test -x $(EXPORT_PYTHON) || (echo "export venv missing, run: make model-export-asr-venv" && exit 1)
@$(EXPORT_PYTHON) export/asr/export.py --out export/asr/assets
@echo "Assets written to export/asr/assets/"
model-export-diarization-venv: ## Create the diarization export venv with its dependencies
@uv venv --python 3.12 export/venv-diarization
@uv pip install --python export/venv-diarization/bin/python torch --index-url $(PYTORCH_INDEX_URL)
@uv pip install --python export/venv-diarization/bin/python -r export/diarization/requirements.txt
model-export-diarization: ## Regenerate diarization ONNX assets from the NVIDIA checkpoint
@test -x $(EXPORT_DIARIZATION_PYTHON) || (echo "export venv missing, run: make model-export-diarization-venv" && exit 1)
@$(EXPORT_DIARIZATION_PYTHON) export/diarization/export.py --out export/diarization/assets
@echo "Assets written to export/diarization/assets/"
### Package (build + publish OCI model images)
# One image per family and precision, with that family's assets inside.
MODEL_IMAGE_REPO ?= ghcr.io/achetronic/parakeet-models-asr
MODEL_IMAGE_REPO_DIARIZATION ?= ghcr.io/achetronic/parakeet-models-diarization
MODEL_TAG ?= latest
model-build-oci-images: model-export-asr model-export-diarization ## Build every model image locally (no push)
@./export/asr/build-image.sh int8 "$(MODEL_IMAGE_REPO)-int8" "$(MODEL_TAG)"
@./export/asr/build-image.sh fp32 "$(MODEL_IMAGE_REPO)-fp32" "$(MODEL_TAG)"
@./export/diarization/build-image.sh int8 "$(MODEL_IMAGE_REPO_DIARIZATION)-int8" "$(MODEL_TAG)"
@./export/diarization/build-image.sh fp32 "$(MODEL_IMAGE_REPO_DIARIZATION)-fp32" "$(MODEL_TAG)"
model-push-oci-images: model-export-asr model-export-diarization ## Build and push every model image to GHCR
@./export/asr/build-image.sh int8 "$(MODEL_IMAGE_REPO)-int8" "$(MODEL_TAG)" --push
@./export/asr/build-image.sh fp32 "$(MODEL_IMAGE_REPO)-fp32" "$(MODEL_TAG)" --push
@./export/diarization/build-image.sh int8 "$(MODEL_IMAGE_REPO_DIARIZATION)-int8" "$(MODEL_TAG)" --push
@./export/diarization/build-image.sh fp32 "$(MODEL_IMAGE_REPO_DIARIZATION)-fp32" "$(MODEL_TAG)" --push
### Download (fetch prebuilt models)
# models/ is organized by model family first, precision second:
# models/asr/{int8,fp32}, models/vad, models/diarization/{int8,fp32}.
# VAD has no precision. model-download pulls everything.
# ASR encoder + decoder assets are pulled from our own OCI images on GHCR
# (see export/README.md). MODEL_TAG pins the version.
MODELS_ASR_INT8_DIR := $(MODELS_DIR)/asr/int8
MODELS_ASR_FP32_DIR := $(MODELS_DIR)/asr/fp32
MODELS_VAD_DIR := $(MODELS_DIR)/vad
model-download: model-download-asr model-download-vad model-download-diarization ## Download every model (ASR, VAD, diarization, both precisions)
model-download-asr: model-download-asr-int8 model-download-asr-fp32 ## Download ASR models (int8 + fp32)
model-download-asr-int8: crane ## Download ASR int8 models from GHCR (~670MB)
@mkdir -p $(MODELS_ASR_INT8_DIR)
@bash -o pipefail -c '"$(CRANE)" export --platform linux/amd64 "$(MODEL_IMAGE_REPO)-int8:$(MODEL_TAG)" - | tar -x -C "$(MODELS_ASR_INT8_DIR)" --strip-components=1 models'
@echo "ASR int8 models extracted to $(MODELS_ASR_INT8_DIR)"
model-download-asr-fp32: crane ## Download ASR fp32 models from GHCR (~2.5GB)
@mkdir -p $(MODELS_ASR_FP32_DIR)
@bash -o pipefail -c '"$(CRANE)" export --platform linux/amd64 "$(MODEL_IMAGE_REPO)-fp32:$(MODEL_TAG)" - | tar -x -C "$(MODELS_ASR_FP32_DIR)" --strip-components=1 models'
@echo "ASR fp32 models extracted to $(MODELS_ASR_FP32_DIR)"
# Silero VAD model for chunk-boundary selection (long-audio mode). Pinned to a
# specific release tag and sha256 so the download is reproducible and verified.
# snakers4/silero-vad is MIT licensed. NOTE: at v6.2.1 the ONNX file lives at
# src/silero_vad/data/silero_vad.onnx (the older files/silero_vad.onnx path is
# gone), so the URL points there. VAD is precision-independent, so it lives
# directly under models/vad/.
SILERO_VAD_VERSION ?= v6.2.1
SILERO_VAD_SHA256 ?= 1a153a22f4509e292a94e67d6f9b85e8deb25b4988682b7e174c65279d8788e3
SILERO_VAD_URL := https://github.com/snakers4/silero-vad/raw/$(SILERO_VAD_VERSION)/src/silero_vad/data/silero_vad.onnx
model-download-vad: ## Download and verify the Silero VAD model (MIT)
@mkdir -p $(MODELS_VAD_DIR)
@echo "Downloading Silero VAD $(SILERO_VAD_VERSION) (MIT) ..."
@curl -L -o $(MODELS_VAD_DIR)/silero_vad.onnx "$(SILERO_VAD_URL)"
@echo "$(SILERO_VAD_SHA256) $(MODELS_VAD_DIR)/silero_vad.onnx" | sha256sum -c -
@echo "Silero VAD model verified"
# Diarization comes from our own OCI images too (see export/README.md).
# .onnx + .onnx_data must stay together: external weights, relative path.
DIARIZATION_INT8_DIR := $(MODELS_DIR)/diarization/int8
DIARIZATION_FP32_DIR := $(MODELS_DIR)/diarization/fp32
model-download-diarization: model-download-diarization-int8 model-download-diarization-fp32 ## Download diarization models (int8 + fp32)
model-download-diarization-int8: crane ## Download int8 diarization models from GHCR (~120MB)
@mkdir -p $(DIARIZATION_INT8_DIR)
@bash -o pipefail -c '"$(CRANE)" export --platform linux/amd64 "$(MODEL_IMAGE_REPO_DIARIZATION)-int8:$(MODEL_TAG)" - | tar -x -C "$(DIARIZATION_INT8_DIR)" --strip-components=1 models'
@echo "Diarization int8 models extracted to $(DIARIZATION_INT8_DIR)"
model-download-diarization-fp32: crane ## Download fp32 diarization models from GHCR (~380MB)
@mkdir -p $(DIARIZATION_FP32_DIR)
@bash -o pipefail -c '"$(CRANE)" export --platform linux/amd64 "$(MODEL_IMAGE_REPO_DIARIZATION)-fp32:$(MODEL_TAG)" - | tar -x -C "$(DIARIZATION_FP32_DIR)" --strip-components=1 models'
@echo "Diarization fp32 models extracted to $(DIARIZATION_FP32_DIR)"
################################################################################
## Docker targets
################################################################################
docker-build-int8: ## Build Docker image with embedded int8 models (~670MB)
docker build --build-arg MODEL_PRECISION=int8 -t $(DOCKER_IMAGE):$(DOCKER_TAG)-int8 .
docker tag $(DOCKER_IMAGE):$(DOCKER_TAG)-int8 $(DOCKER_IMAGE):latest-int8
docker-build-fp32: ## Build Docker image with embedded fp32 models (~2.5GB)
docker build --build-arg MODEL_PRECISION=fp32 -t $(DOCKER_IMAGE):$(DOCKER_TAG)-fp32 .
docker tag $(DOCKER_IMAGE):$(DOCKER_TAG)-fp32 $(DOCKER_IMAGE):latest-fp32
docker-build-cuda: ## Build CUDA/GPU Docker image with embedded fp32 models (linux/amd64)
docker build -f Dockerfile.cuda --build-arg MODEL_PRECISION=fp32 -t $(DOCKER_IMAGE):$(DOCKER_TAG)-cuda .
docker tag $(DOCKER_IMAGE):$(DOCKER_TAG)-cuda $(DOCKER_IMAGE):latest-cuda
docker-run-int8: ## Run Docker container with embedded int8 models
docker run --rm -p 5092:5092 $(DOCKER_IMAGE):$(DOCKER_TAG)-int8
docker-run-fp32: ## Run Docker container with embedded fp32 models
docker run --rm -p 5092:5092 $(DOCKER_IMAGE):$(DOCKER_TAG)-fp32
docker-run-cuda: ## Run CUDA/GPU Docker container (requires nvidia-container-toolkit)
docker run --rm --gpus all -p 5092:5092 $(DOCKER_IMAGE):$(DOCKER_TAG)-cuda
docker-push: ## Push Docker images to registry
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)-int8
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)-fp32
docker push $(DOCKER_IMAGE):latest-int8
docker push $(DOCKER_IMAGE):latest-fp32
################################################################################
## Release targets
################################################################################
release: $(BIN_DIR) release-linux release-darwin release-windows ## Build release binaries for all platforms
release-linux: $(BIN_DIR) ## Build Linux binaries
GOOS=linux GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-linux-amd64 .
GOOS=linux GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-linux-arm64 .
release-darwin: $(BIN_DIR) ## Build macOS binaries
GOOS=darwin GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-amd64 .
GOOS=darwin GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-arm64 .
release-windows: $(BIN_DIR) ## Build Windows binary
GOOS=windows GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BINARY_NAME)-windows-amd64.exe .
################################################################################
## Auxiliary tools
################################################################################
# Small helper binaries the Makefile downloads on first use into $(BIN_DIR).
# Keep every external tool here so the rest of the targets only depend on the
# tool name, not on how it gets installed. Only crane for now.
# crane pulls and unpacks OCI images without Docker. It is a single static
# binary from google/go-containerregistry (issue #44). Supported platforms:
# Linux and macOS on amd64/arm64. Windows users can keep using Docker.
CRANE_VERSION ?= v0.20.2
CRANE := $(BIN_DIR)/crane
crane: $(CRANE) ## Download crane (OCI image exporter) if needed
$(CRANE): $(BIN_DIR)
@OS=$$(uname -s); \
ARCH=$$(uname -m); \
case "$$OS-$$ARCH" in \
Linux-x86_64) ASSET="Linux_x86_64" ;; \
Linux-aarch64) ASSET="Linux_arm64" ;; \
Darwin-x86_64) ASSET="Darwin_x86_64" ;; \
Darwin-arm64) ASSET="Darwin_arm64" ;; \
*) echo "Unsupported platform for crane: $$OS-$$ARCH" >&2; exit 1 ;; \
esac; \
echo "Downloading crane $(CRANE_VERSION) ($$ASSET) ..."; \
tmp="$(BIN_DIR)/go-containerregistry-$(CRANE_VERSION).tar.gz"; \
curl -fL "https://github.com/google/go-containerregistry/releases/download/$(CRANE_VERSION)/go-containerregistry_$$ASSET.tar.gz" -o "$$tmp" || { echo "Failed to download crane" >&2; exit 1; }; \
tar -xzf "$$tmp" -C "$(BIN_DIR)" crane; \
rm -f "$$tmp"; \
chmod +x "$(CRANE)"; \
echo "crane installed to $(CRANE)"
################################################################################
## Help target
################################################################################
help: ## Show this help message
@echo "Parakeet ASR Server - Makefile targets"
@echo ""
@echo "Usage: make [target]"
@echo ""
@echo "\033[1mBuild:\033[0m"
@echo " \033[36mbuild\033[0m Build the binary to ./bin/parakeet"
@echo " \033[36mclean\033[0m Remove build artifacts"
@echo ""
@echo "\033[1mDevelopment:\033[0m"
@echo " \033[36mrun\033[0m Build and run the server with debug mode"
@echo " \033[36mrun-dev\033[0m Run with custom port (5092) for development"
@echo ""
@echo "\033[1mCode Quality:\033[0m"
@echo " \033[36mfmt\033[0m Format source code"
@echo " \033[36mvet\033[0m Run go vet"
@echo " \033[36mlint\033[0m Run all linters (vet + fmt)"
@echo " \033[36mtest\033[0m Run tests"
@echo " \033[36mtest-coverage\033[0m Run tests with coverage report"
@echo ""
@echo "\033[1mDependencies:\033[0m"
@echo " \033[36mdeps\033[0m Download Go dependencies"
@echo " \033[36mdeps-tidy\033[0m Tidy dependencies"
@echo " \033[36mdeps-verify\033[0m Verify dependencies"
@echo " \033[36mdeps-onnxruntime\033[0m Install ONNX Runtime library (requires sudo)"
@echo ""
@echo "\033[1mModels:\033[0m"
@echo " \033[36mmodel-export-asr-venv\033[0m Create the export venv with ASR deps"
@echo " \033[36mmodel-export-asr\033[0m Regenerate ASR ONNX assets from the NVIDIA checkpoint"
@echo " \033[36mmodel-export-diarization-venv\033[0m Create the export venv with diarization deps"
@echo " \033[36mmodel-export-diarization\033[0m Regenerate diarization ONNX assets from the NVIDIA checkpoint"
@echo " \033[36mmodel-build-oci-images\033[0m Build every model image locally"
@echo " \033[36mmodel-push-oci-images\033[0m Build and push every model image to GHCR"
@echo " \033[36mmodel-download\033[0m Download every model (ASR + VAD + diarization, both precisions)"
@echo " \033[36mmodel-download-asr\033[0m Download ASR models (int8 + fp32)"
@echo " \033[36mmodel-download-asr-int8\033[0m Download ASR int8 models from GHCR (~670MB)"
@echo " \033[36mmodel-download-asr-fp32\033[0m Download ASR fp32 models from GHCR (~2.5GB)"
@echo " \033[36mmodel-download-vad\033[0m Download and verify the Silero VAD model (MIT)"
@echo " \033[36mmodel-download-diarization\033[0m Download diarization models (int8 + fp32)"
@echo " \033[36mmodel-download-diarization-int8\033[0m Download int8 diarization models from GHCR (~120MB)"
@echo " \033[36mmodel-download-diarization-fp32\033[0m Download fp32 diarization models from GHCR (~380MB)"
@echo ""
@echo "\033[1mDocker:\033[0m"
@echo " \033[36mdocker-build-int8\033[0m Build Docker image with int8 models"
@echo " \033[36mdocker-build-fp32\033[0m Build Docker image with fp32 models"
@echo " \033[36mdocker-build-cuda\033[0m Build CUDA/GPU image with fp32 models"
@echo " \033[36mdocker-run-int8\033[0m Run Docker container with int8 models"
@echo " \033[36mdocker-run-fp32\033[0m Run Docker container with fp32 models"
@echo " \033[36mdocker-run-cuda\033[0m Run CUDA/GPU container (needs --gpus all)"
@echo " \033[36mdocker-push\033[0m Push Docker images to registry"
@echo ""
@echo "\033[1mRelease:\033[0m"
@echo " \033[36mrelease\033[0m Build release binaries for all platforms"
@echo " \033[36mrelease-linux\033[0m Build Linux binaries (amd64/arm64)"
@echo " \033[36mrelease-darwin\033[0m Build macOS binaries (amd64/arm64)"
@echo " \033[36mrelease-windows\033[0m Build Windows binary (amd64)"
@echo ""
@echo "\033[1mAuxiliary tools:\033[0m"
@echo " \033[36mcrane\033[0m Download crane (OCI image exporter) if needed"
@echo ""
@echo "\033[1mOther:\033[0m"
@echo " \033[36mhelp\033[0m Show this help message"