A simple and robust orchestrator for streamlining local development environments in monorepos.
npx go-dev <preset>
– Go develop!
In complex monorepos, starting your development environment can be a chore. You might need to spin up Docker containers, run multiple Node.js (or other language) development servers, handle pre-builds, and manage inter-service dependencies. go-dev
simplifies this by allowing you to define your entire local development stack in a single YAML configuration file.
go-dev
acts as a central command to bring up your api
, frontend
, database
, and any other microservices, ensuring they start in the correct order, with the right modes, and provide clear, prefixed logs.
-
Unified Configuration: Define all your services, their modes (e.g.,
dev
,docker
,serve
), and dependencies in a singlego-dev.yml
file. -
Service Types:
-
cmd
services: Run any command-line process (e.g.,npm run dev
,rollup -w
,python app.py
). SupportspreCommands
for setup tasks like builds. Commands can be defined in multiple flexible ways to run single or multiple processes in parallel for a service. -
docker
services: Manage Docker containers viadocker compose
. Automatically checks container status and performs health checks.
-
-
Mode-Aware Dependencies: Services can depend on other services running in specific modes (e.g., your
api
dev mode might depend onfrontend
inserve
mode). -
Preset-Driven Startup: Define different "presets" (e.g.,
api
,frontend
,all
) to easily spin up specific combinations of services tailored to your current development focus. -
Automatic Dependency Resolution:
go-dev
builds an intelligent execution graph, starting services in the correct topological order. - Centralized Logging: Prefixes logs from each service, making it easy to follow activity from multiple concurrent processes.
-
Automatic Process Exit: The
go-dev
process will automatically exit when all primary services (those directly listed in the chosen preset) exit cleanly (with a success code of0
). -
Precise Docker Cleanup:
go-dev
intelligently tracks which Docker Compose services it actively started (i.e., those that were not already running). During cleanup, it will only stop these specific services, leaving any pre-existing containers untouched. -
Robust Cleanup: Handles graceful shutdown of all started processes and Docker services on exit (e.g., via
Ctrl+C
or automatic exit).
While tools like concurrently
manage parallel processes and docker compose
handles containers, go-dev
fills a crucial gap by:
-
Integrating
cmd
anddocker
services seamlessly: It bridges the world of host-based processes and containerized applications under one roof. - Providing intelligent mode-aware dependency resolution: It understands that "frontend" might mean a different set of commands (and dependencies) when you're actively developing the frontend vs. when it's just a dependency for API development.
-
Offering a single, declarative interface for your entire dev stack: No more remembering multiple
npm
scripts ordocker compose
commands.
go-dev
is distributed via npm and designed to be used with npx
.
# Install it as a devDependency in your monorepo's root
npm install --save-dev go-dev
# or
yarn add --dev go-dev
Once installed, simply run go-dev
with the name of the preset you want to start:
npx go-dev <preset_name> [config_path]
-
<preset_name>
: The name of the preset defined in yourgo-dev.yml
(e.g.,api
,frontend
,all
). -
[config_path]
: (Optional) Path to yourgo-dev.yml
file. Defaults to looking forgo-dev.yml
,.go-dev.yml
,go-dev.yaml
, or.go-dev.yaml
in the current directory.
Passing Arguments to Service Commands:
To pass additional arguments from the command line to a specific service command, use a keyword flag followed by the target and its arguments.
By default, the keyword flag is --args-for
. This can be customized in your go-dev.yml
using the serviceArgsKeyword
option (e.g., serviceArgsKeyword: pass-to
).
The target for arguments is specified as <service_name>[:<command_index>]
:
Specify the target for arguments as <service_name>:<command_index>
(e.g., api:0
, frontend:1
). The command_index
is 0-based and refers to the position of the command within a service's commands
array. If the :<command_index>
part is omitted (e.g., just <service_name>
), arguments are passed to the first command (index 0
) defined for that service.
You can combine multiple keyword flag blocks for different services or specific commands.
npx go-dev <preset_name> [--<serviceArgsKeyword> <service_name>[:<command_index>] [args...] ] [...]
How Arguments are Applied to cmd
Service Commands:
When arguments are passed to a cmd
type service command, go-dev
processes them in a special way:
-
Placeholder Substitution:
- The command array (e.g.,
[npx, tsx, ./src/$arg.ts]
) is scanned for the special placeholder$arg
. - Each occurrence of
$arg
is replaced, in order, by an argument from the[args...]
provided on the command line. - Example: A command
[npx, tsx, ./src/$arg.ts]
with extra arguments[index, -w]
will become[npx, tsx, ./src/index.ts, -w]
.
- The command array (e.g.,
-
Escaped Placeholders:
- If you need a literal
$arg
in your command that should not be substituted, escape it with a backslash:\$arg
. - Example: A command
[echo, \$arg]
with no extra arguments will result in[echo, $arg]
.
- If you need a literal
-
Remaining Arguments:
- Any arguments from
[args...]
that were not used to substitute an$arg
placeholder will be appended to the end of the command array. - Example: A command
[echo, $arg, fixed]
with extra arguments[first, second, third]
will become[echo, first, fixed, second, third]
. Here,first
replaces$arg
, andsecond
,third
are appended.
- Any arguments from
Full Example:
Consider an api
service with two parallel commands: api:0
(main server, using $arg
) and api:1
(TypeScript compiler watch).
npx go-dev all \
--args-for api:0 main-entrypoint --host 0.0.0.0 --port 8081 \
--args-for api:1 --pretty --diagnostics \
--args-for frontend --log-level verbose
In the example above, --args-for frontend
is equivalent to --args-for frontend:0
.
Press Ctrl+C
at any time to gracefully shut down all running services. go-dev
will also automatically exit once all primary services (those directly listed in your chosen preset) have completed their execution cleanly.
Create a configuration file in your project's root.
By default, go-dev
automatically detects its configuration file. It looks for files named go-dev
(or .go-dev
for a hidden file), optionally including .config
before the .yml
or .yaml
extension. It will search for these files in the directory where you run npx go-dev
.
Common examples include: go-dev.yml
, .go-dev.yml
, and go-dev.config.yaml
.
# go-dev.yml
# Customize the keyword used to pass arguments to service commands from the CLI.
# Change this if 'args-for' conflicts with a command your services use.
serviceArgsKeyword: args-for # Default value
# Define your individual services here
services:
# Example: A Docker-based PostgreSQL database
postgres:
type: docker # This service runs inside Docker
service: postgres # Name of the service in your docker-compose.yml
composeFile: infrastructure/docker-compose.yml # Path to the docker-compose file
healthCheck: true # Enable health checks for this Docker service
# Example: Your API service, which can run in 'dev' (cmd) or 'docker' mode
api:
type: hybrid # This service has multiple modes
defaultMode: dev # Default mode if not specified by a preset or dependency
# Note: The name of the modes is totally arbitrary
modes:
# API in active development mode (runs directly on host)
dev:
type: cmd # This mode runs a command-line process
# The 'commands' property can be specified in three ways:
# 1. As a simple array of strings (for a single command without extra options)
# commands: [npx, rollup, -c, -w]
# 2. As a single command object (for one command with options like 'directory' or 'restartOnError')
# commands:
# command: [npx, rollup, -c, -w]
# directory: ./api
# restartOnError: true # Default is true for 'cmd' services
# 3. As an array of command objects (for multiple parallel commands)
commands:
- command: [npx, rollup, -c, -w] # The primary command for development (index 0)
directory: ./api # Directory to run the command from
# restartOnError: true # (Optional, defaults to true)
# Example of a second parallel command for API (index 1)
# - command: [npx, tsc, --watch]
# directory: ./api
dependencies: # What this mode depends on
- postgres # API dev needs PostgreSQL (will use postgres's default docker mode)
- { service: frontend, mode: serve } # API dev needs frontend running in its 'serve' mode
# API running as a Docker container (e.g., for frontend-only dev)
docker:
type: docker
service: api
composeFile: infrastructure/docker-compose.yml
healthCheck: true
dependencies:
- postgres # Docker API also needs PostgreSQL
# Example: Your Frontend service, which can run in 'dev' (cmd) or 'serve' (cmd) mode
frontend:
type: hybrid
defaultMode: dev
modes:
# Frontend in active development (watch mode)
dev:
type: cmd
commands:
command: [npx, rollup, -c, -w]
directory: ./frontend
dependencies:
# Frontend dev needs API (will use api's default docker mode for this preset)
# Note: No direct circular dependency between dev modes.
# Dev modes often assume peers will eventually be ready.
- { service: api, mode: docker }
# Frontend serving its built assets (e.g., when API depends on it)
serve:
type: cmd
preCommands: # Commands to run and await completion BEFORE the main command
- [npm, --prefix, frontend, run, build] # Build frontend assets first
commands:
command: [node, ./localserver.mjs] # Then start the local server
directory: ./frontend
dependencies:
- api # Frontend serve needs API (will use api's default dev mode for this preset)
# Define different development presets (combinations of services and their modes)
presets:
# Preset: "api" development focus
# Starts API in dev mode, pulling in its dependencies (postgres, frontend:serve)
api:
services: [api] # Only explicitly list top-level services you want to run
modes:
# no explicit modes needed here, as defaultMode and dependency requests handle it
# api: dev # (already default)
# frontend: serve # (requested by api:dev)
# postgres: dev # (pulled by dependencies)
# Preset: "frontend" development focus
# Starts Frontend in dev mode, pulling in its dependencies (api:docker, postgres)
frontend:
services: [frontend]
modes:
# frontend: dev # (already default)
# api: docker # (requested by frontend:dev)
# postgres: dev # (pulled by dependencies)
# Preset: "all" development (both API and Frontend in dev mode concurrently)
all:
services: [api, frontend] # Explicitly list both as top-level focus
modes:
# api: dev # (already default)
# frontend: dev # (already default)
# No need to specify modes if they match the defaultMode
Contributions are welcome! Feel free to open issues or submit pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.