Skip to content
Closed
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
17 changes: 17 additions & 0 deletions .github/workflows/install-script.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ on:
- "run_once_install-packages.sh.tmpl"
- "dot_config/fish/config.fish.tmpl"
- "installer/**"
- "Dockerfile.fedora-test-ci"
pull_request:
paths:
- ".github/workflows/install-script.yml"
- "run_once_install-packages.sh.tmpl"
- "dot_config/fish/config.fish.tmpl"
- "installer/**"
- "Dockerfile.fedora-test-ci"
workflow_dispatch:

jobs:
Expand All @@ -31,6 +33,21 @@ jobs:
- name: Run Arch installer dry-run
run: docker run --rm -v "$PWD:/work:ro" dotfiles-arch-test-ci

fedora:
name: Fedora install
runs-on: ubuntu-latest
timeout-minutes: 45

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Build Fedora test image
run: docker build -f Dockerfile.fedora-test-ci -t dotfiles-fedora-test-ci .

- name: Run Fedora installer dry-run
run: docker run --rm -v "$PWD:/work:ro" dotfiles-fedora-test-ci

apple-silicon:
name: Apple Silicon install
runs-on: macos-15
Expand Down
13 changes: 13 additions & 0 deletions Dockerfile.fedora-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM fedora:latest

RUN dnf install --assumeyes \
rust \
cargo \
chezmoi \
&& dnf clean all

WORKDIR /work

ENV CARGO_TARGET_DIR=/tmp/cargo-target

CMD ["bash", "-lc", "bash -n run_once_install-packages.sh.tmpl && cd installer && cargo build --release && /tmp/cargo-target/release/dotsetup install"]
13 changes: 13 additions & 0 deletions Dockerfile.fedora-test-ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM fedora:latest

RUN dnf install --assumeyes \
rust \
cargo \
chezmoi \
&& dnf clean all

WORKDIR /work

ENV CARGO_TARGET_DIR=/tmp/cargo-target

CMD ["bash", "-lc", "bash -n run_once_install-packages.sh.tmpl && cd installer && cargo build --release && CI=1 DRY_RUN=1 /tmp/cargo-target/release/dotsetup install"]
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dotfiles

Personal dotfiles managed with [chezmoi](https://www.chezmoi.io/). Works for macOS (Apple Silicon) and Linux (Arch/CachyOS). Also installs a large set of tools and packages I want in every system.
Personal dotfiles managed with [chezmoi](https://www.chezmoi.io/). Works for macOS (Apple Silicon) and Linux (Arch/CachyOS/Fedora). Also installs a large set of tools and packages I want in every system.

## Quick start

Expand Down Expand Up @@ -35,6 +35,8 @@ Examples:
```bash
just test-arch
just test-arch-ci
just test-fedora
just test-fedora-ci
```

## Wiki
Expand Down
Binary file modified installer/bin/dotsetup-linux-x86_64
Binary file not shown.
54 changes: 54 additions & 0 deletions installer/packages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,59 @@ pacman = [
]
aur = []

[fedora]
copr = [
"dejan/lazygit",
"varlad/zellij",
"lihaohong/yazi",
]
dnf = [
"git",
"git-lfs",
"fish",
"neovim",
"tree-sitter-cli",
"@development-tools",
"wl-clipboard",
"xclip",
"moby-engine",
"tailscale",
"ripgrep",
"fd-find",
"fzf",
"gdu",
"btop",
"python3",
"go",
"zoxide",
"lsd",
"file",
"ffmpeg-free",
"unzip",
"7zip",
"jq",
"poppler-utils",
"ImageMagick",
"thefuck",
"fastfetch",
"figlet",
"cowsay",
"fortune-mod",
"tealdeer",
"curl",
"jetbrains-mono-fonts",
"just",
"navi",
# Virtualization (QEMU)
"qemu-kvm",
"virt-manager",
"virt-viewer",
"dnsmasq",
"nmap-ncat",
"libvirt",
"edk2-ovmf",
]

[macos.brew]
formula = [
"git",
Expand Down Expand Up @@ -104,6 +157,7 @@ casks = [
packages = [
{ check = "topgrade", package = "topgrade" },
{ check = "tldr", package = "tealdeer" },
{ check = "btm", package = "bottom" },
]

[go]
Expand Down
59 changes: 59 additions & 0 deletions installer/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use indicatif::ProgressBar;
use inquire::{Confirm, Select};
use std::process::Command;

/// Prompts the user with a spinner and a confirmation message.
/// Returns `true` if the user confirms, `false` otherwise.
Expand Down Expand Up @@ -58,3 +59,61 @@ pub fn is_non_interactive() -> bool {
|| std::env::var("DRY_RUN").is_ok()
|| !std::io::IsTerminal::is_terminal(&std::io::stdin())
}

pub fn root_command(command: &str, args: Vec<String>) -> Vec<String> {
root_command_for_uid(command, args, current_uid())
}

fn current_uid() -> Option<u32> {
let output = Command::new("id").arg("-u").output().ok()?;
if !output.status.success() {
return None;
}

std::str::from_utf8(&output.stdout)
.ok()?
.trim()
.parse()
.ok()
}

fn root_command_for_uid(command: &str, args: Vec<String>, uid: Option<u32>) -> Vec<String> {
let mut command_parts = Vec::new();

if uid != Some(0) {
command_parts.push("sudo".into());
}

command_parts.push(command.into());
command_parts.extend(args);
command_parts
}

#[cfg(test)]
mod tests {
use super::root_command_for_uid;

#[test]
fn root_command_skips_sudo_for_root() {
assert_eq!(
root_command_for_uid("dnf", vec!["install".into()], Some(0)),
vec!["dnf".to_string(), "install".to_string()]
);
}

#[test]
fn root_command_uses_sudo_for_non_root() {
assert_eq!(
root_command_for_uid("dnf", vec!["install".into()], Some(1000)),
vec!["sudo".to_string(), "dnf".to_string(), "install".to_string()]
);
}

#[test]
fn root_command_uses_sudo_when_uid_is_unknown() {
assert_eq!(
root_command_for_uid("dnf", vec!["install".into()], None),
vec!["sudo".to_string(), "dnf".to_string(), "install".to_string()]
);
}
}
20 changes: 14 additions & 6 deletions installer/src/installers/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use strum_macros::{Display, EnumIter};
use which::which;

use crate::{
helpers::{is_non_interactive, select_with_spinner},
helpers::{is_non_interactive, root_command, select_with_spinner},
packages::Packages,
platform,
};
Expand Down Expand Up @@ -70,9 +70,17 @@ impl ArchInstaller {
AurHelper::None => return Ok(()),
};

let status = Command::new("sudo")
.args(["pacman", "-S", "--needed", "base-devel", "git"])
.status()?;
let command = root_command(
"pacman",
vec![
"-S".into(),
"--needed".into(),
"base-devel".into(),
"git".into(),
],
);
let status =
spinner.suspend(|| Command::new(&command[0]).args(&command[1..]).status())?;

if !status.success() {
anyhow::bail!("Failed to install AUR build dependencies");
Expand Down Expand Up @@ -108,14 +116,14 @@ impl ArchInstaller {

let pacman = &self.packages.arch.pacman;
if !pacman.is_empty() {
let mut args = vec!["pacman".into(), "-S".into(), "--needed".into()];
let mut args = vec!["-S".into(), "--needed".into()];

if is_non_interactive() {
args.push("--noconfirm".into());
}

args.append(&mut pacman.clone());
self.commands.push(args);
self.commands.push(root_command("pacman", args));
} else {
println!("{}", style("No Pacman packages to install").bold().yellow());
}
Expand Down
70 changes: 70 additions & 0 deletions installer/src/installers/fedora.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use console::style;

use crate::{
helpers::{is_non_interactive, root_command},
packages::Packages,
platform,
};

pub struct FedoraInstaller {
packages: Packages,
commands: Vec<Vec<String>>,
}

impl FedoraInstaller {
pub fn new(packages: Packages) -> Self {
Self {
packages,
commands: Vec::new(),
}
}

pub fn get_commands(&self) -> &[Vec<String>] {
&self.commands
}

pub fn install(&mut self) -> anyhow::Result<()> {
if !platform::is_fedora() {
return Ok(());
}

let copr = &self.packages.fedora.copr;
if !copr.is_empty() {
self.commands.push(root_command(
"dnf",
vec![
"install".into(),
"--assumeyes".into(),
"dnf-plugins-core".into(),
],
));

for repository in copr {
self.commands.push(root_command(
"dnf",
vec![
"copr".into(),
"enable".into(),
"--assumeyes".into(),
repository.clone(),
],
));
}
}

let dnf = &self.packages.fedora.dnf;
if dnf.is_empty() {
println!("{}", style("No DNF packages to install").bold().yellow());
return Ok(());
}

let mut args = vec!["install".into()];
if is_non_interactive() {
args.push("--assumeyes".into());
}
args.append(&mut dnf.clone());
self.commands.push(root_command("dnf", args));

Ok(())
}
}
Loading
Loading