Important
Although this is currently being used in personal projects, it is worth noting that it is not considered to be released yet and breaking changes might be frequent. The project is made public solely as a way to build this more in public and collect feedback and not as an indication that it is fit for use in all scenarios.
DEV is a tool for creating a consistent and evolvable project development environment. It is inspired by an internal Shopify tool, DEV, that I used while at Shopify. It is designed to adjust a development environment to match a configuration defined inside a dev.yml file.
- Cloning
- Plugins
- Preset [Upcoming]
- Custom scripts
- Built-in Procfiles support with grouped serves
- Customisable environment using ShadowEnv
- Project dependencies
- Custom project commands
- Sites
- Single Binary
- Environment Variable management
- Config tracking -
composer.json,composer.lock,package-lock.json, etc. - MySQL Database
At the moment, DEV works on MacOS as some aspect of the code assumes this. The plan is to support other platforms, too.
To install DEV for the first time, you can follow this process to install the pre-built binary:
curl -fsSL https://raw.githubusercontent.com/bosunski/dev/main/scripts/install.sh | bashThe command above will:
- Install latest version of DEV inside
$HOME/.dev/bin - Prepend
$HOME/.dev/binto$PATH - Configure hooks that will make it possible for DEV to provide notices when there are changes in your project and you need to run
dev upcommand
If you already have DEV installed, you can run this to update the existing binary to the latest available version:
dev upgradeIf you want to downgrade/upgrade to a specific version, you can use:
dev upgrade <desired version>DEV uses a dev.yml file at the root of the project to store configurations. So, projects that can be managed using dev must contain this file.
The name, while optional, defines the name of your project. When not, specified, it defaults to the name of the directory containing the dev.yml configuration. You can set the name of the project at the top level like this:
name: coreThe steps define the steps you want to go through to set up a project. These steps are performed one after the other as they have been defined.
You can define projects for which the current projects depend so that DEV can ensure that they are present when provisioning the current project. This is useful when a project depends on another to perform its functions. A common example will be a front-end service depending on a backend service that exposes an API. Normally, one would probably specify this in a README or documentation of the "front end" about this important detail.
When dev up runs, it ensures that all projects defined as a dependency are cloned and then provisioned (if they are managed with DEV ) before the current project is provisioned. You can define projects from the top-level config like this:
name: org/frontend
projects:
- org/backendAs you can see, projects are defined by their repository names as in owner/repo or org/repo so as to inform DEV where to source the project when it is missing locally.
It is also worth noting that, if any dependency project contains dependencies, DEV will also resolve them down to the last one before starting the provisioning process.
The current project is resolved last and will be provisioned last since it depends on other projects. DEV resolves projects in the order in which they are defined in the configuration so, a configuration like this:
name: org/frontend
projects:
- org/payments # depends on org/websocket
- org/backendWill end up being resolved in this order:
org/paymentsorg/websocketorg/backendorg/frontend
As a result, the provisioning will also happen in that order when dev up runs.
Sometimes, you may want to keep utility commands that are specific to the project to help perform tasks around your project. You can define these commands using the commands top-level attribute. For example, if we want to add a command that formats the code, we can add a style command like this:
commands:
style: prettier The command, once defined, can now be run using dev as dev style or dev run style .
You can define sites that are part of the project. This is useful when you have multiple sites that are part of the project and you want to manage them using DEV. You can define sites like this:
sites:
admin: https://example.com/admin
frontend: https://example.comThe serve attribute is used to define processes that should run when your projects are being served - when you run dev serve. This is useful when you have a project that requires multiple services to be running at the same time. You can define the processes like this:
serve:
app: php artisan serve
queue: php artisan queue:work
vite: viteWhile the serve attribute is Procfile-like, it is not a Procfile as it adds some extra features. It is a simple way to define processes that should run when you run dev serve. The processes are started in the order in which they are defined and the stoppage of one of the processes will stop the others as well.
It is also worth noting that when a dependency project has a serve attribute, DEV will also start the processes defined in the dependency project when you run dev serve for the current project. This is useful when you have a project that depends on another project that requires some services to be running.
You can organise serves into named groups. This lets you start a specific subset of processes without running everything:
serve:
db: postgres # flat — always starts regardless of which groups are selected
backend:
api: node server.js
worker: node worker.js
frontend:
vite: vite devdev serve # starts db, backend:api, backend:worker, frontend:vite
dev serve backend # starts db, backend:api, backend:worker
dev serve frontend # starts db, frontend:vite
dev serve backend frontend # starts db, backend:api, backend:worker, frontend:viteFlat (ungrouped) serves act as the shared layer — they always run regardless of which groups are specified. If the same process name appears in multiple groups, it is only started once (first-seen wins), so shared infrastructure like databases can be declared as a flat serve without risk of duplication.
If you have a .env.<environment> file in the project, DEV will make sure that the environment variables are loaded before starting the processes defined in the serve attribute. Env files loaded are per-project basis and not shared across projects. The default env file is .env but you can specify a different one like this:
serve:
web:
run: php artisan serve
env: local # .env.local
queue: php artisan queue:workYou can also instruct DEV to not load env files by setting the env attribute to false like this:
serve:
web:
run: php artisan serve
env: falseThe env attribute is used to define environment variables that should be set at all times during the provisioning of the project, when running custom commands and when running the serve command. They will also be injected into all shells whose working diretory is the project where you defined them. You can define environment variables like this:
env:
SOCK: /var/run/docker.sockSince the environment variables are set at all times, you can use them in your commands, scripts, and processes. For example, you can use the APP_ENV environment variable in your serve attribute like this:
serve:
web: ./bin/serve --sock=${SOCK}
commands:
show-sock: echo ${SOCK}Variables defined in the env attribute can also be dynmically set in cases where you want to set them based on the environment. You can use the ${ENV} syntax to set the value of the environment variable based on the environment or use the backticks to run commands within the env value. We can combine the two like this:
env:
SOCK: "${HOME}/run/`echo $PWD | md5`.sock" # /Users/bosun/run/1a79a4d60de6718e8e5b326e338ae533.sockDEV will evaluate the values of environment variables that are dynamically set and inject them appropriately when running commands, scripts, and processes.
There might be times you want to set a variable based on user input, maybe a password, or a token that you don't want to store in dev.yml. You can define this type of variables like this:
env:
STRIPE_SECRET:
prompt: "What is your Stripe Secret Key?"
hint: "You can get the Secret Key at https://dashboard.stripe.com/test/apikeys"
placeholder: "Looks like sk_test_xGgsdfgvsdf..."When you run dev up, DEV will prompt you to enter the value of the STRIPE_SECRET environment variable. The value entered will be stored in the $PWD/.dev/config.json file where it can be reused when running commands, serve, and provisioning.
Note
It is important to run dev up after modifying the env attributes so that the environment variables can injected in the shells, since the default ShadowEnv lisp file is generated during the provisioning process.
You can create a dev.local.yml file alongside dev.yml to override or extend any part of the configuration without modifying the committed file. This is useful for machine-specific tweaks — enabling a debugger, pointing at a local database, or adding extra environment variables that only apply to your machine.
dev.local.yml is automatically added to .gitignore when you run dev init, so it will never be accidentally committed.
The merge behaviour per key is:
| Key | Behaviour |
|---|---|
env, commands, sites, serve |
Deep merged — local keys override or extend base |
steps / up |
Local fully replaces base |
name |
Local overrides base |
projects |
Concatenated and deduplicated |
For example, to override the api serve to run with the Node debugger enabled locally:
# dev.local.yml (gitignored)
serve:
backend:
api:
run: node --inspect server.js
env: local
env:
DATABASE_URL: postgres://localhost/myapp_local- PHP >=8.2
- Composer
- Clone the project
git clone https://github.com/bosunski/dev- Install composer dependencies
composer install- Use DEV to set up the remaining aspects
php dev upAlternatively, if you have DEV binary already installed, you can use it to set up the project like this:
dev clone bosunski/dev && dev up- Plugins
- Presets
- Automated Tests
- Add PHPStan
- Add Pint for code styling.
- Better CLI UI
- Prioritisation of steps
- Extend functionality to other OSes.
- Documentation
- You can test and give feedback. Feel free to open an issue!
- You can suggest features
- You can also add features (see the ToDo)
Although I use DEV as a daily driver at this point. I won't consider it stable for use since there are other things that affects this stability that are not yet in place.