Migrating
Coming from smug
Translate a smug YAML project into a glaze profile, block by block.
smug and Glazier are close relatives: one Go binary each, a project file that can live next to the code and a YAML or HCL file that a human can read in one sitting. smug’s file is shorter than a .glaze file, and it has a stop list, which Glazier does not. If it works for you, keep using it. I am not here to convince anyone. But if you want to try a .glaze file, and you already have a directory of smug projects, this page translates one into the other block by block, and it is honest about the places where the two tools simply do not line up.
A typical project
A smug project with the usual parts:
session: daemon-run
root: ~/runs/arasaka
env:
ICE_TARGET: arasaka-mainframe
before_start:
- docker compose up -d
stop:
- docker compose stop
attach_hook: echo jacked-in
windows:
- name: editor
root: ice
layout: main-vertical
selected: true
commands:
- nvim .
panes:
- type: horizontal
commands:
- npm run dev
- name: logs
layout: even-horizontal
manual: true
commands:
- tail -f logs/dev.log
panes:
- type: horizontal
commands:
- htopThe same workspace as a profile:
session {
name = "daemon-run"
starting_directory = "~/runs/arasaka"
commands = ["docker compose up -d"]
envs = {
ICE_TARGET = "arasaka-mainframe"
}
hooks = {
"client-attached" = "run-shell 'echo jacked-in'"
}
window {
name = "editor"
layout = "main-vertical"
starting_directory = "ice"
focus = true
pane {
commands = ["nvim ."]
}
pane {
commands = ["npm run dev"]
}
}
window {
name = "logs"
layout = "even-horizontal"
pane {
commands = ["tail -f logs/dev.log"]
}
pane {
commands = ["htop"]
}
}
}The logs window lost its manual: true, and the stop list is gone. Both are explained below.
The translation table
| smug | glaze | Notes |
|---|---|---|
session | session.name | Glazier replaces ., :, $ and a backslash with - and shows a warning. |
root | session.starting_directory | ~ expands. A relative path is relative to the directory of the profile. |
windows[].name | window.name | |
windows[].root | window.starting_directory | In smug, relative to the session root. In Glazier, relative to the directory of the profile. |
layout | window.layout | The same five tmux presets. A raw layout string also works in both tools. |
selected | focus = true on the window | |
windows[].commands | the first pane.commands | smug runs them in the first pane of the window. See below. |
panes[].type | none | The window layout places the panes. See below. |
panes[].root | pane.starting_directory | Relative to the directory of the profile. |
panes[].commands | pane.commands | A list, in order. Glazier waits for each command except the last. |
env | session.envs | Glazier applies them before it creates any window or pane. |
before_start | session.commands, or run it before glaze up | Not the same. See below. |
stop | none | See below. |
attach_hook, detach_hook | session.hooks | A tmux client-attached or client-detached hook. Not the same. See below. |
manual | none | See below. |
smug start daemon-run | glaze up | |
smug start daemon-run:editor | none | Glazier creates every window of the profile. |
smug stop daemon-run | glaze down | A session that is not running causes no error. |
The five layout presets are tmux presets in both tools: even-horizontal, even-vertical, main-horizontal, main-vertical and tiled. The default in Glazier is tiled.
What does not map one to one
Window commands and pane type. In smug a window starts as one pane, and that pane runs the window’s commands. Each entry in panes then splits a new pane off it, horizontal or vertical, and runs its own commands. Glazier has no implicit first pane and no split direction. Every pane is a pane block, in file order, and the window layout places them. So move the window commands into a first pane block, add one pane block for each entry in panes and drop type. When no preset matches the shape that your splits produced, build the window once by hand and run glaze save: it captures the exact geometry as a raw layout string that up replays. See Window and Save a session.
before_start. smug runs these commands in a shell before the session exists. Glazier has commands on the session block, and they run in the active pane after Glazier creates all windows and panes. The command runs, but later, and in a pane that you can see. That is what the example above does with docker compose up -d. For a command that must finish before anything else, run it before glaze up in your shell. See Session.
stop. smug runs these commands before it kills the session. Glazier has no such hook. glaze down kills the session and does nothing else. Put the commands in front of it in your shell, docker compose stop && glaze down, or in the Makefile of the project. See glaze down and Scripting.
manual. smug skips a manual window unless you ask for it with -w or project:window. Glazier creates every window in the profile, every time. There is no flag to pick windows. When you need the split, give the manual windows a second profile with their own session name and start it with --profile-path. Otherwise drop the key, as the example does, and live with one more window.
attach_hook and detach_hook. These are shell commands. A Glazier hooks entry is a tmux command bound to a tmux hook, so wrap the shell command in run-shell, as the example does with client-attached. tmux fires client-attached on every attach and client-detached on every detach, not only for the first or the last client. Glazier checks the hook name against the names that tmux 3.2a to 3.7c know. See Hooks and options.
Differences in the workflow
- smug keeps projects in
~/.config/smug/, or in.smug.ymlin the current directory. Glazier looks for.glazein the current directory, then in$GLAZE_PATH, or where--profile-pathpoints. A profile lives with the project, so it goes into the repository. See Profile resolution. - Glazier has typed
variableblocks and--varflags, so one profile serves many projects. See One profile for many projects. - smug types each command with
send-keys. Glazier loads the commands into a tmux paste buffer and waits for each pane command except the last withtmux wait-for, sonpm installfinishes beforenpm run devstarts. See Run commands reliably.
Check the translation
Run glaze format --validate before the first glaze up. It decodes the profile and reports each problem that it can find, with the file and the line, before tmux starts:
$ glaze format --validate --stdout > /dev/null
Error: Invalid layout specified
on /home/v/runs/arasaka/.glaze line 9, in session:
9: layout = "main-diagonal"
The layout value of "main-diagonal" is not a supported preset
(even-horizontal, even-vertical, main-horizontal, main-vertical, tiled) nor a
valid tmux layout string.
the glaze profile contains errors
Without --stdout, format also rewrites the profile into the canonical form, which is a fine first step for a file you just typed by hand. See glaze format.
Two files, same workspace. Keep the one you prefer.