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
42 changes: 29 additions & 13 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,36 +1,52 @@
# https://editorconfig.org

root = true

# Global defaults for all project files
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
indent_size = 2

# Ansible Playbooks, Tasks, Roles, Vars, and Handlers
[*.{yml,yaml}]
indent_style = space
indent_size = 2
max_line_length = 120

[*.json]
# Jinja2 Templates (used for config templating in Ansible)
[*.{j2,jinja2}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false

# Shell scripts (hooks, dynamic inventories, wrapper scripts)
[*.sh]
indent_size = 4
indent_style = space
indent_size = 2

[install-ansible]
# Python files (custom plugins, filter plugins, modules)
[*.py]
indent_style = space
indent_size = 4
max_line_length = 88

[install-requirements]
indent_size = 4
# Markdown files (project documentation)
[*.md]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false

[run-ansible]
indent_size = 4
# GitLab CI pipeline configuration
[.gitlab-ci.yml]
indent_style = space
indent_size = 2

[run-lint]
indent_size = 4
# Makefiles (if your project uses make for automation)
[Makefile]
indent_style = tab

[nvidia/*]
indent_size = 4
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ jobs:

- name: Install dependencies
run: |
./install-requirements
./install-ansible
./scripts/install-requirements
./scripts/install-ansible

- name: Run linters
run: ./run-lint --github-format
run: ./scripts/ansible-lint --github-format

- name: Publish ansible-lint results
uses: github/codeql-action/upload-sarif@v4
if: >-
always() &&
!(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)
with:
sarif_file: ansible-lint.sarif
sarif_file: scripts/ansible-lint.sarif
category: ansible-lint
19 changes: 11 additions & 8 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@
{
"label": "ansible: install",
"type": "shell",
"command": "${workspaceFolder}/install-requirements && ${workspaceFolder}/install-ansible",
"presentation": {
"reveal": "always",
"panel": "dedicated",
"focus": true },
"command": "${workspaceFolder}/scripts/install-requirements && ${workspaceFolder}/scripts/install-ansible",
"problemMatcher": []
},
{
"label": "ansible: lint",
"type": "shell",
"command": "${workspaceFolder}/run-lint",
"command": "${workspaceFolder}/scripts/ansible-lint",
"problemMatcher": [
{
"owner": "yamllint",
Expand Down Expand Up @@ -50,7 +46,7 @@
{
"label": "ansible: run all roles",
"type": "shell",
"command": "${workspaceFolder}/run-ansible",
"command": "${workspaceFolder}/scripts/deploy --host ${input:remoteHost}",
"presentation": {
"reveal": "always",
"panel": "dedicated",
Expand All @@ -61,7 +57,7 @@
{
"label": "ansible: run by tag",
"type": "shell",
"command": "${workspaceFolder}/run-ansible ${input:roleTag}",
"command": "${workspaceFolder}/scripts/deploy --host ${input:remoteHost} --tag ${input:roleTag}",
"presentation": {
"reveal": "always",
"panel": "dedicated",
Expand All @@ -78,12 +74,19 @@
"options": [
"common",
"shell",
"scripts",
"systemd",
"podman",
"vscode",
"git",
"yakuake"
]
},
{
"id": "remoteHost",
"type": "promptString",
"description": "Remote SSH host or SSH config alias",
"default": "localhost"
}
]
}
157 changes: 98 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,92 +1,131 @@
# linux-setup-scripts
# Linux Setup Scripts

Ansible-based workstation setup for Debian systems.
An Ansible-based workstation configuration for Debian and Ubuntu systems. It
installs a consistent developer environment locally or on a remote machine over
SSH.

## What it sets up
## Included Configuration

| Role | What it does |
|---|---|
| `common` | installs base packages (curl, wget, git, Node.js LTS, …) |
| `shell_config` | fish shell, starship prompt, direnv, fzf, lsd, pyenv, Hack Nerd Font, Konsole profile |
| `podman` | rootless podman, podman-compose, user socket + linger, containers.conf |
| `vscode` | VS Code apt repo, installation, settings and extensions |
| `git_config` | global git configuration (`.gitconfig`) |
| Tag | Role | Configuration |
| --- | --- | --- |
| `common` | `common` | Base development packages and Node.js 20 |
| `systemd` | `systemd` | `systemd-resolved` and NetworkManager DNS integration |
| `podman` | `podman` | Rootless Podman and Podman Compose configuration |
| `shell` | `shell_config` | Fish, Starship, direnv, fzf, pyenv, fonts, and Konsole profile |
| `scripts` | `system_scripts` | Helper scripts in `/usr/local/bin` |
| `vscode` | `vscode` | VS Code repository, application, settings, and extensions |
| `git` | `git_config` | Global Git configuration |
| `yakuake` | `yakuake` | Yakuake configuration and user systemd service |

## Requirements

- Debian / Ubuntu based system
- `sudo` access for package installation
- Debian or Ubuntu system
- `sudo` access on the target host
- Python 3 on the control machine and remote target
- For remote deployments: SSH access with a configured key or SSH agent

## First-time setup
## First-Time Setup

Install the Python tools, Ansible, linting tools, and required collections:

```bash
# 1. Install Python and pipx dependencies
./install-requirements
./scripts/install-requirements
./scripts/install-ansible
```

# 2. Install ansible-core and Ansible collections
./install-ansible
The installation scripts put Ansible in the user-local `pipx` environment. Open
a new shell after installation if its executable directory was not already on
your `PATH`.

# 3. Run the playbook
./run-ansible
```
## Deploy Locally

## Day-to-day usage
Apply all roles to the local workstation:

```bash
./run-ansible
./scripts/deploy
```

To run only a specific role use tags:
The command prompts for the local `sudo` password when privileged tasks need
it. `localhost`, `127.0.0.1`, and `::1` passed with `--host` also use the local
Ansible connection.

## Deploy Remotely

Pass an SSH host name, address, or alias to deploy to a remote workstation:

```bash
cd ansible
ansible-playbook -i inventories/production/hosts.yml playbooks/workstation.yml \
--ask-become-pass --tags shell
./scripts/deploy --host mars
./scripts/deploy --host 192.168.1.50
```

Available tags: `common`, `shell`, `podman`, `vscode`, `git`
The inventory contains a `remote` placeholder with `ansible_connection: ssh`.
At runtime, `--host` supplies its `ansible_host`, so remote machines do not need
to be committed to the inventory. The remote SSH user defaults to the current
control user's name; configure it in `ansible/inventories/production/group_vars/all.yml`
when the target uses a different account. Ansible displays the resolved
deployment target before it applies roles.

## Run Selected Roles

## Repository layout
Use `--tag` (or its short form `-t`) to apply only one role:

```bash
./scripts/deploy --tag shell
./scripts/deploy -H mars -t vscode
```
ansible/
ansible.cfg
requirements.yml
inventories/production/
hosts.yml
group_vars/all.yml
playbooks/
workstation.yml
roles/
common/
shell_config/
podman/
vscode/
git_config/
install-requirements # installs python3, pip, venv
install-ansible # installs ansible-core + collections
run-ansible # runs the workstation playbook
requirements.txt # pip packages (pipx, podman-compose)
requirements.yml # Ansible collections

For compatibility, a bare tag is also accepted:

```bash
./scripts/deploy podman
```

Run `./scripts/deploy --help` for all command-line options.

## VS Code Tasks

The workspace provides tasks for installing dependencies, linting, local
deployment, and remote deployment. Run them from **Tasks: Run Task**:

- `ansible: install`
- `ansible: lint`
- `ansible: run all roles`
- `ansible: run by tag`
- `ansible: run all roles remote`
- `ansible: run by tag remote`

The remote tasks prompt for an SSH host; their default is `localhost`.

## Linting

Run YAML and Ansible linting with:

```bash
./scripts/ansible-lint
```

## NVIDIA (manual scripts)
## Repository Layout

The `nvidia/` folder contains standalone shell scripts for setting up the NVIDIA driver stack on Debian 12. These are **not** part of the Ansible playbook and must be run manually.
```text
ansible/
inventories/production/hosts.yml Local and remote deployment entries
playbooks/workstation.yml Workstation playbook
roles/ Individual workstation roles
scripts/
deploy Local and remote deployment launcher
install-requirements Installs Python package requirements
install-ansible Installs Ansible and collections
ansible-lint Runs yamllint and ansible-lint
nvidia/ Standalone NVIDIA driver helper scripts
```

| Script | Purpose |
|---|---|
| `install_nvidia_keyring` | downloads and installs the CUDA apt keyring from NVIDIA |
| `install_nvidia_packages` | installs the full NVIDIA driver stack at a pinned version |
| `list_installed_nvidia_packages` | lists currently installed NVIDIA packages and their versions |
| `installed_nvidia_packages_version_580` | reference list of all packages installed for driver version 580 |
## NVIDIA Driver Scripts

### Usage
The scripts in `nvidia/` are standalone helpers for the NVIDIA driver stack on
Debian 12. They are intentionally not managed by the Ansible workstation
playbook.

```bash
# Step 1 — add the NVIDIA apt repository keyring
sudo ./nvidia/install_nvidia_keyring

# Step 2 — install the driver stack (edit VERSION in the script first if needed)
sudo ./nvidia/install_nvidia_packages
```
3 changes: 3 additions & 0 deletions ansible/inventories/production/hosts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ all:
ansible_host: localhost
ansible_connection: local
ansible_python_interpreter: /usr/bin/python3.13
remote:
ansible_connection: ssh
ansible_python_interpreter: /usr/bin/python3.13
10 changes: 9 additions & 1 deletion ansible/playbooks/workstation.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
---
- name: Configure workstation
hosts: workstations
hosts: all
become: false

pre_tasks:
- name: Show deployment target
ansible.builtin.debug:
msg: "Deploying to {{ inventory_hostname }} ({{ ansible_host | default(inventory_hostname) }})"
tags: [always]

roles:
- role: common
tags: [common]
Expand All @@ -12,6 +18,8 @@
tags: [podman]
- role: shell_config
tags: [shell]
- role: system_scripts
tags: [scripts]
- role: vscode
tags: [vscode]
- role: git_config
Expand Down
2 changes: 2 additions & 0 deletions ansible/roles/system_scripts/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
system_scripts_install:
- su-session
15 changes: 15 additions & 0 deletions ansible/roles/system_scripts/files/su-session
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

set -Eeuo pipefail

if [[ $# -ne 1 ]]; then
echo "Usage: $0 <username>" >&2
exit 1
fi

user="$1"

exec sudo systemd-run \
--system \
--scope \
su - "$user"
Loading
Loading