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: 16 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.24', 'stable' ]
go-version: [ '1.26', 'stable' ]

steps:
- uses: actions/checkout@v4
Expand All @@ -24,10 +24,12 @@ jobs:
check-latest: true

- name: Generate mocks
run: make generate-mocks
run: |
go install github.com/vektra/mockery/v3@latest
mockery --config .mockery.yaml

- name: Run golangci-lint (latest)
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v9
with:
version: latest
args: --timeout=5m
Expand All @@ -37,7 +39,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.24', 'stable' ]
go-version: [ '1.26', 'stable' ]

steps:
- uses: actions/checkout@v4
Expand All @@ -50,13 +52,20 @@ jobs:
cache: true

- name: Generate mocks
run: make generate-mocks
run: |
go install github.com/vektra/mockery/v3@latest
mockery --config .mockery.yaml

- name: Verify dependencies
run: go mod tidy && git diff --exit-code go.mod go.sum

- name: Build
run: make build
run: go build -o berth ./cmd/berth

- name: Test
run: make test
run: go test ./... -coverprofile=coverage.txt

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Setup Go 1.24 (or higher)
- name: Setup Go 1.26
uses: actions/setup-go@v5
with:
go-version: '1.24'
go-version: '1.26'
check-latest: true

# - name: Run tests
Expand Down
18 changes: 18 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM docker.io/golangci/golangci-lint:v2.12.1 AS golangci-lint

FROM golang:1.26-alpine

RUN apk add --no-cache git curl

# golangci-lint for local Podman lint runs.
COPY --from=golangci-lint /usr/bin/golangci-lint /usr/local/bin/golangci-lint

# mockery (matches CI: go install github.com/vektra/mockery/v3@latest)
RUN go install github.com/vektra/mockery/v3@latest

# pre-cache module deps — layer cache survives image rebuilds when go.mod unchanged
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

WORKDIR /app
49 changes: 38 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
GO := go
APP_NAME := berth
APP_PATH := ./cmd/berth
GO := go
APP_NAME := berth
APP_PATH := ./cmd/berth
PODMAN_IMAGE := berth-dev
PODMAN_RUN := podman run --rm -v $(shell pwd):/app:Z -w /app $(PODMAN_IMAGE)

.PHONY: all build run clean test lint help
.PHONY: all build run clean test lint help podman-image podman-build podman-test podman-lint docker-image docker-build docker-test docker-lint

all: build

Expand Down Expand Up @@ -36,13 +38,38 @@ lint:
# go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run ./...

podman-image:
@echo "Building Podman dev image (Go 1.26)..."
podman build -t $(PODMAN_IMAGE) -f Dockerfile.dev .

podman-build: podman-image
@echo "Building $(APP_NAME) in Podman..."
$(PODMAN_RUN) go build -o $(APP_NAME) $(APP_PATH)

podman-test: podman-image
@echo "Running tests in Podman..."
$(PODMAN_RUN) sh -c "mockery --config .mockery.yaml && go test ./..."

podman-lint: podman-image
@echo "Running lint in Podman..."
$(PODMAN_RUN) sh -c "mockery --config .mockery.yaml && golangci-lint run --timeout=5m ./..."

docker-image: podman-image
docker-build: podman-build
docker-test: podman-test
docker-lint: podman-lint

help:
@echo "Usage: make <command>"
@echo "\nCommands:"
@echo " all : Builds the application (default)"
@echo " build : Compiles the application binary"
@echo " run : Runs the application"
@echo " clean : Removes build artifacts and the application binary"
@echo " test : Runs all tests"
@echo " lint : Runs go fmt and go vet"
@echo " help : Displays this help message"
@echo " all : Builds the application (default)"
@echo " build : Compiles the application binary"
@echo " run : Runs the application"
@echo " clean : Removes build artifacts and the application binary"
@echo " test : Runs all tests"
@echo " lint : Runs golangci-lint"
@echo " podman-image : Builds Podman dev image (Go 1.26)"
@echo " podman-build : Builds the binary inside Podman"
@echo " podman-test : Runs tests inside Podman"
@echo " podman-lint : Runs lint inside Podman"
@echo " help : Displays this help message"
114 changes: 75 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<div align="center">
<p align="center">
<img src="docs/assets/berth-logo.png" alt="canery logo" height="240">
</p>

# 🚢 Berth – Terminal UI for Containers
<div align="center">

[![Go Version](https://img.shields.io/badge/go-1.24-blue?logo=go)](https://golang.org)
[![CI](https://github.com/rluders/berth/actions/workflows/ci.yml/badge.svg)](https://github.com/rluders/berth/actions/workflows/ci.yml)
![Go](https://img.shields.io/badge/go-1.26+-00ADD8?logo=go&logoColor=white)
[![Go Report Card](https://goreportcard.com/badge/github.com/rluders/berth)](https://goreportcard.com/report/github.com/rluders/berth)
[![GitHub release](https://img.shields.io/github/v/release/rluders/berth?sort=semver)](https://github.com/rluders/berth/releases)
[![codecov](https://codecov.io/gh/rluders/berth/graph/badge.svg)](https://codecov.io/gh/rluders/berth)
[![License](https://img.shields.io/github/license/rluders/berth)](LICENSE)
[![Build](https://img.shields.io/badge/build-passing-brightgreen)]()
[![Status](https://img.shields.io/badge/status-alpha-orange)]()
[![Made with Bubbletea](https://img.shields.io/badge/made%20with-bubbletea-ff69b4?logo=github)](https://github.com/charmbracelet/bubbletea)

> **Berth** is a terminal-based UI to manage your containers, images, volumes, networks, and system usage — with support for **Docker** and **Podman**.
> 🧠 Name origin: In maritime terms, a **berth** is a designated place where a ship is docked — just like containers in your stack. Clean, organized, and under control.
**Berth** is a terminal-based UI to manage your containers, images, volumes, networks, and system usage — with support for **Docker** and **Podman**. Name origin: In maritime terms, a **berth** is a designated place where a ship is docked — just like containers in your stack. Clean, organized, and under control.

</div>

Expand All @@ -31,7 +34,7 @@ Berth is a comprehensive terminal user interface (TUI) application built in Go,

### Prerequisites

- [Go](https://golang.org/doc/install) (version 1.24 or higher recommended)
- [Go](https://golang.org/doc/install) (version 1.26 or higher recommended)
- [Docker](https://docs.docker.com/get-docker/) or [Podman](https://podman.io/docs/installation) installed and running

### Steps
Expand All @@ -54,39 +57,72 @@ make run

Berth provides an intuitive keyboard-driven interface.

### 🎹 Navigation
### 🎹 Global Keys

* `1` — Containers View
* `2` — Images View
* `3` — Volumes View
* `4` — Networks View
* `5` — System View
| Key | Action |
| --------- | ------------------- |
| `1` | Containers view |
| `2` | Images view |
| `3` | Volumes view |
| `4` | Networks view |
| `5` | System view |
| `?` | Toggle help overlay |
| `q` / `esc` | Back / quit |
| `ctrl+c` | Quit |

### 🛠️ Container Actions

| Key | Action |
| --- | ------------------------ |
| `s` | Start selected container |
| `x` | Stop selected container |
| `d` | Remove container |
| `l` | View logs |
| `i` | Inspect container |

### 📦 Image & Volume Actions

* `d` — Remove selected image or volume
| Key | Action |
| ------- | ------------------------- |
| `enter` | Container details |
| `s` | Start container |
| `x` | Stop container |
| `r` | Restart container |
| `d` | Remove container |
| `l` | View logs |
| `i` | Inspect container |
| `e` | Exec shell |
| `/` | Filter containers |
| `g` | Toggle group by compose |
| `→` | Expand compose group |
| `←` | Collapse compose group |

### 📦 Image Actions

| Key | Action |
| --- | -------------------- |
| `d` | Remove image |
| `P` | Prune dangling images |
| `/` | Filter images |

### 💾 Volume Actions

| Key | Action |
| --- | ------------- |
| `d` | Remove volume |
| `/` | Filter volumes |

### 🌐 Network Actions

| Key | Action |
| --- | --------------- |
| `i` | Inspect network |

### 🧼 System Cleanup

| Key | Action |
| --- | ---------------- |
| `b` | Basic Cleanup |
| `a` | Advanced Cleanup |
| `t` | Total Cleanup |
| Key | Action |
| --- | --------------------------------------------------- |
| `b` | Basic cleanup — stopped containers, unused networks, dangling images |
| `a` | Advanced cleanup — basic + unused volumes |
| `t` | Total cleanup — all unused resources |

### 🔙 Back / Exit
### 📋 Logs View

* `q` or `esc` — Return to the previous view or quit the application from the main views.
| Key | Action |
| --- | ------------------- |
| `p` | Pause log stream |
| `f` | Follow (tail) logs |
| `n` | Toggle line numbers |

## 🛠️ Technology Stack

Expand All @@ -99,14 +135,14 @@ Berth provides an intuitive keyboard-driven interface.

```
.
├── cmd/ # CLI entry point (e.g., main.go)
├── cmd/ # CLI entry point
├── internal/
│ ├── tui/ # All Bubbletea models/views/components
│ ├── engine/ # Docker/Podman abstraction layer
│ ├── controller/ # Logic for container/image/volume actions
│ ├── state/ # Global state models
│ └── utils/ # Helpers: formatting, exec wrappers, etc.
├── assets/ # Logo, themes, maybe future plugins
│ ├── tui/ # Bubble Tea models, views, and components
│ ├── engine/ # Docker/Podman client abstraction
│ ├── service/ # Service layer (container, image, volume, network, system)
│ ├── controller/ # Action handlers (start, stop, remove, inspect, …)
│ └── utils/ # Formatting helpers and exec wrappers
├── docs/ # Assets (logo, screenshots)
├── go.mod
└── README.md
```
Expand Down
10 changes: 7 additions & 3 deletions cmd/berth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"log/slog"
"os"

tea "github.com/charmbracelet/bubbletea"
tea "charm.land/bubbletea/v2"
"github.com/rluders/berth/internal/tui"
)

Expand All @@ -18,7 +18,11 @@ func main() {
fmt.Printf("Error opening log file: %v\n", err)
os.Exit(1)
}
defer logFile.Close()
defer func() {
if err := logFile.Close(); err != nil {
fmt.Fprintf(os.Stderr, "Error closing log file: %v\n", err)
}
}()

handler := slog.NewTextHandler(logFile, &slog.HandlerOptions{Level: slog.LevelDebug})
slog.SetDefault(slog.New(handler))
Expand All @@ -42,7 +46,7 @@ func main() {
}
}()
slog.Debug("Initializing Bubble Tea program...")
program = tea.NewProgram(tui.InitialModel(), tea.WithAltScreen())
program = tea.NewProgram(tui.InitialModel())
}()

slog.Debug("Running Bubble Tea program...")
Expand Down
Binary file added docs/assets/berth-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading