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
9 changes: 0 additions & 9 deletions .mise-tasks/try-dotbee
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,6 @@ fi

BIN_PATH="$(pwd)/target/${usage_target}/${BUILD_MODE}/dotbee"

echo
echo "Smoke-testing the binary inside the container..."
podman run --rm \
$PODMAN_FLAGS \
-v "${BIN_PATH}:/usr/local/bin/dotbee:Z" \
dotbee-dev \
bash -c "dotbee list && echo && dotbee doctor"
echo

echo "Running testing container..."
podman run --hostname="laptop" -it --rm \
$PODMAN_FLAGS \
Expand Down
4 changes: 3 additions & 1 deletion .nvim.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
vim.lsp.config("rust_analyzer", {
---@type lspconfig.settings.rust_analyzer
settings = {
["rust-analyzer"] = {},
["rust-analyzer"] = {
check = { command = "clippy" },
},
},
})
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ RUN <<EOF cat > global/git/gitconfig
email = test@localhost
EOF

RUN << EOF cat > script.sh
#!/usr/bin/env bash
echo 'hello script'
EOF

RUN chmod +x script.sh

# Create dotbee.toml
RUN <<EOF cat > dotbee.toml
[settings]
Expand All @@ -60,10 +67,18 @@ config = "~/.config"
[profiles.global.links]
gitconfig = { src = "global/git/gitconfig", dst = "~/.gitconfig" }

[profiles.laptop]
pre_hook = "echo 'hello laptop'"
post_hook.path = "./script.sh"

[profiles.laptop.links]
nvim = { src = "profiles/laptop/nvim", dst = "{config}/nvim" }
kitty = { src = "profiles/laptop/kitty", dst = "{config}/kitty" }

[profiles.termux]
pre_hook = "echo 'hello termux'"
post_hook = "goodbye 'termux'"

[profiles.termux.links]
nvim = { src = "profiles/termux/nvim", dst = "{config}/nvim" }
tmux = { src = "profiles/termux/tmux", dst = "{config}/tmux" }
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ config = "~/.config"
nvim = { src = "editors/nvim/", dst = "{config}/nvim" }
```

- [ ] **Pre/Post Hooks:** Run shell commands before/after profile switch (useful for things like enabling systemd services, but I wouldn't recommend for installing packages.).
- [x] **Pre/Post Hooks:** Run shell commands before/after profile switch (useful for things like enabling systemd services, but I wouldn't recommend for installing packages.).
- [ ] **Dotfiles Fetching:** Dotbee should be able to fetch a remote dotfiles repository.
- Using git
- Using http (simply download it)
Expand Down
13 changes: 13 additions & 0 deletions docs/HOWTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,16 @@ nvim = { src = "editors/nvim/", dst = "{cfg}/nvim" }
```

Variables are plain strings and are not interpolated themselves. An undefined `{name}` causes a configuration error.

### Pre/Post Hooks

Hooks are simply scripts that get executed when switching between profiles. Each profile can have a `pre_hook` and a `post_hook`.

- **pre_hook:** Runs prior to profile switch (useful for things like enabling systemd services for example).
- **post_hook:** Runs before switching into the new profile (useful for cleaning up things).

```toml
[profiles.destktop]
pre_hook = "systemctl enable foo.service"
post_hook.path = "./path/to/script.sh"
```
35 changes: 35 additions & 0 deletions src/context/config/hook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use anyhow::Result;
use nix::libc;
use serde::Deserialize;
use std::{path::PathBuf, process::Command};

#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum Hook {
File { path: PathBuf },
Inline(String),
}

impl Hook {
pub fn execute(&self) -> Result<()> {
match self {
Hook::File { path } => match Command::new(path).status() {
Ok(_status) => {}
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
eprintln!("Error: The script at {:?} is not executable. Try 'chmod +x {:?}'", path, path);
}
Err(e) if e.raw_os_error() == Some(libc::ENOEXEC) => {
eprintln!(
"Error: Exec format error. Your script at '{:?}' is missing a shebang (e.g., #!/bin/sh) at the top of the file.",
path
);
}
Err(e) => eprintln!("An unexpected error occurred: {}", e),
},
Hook::Inline(code) => {
Command::new("sh").arg("-c").arg(code).status()?;
}
}
Ok(())
}
}
4 changes: 4 additions & 0 deletions src/context/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod conflict;
pub mod hook;
pub mod vars;

use anyhow::anyhow;
Expand All @@ -8,6 +9,7 @@ use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};

use crate::context::config::hook::Hook;
use crate::utils::message;

#[derive(Debug, Deserialize, Clone, Default)]
Expand All @@ -28,6 +30,8 @@ pub struct Link {
#[serde(deny_unknown_fields)]
pub struct Profile {
pub links: IndexMap<String, Link>,
pub pre_hook: Option<Hook>,
pub post_hook: Option<Hook>,
}

/// reserved profile name for links applied regardless of the active profile
Expand Down
8 changes: 8 additions & 0 deletions src/subcommands/purge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ use colored::Colorize;
use std::fs;

pub fn run(context: &mut Context) -> anyhow::Result<(), anyhow::Error> {
// execute post_hook of the currently active profile
if let Some(stale_profile) = context.state.get_active_profile()
&& let Some(ref hook) = context.config.get_profile(stale_profile)?.post_hook
{
println!("Running {} profile post hook", stale_profile);
hook.execute()?;
}

let links = context.state.get_links();

if links.is_empty() {
Expand Down
14 changes: 14 additions & 0 deletions src/subcommands/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ pub fn run(profile_name: Option<String>, context: &mut crate::context::Context)
let dotfiles_root = context.state.get_dotfiles_root()?;
let dry_run = context.dry_run;

// execute post_hook of the currently active profile
if let Some(old_profile) = context.state.get_active_profile()
&& let Some(ref hook) = context.config.get_profile(old_profile)?.post_hook
{
hook.execute()?;
}

// execute pre_hook of the new profile
if let Some(new_profile) = &target_profile
&& let Some(ref hook) = context.config.get_profile(new_profile)?.pre_hook
{
hook.execute()?
}

// build desired links map from config
let mut desired_links: IndexMap<String, Link> = IndexMap::new();

Expand Down