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
2 changes: 1 addition & 1 deletion .githooks/install
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ source_hook="$repo_root/.githooks/pre-commit"
installed_hook="$hooks_dir/pre-commit"

mkdir -p "$hooks_dir"
mkdir -p "$hooks_dir/pre-commit.d/backend" "$hooks_dir/pre-commit.d/frontend"
mkdir -p "$hooks_dir/pre-commit.d"
if [ -e "$installed_hook" ] || [ -L "$installed_hook" ]; then
if [ -L "$installed_hook" ] && [ "$(readlink "$installed_hook")" = "$source_hook" ]; then
echo "Pre-commit hook is already installed."
Expand Down
27 changes: 9 additions & 18 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,14 @@ run_extra_dir() {
done
}

check_go_format() {
local files
files=$(gofmt -l cmd internal) || return 1
if [ -n "$files" ]; then
printf '%s\n' "$files" >&2
return 1
fi
}

echo "Running pre-commit checks ($mode_label)... ${REPO_ROOT}" >&2
go_changed=0
frontend_changed=0

while IFS= read -r path; do
[ -n "$path" ] || continue
case "$path" in
cmd/*.go | internal/*.go | go.mod | go.sum | go.work | go.work.sum)
cmd/*.go | internal/*.go | go.mod | go.sum | go.work | go.work.sum | .golangci.yml | .githooks/pre-commit)
go_changed=1
;;
.editorconfig | mise.toml)
Expand All @@ -99,21 +90,21 @@ EOF

if [ "$frontend_changed" -eq 1 ]; then
echo "Preparing frontend..." >&2
NODE_ENV=development bun --cwd="$PWD/frontend/" run postinstall --envName=development
NODE_ENV=development mise exec -- bun --cwd="$PWD/frontend/" run nuxt prepare --envName=development
echo "Frontend preparation passed." >&2
fi

if [ "$go_changed" -eq 1 ]; then
run_step "Go: Formatter" check_go_format
run_step "Go: Tests" go test -race ./cmd/... ./internal/...
run_step "Go: Vet" go vet ./cmd/... ./internal/...
run_step "Go: Formatter" mise exec -- golangci-lint fmt --diff ./cmd/... ./internal/...
run_step "Go: Tests" mise exec -- go test -race ./cmd/... ./internal/...
run_step "Go: Linter" mise exec -- golangci-lint run ./cmd/... ./internal/...
fi

if [ "$frontend_changed" -eq 1 ]; then
run_step "Frontend: Formatter" bun --cwd="$PWD/frontend/" format:check
run_step "Frontend: Linter" bun --cwd="$PWD/frontend/" lint
run_step "Frontend: Tests" bun --cwd="$PWD/frontend/" run test
run_step "Frontend: TSC" bun --cwd="$PWD/frontend/" lint:tsc
run_step "Frontend: Formatter" mise exec -- bun --cwd="$PWD/frontend/" run format:check
run_step "Frontend: Linter" mise exec -- bun --cwd="$PWD/frontend/" run lint
run_step "Frontend: Tests" mise exec -- bun --cwd="$PWD/frontend/" test
run_step "Frontend: TSC" mise exec -- bun --cwd="$PWD/frontend/" run lint:tsc
fi

extra_dir=$(git rev-parse --git-path hooks)/pre-commit.d
Expand Down
38 changes: 19 additions & 19 deletions .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ on:
- '**.md'
- '.github/ISSUE_TEMPLATE/**'

env:
BUN_VERSION: '1.4.4'
NODE_VERSION: 22
GO_VERSION: '1.27.3'

jobs:
test:
name: Run Tests & Build Validation
Expand All @@ -27,11 +22,26 @@ jobs:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v6
- name: Set up tools
uses: jdx/mise-action@v5
with:
install_args: go bun node golangci-lint

- name: Locate Go caches
id: go-cache
run: |
echo "modules=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
echo "build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
echo "version=$(go env GOVERSION)" >> "$GITHUB_OUTPUT"

- name: Cache Go dependencies and builds
uses: actions/cache@v6
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: go.sum
path: |
${{ steps.go-cache.outputs.modules }}
${{ steps.go-cache.outputs.build }}
key: ${{ runner.os }}-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-${{ github.job }}-${{ hashFiles('go.sum') }}
restore-keys: ${{ runner.os }}-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-${{ github.job }}-

- name: Cache Bun dependencies
uses: actions/cache@v6
Expand All @@ -41,16 +51,6 @@ jobs:
restore-keys: |
${{ runner.os }}-bun-

- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}

- name: Install frontend dependencies
env:
NODE_ENV: development
Expand Down
53 changes: 36 additions & 17 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ env:
BUILD_ARM64: ${{ vars.BUILD_ARM64 != 'false' && 'true' || 'false' }}
DOCKERHUB_SLUG: arabcoders/dcompose
GHCR_SLUG: ghcr.io/arabcoders/dcompose
BUN_VERSION: '1.4.4'
NODE_VERSION: '22'
GO_VERSION: '1.27.3'

jobs:
validate-build-config:
Expand All @@ -53,10 +50,25 @@ jobs:
steps:
- uses: actions/checkout@v7

- uses: actions/setup-go@v6
- uses: jdx/mise-action@v5
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: go.sum
install_args: go bun node golangci-lint

- name: Locate Go caches
id: go-cache
run: |
echo "modules=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
echo "build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
echo "version=$(go env GOVERSION)" >> "$GITHUB_OUTPUT"

- name: Cache Go dependencies and builds
uses: actions/cache@v6
with:
path: |
${{ steps.go-cache.outputs.modules }}
${{ steps.go-cache.outputs.build }}
key: ${{ runner.os }}-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-${{ github.job }}-${{ hashFiles('go.sum') }}
restore-keys: ${{ runner.os }}-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-${{ github.job }}-

- name: Cache Bun dependencies
uses: actions/cache@v6
Expand All @@ -65,14 +77,6 @@ jobs:
key: ${{ runner.os }}-bun-${{ hashFiles('frontend/bun.lock') }}
restore-keys: ${{ runner.os }}-bun-

- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}

- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}

- name: Install frontend dependencies
run: bun --cwd=$PWD/frontend/ install --frozen-lockfile

Expand Down Expand Up @@ -124,10 +128,25 @@ jobs:
name: frontend-build
path: frontend/exported/

- uses: actions/setup-go@v6
- uses: jdx/mise-action@v5
with:
install_args: go

- name: Locate Go caches
id: go-cache
run: |
echo "modules=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
echo "build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
echo "version=$(go env GOVERSION)" >> "$GITHUB_OUTPUT"

- name: Cache Go dependencies and builds
uses: actions/cache@v6
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: go.sum
path: |
${{ steps.go-cache.outputs.modules }}
${{ steps.go-cache.outputs.build }}
key: ${{ runner.os }}-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-${{ github.job }}-${{ hashFiles('go.sum') }}
restore-keys: ${{ runner.os }}-${{ runner.arch }}-go-${{ steps.go-cache.outputs.version }}-${{ github.job }}-

- name: Build and package native binaries
run: |
Expand Down
23 changes: 23 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: '2'

formatters:
enable:
- gofmt
- golines
settings:
golines:
max-len: 140

linters:
default: none
enable:
- govet
- staticcheck
- ineffassign
- unused
- errcheck
- modernize
settings:
staticcheck:
checks:
- SA*
38 changes: 29 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ review.

## Before you start

Report vulnerabilities privately through [SECURITY.md](SECURITY.md). For a bug fix or small feature, open a GitHub issue.
Use a discussion for larger changes or general questions. Explain what you want to change, why, your proposed approach
and which existing behavior or use cases it affects.
Report vulnerabilities privately through [SECURITY.md](SECURITY.md). For bugs, proposed changes or general questions,
open a GitHub issue. Explain what you want to change, why, your proposed approach and which existing behavior or use
cases it affects.

Bug fixes, functional improvements and measurable performance work take priority. An interface change needs more than
personal preference. I will decline a proposal or ask for a different scope or approach.
Expand All @@ -22,7 +22,17 @@ documentation will be rejected. Write tests that check behavior, not just increa

## Development setup

You'll need Go 1.27.3+ and Bun 1.4+. Run commands from the repository root. Install frontend dependencies with:
Branch from `dev`:

```sh
git switch dev
git pull origin dev
git switch -c feature/descriptive-name
```

Install [mise](https://mise.jdx.dev/getting-started.html), then run `mise install` to install the development tools from
`mise.toml`. The pre-commit checks use `mise exec` so local checks and CI use the same tool versions.
Run commands from the repository root. Install frontend dependencies with:

```sh
bun --cwd=$PWD/frontend/ install --frozen-lockfile
Expand All @@ -40,8 +50,9 @@ Install the local pre-commit hook once per clone:
.githooks/pre-commit --all # all tracked files
```

The installer preserves an existing pre-commit hook unless you pass `--force`. To keep local checks, place executable
files in `.git/hooks/pre-commit.d/` (always run), `backend/` (Go changes), or `frontend/` (frontend changes).
The installer preserves an existing pre-commit hook unless you pass `--force`. Put executable local checks in
`.git/hooks/pre-commit.d/` to run them for all changes. Create `backend/` or `frontend/` subdirectories there for
Go-only or frontend-only checks.

## Check changes

Expand All @@ -57,10 +68,13 @@ bun --cwd=$PWD/frontend/ format:check
For Go changes:

```sh
go test -race ./cmd/... ./internal/...
go vet ./cmd/... ./internal/...
mise exec -- golangci-lint fmt ./cmd/... ./internal/...
mise exec -- go test -race ./cmd/... ./internal/...
mise exec -- golangci-lint run ./cmd/... ./internal/...
```

Go formatting uses gofmt and golines with a 140-column target. Pre-commit and CI check formatting without rewriting files.

## Local hot reload

Install the tools from `mise.toml` and start the backend watcher:
Expand Down Expand Up @@ -113,6 +127,12 @@ go build -o dist/dagent ./cmd/dagent

The deployed server includes the web interface and doesn't need Go or Bun.

## Pull requests

Every pull request must target `dev`, never `master`. Reference the approved issue, explain what changed
and why, and include any breaking changes or setup steps. Run the checks that apply to your changes and update the
documentation where needed. Pull requests targeting `master` will be closed without review.

## API versions

`GET /api/version` returns the server's application version, API version and supported node API version. A node's
Expand All @@ -127,5 +147,5 @@ The image includes both binaries, Docker Compose and the Podman remote client. I
the command with `dagent` for a node. To build it:

```sh
docker build -t arabcoders/dcompose .
docker build -t ghcr.io/arabcoders/dcompose .
```
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# DCompose

![Build Status](https://github.com/arabcoders/dcompose/actions/workflows/main.yml/badge.svg)
![MIT License](https://img.shields.io/github/license/arabcoders/dcompose.svg)
![Docker Pulls](https://img.shields.io/docker/pulls/arabcoders/dcompose.svg)
![GHCR Pulls](https://ghcr-badge.elias.eu.org/shield/arabcoders/dcompose/dcompose)

DCompose manages your compose stacks on local and remote hosts from one web interface. The project is
inspired by [Dockge](https://github.com/louislam/dockge).
Expand All @@ -22,10 +25,12 @@ Only `dagent` needs access to Docker or Podman.
On Linux, run the installer and choose server, node, or both:

```sh
curl -fL https://raw.githubusercontent.com/arabcoders/dcompose/master/install.sh -o install.sh
curl -fL https://raw.githubusercontent.com/arabcoders/dcompose/HEAD/install.sh -o install.sh
bash install.sh
```

The installer previews commands by default. Review them, then run `bash install.sh --execute` to install.

See [Installation](installation.md) for requirements. For manual setup, use the Docker instructions below or the
[native and Podman instructions](packaging/README.md).

Expand Down Expand Up @@ -106,6 +111,6 @@ is not good fit for you. Build your own using technologies you approve of.

## Other projects

- [YTPTube](https://github.com/arabcoders/ytptube): Download manager and automation interface for yt-dlp.
- [WatchState](https://github.com/arabcoders/watchstate): Sync play states between media servers without third-party services.
- [Millwright](https://github.com/arabcoders/millwright): automation interface for FFmpeg.
* [WatchState](https://github.com/arabcoders/watchstate): Sync your media servers without relying on 3rd parties.
* [YTPTube](https://github.com/arabcoders/ytptube): Download manager and automation interface for yt-dlp.
* [Millwright](https://github.com/arabcoders/millwright): Automation interface for FFmpeg.
23 changes: 20 additions & 3 deletions cmd/dagent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ func main() {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
if err := agent.RunOperation(ctx, *operationFile); nil != err {
slog.Error(fmt.Sprintf("Detached stack operation from request %q failed: %v", *operationFile, err), "request", *operationFile, "error", err)
slog.Error(
fmt.Sprintf("Detached stack operation from request %q failed: %v", *operationFile, err),
"request",
*operationFile,
"error",
err,
)
os.Exit(1)
}
return
Expand All @@ -51,10 +57,21 @@ func main() {
if "" != *certDir {
fingerprint, err := agent.GenerateCert(*certDir)
if nil != err {
slog.Error(fmt.Sprintf("Could not create the node TLS certificate and private key in %q: %v", *certDir, err), "directory", *certDir, "error", err)
slog.Error(
fmt.Sprintf("Could not create the node TLS certificate and private key in %q: %v", *certDir, err),
"directory",
*certDir,
"error",
err,
)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Created the node TLS certificate at %s and private key at %s.\n", filepath.Join(*certDir, "node.crt"), filepath.Join(*certDir, "node.key"))
fmt.Fprintf(
os.Stderr,
"Created the node TLS certificate at %s and private key at %s.\n",
filepath.Join(*certDir, "node.crt"),
filepath.Join(*certDir, "node.key"),
)
fmt.Printf("TLS fingerprint: %s\n", fingerprint)
}
if "" != *tokenPath {
Expand Down
Loading
Loading