feat: added new roles to match daily driver desktop; full idempotency; several fixes and tweaks; re-added hosts in inventory

This commit is contained in:
NaeiKinDus 2023-11-19 00:00:00 +00:00
parent 555fde4351
commit 726b7668f9
Signed by: WoodSmellParticle
GPG key ID: 8E52ADFF7CA8AE56
65 changed files with 10012 additions and 377 deletions

15
.editorconfig Normal file
View file

@ -0,0 +1,15 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
max_line_length = 160
[*.{py,py3}]
indent_style = space
indent_size = 4
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
*.ttf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored
View file

@ -7,5 +7,4 @@ collections/ansible_collections/*/*/logs/*
!collections/ansible_collections/*/*/tests/config.yml !collections/ansible_collections/*/*/tests/config.yml
vault.yml vault.yml
inventory/inventory.yml inventory/inventory.yml
inventory/host_vars/*
!.gitkeep !.gitkeep

View file

@ -6,8 +6,11 @@
- Python3.9+, - Python3.9+,
- PIP, - PIP,
- Virtualenv - Virtualenv
***Dependencies installed using the `Installation` instructions***
- [Task](https://taskfile.dev/), - [Task](https://taskfile.dev/),
- Debian packages: - Debian packages:
- curl
- libcurl4-openssl-dev, - libcurl4-openssl-dev,
- libssl-dev, - libssl-dev,
- libcairo2, - libcairo2,
@ -22,9 +25,8 @@
## Installation ## Installation
```shell ```shell
# Debian amd64 # Debian amd64
TASK_VERSION=3.29.1;
sudo apt install -y \ sudo apt install -y \
curl \
libcurl4-openssl-dev \ libcurl4-openssl-dev \
libssl-dev \ libssl-dev \
libcairo2 \ libcairo2 \
@ -32,18 +34,46 @@ sudo apt install -y \
libffi-dev \ libffi-dev \
python3-virtualenv \ python3-virtualenv \
python3-dev; python3-dev;
wget https://github.com/go-task/task/releases/download/v"${TASK_VERSION}"/task_linux_amd64.deb; TASK_VERSION=$(curl -fsSL -XGET https://api.github.com/repos/go-task/task/releases/latest | grep tag_name | tr -d ' ",' | cut -d ':' -f 2)
curl -fsSLO https://github.com/go-task/task/releases/download/"${TASK_VERSION}"/task_linux_amd64.deb;
sudo dpkg -i task_linux_amd64.deb; sudo dpkg -i task_linux_amd64.deb;
task venv; rm task_linux_amd64.deb;
``` ```
## General Setup ## Setup
```shell
# Generate default ansible configuration
./scripts/generate_ansible_config.sh > "${HOME}"/.ansible.cfg
# Setup Python virtualenv
task venv:setup
# Prepare and edit your inventory as needed
cp inventory/inventory.yml.dist inventory/inventory.yml
# Prepare and edit the global vault as needed
cp inventory/vault.yml.dist inventory/vault.yml
```
## Usage
```shell
# encrypt vault
task venv -- ansible-vault encrypt configuration/host_vars/vault.yml
# decrypt vault if needed
task venv -- ansible-vault decrypt configuration/host_vars/vault.yml
# run ansible command with vault-encrypted data for one specific host
task venv -- ansible-playbook --ask-vault-password -l my_host playbooks/test.yml
# run a specific role, e.g. security, for a host
task venv -- ansible --ask-vault-password -m import_role --args 'name=nullified.infrastructure.security' my_host
```
### Generic collection / roles commands
```shell ```shell
mkdir -p collections/ansible_collections mkdir -p collections/ansible_collections
cd collections/ansible_collections cd collections/ansible_collections
ansible-galaxy collection init nullified.infrastructure task venv -- ansible-galaxy collection init nullified.infrastructure
cd nullified/infrastructure/roles cd nullified/infrastructure/roles
ansible-galaxy collection init tooling task venv -- ansible-galaxy collection init tooling
``` ```
--- ---
@ -59,8 +89,8 @@ ansible-galaxy collection init tooling
***handlers*** ***handlers***
invoked by a task through `notify`, executed only if caller triggered a state change; runs at the end of the play in the order invoked by a task through `notify`, executed only if caller triggered a state change; runs at the end of the play in the order
they are declared; they are declared;
-> force handlers to run:
```yaml ```yaml
# -> force handlers to run:
- name: some task - name: some task
meta: flush_handlers meta: flush_handlers
``` ```
@ -112,18 +142,8 @@ tasks:
### Notes / Todo ### Notes / Todo
***dir layout*** ***dir layout***
- collections: ansible root dir for all modules, playbooks and collections - collections: ansible root dir for all collections to reside in;
- configuration: <DEPRECATED> ansible root dir for inventory - images: docker images, mostly used for ansible-test / molecule;
- images: docker images, mostly used for ansible-test / molecule - inventory: all inventory related files are stored here;
- scripts: scripts used by go-task - playbooks: top level playbooks, describe the way the infrastructure is laid out;
- scripts: various scripts and helpers;
### Setup
```shell
cp configuration/group_vars/vault.yml.dist configuration/group_vars/vault.yml
# encrypt vault
ansible-vault encrypt configuration/group_vars/vault.yml
# decrypt vault
ansible-vault decrypt configuration/group_vars/vault.yml
# run ansible command with vault-encrypted data
ansible-playbook --ask-vault-password -i inventories/test playbooks/test.yml
```

7
TODO Normal file
View file

@ -0,0 +1,7 @@
- /etc/lvm/lvm.conf -> issue_discards = 1
- deploy hashicorp/vault to store encrypted files:
- user SSH keys
- user passwords
- secure files
- setup fstab with sshfs, noatime
- add smartmontools & conf

View file

@ -37,7 +37,7 @@ tasks:
preconditions: preconditions:
- test -d tests - test -d tests
cmds: cmds:
- '{{.PYTHON_WRAPPER}} ansible-test sanity --docker default' - '{{.PYTHON_WRAPPER}} ansible-test sanity --venv'
test:collections: test:collections:
desc: run molecule tests for all roles and collections. desc: run molecule tests for all roles and collections.

View file

@ -0,0 +1,18 @@
---
- hosts: main_desktop
tasks:
- name: include common role
ansible.builtin.include_role:
name: nullified.infrastructure.common
- name: include workstation role
ansible.builtin.include_role:
name: nullified.infrastructure.workstation
- name: include development role
ansible.builtin.include_role:
name: nullified.infrastructure.development
- name: include security role
ansible.builtin.include_role:
name: nullified.infrastructure.security
- name: include gaming role
ansible.builtin.include_role:
name: nullified.infrastructure.gaming

View file

@ -17,12 +17,6 @@ description: vaguely similar to a package manager, but for GitHub artifacts.
version_added: "2.15.0" version_added: "2.15.0"
options: options:
artifacts:
description: a list of artifacts to retrieve
type: list
required: true
elements: dict
suboptions:
asset_name: asset_name:
description: filename of the asset to retrieve, used only for release type; supports templating description: filename of the asset to retrieve, used only for release type; supports templating
type: str type: str
@ -41,6 +35,14 @@ options:
elements: str elements: str
required: false required: false
default: [] default: []
creates:
description: if provided and target file / directory exists, this step will **not** be run
type: str
required: false
github_token:
description: a GitHub app token if you have one; limits impact of rate-limiting errors
type: str
required: false
repository: repository:
description: repository to query, formatted like "<owner>/<repo>" description: repository to query, formatted like "<owner>/<repo>"
required: true required: true
@ -50,10 +52,6 @@ options:
required: false required: false
type: str type: str
default: latest default: latest
github_token:
description: a GitHub app token if you have one; limits impact of rate-limiting errors
type: str
required: false
notes: notes:
- "Strings that allow the use of templating variables support the following:" - "Strings that allow the use of templating variables support the following:"
@ -72,23 +70,18 @@ author:
EXAMPLES = r''' EXAMPLES = r'''
- name: Install dependencies from GitHub - name: Install dependencies from GitHub
become: yes become: true
tags: tags:
- molecule-idempotence-notest - molecule-idempotence-notest
github_artifact: nullified.infrastructure.github_artifact:
artifacts: asset_type: tag
- asset_type: tag
repository: smxi/inxi repository: smxi/inxi
cmds: cmds:
- tar -zxf {asset_dirname}/{asset_filename} - tar -zxf {asset_dirname}/{asset_filename}
- install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/bin - install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/bin
- install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1 - install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1
- apt-get install libdata-dump-perl - apt-get install libdata-dump-perl
- asset_name: dive_{version}_linux_amd64.deb creates: /usr/bin/inxi
asset_type: release
repository: wagoodman/dive
cmds:
- dpkg -i {asset_dirname}/{asset_filename}
''' '''
RETURN = r''' RETURN = r'''
@ -122,7 +115,7 @@ try:
from datetime import datetime from datetime import datetime
from difflib import SequenceMatcher from difflib import SequenceMatcher
from json import loads from json import loads
from os import environ, sep from os import environ, sep, path
from platform import system, machine from platform import system, machine
from typing import Any from typing import Any
LIB_IMPORTED = True LIB_IMPORTED = True
@ -260,7 +253,7 @@ def get_tagged_asset(artifact: dict[str, Any]) -> tuple[dict[str, str], dict[str
}, info }, info
def fetch_metadata(artifact: dict[str, str]) -> dict[str, str] | None: def fetch_metadata(artifact: dict[str, str | list[str]]) -> dict[str, str] | None:
""" retrieve metadata from the specified repository """ """ retrieve metadata from the specified repository """
if artifact["asset_type"] == "tag": if artifact["asset_type"] == "tag":
metadata, info = get_tagged_asset(artifact) metadata, info = get_tagged_asset(artifact)
@ -278,11 +271,6 @@ def main():
global ANSIBLE_MODULE global ANSIBLE_MODULE
module_args: dict[str, dict[str, Any]] = { module_args: dict[str, dict[str, Any]] = {
"artifacts": {
"type": "list",
"elements": "dict",
"required": True,
"options": {
"asset_name": { "asset_name": {
"type": "str", "type": "str",
"required": False, "required": False,
@ -299,6 +287,10 @@ def main():
"required": False, "required": False,
"default": [] "default": []
}, },
"creates": {
"type": "str",
"required": False
},
"repository": { "repository": {
"type": "str", "type": "str",
"required": True "required": True
@ -307,8 +299,6 @@ def main():
"type": "str", "type": "str",
"required": False, "required": False,
"default": "latest" "default": "latest"
}
}
}, },
"github_token": { "github_token": {
"type": "str", "type": "str",
@ -317,9 +307,13 @@ def main():
}, },
} }
result: dict[str, Any] = { result: dict[str, Any] = {
"artifacts": [],
"changed": False, "changed": False,
"msg": "" "commands": [],
"failed": False,
"filepath": "",
"msg": "",
"state": "",
"version": ""
} }
ANSIBLE_MODULE = AnsibleModule(argument_spec=module_args, supports_check_mode=True) ANSIBLE_MODULE = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
@ -328,6 +322,11 @@ def main():
if ANSIBLE_MODULE.params["github_token"]: if ANSIBLE_MODULE.params["github_token"]:
DEFAULT_HEADERS["Authorization"] = "Bearer {}".format(ANSIBLE_MODULE.params["github_token"]) DEFAULT_HEADERS["Authorization"] = "Bearer {}".format(ANSIBLE_MODULE.params["github_token"])
creates_file: str | None = ANSIBLE_MODULE.params.get("creates", None)
if creates_file and path.exists(creates_file):
result["state"] = "ignored"
ANSIBLE_MODULE.exit_json(**result)
if not LIB_IMPORTED: if not LIB_IMPORTED:
ANSIBLE_MODULE.fail_json(msg=missing_required_lib(IMPORT_LIB_NAME), exception=IMPORT_LIB_ERROR) # pylint: disable=used-before-assignment ANSIBLE_MODULE.fail_json(msg=missing_required_lib(IMPORT_LIB_NAME), exception=IMPORT_LIB_ERROR) # pylint: disable=used-before-assignment
@ -335,46 +334,43 @@ def main():
TEMPLATE_ASSET_NAME_VARS.update({key.lower(): value for key, value in freedesktop_os_release().items()}) TEMPLATE_ASSET_NAME_VARS.update({key.lower(): value for key, value in freedesktop_os_release().items()})
TEMPLATE_ASSET_NAME_VARS["system"] = system().lower() TEMPLATE_ASSET_NAME_VARS["system"] = system().lower()
TEMPLATE_ASSET_NAME_VARS["machine"] = machine().lower() TEMPLATE_ASSET_NAME_VARS["machine"] = machine().lower()
for artifact in ANSIBLE_MODULE.params["artifacts"]:
# fetch artifact metadata
artifact_result: dict[str, Any] = {
"asset_data": fetch_metadata(artifact),
"repository": artifact.get("repository"),
"version": artifact.get("version"),
"cmds": []
}
result["rate_limit_remaining"] = artifact_result["asset_data"].get("rate_limit_remaining", "unknown")
result["rate_limit_max"] = artifact_result["asset_data"].get("rate_limit_max", "unknown")
if "error" in artifact_result["asset_data"]: artifact: dict[str, str | list[str]] = {}
result["artifacts"].append(artifact_result) for param_name in ["asset_name", "asset_type", "cmds", "repository", "version"]:
result["msg"] = artifact_result["asset_data"].get("error") artifact[param_name] = ANSIBLE_MODULE.params[param_name]
asset_data: dict[str, str] = fetch_metadata(artifact)
result["rate_limit_remaining"] = asset_data.get("rate_limit_remaining", "unknown")
result["rate_limit_max"] = asset_data.get("rate_limit_max", "unknown")
result["version"] = asset_data.get("version", artifact.get("version"))
if "error" in asset_data:
result["state"] = "fetch failed"
result["msg"] = asset_data.get("error", "unknown error encountered")
result["failed"] = True result["failed"] = True
ANSIBLE_MODULE.fail_json(**result) ANSIBLE_MODULE.fail_json(**result)
# download artifact # download artifact
if ANSIBLE_MODULE.check_mode: if ANSIBLE_MODULE.check_mode:
artifact_result["download_dir"] = "unknown" result["filepath"] = "unknown"
else: else:
artifact_result["download_dir"] = fetch_file(ANSIBLE_MODULE, artifact_result["asset_data"].get("download_url", "unknown"), decompress=False) result["filepath"] = fetch_file(ANSIBLE_MODULE, asset_data.get("download_url", "unknown"), decompress=False)
TEMPLATE_ASSET_NAME_VARS["asset_name"] = artifact_result["asset_data"].get("asset_name", "unknown") TEMPLATE_ASSET_NAME_VARS["asset_name"] = asset_data.get("asset_name", "unknown")
TEMPLATE_ASSET_NAME_VARS["asset_version"] = artifact_result["asset_data"].get("version", "unknown") TEMPLATE_ASSET_NAME_VARS["asset_version"] = asset_data.get("version", "unknown")
parts = artifact_result["download_dir"].rsplit(sep, 1) parts = result["filepath"].rsplit(sep, 1)
TEMPLATE_ASSET_NAME_VARS["asset_dirname"] = parts[0] if len(parts) > 1 else "" TEMPLATE_ASSET_NAME_VARS["asset_dirname"] = parts[0] if len(parts) > 1 else ""
TEMPLATE_ASSET_NAME_VARS["asset_filename"] = parts[1] if len(parts) > 1 else parts[0] TEMPLATE_ASSET_NAME_VARS["asset_filename"] = parts[1] if len(parts) > 1 else parts[0]
# install artifact # install artifact
artifact_commands = [line.format(**TEMPLATE_ASSET_NAME_VARS) for line in artifact["cmds"]] artifact_commands = [line.format(**TEMPLATE_ASSET_NAME_VARS) for line in artifact["cmds"]]
if ANSIBLE_MODULE.check_mode: if ANSIBLE_MODULE.check_mode:
artifact_result["stdout"] = artifact_result["stderr"] = "" result["commands"] = artifact_commands
artifact_result["ret_code"] = None result["state"] = "should be installed" if len(artifact_commands) else "should be downloaded"
artifact_result["cmds"] = artifact_commands
artifact_result["state"] = "should be installed" if len(artifact_commands) else "should be downloaded"
else: else:
for command_line in artifact_commands: for command_line in artifact_commands:
cmd_rc, cmd_out, cmd_err = ANSIBLE_MODULE.run_command(command_line, use_unsafe_shell=True, cwd=ANSIBLE_MODULE.tmpdir) cmd_rc, cmd_out, cmd_err = ANSIBLE_MODULE.run_command(command_line, use_unsafe_shell=True, cwd=ANSIBLE_MODULE.tmpdir)
result["changed"] = True result["changed"] = True
artifact_result["cmds"].append({ result["commands"].append({
"command": command_line, "command": command_line,
"stdout": cmd_out, "stdout": cmd_out,
"stderr": cmd_err, "stderr": cmd_err,
@ -382,19 +378,14 @@ def main():
}) })
if cmd_rc: if cmd_rc:
artifact_result["state"] = "installation failed" result["state"] = "installation failed"
result["msg"] = cmd_err
result["failed"] = True result["failed"] = True
result["artifacts"].append(artifact_result)
ANSIBLE_MODULE.fail_json(**result) ANSIBLE_MODULE.fail_json(**result)
try: result["state"] = "installed" if len(artifact_commands) else "downloaded"
del artifact_result["command"], artifact_result["stdout"], artifact_result["stderr"], artifact_result["ret_code"]
except: # pylint: disable=bare-except # noqa: 722
pass
artifact_result["state"] = "installed" if len(artifact_commands) else "downloaded"
result["artifacts"].append(artifact_result) result["msg"] = "Successful"
result["msg"] = "OK"
ANSIBLE_MODULE.exit_json(**result) ANSIBLE_MODULE.exit_json(**result)

View file

@ -1,42 +1,16 @@
{ {
"ANSIBLE_MODULE_ARGS": { "ANSIBLE_MODULE_ARGS": {
"github_token": "", "github_token": "",
"artifacts": [
{
"asset_type": "tag", "asset_type": "tag",
"repository": "smxi/inxi", "repository": "smxi/inxi",
"version": "3.3.29-1", "version": "3.3.29-1",
"cmds": [ "cmds": [
"echo asset_name: {asset_name}", "echo \"asset_name: {asset_name}\nasset_dirname: {asset_dirname}\"",
"echo asset_dirname: {asset_dirname}", "echo \"asset_filename: {asset_filename}\nasset_version: {asset_version}\"",
"echo asset_filename: {asset_filename}", "echo \"system: {system}\nmachine: {machine}\nversion: {version}\"",
"echo asset_version: {asset_version}", "ls -lahv {asset_dirname}",
"echo system: {system}", "test -f {asset_dirname}/{asset_filename}"
"echo machine: {machine}", ],
"echo version: {version}" "creates": "/usr/local/bin/inxi2"
]
},
{
"asset_type": "tag",
"repository": "smxi/inxi",
"cmds": [
"tar -zxf {asset_dirname}/{asset_filename}",
"install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/bin",
"install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1",
"apt-get install libdata-dump-perl"
]
},
{
"asset_name": "dive_{version}_linux_amd64.deb",
"asset_type": "release",
"repository": "wagoodman/dive"
},
{
"asset_name": "dive_{version}_linux_amd64.deb",
"asset_type": "release",
"repository": "wagoodman/dive",
"version": "v0.10.0"
}
]
} }
} }

View file

@ -1,2 +1,10 @@
--- ---
custom_github_token: "" custom_github_token: ""
common_user_account: "{{ custom_base_user_account | lower }}"
common_gitconfig_enable: false
common_gitconfig_username: ""
common_gitconfig_email: ""
common_gitconfig_force_sign: false
common_gitconfig_signingkey: ""
common_apt_packages: []
common_install_fonts: false

View file

@ -1 +1,5 @@
--- ---
- name: '[system] reload sysctl configuration'
become: true
ansible.builtin.command:
cmd: sysctl --system

View file

@ -0,0 +1,41 @@
---
- name: '[home] get user account information'
ansible.builtin.getent:
database: passwd
key: "{{ common_user_account }}"
split: ":"
changed_when: false
when: getent_passwd is undefined or common_user_account not in getent_passwd
- name: '[home] create common directories'
become: true
become_user: "{{ common_user_account }}"
ansible.builtin.file:
path: "{{ getent_passwd[common_user_account][4] }}/{{ item }}"
state: directory
mode: '0750'
loop:
- .local/bin
- .local/share/fonts
- .config
- .ssh
- name: '[home] setup home files'
become: true
become_user: "{{ common_user_account }}"
block:
- name: '[home] git configuration'
ansible.builtin.template:
src: ../templates/home/.gitconfig.j2
dest: "{{ getent_passwd[common_user_account][4] }}/.gitconfig"
mode: '0640'
when: common_gitconfig_enable | bool
- name: '[home] basic files'
ansible.builtin.copy:
src: "../templates/home/{{ item.name }}"
dest: "{{ getent_passwd[common_user_account][4] }}/{{ item.name }}"
mode: "{{ item.mode | default('0640') }}"
loop:
- { name: ".lessfilter", mode: '0750' }
- { name: ".pythonrc" }

View file

@ -1,47 +1,68 @@
--- ---
- name: '[APT] install dependencies and tools' - name: '[apt] install dependencies and tools'
become: yes become: true
ansible.builtin.apt: ansible.builtin.apt:
update_cache: yes update_cache: true
force_apt_get: true force_apt_get: true
cache_valid_time: 3600 cache_valid_time: 3600
pkg: pkg:
- apt-transport-https
- bzip2 - bzip2
- catimg
- cron - cron
- dateutils
- emacs-nox - emacs-nox
- firmware-misc-nonfree
- firmware-linux-nonfree
- fwupd
- gir1.2-fwupd-2.0 # fwupd
- git - git
- iotop
- ioping
- jq - jq
- less - less
- libdata-dump-perl # inxi - libdata-dump-perl # inxi
- libxml-dumper-perl # inxi - libxml-dumper-perl # inxi
- lm-sensors
- ncdu - ncdu
- openssh-server - nvme-cli
- procps - procps
- python3-pygments
- rsync - rsync
- zsh - smartmontools
- tree
- xz-utils
- yq
state: present state: present
- name: '[GitHub] install tools' - name: '[GitHub] install tools'
become: yes become: true
tags:
- molecule-idempotence-notest
nullified.infrastructure.github_artifact: nullified.infrastructure.github_artifact:
github_token: '{{ custom_github_token }}' asset_name: "{{ item.asset_name | default('') }}"
artifacts: asset_type: "{{ item.asset_type }}"
cmds: "{{ item.cmds | default([]) }}"
creates: "{{ item.creates | default('') }}"
github_token: "{{ custom_github_token }}"
repository: "{{ item.repository }}"
version: "{{ item.version | default('') }}"
loop:
- repository: smxi/inxi - repository: smxi/inxi
asset_type: tag asset_type: tag
cmds: cmds:
- tar -zxf {asset_dirname}/{asset_filename} - tar -zxf {asset_dirname}/{asset_filename}
- install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/local/bin - install --group=root --mode=755 --owner=root smxi-inxi-*/inxi /usr/local/bin
- install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1 - install --group=root --mode=644 --owner=root smxi-inxi-*/inxi.1 /usr/share/man/man1
creates: /usr/local/bin
- repository: sharkdp/bat - repository: sharkdp/bat
asset_name: bat_{version}_amd64.deb asset_name: bat_{version}_amd64.deb
asset_type: release asset_type: release
creates: /usr/bin/bat
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- repository: aristocratos/btop - repository: aristocratos/btop
asset_name: btop-x86_64-linux-musl.tbz asset_name: btop-x86_64-linux-musl.tbz
asset_type: release asset_type: release
creates: /usr/bin/btop
cmds: cmds:
- tar -xjf {asset_dirname}/{asset_filename} - tar -xjf {asset_dirname}/{asset_filename}
- install --group=root --mode=755 --owner=root btop/bin/btop /usr/bin - install --group=root --mode=755 --owner=root btop/bin/btop /usr/bin
@ -50,11 +71,36 @@
- repository: eza-community/eza - repository: eza-community/eza
asset_name: eza_x86_64-unknown-linux-gnu.tar.gz asset_name: eza_x86_64-unknown-linux-gnu.tar.gz
asset_type: release asset_type: release
creates: /usr/bin/eza
cmds: cmds:
- tar -zxf {asset_dirname}/{asset_filename} - tar -zxf {asset_dirname}/{asset_filename}
- install --group=root --mode=755 --owner=root eza /usr/bin - install --group=root --mode=755 --owner=root eza /usr/bin
- repository: muesli/duf - repository: muesli/duf
asset_name: duf_{version}_linux_amd64.deb asset_name: duf_{version}_linux_amd64.deb
asset_type: release asset_type: release
creates: /usr/bin/duf
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- name: '[system] add sysctl tweaks'
become: true
ansible.builtin.template:
src: ../templates/system/sysctld.local.conf.j2
dest: /etc/sysctl.d/local.conf
mode: '0644'
when: custom_sysctl is defined
notify:
- 'common : [system] reload sysctl configuration'
- name: '[apt] install custom packages'
become: true
ansible.builtin.apt:
update_cache: true
force_apt_get: true
cache_valid_time: 3600
pkg:
"{{ common_apt_packages }}"
- include_tasks: home_setup.yml
- include_tasks: shell_customization.yml

View file

@ -0,0 +1,89 @@
---
- name: '[home] get user account information'
ansible.builtin.getent:
database: passwd
key: "{{ common_user_account }}"
split: ":"
changed_when: false
when: getent_passwd is undefined or common_user_account not in getent_passwd
- name: '[shell] install ZSH and dependencies'
become: true
ansible.builtin.apt:
update_cache: true
force_apt_get: true
cache_valid_time: 3600
pkg:
- git
- zsh
state: present
- name: '[shell] install custom fonts'
become: true
become_user: "{{ common_user_account }}"
block:
- name: '[fonts] add fonts tooling'
become_user: root
ansible.builtin.apt:
update_cache: true
force_apt_get: true
cache_valid_time: 3600
pkg:
- fontconfig
- name: '[fonts] adding fonts'
ansible.builtin.copy:
src: ../assets/fonts/
dest: "{{ getent_passwd[common_user_account][4] }}/.local/share/fonts"
mode: '0640'
- name: '[fonts] refresh fonts cache'
ansible.builtin.command:
cmd: fc-cache
changed_when: false
when: common_install_fonts | bool
- name: '[shell] install Oh-My-ZSH'
become: true
become_user: "{{ common_user_account }}"
block:
- name: '[omz] get install script'
ansible.builtin.get_url:
url: https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
dest: /tmp/zsh-install.sh
mode: '0750'
- name: '[omz] install OMZ'
ansible.builtin.command:
cmd: sh /tmp/zsh-install.sh --unattended
creates: "{{ getent_passwd[common_user_account][4] }}/.oh-my-zsh"
- name: '[shell] install powerlevel10k customization for OMZ'
become: true
become_user: "{{ common_user_account }}"
ansible.builtin.git:
repo: https://github.com/romkatv/powerlevel10k.git
dest: "{{ getent_passwd[common_user_account][4] }}/.oh-my-zsh/custom/themes/powerlevel10k"
depth: 1
- name: '[home] copy zsh files'
become: true
become_user: "{{ common_user_account }}"
ansible.builtin.copy:
src: "../templates/home/{{ item }}"
dest: "{{ getent_passwd[common_user_account][4] }}/{{ item }}"
mode: '0640'
loop:
- .p10k.zsh
- .zsh_aliases
- .zsh_completions
- .zsh_exports
- .zsh_functions
- .zshrc
- name: '[shell] update user shell to ZSH'
become: true
ansible.builtin.user:
name: "{{ common_user_account }}"
shell: "/usr/bin/zsh"
state: present

View file

@ -0,0 +1,107 @@
[user]
name = {{ common_gitconfig_username }}
email = {{ common_gitconfig_email }}
{% if common_gitconfig_force_sign and common_gitconfig_signingkey %}
signingkey = {{ common_gitconfig_signingkey }}
{% endif %}
[commit]
{% if common_gitconfig_force_sign %}
gpgsign = true
{% else %}
gpgsign = false
{% endif %}
[tag]
{% if common_gitconfig_force_sign %}
gpgsign = true
{% else %}
gpgsign = false
{% endif -%}
{% raw %}
[alias]
br = "branch"
ci = "commit"
cmp = "!f() { git log --graph --color --boundary --oneline HEAD...origin/$1; }; f"
co = "checkout"
cp = "cherry-pick"
cpo = "cherry-pick --strategy=recursive -Xours --allow-empty"
cpt = "cherry-pick --strategy=recursive -Xtheirs --allow-empty"
dm = "log --graph --color --boundary --oneline HEAD...origin/master"
dup = "!git log --graph --color --boundary --oneline HEAD...origin/$(git rev-parse --abbrev-ref HEAD)"
psuo = "!git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD)"
rf = "!git reflog --date=iso"
ru = "remote update"
rup = "!f() { git remote update && git pull --rebase; }; f"
pr = "pull --rebase"
st = "status"
subup = "!git submodule foreach git remote update"
undo = "!f() { git reset --soft HEAD~${1:-1}; }; f"
lg = lg1
lg1 = lg1-specific --all
lg2 = lg2-specific --all
lg3 = lg3-specific --all
lg1-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)'
lg2-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(auto)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
lg3-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset) %C(bold cyan)(committed: %cD)%C(reset) %C(auto)%d%C(reset)%n'' %C(white)%s%C(reset)%n'' %C(dim white)- %an <%ae> %C(reset) %C(dim white)(committer: %cn <%ce>)%C(reset)'
[core]
editor = emacs
autocrlf = input
pager = delta
[color]
status = auto
branch = auto
interactive = auto
diff = auto
ui = true
pager = true
[delta]
navigate = true
line-numbers = true
[push]
default = simple
[merge]
conflictstyle = diff3
[pull]
rebase = true
[diff]
colorMoved = default
[diff "pdf"]
textconv = pdfinfo
[diff "pdfdiff"]
command = diffpdf
[init]
defaultBranch = main
[interactive]
diffFilter = delta --color-only
[submodule]
recurse = true
[advice]
addEmbeddedRepo = false
addEmptyPathspec = false
addIgnoredFile = false
amWorkDir = false
checkoutAmbiguousRemoteBranchName = false
commitBeforeMerge = false
detachedHead = false
fetchShowForcedUpdates = false
ignoredHook = false
implicitIdentity = false
nestedTag = false
pushAlreadyExists = true
pushFetchFirst = true
pushNeedsForce = true
pushNonFFCurrent = true
pushNonFFMatching = true
pushRefNeedsUpdate = true
pushUnqualifiedRefname = true
pushUpdateRejected = true
resetQuiet = false
resolveConflict = true
rmHints = false
sequencerInUse = false
statusAheadBehind = false
statusHints = false
statusUoption = false
submoduleAlternateErrorStrategyDie = false
waitingForEditor = false
{% endraw %}

View file

@ -0,0 +1,68 @@
#!/bin/sh
# Best effort auto-pygmentization with transparent decompression
# (c) Reuben Thomas 2012
# This program is in the public domain.
# Strategy: first see if pygmentize can find a lexer; if not, ask file; if that finds nothing, fail
# Set the environment variable PYGMENTIZE_OPTS to configure pygments.
# This program can be used as a .lessfilter for the less pager to auto-color less's output
if [ `pygmentize -N $1` != "text" ]; then
pygmentize $PYGMENTIZE_OPTS "$1"
exit 0
fi
file_common_opts="--brief --dereference --uncompress"
unset lexer
case `file --mime-type $file_common_opts "$1"` in
application/xml|image/svg+xml) lexer=xml;;
text/html) lexer=html;;
text/troff) lexer=nroff;;
text/x-asm) lexer=nasm;;
text/x-awk) lexer=awk;;
text/x-c) lexer=c;;
text/x-c++) lexer=cpp;;
text/x-diff) lexer=diff;;
text/x-fortran) lexer=fortran;;
text/x-gawk) lexer=gawk;;
text/x-java) lexer=java;;
text/x-lisp) lexer=common-lisp;;
text/x-lua) lexer=lua;;
text/x-makefile) lexer=make;;
text/x-msdos-batch) lexer=bat;;
text/x-nawk) lexer=nawk;;
text/x-pascal) lexer=pascal;;
text/x-perl) lexer=perl;;
text/x-php) lexer=php;;
text/x-po) lexer=po;;
text/x-python) lexer=python;;
text/x-ruby) lexer=ruby;;
text/x-shellscript) lexer=sh;;
text/x-tcl) lexer=tcl;;
text/x-tex|text/x-texinfo) lexer=latex;; # FIXME: texinfo really needs its own lexer
# Types that file outputs which pygmentize didn't support as of file 5.11, pygments 1.5
# text/calendar
# text/PGP
# text/rtf
# text/texmacs
# text/x-bcpl
# text/x-info
# text/x-m4
# text/x-vcard
# text/x-xmcd
esac
encoding=`file --mime-encoding $file_common_opts "$1"`
if [ -n "$lexer" ]; then
# FIXME: Specify input encoding rather than output encoding https://bitbucket.org/birkenfeld/pygments-main/issue/800
# FIXME: Encoding argument ignored on stdin https://bitbucket.org/birkenfeld/pygments-main/issue/799
#zcat "$1" | pygmentize -O encoding=$encoding,outencoding=UTF-8 $PYGMENTIZE_OPTS -l $lexer
pygmentize -O encoding=$encoding,outencoding=UTF-8 $PYGMENTIZE_OPTS -l $lexer $1
exit 0
fi
exit 1

View file

@ -0,0 +1,7 @@
try:
import readline
except ImportError:
print("Module readline not available.")
else:
import rlcompleter
readline.parse_and_bind("tab: complete")

View file

@ -0,0 +1,34 @@
# Utilities
alias dig=ydig
alias e="emacs"
alias grep="egrep --color"
alias cpr="rsync -rlptgoDAXhP --info=all0,progress2"
alias rcp="cpr"
alias ll="eza -lahv --color"
alias l="eza -l -g --icons --all --all"
alias ls="eza"
alias xclip="xclip -sel clipboard"
alias git='GIT_COMMITTER_DATE="$(date +%Y-%m-%d) 00:00:00+0000" GIT_AUTHOR_DATE="$(date +%Y-%m-%d) 00:00:00+0000" git'
alias cat=bat
alias less=bat
# Dev
alias dcl="docker container ls -a --format='{{ .ID }}\t{{ .Names }}\t{{ index (split .Status \" \") 0 }}' | sort -k3r -k2 | column -t -N ID,Name,State"
alias composer80="composerX 8.0"
alias composer81="composerX 8.1"
alias composer82="composerX 8.2"
alias phpqa="phpqa82"
alias phpqa81='docker run --init -it --rm -v "$(pwd):/project" -v "$(pwd)/tmp-phpqa:/tmp" -w /project jakzal/phpqa:php8.1-alpine'
alias phpqa82='docker run --init -it --rm -v "$(pwd):/project" -v "$(pwd)/tmp-phpqa:/tmp" -w /project jakzal/phpqa:php8.2-alpine'
# DevOps / Admin
alias ssl_scan="docker run -ti --rm drwetter/testssl.sh"
alias tf="tofu"
alias terraform="tofu"
alias ks="kubeshell"
# Personal
alias awesome_test="Xephyr -screen 1440x1080 :5 & sleep 1 ; DISPLAY=:5 awesome"
alias clean_pa='pkill -U ${USER} pulseaudio; systemctl --user stop pulseaudio.socket && systemctl --user start pulseaudio.socket'
alias ssh_jump="ssh -qTNn"
alias x11_paste='sleep 2; xdotool type "$(xclip -o -selection clipboard)"'

View file

@ -0,0 +1,21 @@
# https://github.com/zsh-users/zsh-completions
fpath+=${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-completions/src
# shellcheck disable=SC1090
if command -v ansible &> /dev/null; then
source <(register-python-argcomplete ansible)
source <(register-python-argcomplete ansible-config)
source <(register-python-argcomplete ansible-console)
source <(register-python-argcomplete ansible-doc)
source <(register-python-argcomplete ansible-galaxy)
source <(register-python-argcomplete ansible-inventory)
source <(register-python-argcomplete ansible-playbook)
source <(register-python-argcomplete ansible-pull)
source <(register-python-argcomplete ansible-vault)
fi
autoload -U compinit && compinit
command -v boundary &> /dev/null && complete -o nospace -C /usr/bin/boundary boundary || true
command -v molecule &> /dev/null && source <(_MOLECULE_COMPLETE=zsh_source molecule) || true
command -v helm &> /dev/null && source <(helm completion zsh) || true

View file

@ -0,0 +1,29 @@
# Versions
export RUBY_VERSION="3.1"
# Settings
export DEFAULT_USER=$(id -un)
export EDITOR=emacs
export TERM=xterm-256color
export LESS='-R'
export LESSOPEN='|~/.lessfilter %s'
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export SSH_KEY_PATH="${HOME}/.ssh/id_ed25519"
export PAGER=less
export GPG_TTY="${TTY}"
# Dev
[ -d "$HOME/.nvm" ] && export NVM_DIR="$HOME/.nvm"
[ -d "$HOME/.nvm" ] && export NVM_SYMLINK_CURRENT=true
# shellcheck disable=SC1090
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" # This loads nvm
[ -d "${HOME}/.local/share/gems/ruby/${RUBY_VERSION}" ] && export GEM_DIR="${HOME}/.local/share/gems/ruby/${RUBY_VERSION}"
[ -d "${HOME}/.cargo/env" ] && source "${HOME}/.cargo/env"
# Path
export PATH="${PATH}:${HOME}/.local/bin"
[ -d "${HOME}/.nvm/current/bin" ] && export PATH="${PATH}:${HOME}/.nvm/current/bin"
[ -d "${GEM_DIR}/bin" ] && export PATH="${PATH}:${GEM_DIR}/bin"
[ -d "/usr/local/go/bin" ] && export PATH="${PATH}:/usr/local/go/bin"
[ -d "$HOME/.cargo/bin" ] && export PATH="${PATH}:$HOME/.cargo/bin"
[ -d "$HOME/.local/share/JetBrains/Toolbox/scripts" ] && export PATH="${PATH}:$HOME/.local/share/JetBrains/Toolbox/scripts"

View file

@ -0,0 +1,21 @@
function ydig() {
dig +yaml "${@}" | yq '.[].message.response_message_data|{"answer": .ANSWER_SECTION, status}'
}
function kubeshell() {
if [ $# -lt 3 ]; then
print "Invalid parameters: kubeshell NAMESPACE CONTAINER POD "
return 1
fi
local kubeBinary
local namespace="${1}"
shift
local container="${1}"
shift
local pod="${1}"
shift
kubeBinary=$(command -v kubectl)
"${kubeBinary}" exec -i -t -n "${namespace}" "${pod}" -c "${container}" "${@}" -- sh -c "clear; (zsh || bash || ash || sh)"
}

View file

@ -0,0 +1,54 @@
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
# Path to your oh-my-zsh installation.
export ZSH="${HOME}/.oh-my-zsh"
export TERM="xterm-256color"
ZSH_THEME="powerlevel10k/powerlevel10k"
CASE_SENSITIVE="true"
# Uncomment the following line to use hyphen-insensitive completion.
# Case-sensitive completion must be off. _ and - will be interchangeable.
# HYPHEN_INSENSITIVE="true"
# Uncomment the following line to disable bi-weekly auto-update checks.
# DISABLE_AUTO_UPDATE="true"
# Uncomment the following line to automatically update without prompting.
# DISABLE_UPDATE_PROMPT="true"
# Uncomment the following line to change how often to auto-update (in days).
# export UPDATE_ZSH_DAYS=13
# Uncomment the following line if pasting URLs and other text is messed up.
# DISABLE_MAGIC_FUNCTIONS=true
# Uncomment the following line to disable colors in ls.
# DISABLE_LS_COLORS="true"
# Uncomment the following line to disable auto-setting terminal title.
# DISABLE_AUTO_TITLE="true"
# Uncomment the following line to enable command auto-correction.
# ENABLE_CORRECTION="true"
# Uncomment the following line to display red dots whilst waiting for completion.
# COMPLETION_WAITING_DOTS="true"
HIST_STAMPS="yyyy.mm.dd"
plugins=(autopep8 aws branch colored-man-pages colorize composer docker docker-compose git kubectl kube-ps1 npm nvm pass pep8 pip redis-cli rsync rust terraform)
source $ZSH/oh-my-zsh.sh
[ -f ~/.zsh_exports ] && source ~/.zsh_exports
[ -f ~/.p10k.zsh ] && source ~/.p10k.zsh
[ -f ~/.zsh_functions ] && source ~/.zsh_functions
[ -f ~/.zsh_aliases ] && source ~/.zsh_aliases
[ -f ~/.env ] && source ~/.env
[ -f ~/.zsh_completions ] && source ~/.zsh_completions

View file

@ -0,0 +1,3 @@
{% for item in custom_sysctl.keys() -%}
{{ item }} = {{ custom_sysctl[item] }}
{% endfor %}

View file

@ -1,2 +1,4 @@
--- ---
custom_github_token: "" custom_github_token: ""
development_docker_remap_user: "{{ custom_base_user_account }}"
development_docker_remap_group: "{{ custom_base_user_account }}"

View file

@ -1,2 +1,7 @@
--- ---
# handlers file for development - name: '[docker] restart service'
become: true
ansible.builtin.systemd_service:
name: docker
enabled: true
state: restarted

View file

@ -1,60 +1,106 @@
--- ---
- name: '[APT] install dependencies and tools' - name: '[setup] gather facts if not already done'
setup:
gather_subset:
- distribution
- distribution_release
- name: '[home] get user account information'
ansible.builtin.getent:
database: passwd
key: "{{ custom_base_user_account }}"
split: ":"
changed_when: false
when: getent_passwd is undefined or custom_base_user_account not in getent_passwd
- name: '[apt] install dependencies and tools'
become: true become: true
ansible.builtin.apt: ansible.builtin.apt:
update_cache: true update_cache: true
force_apt_get: true force_apt_get: true
cache_valid_time: 3600 cache_valid_time: 3600
pkg: pkg:
- apt-transport-https # docker-ce
- autoconf - autoconf
- automake
- bc - bc
- build-essential
- ca-certificates # docker-ce - ca-certificates # docker-ce
- curl - curl
- g++ - g++
- gcc - gcc
- git - git
- gnupg # docker-ce - git-lfs
- gnupg2 # docker-ce
- jq - jq
- libasound2 # draw.io - libasound2 # draw.io
- libatspi2.0-0 # draw.io - libatspi2.0-0 # draw.io
- libcairo2
- libcairo2-dev
- libcurl4-openssl-dev
- libffi-dev
- libgtk-3-0 # draw.io - libgtk-3-0 # draw.io
- libnotify4 # draw.io - libnotify4 # draw.io
- libnss3 # draw.io - libnss3 # draw.io
- libsecret-1-0 # draw.io - libsecret-1-0 # draw.io
- libssl-dev
- libtool
- libxss1 # draw.io - libxss1 # draw.io
- libxtst6 # draw.io - libxtst6 # draw.io
- make - make
- mariadb-client
- pipx
- postgresql-client
- python3-dev
- python3-pip
- python3-virtualenv
- shellcheck - shellcheck
- sqlite3
- valgrind - valgrind
- xdg-utils # draw.io - xdg-utils # draw.io
state: present state: present
- name: '[GitHub] install tools' - name: '[github] install tools'
become: true become: true
tags:
- molecule-idempotence-notest
nullified.infrastructure.github_artifact: nullified.infrastructure.github_artifact:
github_token: '{{ custom_github_token }}' github_token: '{{ custom_github_token }}'
artifacts: asset_name: "{{ item.asset_name | default('') }}"
asset_type: "{{ item.asset_type }}"
cmds: "{{ item.cmds | default([]) }}"
creates: "{{ item.creates | default('') }}"
repository: "{{ item.repository }}"
version: "{{ item.version | default('') }}"
loop:
- asset_name: kind-linux-amd64
asset_type: release
repository: kubernetes-sigs/kind
creates: /usr/local/bin/kind
cmds:
- install --group=root --owner=root --mode=755 {asset_dirname}/{asset_filename} /usr/local/bin/kind
- rm {asset_dirname}/{asset_filename}
- asset_name: dive_{version}_linux_amd64.deb - asset_name: dive_{version}_linux_amd64.deb
asset_type: release asset_type: release
repository: wagoodman/dive repository: wagoodman/dive
creates: /usr/bin/dive
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- asset_name: kubeconform-linux-amd64.tar.gz - asset_name: kubeconform-linux-amd64.tar.gz
asset_type: release asset_type: release
repository: yannh/kubeconform repository: yannh/kubeconform
creates: /usr/local/bin/kubeconform
cmds: cmds:
- tar -zxf {asset_dirname}/{asset_filename} - tar -zxf {asset_dirname}/{asset_filename}
- install --group=root --mode=755 --owner=root kubeconform /usr/local/bin - install --group=root --mode=755 --owner=root kubeconform /usr/local/bin
- asset_name: git-delta_{version}_amd64.deb - asset_name: git-delta_{version}_amd64.deb
asset_type: release asset_type: release
repository: dandavison/delta repository: dandavison/delta
creates: /usr/bin/delta
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- asset_name: docker-compose-linux-x86_64 - asset_name: docker-compose-linux-x86_64
asset_type: release asset_type: release
repository: docker/compose repository: docker/compose
creates: /usr/local/bin/docker-compose
cmds: cmds:
- install --group=root --mode=755 --owner=root {asset_dirname}/{asset_filename} /usr/local/bin/docker-compose - install --group=root --mode=755 --owner=root {asset_dirname}/{asset_filename} /usr/local/bin/docker-compose
- test -d /usr/local/lib/docker/cli-plugins && (rm /usr/local/lib/docker/cli-plugins/docker-compose; ln -s /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins) || true - test -d /usr/local/lib/docker/cli-plugins && (rm /usr/local/lib/docker/cli-plugins/docker-compose; ln -s /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins) || true
@ -64,6 +110,7 @@
- asset_name: buildx-{version}.linux-amd64 - asset_name: buildx-{version}.linux-amd64
asset_type: release asset_type: release
repository: docker/buildx repository: docker/buildx
creates: /usr/local/bin/docker-buildx
cmds: cmds:
- install --group=root --mode=755 --owner=root {asset_dirname}/{asset_filename} /usr/local/bin/docker-buildx - install --group=root --mode=755 --owner=root {asset_dirname}/{asset_filename} /usr/local/bin/docker-buildx
- test -d /usr/local/lib/docker/cli-plugins && (rm /usr/local/lib/docker/cli-plugins/docker-compose; ln -s /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins) || true - test -d /usr/local/lib/docker/cli-plugins && (rm /usr/local/lib/docker/cli-plugins/docker-compose; ln -s /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins) || true
@ -73,68 +120,111 @@
- asset_name: drawio-amd64-{version}.deb - asset_name: drawio-amd64-{version}.deb
asset_type: release asset_type: release
repository: jgraph/drawio-desktop repository: jgraph/drawio-desktop
creates: /usr/bin/drawio
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- asset_name: OpenLens-{version}.amd64.deb - asset_name: OpenLens-{version}.amd64.deb
asset_type: release asset_type: release
repository: MuhammedKalkan/OpenLens repository: MuhammedKalkan/OpenLens
creates: /usr/bin/open-lens
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- asset_name: stern_{version}_linux_amd64.tar.gz - asset_name: stern_{version}_linux_amd64.tar.gz
asset_type: release asset_type: release
repository: stern/stern repository: stern/stern
creates: /usr/local/bin/stern
cmds: cmds:
- tar -zxf {asset_dirname}/{asset_filename} - tar -zxf {asset_dirname}/{asset_filename}
- install --group=root --mode=755 --owner=root stern /usr/local/bin - install --group=root --mode=755 --owner=root stern /usr/local/bin
- asset_name: tofu_{version}_amd64.deb - asset_name: tofu_{version}_amd64.deb
asset_type: release asset_type: release
repository: opentofu/opentofu repository: opentofu/opentofu
creates: /usr/bin/tofu
cmds: cmds:
- dpkg -i {asset_dirname}/{asset_filename} - dpkg -i {asset_dirname}/{asset_filename}
- name: '[Custom] install latest kubectl' - name: '[custom] install latest kubectl'
become: yes become: true
tags: block:
- molecule-idempotence-notest - name: '[kubectl] find latest version available'
ansible.builtin.shell: | ansible.builtin.command:
kubeVersion=$(curl -sSL -f https://storage.googleapis.com/kubernetes-release/release/stable.txt 2> /dev/null) cmd: curl -L -s https://dl.k8s.io/release/stable.txt
kubeVersion=${kubeVersion:-v1.28.2} register: latest_kube_version
curl --silent --compressed -L -XGET https://storage.googleapis.com/kubernetes-release/release/${kubeVersion}/bin/linux/amd64/kubectl -o kubectl changed_when: false
install --group=root --mode=755 --owner=root kubectl /usr/local/bin && rm kubectl
- name: '[Custom] install latest Helm' - name: '[kubectl] fetch binary'
become: yes ansible.builtin.get_url:
tags: url: "https://dl.k8s.io/release/{{ latest_kube_version.stdout }}/bin/linux/amd64/kubectl"
- molecule-idempotence-notest dest: /usr/local/bin/kubectl
ansible.builtin.shell: | owner: root
helmVersion=$(curl -sSL https://api.github.com/repos/helm/helm/releases/latest | jq -r '.tag_name') group: root
helmVersion=${helmVersion:-v3.13.0} mode: '0755'
curl --silent --compressed -L -XGET https://get.helm.sh/helm-${helmVersion}-linux-amd64.tar.gz -o helm.tar.gz
tar -zxf helm.tar.gz - name: '[custom] install latest Helm'
install --group=root --mode=755 --owner=root linux-amd64/helm /usr/local/bin && rm -rf linux-amd64 helm.tar.gz become: true
block:
- name: '[helm] find latest version available'
ansible.builtin.shell: |-
curl -sSL https://api.github.com/repos/helm/helm/releases/latest | jq -r '.tag_name'
register: latest_helm_version
changed_when: false
- name: '[helm] find if binary is already installed'
ansible.builtin.file:
path: /usr/local/bin/helm
register: helm_stat
changed_when: false
failed_when: false
- name: '[helm] setup temp directory'
ansible.builtin.file:
path: /tmp/helm-unarchive
state: directory
owner: root
group: root
mode: '0700'
when: helm_stat.state is match("absent")
- name: '[helm] fetch archive'
ansible.builtin.unarchive:
remote_src: true
src: "https://get.helm.sh/helm-{{ latest_helm_version.stdout }}-linux-amd64.tar.gz"
dest: /tmp/helm-unarchive
when: helm_stat.state is match("absent")
- name: '[helm] install binary'
ansible.builtin.copy:
remote_src: true
src: /tmp/helm-unarchive/linux-amd64/helm
dest: /usr/local/bin/helm
owner: root
group: root
mode: '0755'
when: helm_stat.state is match("absent")
- name: '[helm] cleanup'
ansible.builtin.file:
path: /tmp/helm-unarchive
state: absent
when: helm_stat.state is match("absent")
- name: '[custom] install Docker CE repository' - name: '[custom] install Docker CE repository'
become: true
block: block:
- name: '[apt key] retrieve GPG key' - name: '[apt key] add docker key'
tags: ansible.builtin.get_url:
- molecule-idempotence-notest url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg"
ansible.builtin.shell: |- dest: /etc/apt/trusted.gpg.d/docker.asc
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg mode: '0644'
chmod a+r /etc/apt/keyrings/docker.gpg
- name: '[apt key] add source' - name: '[apt key] add source'
ansible.builtin.apt_repository: ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable" repo: "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/docker.asc] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
state: present state: present
filename: docker
- name: '[Apt Key] refresh repository'
ansible.builtin.apt:
update_cache: true update_cache: true
force_apt_get: true
cache_valid_time: 0
- name: '[Apt] install Docker CE' - name: '[apt] install Docker CE'
become: yes
ansible.builtin.apt: ansible.builtin.apt:
update_cache: true update_cache: true
force_apt_get: true force_apt_get: true
@ -144,3 +234,96 @@
- docker-ce-cli - docker-ce-cli
- containerd.io - containerd.io
state: present state: present
- name: '[docker] update daemon configuration'
ansible.builtin.template:
src: ../templates/docker-ce/daemon.json.j2
dest: /etc/docker/daemon.json
mode: '0644'
notify:
- 'development : [docker] restart service'
- name: '[docker] add default user to docker group'
ansible.builtin.user:
name: "{{ development_docker_remap_user }}"
append: true
groups: docker
state: present
notify:
- 'development : [docker] restart service'
- name: '[python] install tools'
become: true
become_user: "{{ custom_base_user_account }}"
ansible.builtin.command:
cmd: "pipx install {{ item.cmd }}"
creates: "{{ getent_passwd[custom_base_user_account][4] }}/.local/bin/{{ item.creates }}"
loop:
- { "cmd": "black", "creates": "black" }
- { "cmd": "flake8", "creates": "flake8" }
- name: '[python] install pipx packages dependencies'
become: true
become_user: "{{ custom_base_user_account }}"
ansible.builtin.command:
cmd: "pipx inject {{ item.venv }} {{ item.extension }}"
creates:
"{{ getent_passwd[custom_base_user_account][4] }}/.local/pipx/venvs/{{ item.venv }}/lib/python3.11/site-packages/{{ item.creates }}"
loop:
- venv: "flake8"
extension: "flake8-annotations-complexity"
creates: "flake8_annotations_complexity"
- venv: "flake8"
extension: "flake8-bandit"
creates: "flake8_bandit.py"
- venv: "flake8"
extension: "flake8-breakpoint"
creates: "flake8_breakpoint"
- venv: "flake8"
extension: "flake8-bugbear"
creates: "bugbear.py"
- venv: "flake8"
extension: "flake8-builtins"
creates: "flake8_builtins.py"
- venv: "flake8"
extension: "flake8-comprehensions"
creates: "flake8_comprehensions"
- venv: "flake8"
extension: "flake8-docstrings"
creates: "flake8_docstrings.py"
- venv: "flake8"
extension: "flake8-eradicate"
creates: "flake8_eradicate.py"
- venv: "flake8"
extension: "flake8-expression-complexity"
creates: "flake8_expression_complexity"
- venv: "flake8"
extension: "flake8-if-expr"
creates: "flake8_if_expr"
- venv: "flake8"
extension: "flake8-isort"
creates: "flake8_isort.py"
- venv: "flake8"
extension: "flake8-logging-format"
creates: "logging_format"
- venv: "flake8"
extension: "flake8-print"
creates: "flake8_print.py"
- venv: "flake8"
extension: "flake8-pytest"
creates: "flake8_pytest.py"
- venv: "flake8"
extension: "flake8-pytest-style"
creates: "flake8_pytest_style"
- venv: "flake8"
extension: "flake8-requirements"
creates: "flake8_requirements"
- venv: "flake8"
extension: "flake8-return"
creates: "flake8_return"
- venv: "flake8"
extension: "flake8-rst-docstrings"
creates: "flake8_rst_docstrings.py"
- venv: "flake8"
extension: "pep8-naming"
creates: "pep8ext_naming.py"

View file

@ -0,0 +1,4 @@
{
"userns-remap": "{{ development_docker_remap_user }}:{{ development_docker_remap_group }}",
"cgroup-parent": "{{ development_docker_systemd_slice }}"
}

View file

@ -1,2 +1,2 @@
--- ---
# vars file for development development_docker_systemd_slice: docker.slice

View file

@ -0,0 +1,2 @@
---
custom_github_token: ""

View file

@ -0,0 +1,21 @@
---
galaxy_info:
author: Florian L.
namespace: nullified
description: Install games and gaming related software
# issue_tracker_url: http://example.com/issue/tracker
license: MIT
min_ansible_version: 2.15
# https://galaxy.ansible.com/api/v1/platforms/
platforms:
- name: Debian
versions:
- bookworm
galaxy_tags:
- github
- steam
- games
dependencies: []

View file

@ -0,0 +1,56 @@
---
- name: '[games] install Steam'
become: true
block:
- name: '[system] get existing architectures'
ansible.builtin.command:
cmd: dpkg --print-foreign-architectures
register: dpkg_archs
changed_when: false
when: dpkg_archs is not defined
- name: '[steam] enable i386 architecture'
command:
cmd: dpkg --add-architecture i386
when: dpkg_archs.stdout is not regex("(^|\b)i386($|\b)", multiline = true)
- name: '[apt key] add Steam GPG key'
ansible.builtin.get_url:
url: "https://repo.steampowered.com/steam/archive/stable/steam.gpg"
dest: /usr/share/keyrings/steam.gpg
mode: '0644'
- name: '[apt key] add source'
apt_repository:
repo: "{{ item }} [arch=amd64,i386 signed-by=/usr/share/keyrings/steam.gpg] https://repo.steampowered.com/steam/ stable steam"
state: present
filename: steam
update_cache: true
loop:
- deb
- deb-src
- name: '[steam] install dependencies'
ansible.builtin.apt:
update_cache: true
force_apt_get: true
cache_valid_time: 3600
pkg:
- libgl1-mesa-dri:amd64
- libgl1-mesa-dri:i386
- libgl1-mesa-glx:amd64
- libgl1-mesa-glx:i386
- steam-launcher
- name: '[games] install Heroic Games Launcher'
become: true
block:
- name: '[hgl] fetch assets from github'
nullified.infrastructure.github_artifact:
github_token: '{{ custom_github_token }}'
asset_name: heroic_{version}_amd64.deb
asset_type: release
repository: Heroic-Games-Launcher/HeroicGamesLauncher
creates: /usr/bin/heroic
cmds:
- dpkg -i {asset_dirname}/{asset_filename}

View file

@ -1,27 +1,32 @@
--- ---
- name: '[ssh] restart service' - name: '[ssh] restart service'
become: true
ansible.builtin.systemd_service: ansible.builtin.systemd_service:
name: sshd.service name: sshd.service
enabled: true enabled: true
state: restarted state: restarted
- name: '[clamav] daemon reload' - name: '[clamav] daemon reload'
become: true
ansible.builtin.systemd_service: ansible.builtin.systemd_service:
daemon_reload: true daemon_reload: true
- name: '[freshclam] restart service' - name: '[freshclam] restart service'
become: true
ansible.builtin.systemd_service: ansible.builtin.systemd_service:
name: sshd.service name: sshd.service
enabled: true enabled: true
state: restarted state: restarted
- name: '[clamd] wait for signatures' - name: '[clamd] wait for signatures'
become: true
ansible.builtin.wait_for: ansible.builtin.wait_for:
path: /var/lib/clamav/bytecode.cvd path: /var/lib/clamav/bytecode.cvd
timeout: 600 timeout: 600
state: present state: present
- name: '[clamd] restart service' - name: '[clamd] restart service'
become: true
ansible.builtin.systemd_service: ansible.builtin.systemd_service:
name: sshd.service name: sshd.service
enabled: true enabled: true

View file

@ -1,29 +1,36 @@
--- ---
- name: '[setup] gather facts is not already done' - name: '[setup] gather facts if not already done'
setup: setup:
gather_subset: gather_subset:
- distribution - distribution
- name: '[ssh] hardening sshd' - name: '[ssh] hardening sshd'
become: yes become: true
block: block:
- name: '[ssh] setup sshd_config' - name: '[ssh] setup sshd_config'
ansible.builtin.template: ansible.builtin.template:
src: ../templates/openssh-server/sshd_config.j2 src: ../templates/openssh-server/sshd_config.j2
dest: /etc/ssh/sshd_config dest: /etc/ssh/sshd_config
mode: 644 mode: '0644'
notify:
- '[ssh] restart service'
- name: '[ssh] setup sshd_config.d' - name: '[ssh] setup sshd_config.d'
ansible.builtin.template: ansible.builtin.template:
src: ../templates/openssh-server/sshd_config.d/encryption.conf.j2 src: ../templates/openssh-server/sshd_config.d/encryption.conf.j2
dest: /etc/ssh/sshd_config.d/encryption.conf dest: /etc/ssh/sshd_config.d/encryption.conf
mode: 644 mode: '0644'
- name: '[ssh] remove low security keys'
ansible.builtin.file:
path: "/etc/ssh/{{ item }}"
state: absent
loop:
- ssh_host_ecdsa_key
- ssh_host_ecdsa_key.pub
- ssh_host_rsa_key
- ssh_host_rsa_key.pub
notify: notify:
- 'security : [ssh] restart service' - 'security : [ssh] restart service'
- name: '[utils] install security and audit tools' - name: '[utils] install security and audit tools'
become: yes become: true
ansible.builtin.apt: ansible.builtin.apt:
update_cache: true update_cache: true
force_apt_get: true force_apt_get: true
@ -35,7 +42,7 @@
state: present state: present
- name: '[system] configure rkhunter' - name: '[system] configure rkhunter'
become: yes become: true
block: block:
- name: '[rkhunter] create include dir' - name: '[rkhunter] create include dir'
ansible.builtin.file: ansible.builtin.file:
@ -57,7 +64,7 @@
state: present state: present
- name: '[system] clamav' - name: '[system] clamav'
become: yes become: true
block: block:
- name: '[clamav] retrieve and install clamav package' - name: '[clamav] retrieve and install clamav package'
ansible.builtin.apt: ansible.builtin.apt:
@ -80,35 +87,16 @@
system: true system: true
state: present state: present
- name: '[clamav] setup directories' - name: '[clamav] setup directories'
block:
- name: '[clamav] ensure /etc/clamav dir exists'
ansible.builtin.file: ansible.builtin.file:
path: /etc/clamav path: "{{ item }}"
state: directory
owner: clamav
group: clamav
mode: '0750'
- name: '[clamav] ensure /var/lib/clamav dir exists'
ansible.builtin.file:
path: /var/lib/clamav
state: directory
owner: clamav
group: clamav
mode: '0750'
- name: '[clamav] ensure /var/lib/clamav/quarantine dir exists'
ansible.builtin.file:
path: /var/lib/clamav/quarantine
state: directory
owner: clamav
group: clamav
mode: '0750'
- name: '[clamav] ensure /var/log/clamav dir exists'
ansible.builtin.file:
path: /var/log/clamav
state: directory state: directory
owner: clamav owner: clamav
group: clamav group: clamav
mode: '0750' mode: '0750'
loop:
- /etc/clamav
- /var/lib/clamav/quarantine
- /var/log/clamav
- name: '[clamav] copy clamd.conf' - name: '[clamav] copy clamd.conf'
ansible.builtin.template: ansible.builtin.template:
src: '../templates/clamav/clamd.conf.j2' src: '../templates/clamav/clamd.conf.j2'
@ -123,15 +111,11 @@
owner: clamav owner: clamav
group: clamav group: clamav
mode: '0640' mode: '0640'
- name: '[clamav] setup freshclam service'
block:
- name: '[clamav] copy freshclam service file' - name: '[clamav] copy freshclam service file'
ansible.builtin.template: ansible.builtin.template:
src: '../templates/clamav/clamav-freshclam.service.j2' src: '../templates/clamav/clamav-freshclam.service.j2'
dest: /usr/lib/systemd/system/clamav-freshclam.service dest: /usr/lib/systemd/system/clamav-freshclam.service
mode: '0644' mode: '0644'
- name: '[clamav] setup clamd service'
block:
- name: '[clamav] copy clamd service file' - name: '[clamav] copy clamd service file'
ansible.builtin.template: ansible.builtin.template:
src: '../templates/clamav/clamav-clamd.service.j2' src: '../templates/clamav/clamav-clamd.service.j2'
@ -152,7 +136,7 @@
- 'security : [clamd] restart service' - 'security : [clamd] restart service'
- name: '[system] hardening system' - name: '[system] hardening system'
become: yes become: true
block: block:
- name: '[system] login.defs' - name: '[system] login.defs'
ansible.builtin.template: ansible.builtin.template:

View file

@ -1,2 +1,4 @@
--- ---
custom_base_user_account: "root" workstation_user_account: "{{ custom_base_user_account }}"
custom_github_token: ""
custom_sysctl: {}

View file

@ -1,2 +1 @@
--- ---
# handlers file for development

View file

@ -1,25 +1,181 @@
--- ---
- name: '[APT] install dependencies and tools' - name: '[home] get user account information'
become: yes ansible.builtin.getent:
database: passwd
key: "{{ workstation_user_account }}"
split: ":"
changed_when: false
when: getent_passwd is undefined or workstation_user_account not in getent_passwd
- name: '[apt] install dependencies and tools'
become: true
ansible.builtin.apt: ansible.builtin.apt:
update_cache: true update_cache: true
force_apt_get: true force_apt_get: true
cache_valid_time: 3600 cache_valid_time: 3600
pkg: pkg:
- curl - curl
- dbus-x11 # terminator
- diffpdf
- feh
- ffmpeg
- flatpak - flatpak
- gnupg - gettext # terminator
- gir1.2-keybinder-3.0 # terminator
- gir1.2-vte-2.91 # terminator
- gnupg2
- gvfs-backends
- intltool # terminator
- mpc
- mpd
- ncmpcpp
- numlockx
- openssh-server
- pass
- poppler-utils
- pwgen - pwgen
- python3-configobj # terminator
- python3-gi # terminator
- python3-gi-cairo # terminator
- python3-pip
- python3-psutil # terminator
- ruby
- scrot
- smbclient
- socat
- sshfs
- suckless-tools
- sudo - sudo
- unclutter-xfixes
- vlc
- wireshark
- xdotool
- xsel
- xserver-xephyr
state: present state: present
- name: '[Setup] setup Flatpak' - name: '[setup] add user to sudo group'
become: yes become: true
become_user: "{{ custom_base_user_account }}" ansible.builtin.user:
become_method: su name: "{{ workstation_user_account }}"
tags: groups:
- molecule-idempotence-notest - sudo
ansible.builtin.shell: | append: true
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install --noninteractive flathub com.discordapp.Discord - name: '[setup] setup Flatpak'
flatpak install --noninteractive flathub md.obsidian.Obsidian become: true
become_user: "{{ workstation_user_account }}"
block:
- name: '[flatpak] add flatpak repos'
command:
cmd: flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
changed_when: false
- name: '[flatpak] install flatpak apps'
command:
cmd: "flatpak install --noninteractive {{ item.repo }} {{ item.app }}"
creates: "{{ getent_passwd[workstation_user_account][4] }}/.var/app/{{ item.app }}"
loop:
- repo: flathub
app: com.discordapp.Discord
- repo: flathub
app: md.obsidian.Obsidian
- name: '[github] install tools'
become: true
nullified.infrastructure.github_artifact:
github_token: '{{ custom_github_token }}'
asset_name: "{{ item.asset_name | default('') }}"
asset_type: "{{ item.asset_type }}"
cmds: "{{ item.cmds | default([]) }}"
creates: "{{ item.creates | default('') }}"
repository: "{{ item.repository }}"
version: "{{ item.version | default('') }}"
loop:
- asset_name: terminator-{version}.tar.gz
asset_type: release
repository: gnome-terminator/terminator
creates: /usr/local/bin/terminator
cmds:
- tar -zxf {asset_dirname}/{asset_filename}
- cd $(find . -maxdepth 1 -name terminator\* -type d); python3 setup.py build; python3 setup.py install --single-version-externally-managed --record=install-files.txt
- rm -rf {asset_dirname}/{asset_filename}
- name: '[ruby] install tools'
become: true
ansible.builtin.command:
cmd: "gem install {{ item.app }}"
creates: "{{ item.creates }}"
loop:
- app: mdless
creates: /usr/local/bin/mdless
- name: '[system] setup various directories'
become: true
ansible.builtin.file:
path: "{{ item.path }}"
mode: "{{ item.mode | default('0750') }}"
owner: "{{ item.owner | default(workstation_user_account) }}"
group: "{{ item.group | default(workstation_user_account) }}"
state: directory
loop:
- { path: '/opt/git/foss' }
- { path: '/opt/git/perso' }
- { path: '/opt/git/work' }
- { path: '/srv/storage' }
- name: '[emacs] fetch emacs configuration files'
become: true
become_user: "{{ workstation_user_account }}"
ansible.builtin.git:
repo: "ssh://git@gitlab.0x2a.ninja:4222/naeikindus/emacsd.git"
dest: "{{ getent_passwd[workstation_user_account][4] }}/.emacs.d"
force: false
- name: '[config] set tools configuration'
become: true
become_user: "{{ workstation_user_account }}"
ansible.builtin.copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode | default('0640') }}"
loop:
- { src: "../templates/.config/terminator", dest: "{{ getent_passwd[workstation_user_account][4] }}/.config" }
- { src: "../templates/.config/vlc", dest: "{{ getent_passwd[workstation_user_account][4] }}/.config" }
- name: '[yubico] install authenticator'
become: true
block:
- name: '[yubico] fetch latest authenticator source'
ansible.builtin.get_url:
url: https://developers.yubico.com/yubioath-flutter/Releases/yubico-authenticator-latest-linux.tar.gz
dest: /tmp/yubico-authenticator-latest-linux.tar.gz
mode: '0640'
- name: '[yubico] prepare target directory'
file:
path: /opt/yubico-authenticator
owner: "{{ workstation_user_account }}"
group: "{{ workstation_user_account }}"
mode: '0750'
state: directory
- name: '[yubico] extract data'
ansible.builtin.unarchive:
src: /tmp/yubico-authenticator-latest-linux.tar.gz
remote_src: true
dest: /opt/yubico-authenticator
owner: "{{ workstation_user_account }}"
group: "{{ workstation_user_account }}"
creates: /opt/yubico-authenticator/authenticator
- name: '[yubico] create shell wrapper'
ansible.builtin.template:
src: ../templates/bin_wrapper.sh.j2
dest: "{{ getent_passwd[workstation_user_account][4] }}/.local/bin/authenticator"
mode: '0750'
owner: "{{ workstation_user_account }}"
group: "{{ workstation_user_account }}"
vars:
application: "/opt/yubico-authenticator/authenticator"
- include_tasks: window_manager.yml

View file

@ -0,0 +1,150 @@
---
- name: '[home] get user account information'
ansible.builtin.getent:
database: passwd
key: "{{ workstation_user_account }}"
split: ":"
changed_when: false
when: getent_passwd is undefined or workstation_user_account not in getent_passwd
- name: '[awesomewm] install dependencies'
become: true
ansible.builtin.apt:
update_cache: true
force_apt_get: true
cache_valid_time: 3600
pkg:
- asciidoctor
- build-essential
- cmake
- gir1.2-freedesktop
- gir1.2-gdkpixbuf-2.0
- gir1.2-glib-2.0
- gir1.2-gtk-3.0
- gir1.2-pango-1.0
- imagemagick
- libcairo-gobject2
- libcairo2-dev
- libdbus-1-dev
- libgdk-pixbuf-2.0-dev
- libgirepository1.0-dev
- libglib2.0-dev
- liblua5.4-dev
- liblua5.4-dev
- librsvg2-dev
- libstartup-notification0-dev
- libx11-dev
- libx11-xcb-dev
- libxcb-cursor-dev
- libxcb-icccm4-dev
- libxcb-keysyms1-dev
- libxcb-randr0-dev
- libxcb-shape0-dev
- libxcb-util0-dev
- libxcb-xfixes0-dev
- libxcb-xinerama0-dev
- libxcb-xkb-dev
- libxcb-xrm-dev
- libxcb-xtest0-dev
- libxcb1-dev
- libxdg-basedir-dev
- libxkbcommon-x11-dev
- libxkbcommon-dev
- lua5.4
- luajit2
- menu
- picom
- x11-xserver-utils
- xutils-dev
- name: '[lua-lgi] install lua-lgi'
block:
- name: '[lua-lgi] fetch source'
become: true
become_user: "{{ workstation_user_account }}"
ansible.builtin.git:
repo: https://github.com/lgi-devs/lgi.git
dest: /opt/git/foss/lua-lgi
force: false
register: repo_clone
failed_when:
- repo_clone.failed
- not 'Local modifications exist in the destination' in repo_clone.msg
- name: '[lua-lgi] switch from LUA 5.1 to LUA 5.4'
ansible.builtin.replace:
path: /opt/git/foss/lua-lgi/lgi/Makefile
regexp: '5\.1'
replace: '5.4'
- name: '[lua-lgi] building project'
ansible.builtin.command:
chdir: /opt/git/foss/lua-lgi
cmd: LUA_CFLAGS="-I/usr/include/lua5.4" make all
creates: /opt/git/foss/lua-lgi/lgi/corelgilua51.so
- name: '[lua-lgi] compile and install'
become: true
ansible.builtin.command:
chdir: /opt/git/foss/lua-lgi
cmd: make install
creates: /usr/local/lib/lua/5.4/lgi/corelgilua51.so
- name: '[awesomewm] install window manager'
block:
- name: '[awesomewm] fetch source'
become: true
become_user: "{{ workstation_user_account }}"
ansible.builtin.git:
repo: https://github.com/awesomeWM/awesome.git
dest: /opt/git/foss/awesomeWM
force: false
register: repo_clone
failed_when:
- repo_clone.failed
- not 'Local modifications exist in the destination' in repo_clone.msg
- name: '[awesomewm] building project'
become: true
become_user: "{{ workstation_user_account }}"
ansible.builtin.command:
chdir: /opt/git/foss/awesomeWM
cmd: 'CMAKE_ARGS="-DWITH_DBUS=ON -DLUA_LIBRARY=/usr/lib/x86_64-linux-gnu/liblua5.4.so.0 -DLUA_INCLUDE_DIR=/usr/include/lua5.4" make'
creates: /opt/git/foss/awesomeWM/build
- name: '[awesomewm] building project'
become: true
ansible.builtin.command:
chdir: /opt/git/foss/awesomeWM
cmd: make install
creates: /usr/local/bin/awesome
- name: '[awesomewm] setup configuration'
become: true
become_user: "{{ workstation_user_account }}"
block:
- name: '[awesomewm] fetch copycats base'
ansible.builtin.git:
repo: https://github.com/lcpz/awesome-copycats.git
dest: "{{ getent_passwd[workstation_user_account][4] }}/.config/awesome"
depth: 1
recursive: true
force: false
- name: '[awesomewm] copy customization'
ansible.builtin.copy:
src: ../templates/.config/awesome/
dest: "{{ getent_passwd[workstation_user_account][4] }}/.config/awesome"
mode: '0640'
- name: '[home] copy X related configuration'
become: true
block:
- name: '[x11] xorg.conf'
ansible.builtin.copy:
src: ../templates/system/xorg.conf
dest: /etc/X11/xorg.conf
mode: '0640'
- name: '[x11] user .xsession'
ansible.builtin.copy:
src: ../templates/.xsession
dest: "{{ getent_passwd[workstation_user_account][4] }}/.xsession"

View file

@ -0,0 +1,138 @@
-------------------------------------------------
-- Battery Widget for Awesome Window Manager
-- Shows the battery status using the ACPI tool
-- More details could be found here:
-- https://github.com/streetturtle/awesome-wm-widgets/tree/master/battery-widget
-- @author Pavel Makhov
-- @copyright 2017 Pavel Makhov
-------------------------------------------------
local awful = require("awful")
local naughty = require("naughty")
local watch = require("awful.widget.watch")
local wibox = require("wibox")
-- acpi sample outputs
-- Battery 0: Discharging, 75%, 01:51:38 remaining
-- Battery 0: Charging, 53%, 00:57:43 until charged
local PATH_TO_ICONS = "/usr/share/icons/Arc/status/symbolic/"
local HOME = os.getenv("HOME")
local battery_widget = wibox.widget {
{
id = "icon",
widget = wibox.widget.imagebox,
resize = true
},
layout = wibox.container.margin(_, 0, 0, 3)
}
-- Popup with battery info
-- One way of creating a pop-up notification - naughty.notify
local notification
local function show_battery_status()
awful.spawn.easy_async([[bash -c 'acpi']],
function(stdout, _, _, _)
naughty.destroy(notification)
notification = naughty.notify{
text = stdout,
title = "Battery status",
timeout = 5, hover_timeout = 0.5,
width = 200,
}
end
)
end
-- Alternative to naughty.notify - tooltip. You can compare both and choose the preferred one
--battery_popup = awful.tooltip({objects = {battery_widget}})
-- To use colors from beautiful theme put
-- following lines in rc.lua before require("battery"):
-- beautiful.tooltip_fg = beautiful.fg_normal
-- beautiful.tooltip_bg = beautiful.bg_normal
local function show_battery_warning()
naughty.notify{
-- icon = HOME .. "/.config/awesome/nichosi.png",
-- icon_size=100,
text = "Huston, we have a problem",
title = "Battery is dying",
timeout = 5, hover_timeout = 0.5,
position = "bottom_right",
bg = "#F06060",
fg = "#EEE9EF",
width = 300,
}
end
local last_battery_check = os.time()
watch("acpi -i", 10,
function(widget, stdout, stderr, exitreason, exitcode)
local batteryType
local battery_info = {}
local capacities = {}
for s in stdout:gmatch("[^\r\n]+") do
local status, charge_str, time = string.match(s, '.+: (%a+), (%d?%d?%d)%%,?.*')
if string.match(s, 'rate information') then
-- ignore such line
elseif status ~= nil then
table.insert(battery_info, {status = status, charge = tonumber(charge_str)})
else
local cap_str = string.match(s, '.+:.+last full capacity (%d+)')
table.insert(capacities, tonumber(cap_str))
end
end
local capacity = 0
for i, cap in ipairs(capacities) do
capacity = capacity + cap
end
local charge = 0
local status
for i, batt in ipairs(battery_info) do
if batt.charge >= charge then
status = batt.status -- use most charged battery status
-- this is arbitrary, and maybe another metric should be used
end
charge = charge + batt.charge * capacities[i]
end
charge = charge / capacity
if (charge >= 0 and charge < 15) then
batteryType = "battery-empty%s-symbolic"
if status ~= 'Charging' and os.difftime(os.time(), last_battery_check) > 300 then
-- if 5 minutes have elapsed since the last warning
last_battery_check = time()
show_battery_warning()
end
elseif (charge >= 15 and charge < 40) then batteryType = "battery-caution%s-symbolic"
elseif (charge >= 40 and charge < 60) then batteryType = "battery-low%s-symbolic"
elseif (charge >= 60 and charge < 80) then batteryType = "battery-good%s-symbolic"
elseif (charge >= 80 and charge <= 100) then batteryType = "battery-full%s-symbolic"
end
if status == 'Charging' then
batteryType = string.format(batteryType, '-charging')
else
batteryType = string.format(batteryType, '')
end
widget.icon:set_image(PATH_TO_ICONS .. batteryType .. ".svg")
-- Update popup text
-- battery_popup.text = string.gsub(stdout, "\n$", "")
end,
battery_widget)
battery_widget:connect_signal("mouse::enter", function() show_battery_status() end)
battery_widget:connect_signal("mouse::leave", function() naughty.destroy(notification) end)
return battery_widget

View file

@ -0,0 +1,759 @@
--[[
Awesome WM configuration template
github.com/lcpz
--]]
-- {{{ Required libraries
local awesome, client, mouse, screen, tag = awesome, client, mouse, screen, tag
local ipairs, string, os, table, tostring, tonumber, type = ipairs, string, os, table, tostring, tonumber, type
local gears = require("gears")
local awful = require("awful")
require("awful.autofocus")
local wibox = require("wibox")
local beautiful = require("beautiful")
local naughty = require("naughty")
local lain = require("lain")
local menubar = require("menubar")
local freedesktop = require("freedesktop")
local hotkeys_popup = require("awful.hotkeys_popup").widget
require("awful.hotkeys_popup.keys")
local my_table = awful.util.table or gears.table -- 4.{0,1} compatibility
local dpi = require("beautiful.xresources").apply_dpi
-- }}}
-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
naughty.notify({ preset = naughty.config.presets.critical,
title = "Oops, there were errors during startup!",
text = awesome.startup_errors })
end
-- Handle runtime errors after startup
do
local in_error = false
awesome.connect_signal("debug::error", function (err)
if in_error then return end
in_error = true
naughty.notify({ preset = naughty.config.presets.critical,
title = "Oops, an error happened!",
text = tostring(err) })
in_error = false
end)
end
-- }}}
-- {{{ Autostart windowless processes
-- This function will run once every time Awesome is started
local function run_once(cmd_arr)
for _, cmd in ipairs(cmd_arr) do
awful.spawn.with_shell(string.format("pgrep -u $USER -fx '%s' > /dev/null || (%s)", cmd, cmd))
end
end
run_once({ "unclutter -root", os.getenv("HOME") .. "/bin/autorun.sh" }) -- entries must be separated by commas
-- }}}
-- {{{ Variable definitions
local themes = {
"blackburn", -- 1
"copland", -- 2
"dremora", -- 3
"holo", -- 4
"multicolor", -- 5
"powerarrow", -- 6
"powerarrow-dark", -- 7
"rainbow", -- 8
"steamburn", -- 9
"vertex", -- 10
}
local chosen_theme = themes[6]
local modkey = "Mod4"
local altkey = "Mod1"
local terminal = "terminator"
local vi_focus = false -- vi-like client focus - https://github.com/lcpz/awesome-copycats/issues/275
local cycle_prev = true -- cycle trough all previous client or just the first -- https://github.com/lcpz/awesome-copycats/issues/274
local editor = os.getenv("EDITOR") or "emacs"
local gui_editor = os.getenv("GUI_EDITOR") or "gvim"
local browser = os.getenv("BROWSER") or "firefox"
local scrshooter = os.getenv("SCREENSHOOTER") or "xfce4-screenshooter"
local scrlocker = "slock"
awful.util.terminal = terminal
awful.util.tagnames = { "Web", "Dev", "Admin", "Media" }
awful.layout.layouts = {
awful.layout.suit.tile,
awful.layout.suit.fair,
lain.layout.centerwork
--awful.layout.suit.floating,
--awful.layout.suit.tile.left,
--awful.layout.suit.tile.bottom,
--awful.layout.suit.tile.top,
--awful.layout.suit.fair,
--awful.layout.suit.fair.horizontal,
--awful.layout.suit.spiral,
--awful.layout.suit.spiral.dwindle,
--awful.layout.suit.max,
--awful.layout.suit.max.fullscreen,
--awful.layout.suit.magnifier,
--awful.layout.suit.corner.nw,
--awful.layout.suit.corner.ne,
--awful.layout.suit.corner.sw,
--awful.layout.suit.corner.se,
--lain.layout.cascade,
--lain.layout.cascade.tile,
--lain.layout.centerwork,
--lain.layout.centerwork.horizontal,
--lain.layout.termfair,
--lain.layout.termfair.center,
}
awful.util.taglist_buttons = my_table.join(
awful.button({ }, 1, function(t) t:view_only() end),
awful.button({ modkey }, 1, function(t)
if client.focus then
client.focus:move_to_tag(t)
end
end),
awful.button({ }, 3, awful.tag.viewtoggle),
awful.button({ modkey }, 3, function(t)
if client.focus then
client.focus:toggle_tag(t)
end
end),
awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
)
awful.util.tasklist_buttons = my_table.join(
awful.button({ }, 1, function (c)
if c == client.focus then
c.minimized = true
else
--c:emit_signal("request::activate", "tasklist", {raise = true})<Paste>
-- Without this, the following
-- :isvisible() makes no sense
c.minimized = false
if not c:isvisible() and c.first_tag then
c.first_tag:view_only()
end
-- This will also un-minimize
-- the client, if needed
client.focus = c
c:raise()
end
end),
awful.button({ }, 2, function (c) c:kill() end),
awful.button({ }, 3, function ()
local instance = nil
return function ()
if instance and instance.wibox.visible then
instance:hide()
instance = nil
else
instance = awful.menu.clients({theme = {width = dpi(250)}})
end
end
end),
awful.button({ }, 4, function () awful.client.focus.byidx(1) end),
awful.button({ }, 5, function () awful.client.focus.byidx(-1) end)
)
lain.layout.termfair.nmaster = 3
lain.layout.termfair.ncol = 1
lain.layout.termfair.center.nmaster = 3
lain.layout.termfair.center.ncol = 1
lain.layout.cascade.tile.offset_x = dpi(2)
lain.layout.cascade.tile.offset_y = dpi(32)
lain.layout.cascade.tile.extra_padding = dpi(5)
lain.layout.cascade.tile.nmaster = 5
lain.layout.cascade.tile.ncol = 2
beautiful.init(string.format("%s/.config/awesome/themes/%s/theme-personal.lua", os.getenv("HOME"), chosen_theme))
-- }}}
-- {{{ Menu
local myawesomemenu = {
{ "hotkeys", function() return false, hotkeys_popup.show_help end },
{ "manual", terminal .. " -e man awesome" },
{ "edit config", string.format("%s -e %s %s", terminal, editor, awesome.conffile) },
{ "restart", awesome.restart },
{ "quit", function() awesome.quit() end }
}
awful.util.mymainmenu = freedesktop.menu.build({
icon_size = beautiful.menu_height or dpi(16),
before = {
{ "Awesome", myawesomemenu, beautiful.awesome_icon },
-- other triads can be put here
},
after = {
{ "Open terminal", terminal },
-- other triads can be put here
}
})
-- hide menu when mouse leaves it
--awful.util.mymainmenu.wibox:connect_signal("mouse::leave", function() awful.util.mymainmenu:hide() end)
menubar.utils.terminal = terminal -- Set the Menubar terminal for applications that require it
-- }}}
-- {{{ Screen
-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal("property::geometry", function(s)
-- Wallpaper
if beautiful.wallpaper then
local wallpaper = beautiful.wallpaper
-- If wallpaper is a function, call it with the screen
if type(wallpaper) == "function" then
wallpaper = wallpaper(s)
end
gears.wallpaper.maximized(wallpaper, s, true)
end
end)
-- No borders when rearranging only 1 non-floating or maximized client
screen.connect_signal("arrange", function (s)
local only_one = #s.tiled_clients == 1
for _, c in pairs(s.clients) do
if only_one and not c.floating or c.maximized then
c.border_width = 0
else
c.border_width = beautiful.border_width
end
end
end)
-- Create a wibox for each screen and add it
awful.screen.connect_for_each_screen(function(s) beautiful.at_screen_connect(s) end)
-- }}}
-- {{{ Mouse bindings
root.buttons(my_table.join(
awful.button({ }, 3, function () awful.util.mymainmenu:toggle() end),
awful.button({ }, 4, awful.tag.viewnext),
awful.button({ }, 5, awful.tag.viewprev)
))
-- }}}
-- {{{ Key bindings
globalkeys = my_table.join(
-- X screen locker
awful.key({ altkey, "Control" }, "l", function () awful.spawn(scrlocker) end,
{description = "lock screen", group = "hotkeys"}),
-- Hotkeys
awful.key({ modkey, }, "s", hotkeys_popup.show_help,
{description = "show help", group="awesome"}),
-- Tag browsing
awful.key({ modkey, }, "Left", awful.tag.viewprev,
{description = "view previous", group = "tag"}),
awful.key({ modkey, }, "Right", awful.tag.viewnext,
{description = "view next", group = "tag"}),
awful.key({ modkey, }, "Escape", awful.tag.history.restore,
{description = "go back", group = "tag"}),
-- Non-empty tag browsing
awful.key({ altkey }, "Left", function () lain.util.tag_view_nonempty(-1) end,
{description = "view previous nonempty", group = "tag"}),
awful.key({ altkey }, "Right", function () lain.util.tag_view_nonempty(1) end,
{description = "view previous nonempty", group = "tag"}),
-- Default client focus
awful.key({ altkey, }, "j",
function ()
awful.client.focus.byidx( 1)
end,
{description = "focus next by index", group = "client"}
),
awful.key({ altkey, }, "k",
function ()
awful.client.focus.byidx(-1)
end,
{description = "focus previous by index", group = "client"}
),
-- By direction client focus
awful.key({ modkey }, "j",
function()
awful.client.focus.global_bydirection("down")
if client.focus then client.focus:raise() end
end,
{description = "focus down", group = "client"}),
awful.key({ modkey }, "k",
function()
awful.client.focus.global_bydirection("up")
if client.focus then client.focus:raise() end
end,
{description = "focus up", group = "client"}),
awful.key({ modkey }, "h",
function()
awful.client.focus.global_bydirection("left")
if client.focus then client.focus:raise() end
end,
{description = "focus left", group = "client"}),
awful.key({ modkey }, "l",
function()
awful.client.focus.global_bydirection("right")
if client.focus then client.focus:raise() end
end,
{description = "focus right", group = "client"}),
awful.key({ modkey, }, "w", function () awful.util.mymainmenu:show() end,
{description = "show main menu", group = "awesome"}),
-- Layout manipulation
awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end,
{description = "swap with next client by index", group = "client"}),
awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end,
{description = "swap with previous client by index", group = "client"}),
awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
{description = "focus the next screen", group = "screen"}),
awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
{description = "focus the previous screen", group = "screen"}),
awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
{description = "jump to urgent client", group = "client"}),
awful.key({ modkey, }, "Tab",
function ()
if cycle_prev then
awful.client.focus.history.previous()
else
awful.client.focus.byidx(-1)
end
if client.focus then
client.focus:raise()
end
end,
{description = "cycle with previous/go back", group = "client"}),
awful.key({ modkey, "Shift" }, "Tab",
function ()
if cycle_prev then
awful.client.focus.byidx(1)
if client.focus then
client.focus:raise()
end
end
end,
{description = "go forth", group = "client"}),
-- Show/Hide Wibox
awful.key({ modkey }, "b", function ()
for s in screen do
s.mywibox.visible = not s.mywibox.visible
if s.mybottomwibox then
s.mybottomwibox.visible = not s.mybottomwibox.visible
end
end
end,
{description = "toggle wibox", group = "awesome"}),
-- On the fly useless gaps change
awful.key({ altkey, "Control" }, "+", function () lain.util.useless_gaps_resize(1) end,
{description = "increment useless gaps", group = "tag"}),
awful.key({ altkey, "Control" }, "-", function () lain.util.useless_gaps_resize(-1) end,
{description = "decrement useless gaps", group = "tag"}),
-- Dynamic tagging
awful.key({ modkey, "Shift" }, "n", function () lain.util.add_tag() end,
{description = "add new tag", group = "tag"}),
awful.key({ modkey, "Shift" }, "r", function () lain.util.rename_tag() end,
{description = "rename tag", group = "tag"}),
awful.key({ modkey, "Shift" }, "Left", function () lain.util.move_tag(-1) end,
{description = "move tag to the left", group = "tag"}),
awful.key({ modkey, "Shift" }, "Right", function () lain.util.move_tag(1) end,
{description = "move tag to the right", group = "tag"}),
awful.key({ modkey, "Shift" }, "d", function () lain.util.delete_tag() end,
{description = "delete tag", group = "tag"}),
-- Standard program
awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end,
{description = "open a terminal", group = "launcher"}),
awful.key({ modkey, "Control" }, "r", awesome.restart,
{description = "reload awesome", group = "awesome"}),
awful.key({ modkey, "Shift" }, "q", awesome.quit,
{description = "quit awesome", group = "awesome"}),
awful.key({ altkey, "Shift" }, "l", function () awful.tag.incmwfact( 0.05) end,
{description = "increase master width factor", group = "layout"}),
awful.key({ altkey, "Shift" }, "h", function () awful.tag.incmwfact(-0.05) end,
{description = "decrease master width factor", group = "layout"}),
awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end,
{description = "increase the number of master clients", group = "layout"}),
awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end,
{description = "decrease the number of master clients", group = "layout"}),
awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1, nil, true) end,
{description = "increase the number of columns", group = "layout"}),
awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1, nil, true) end,
{description = "decrease the number of columns", group = "layout"}),
awful.key({ modkey, }, "space", function () awful.layout.inc( 1) end,
{description = "select next", group = "layout"}),
awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(-1) end,
{description = "select previous", group = "layout"}),
awful.key({ modkey, "Control" }, "n",
function ()
local c = awful.client.restore()
-- Focus restored client
if c then
client.focus = c
c:raise()
end
end,
{description = "restore minimized", group = "client"}),
-- Dropdown application
awful.key({ modkey, }, "z", function () awful.screen.focused().quake:toggle() end,
{description = "dropdown application", group = "launcher"}),
-- Widgets popups
awful.key({ altkey, }, "c", function () if beautiful.cal then beautiful.cal.show(7) end end,
{description = "show calendar", group = "widgets"}),
awful.key({ altkey, }, "h", function () if beautiful.fs then beautiful.fs.show(7) end end,
{description = "show filesystem", group = "widgets"}),
awful.key({ altkey, }, "w", function () if beautiful.weather then beautiful.weather.show(7) end end,
{description = "show weather", group = "widgets"}),
-- Brightness
awful.key({ }, "XF86MonBrightnessUp", function () awful.spawn("xbacklight -inc 10") end,
{description = "+10%", group = "hotkeys"}),
awful.key({ }, "XF86MonBrightnessDown", function () awful.spawn("xbacklight -dec 10") end,
{description = "-10%", group = "hotkeys"}),
-- ALSA volume control
awful.key({ altkey }, "Up",
function ()
awful.spawn(string.format("amixer -q set %s 1%%+", beautiful.volume.channel))
beautiful.volume.update()
end,
{description = "volume up", group = "hotkeys"}),
awful.key({ altkey }, "Down",
function ()
awful.spawn(string.format("amixer -q set %s 1%%-", beautiful.volume.channel))
beautiful.volume.update()
end,
{description = "volume down", group = "hotkeys"}),
awful.key({ altkey }, "m",
function ()
awful.spawn(string.format("amixer -q set %s toggle", beautiful.volume.togglechannel or beautiful.volume.channel))
beautiful.volume.update()
end,
{description = "toggle mute", group = "hotkeys"}),
awful.key({ altkey, "Control" }, "m",
function ()
awful.spawn(string.format("amixer -q set %s 100%%", beautiful.volume.channel))
beautiful.volume.update()
end,
{description = "volume 100%", group = "hotkeys"}),
awful.key({ altkey, "Control" }, "0",
function ()
awful.spawn(string.format("amixer -q set %s 0%%", beautiful.volume.channel))
beautiful.volume.update()
end,
{description = "volume 0%", group = "hotkeys"}),
-- MPD control
awful.key({ altkey, "Control" }, "Up",
function ()
awful.spawn("mpc toggle")
beautiful.mpd.update()
end,
{description = "mpc toggle", group = "widgets"}),
awful.key({ altkey, "Control" }, "Down",
function ()
awful.spawn("mpc stop")
beautiful.mpd.update()
end,
{description = "mpc stop", group = "widgets"}),
awful.key({ altkey, "Control" }, "Left",
function ()
awful.spawn("mpc prev")
beautiful.mpd.update()
end,
{description = "mpc prev", group = "widgets"}),
awful.key({ altkey, "Control" }, "Right",
function ()
awful.spawn("mpc next")
beautiful.mpd.update()
end,
{description = "mpc next", group = "widgets"}),
awful.key({ altkey }, "0",
function ()
local common = { text = "MPD widget ", position = "top_middle", timeout = 2 }
if beautiful.mpd.timer.started then
beautiful.mpd.timer:stop()
common.text = common.text .. lain.util.markup.bold("OFF")
else
beautiful.mpd.timer:start()
common.text = common.text .. lain.util.markup.bold("ON")
end
naughty.notify(common)
end,
{description = "mpc on/off", group = "widgets"}),
-- Copy primary to clipboard (terminals to gtk)
awful.key({ modkey }, "c", function () awful.spawn.with_shell("xsel | xsel -i -b") end,
{description = "copy terminal to gtk", group = "hotkeys"}),
-- Copy clipboard to primary (gtk to terminals)
awful.key({ modkey }, "v", function () awful.spawn.with_shell("xsel -b | xsel") end,
{description = "copy gtk to terminal", group = "hotkeys"}),
-- User programs
awful.key({ modkey }, "q", function () awful.spawn(browser) end,
{description = "run browser", group = "launcher"}),
awful.key({ modkey }, "a", function () awful.spawn(gui_editor) end,
{description = "run gui editor", group = "launcher"}),
awful.key({ }, "Print", function () awful.spawn(scrshooter) end,
{description = "run screenshot program", group = "hotkeys"}),
-- Menubar
awful.key({ modkey }, "p", function() menubar.show() end,
{description = "show the menubar", group = "launcher"}),
-- Prompt
awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end,
{description = "run prompt", group = "launcher"}),
awful.key({ modkey }, "x",
function ()
awful.prompt.run {
prompt = "Run Lua code: ",
textbox = awful.screen.focused().mypromptbox.widget,
exe_callback = awful.util.eval,
history_path = awful.util.get_cache_dir() .. "/history_eval"
}
end,
{description = "lua execute prompt", group = "awesome"})
--
)
clientkeys = my_table.join(
awful.key({ altkey, "Shift" }, "m", lain.util.magnify_client,
{description = "magnify client", group = "client"}),
awful.key({ modkey, }, "f",
function (c)
c.fullscreen = not c.fullscreen
c:raise()
end,
{description = "toggle fullscreen", group = "client"}),
awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end,
{description = "close", group = "client"}),
awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ,
{description = "toggle floating", group = "client"}),
awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
{description = "move to master", group = "client"}),
awful.key({ modkey, }, "o", function (c) c:move_to_screen() end,
{description = "move to screen", group = "client"}),
awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end,
{description = "toggle keep on top", group = "client"}),
awful.key({ modkey, }, "n",
function (c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end ,
{description = "minimize", group = "client"}),
awful.key({ modkey, }, "m",
function (c)
c.maximized = not c.maximized
c:raise()
end ,
{description = "maximize", group = "client"})
)
-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it works on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
-- Hack to only show tags 1 and 9 in the shortcut window (mod+s)
local descr_view, descr_toggle, descr_move, descr_toggle_focus
if i == 1 or i == 9 then
descr_view = {description = "view tag #", group = "tag"}
descr_toggle = {description = "toggle tag #", group = "tag"}
descr_move = {description = "move focused client to tag #", group = "tag"}
descr_toggle_focus = {description = "toggle focused client on tag #", group = "tag"}
end
globalkeys = my_table.join(globalkeys,
-- View tag only.
awful.key({ modkey }, "#" .. i + 9,
function ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
if tag then
tag:view_only()
end
end,
descr_view),
-- Toggle tag display.
awful.key({ modkey, "Control" }, "#" .. i + 9,
function ()
local screen = awful.screen.focused()
local tag = screen.tags[i]
if tag then
awful.tag.viewtoggle(tag)
end
end,
descr_toggle),
-- Move client to tag.
awful.key({ modkey, "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = client.focus.screen.tags[i]
if tag then
client.focus:move_to_tag(tag)
end
end
end,
descr_move),
-- Toggle tag on focused client.
awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = client.focus.screen.tags[i]
if tag then
client.focus:toggle_tag(tag)
end
end
end,
descr_toggle_focus)
)
end
clientbuttons = gears.table.join(
awful.button({ }, 1, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
end),
awful.button({ modkey }, 1, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
awful.mouse.client.move(c)
end),
awful.button({ modkey }, 3, function (c)
c:emit_signal("request::activate", "mouse_click", {raise = true})
awful.mouse.client.resize(c)
end)
)
-- Set keys
root.keys(globalkeys)
-- }}}
-- {{{ Rules
-- Rules to apply to new clients (through the "manage" signal).
awful.rules.rules = {
-- All clients will match this rule.
{ rule = { },
properties = { border_width = beautiful.border_width,
border_color = beautiful.border_normal,
focus = awful.client.focus.filter,
raise = true,
keys = clientkeys,
buttons = clientbuttons,
screen = awful.screen.preferred,
placement = awful.placement.no_overlap+awful.placement.no_offscreen,
size_hints_honor = false
}
},
-- Titlebars
{ rule_any = { type = { "dialog", "normal" } },
properties = { titlebars_enabled = true } },
-- Set Firefox to always map on the first tag on screen 1.
{ rule = { class = "Firefox" },
properties = { screen = 1, tag = awful.util.tagnames[1] } },
{ rule = { class = "Gimp", role = "gimp-image-window" },
properties = { maximized = true } },
}
-- }}}
-- {{{ Signals
-- Signal function to execute when a new client appears.
client.connect_signal("manage", function (c)
-- Set the windows at the slave,
-- i.e. put it at the end of others instead of setting it master.
-- if not awesome.startup then awful.client.setslave(c) end
if awesome.startup and
not c.size_hints.user_position
and not c.size_hints.program_position then
-- Prevent clients from being unreachable after screen count changes.
awful.placement.no_offscreen(c)
end
end)
-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal("request::titlebars", function(c)
-- Custom
if beautiful.titlebar_fun then
beautiful.titlebar_fun(c)
return
end
-- Default
-- buttons for the titlebar
local buttons = my_table.join(
awful.button({ }, 1, function()
c:emit_signal("request::activate", "titlebar", {raise = true})
awful.mouse.client.move(c)
end),
awful.button({ }, 2, function() c:kill() end),
awful.button({ }, 3, function()
c:emit_signal("request::activate", "titlebar", {raise = true})
awful.mouse.client.resize(c)
end)
)
awful.titlebar(c, {size = dpi(16)}) : setup {
{ -- Left
awful.titlebar.widget.iconwidget(c),
buttons = buttons,
layout = wibox.layout.fixed.horizontal
},
{ -- Middle
{ -- Title
align = "center",
widget = awful.titlebar.widget.titlewidget(c)
},
buttons = buttons,
layout = wibox.layout.flex.horizontal
},
{ -- Right
awful.titlebar.widget.floatingbutton (c),
awful.titlebar.widget.maximizedbutton(c),
awful.titlebar.widget.stickybutton (c),
awful.titlebar.widget.ontopbutton (c),
awful.titlebar.widget.closebutton (c),
layout = wibox.layout.fixed.horizontal()
},
layout = wibox.layout.align.horizontal
}
end)
-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
c:emit_signal("request::activate", "mouse_enter", {raise = vi_focus})
end)
client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
-- possible workaround for tag preservation when switching back to default screen:
-- https://github.com/lcpz/awesome-copycats/issues/251
-- }}}
-- Test to work around fullscreen game issues
client.connect_signal("property::fullscreen", function(c)
if c.fullscreen then
gears.timer.delayed_call(function()
if c.valid then
c:geometry(c.screen.geometry)
end
end)
end
end)

View file

@ -0,0 +1,386 @@
--[[
Powerarrow Awesome WM theme
github.com/lcpz
--]]
local gears = require("gears")
local lain = require("lain")
local awful = require("awful")
local wibox = require("wibox")
local dpi = require("beautiful.xresources").apply_dpi
local math, string, os = math, string, os
local my_table = awful.util.table or gears.table -- 4.{0,1} compatibility
local output_sink_index = 1
local theme = {}
theme.dir = os.getenv("HOME") .. "/.config/awesome/themes/powerarrow"
theme.wallpaper = os.getenv("HOME") .. "/.config/awesome/background.png"
theme.font = "Roboto Slab 9"
theme.fg_normal = "#FEFEFE"
theme.fg_focus = "#32D6FF"
theme.fg_urgent = "#C83F11"
theme.bg_normal = "#222222"
theme.bg_focus = "#1E2320"
theme.bg_urgent = "#3F3F3F"
theme.taglist_fg_focus = "#00CCFF"
theme.tasklist_bg_focus = "#222222"
theme.tasklist_fg_focus = "#00CCFF"
theme.border_width = dpi(2)
theme.border_normal = "#3F3F3F"
theme.border_focus = "#6F6F6F"
theme.border_marked = "#CC9393"
theme.titlebar_bg_focus = "#3F3F3F"
theme.titlebar_bg_normal = "#3F3F3F"
theme.titlebar_bg_focus = theme.bg_focus
theme.titlebar_bg_normal = theme.bg_normal
theme.titlebar_fg_focus = theme.fg_focus
theme.menu_height = dpi(16)
theme.menu_width = dpi(140)
theme.menu_submenu_icon = theme.dir .. "/icons/submenu.png"
theme.awesome_icon = theme.dir .. "/icons/awesome.png"
theme.taglist_squares_sel = theme.dir .. "/icons/square_sel.png"
theme.taglist_squares_unsel = theme.dir .. "/icons/square_unsel.png"
theme.layout_tile = theme.dir .. "/icons/tile.png"
theme.layout_tileleft = theme.dir .. "/icons/tileleft.png"
theme.layout_tilebottom = theme.dir .. "/icons/tilebottom.png"
theme.layout_tiletop = theme.dir .. "/icons/tiletop.png"
theme.layout_fairv = theme.dir .. "/icons/fairv.png"
theme.layout_fairh = theme.dir .. "/icons/fairh.png"
theme.layout_spiral = theme.dir .. "/icons/spiral.png"
theme.layout_dwindle = theme.dir .. "/icons/dwindle.png"
theme.layout_max = theme.dir .. "/icons/max.png"
theme.layout_fullscreen = theme.dir .. "/icons/fullscreen.png"
theme.layout_magnifier = theme.dir .. "/icons/magnifier.png"
theme.layout_floating = theme.dir .. "/icons/floating.png"
theme.widget_ac = theme.dir .. "/icons/ac.png"
theme.widget_battery = theme.dir .. "/icons/battery.png"
theme.widget_battery_low = theme.dir .. "/icons/battery_low.png"
theme.widget_battery_empty = theme.dir .. "/icons/battery_empty.png"
theme.widget_brightness = theme.dir .. "/icons/brightness.png"
theme.widget_mem = theme.dir .. "/icons/mem.png"
theme.widget_cpu = theme.dir .. "/icons/cpu.png"
theme.widget_temp = theme.dir .. "/icons/temp.png"
theme.widget_net = theme.dir .. "/icons/net.png"
theme.widget_hdd = theme.dir .. "/icons/hdd.png"
theme.widget_music = theme.dir .. "/icons/note.png"
theme.widget_music_on = theme.dir .. "/icons/note_on.png"
theme.widget_music_pause = theme.dir .. "/icons/pause.png"
theme.widget_music_stop = theme.dir .. "/icons/stop.png"
-- Borrow volume icons from powerarrow-dark
theme.widget_vol = theme.dir .. "/../powerarrow-dark/icons/vol.png"
theme.widget_vol_low = theme.dir .. "/../powerarrow-dark/icons/vol_low.png"
theme.widget_vol_no = theme.dir .. "/../powerarrow-dark/icons/vol_no.png"
theme.widget_vol_mute = theme.dir .. "/../powerarrow-dark/icons/vol_mute.png"
--
theme.widget_mail = theme.dir .. "/icons/mail.png"
theme.widget_mail_on = theme.dir .. "/icons/mail_on.png"
theme.widget_task = theme.dir .. "/icons/task.png"
theme.widget_scissors = theme.dir .. "/icons/scissors.png"
theme.tasklist_plain_task_name = true
theme.tasklist_disable_icon = true
theme.useless_gap = 0
theme.titlebar_close_button_focus = theme.dir .. "/icons/titlebar/close_focus.png"
theme.titlebar_close_button_normal = theme.dir .. "/icons/titlebar/close_normal.png"
theme.titlebar_ontop_button_focus_active = theme.dir .. "/icons/titlebar/ontop_focus_active.png"
theme.titlebar_ontop_button_normal_active = theme.dir .. "/icons/titlebar/ontop_normal_active.png"
theme.titlebar_ontop_button_focus_inactive = theme.dir .. "/icons/titlebar/ontop_focus_inactive.png"
theme.titlebar_ontop_button_normal_inactive = theme.dir .. "/icons/titlebar/ontop_normal_inactive.png"
theme.titlebar_sticky_button_focus_active = theme.dir .. "/icons/titlebar/sticky_focus_active.png"
theme.titlebar_sticky_button_normal_active = theme.dir .. "/icons/titlebar/sticky_normal_active.png"
theme.titlebar_sticky_button_focus_inactive = theme.dir .. "/icons/titlebar/sticky_focus_inactive.png"
theme.titlebar_sticky_button_normal_inactive = theme.dir .. "/icons/titlebar/sticky_normal_inactive.png"
theme.titlebar_floating_button_focus_active = theme.dir .. "/icons/titlebar/floating_focus_active.png"
theme.titlebar_floating_button_normal_active = theme.dir .. "/icons/titlebar/floating_normal_active.png"
theme.titlebar_floating_button_focus_inactive = theme.dir .. "/icons/titlebar/floating_focus_inactive.png"
theme.titlebar_floating_button_normal_inactive = theme.dir .. "/icons/titlebar/floating_normal_inactive.png"
theme.titlebar_maximized_button_focus_active = theme.dir .. "/icons/titlebar/maximized_focus_active.png"
theme.titlebar_maximized_button_normal_active = theme.dir .. "/icons/titlebar/maximized_normal_active.png"
theme.titlebar_maximized_button_focus_inactive = theme.dir .. "/icons/titlebar/maximized_focus_inactive.png"
theme.titlebar_maximized_button_normal_inactive = theme.dir .. "/icons/titlebar/maximized_normal_inactive.png"
local markup = lain.util.markup
local separators = lain.util.separators
-- Conventional clock
local convclock = wibox.widget.textclock()
-- Calendar
theme.cal = lain.widget.cal({
--cal = "cal --color=always",
--attach_to = { binclock.widget },
attach_to = { convclock },
notification_preset = {
-- font = "Terminus 10",
font = "Roboto Slab 10",
fg = theme.fg_normal,
bg = theme.bg_normal
}
})
-- Taskwarrior
local task = wibox.widget.imagebox(theme.widget_task)
lain.widget.contrib.task.attach(task, {
-- do not colorize output
show_cmd = "task | sed -r 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g'"
})
task:buttons(my_table.join(awful.button({}, 1, lain.widget.contrib.task.prompt)))
-- Scissors (xsel copy and paste)
local scissors = wibox.widget.imagebox(theme.widget_scissors)
scissors:buttons(my_table.join(awful.button({}, 1, function() awful.spawn.with_shell("xsel | xsel -i -b") end)))
-- PulseAudio volume
local volicon = wibox.widget.imagebox(theme.widget_vol)
theme.volume = lain.widget.pulse({
settings = function()
if volume_now == nil or volume_now.muted == "yes" then
volicon:set_image(theme.widget_vol_mute)
elseif volume_now.left == "N/A" then
volicon:set_image(theme.widget_vol_no)
elseif tonumber(volume_now.left) == 0 then
volicon:set_image(theme.widget_vol_no)
elseif tonumber(volume_now.left) <= 50 then
volicon:set_image(theme.widget_vol_low)
else
volicon:set_image(theme.widget_vol)
end
widget:set_markup(markup.font(theme.font, " " .. volume_now.left .. "% "))
end
})
theme.volume.widget:buttons(awful.util.table.join(
awful.button({}, 4, function ()
awful.spawn("pactl set-sink-volume " .. output_sink_index .. " +3%")
theme.volume.update()
end),
awful.button({}, 5, function ()
awful.spawn("pactl set-sink-volume " .. output_sink_index .. " -3%")
theme.volume.update()
end)
))
-- MPD
local musicplr = awful.util.terminal .. " --title Music -g 130x34-320+16 -e ncmpcpp"
local mpdicon = wibox.widget.imagebox(theme.widget_music)
mpdicon:buttons(my_table.join(
awful.button({ modkey }, 1, function () awful.spawn.with_shell(musicplr) end),
awful.button({ }, 1, function ()
os.execute("mpc prev")
theme.mpd.update()
end),
awful.button({ }, 2, function ()
os.execute("mpc toggle")
theme.mpd.update()
end),
awful.button({ }, 3, function ()
os.execute("mpc next")
theme.mpd.update()
end)))
theme.mpd = lain.widget.mpd({
settings = function()
if mpd_now.state == "play" then
artist = " " .. mpd_now.artist .. " "
title = mpd_now.title .. " "
mpdicon:set_image(theme.widget_music_on)
widget:set_markup(markup.font(theme.font, markup("#FF8466", artist) .. " " .. title))
elseif mpd_now.state == "pause" then
widget:set_markup(markup.font(theme.font, " mpd paused "))
mpdicon:set_image(theme.widget_music_pause)
else
widget:set_text("")
mpdicon:set_image(theme.widget_music)
end
end
})
-- MEM
local memicon = wibox.widget.imagebox(theme.widget_mem)
local mem = lain.widget.mem({
settings = function()
widget:set_markup(markup.font(theme.font, " " .. mem_now.used .. "MB "))
end
})
-- CPU
local cpuicon = wibox.widget.imagebox(theme.widget_cpu)
local cpu = lain.widget.cpu({
settings = function()
widget:set_markup(markup.font(theme.font, " " .. cpu_now.usage .. "% "))
end
})
-- Coretemp (lain, average)
local temp = lain.widget.temp({
settings = function()
widget:set_markup(markup.font(theme.font, " " .. coretemp_now .. "°C "))
end
})
--
local tempicon = wibox.widget.imagebox(theme.widget_temp)
-- / fs
local fsicon = wibox.widget.imagebox(theme.widget_hdd)
-- commented because it needs Gio/Glib >= 2.54
theme.fs = lain.widget.fs({
-- notification_preset = { fg = theme.fg_normal, bg = theme.bg_normal, font = "Terminus 10" },
notification_preset = { fg = theme.fg_normal, bg = theme.bg_normal, font = "Roboto Slab 10" },
settings = function()
local fsp = string.format(" %3.2f %s ", fs_now["/"].free, fs_now["/"].units)
widget:set_markup(markup.font(theme.font, fsp))
end
})
--
-- Battery
local baticon = wibox.widget.imagebox(theme.widget_battery)
local bat = lain.widget.bat({
settings = function()
if bat_now.status and bat_now.status ~= "N/A" then
if bat_now.ac_status == 1 then
widget:set_markup(markup.font(theme.font, " AC "))
baticon:set_image(theme.widget_ac)
return
elseif not bat_now.perc and tonumber(bat_now.perc) <= 5 then
baticon:set_image(theme.widget_battery_empty)
elseif not bat_now.perc and tonumber(bat_now.perc) <= 15 then
baticon:set_image(theme.widget_battery_low)
else
baticon:set_image(theme.widget_battery)
end
widget:set_markup(markup.font(theme.font, " " .. bat_now.perc .. "% "))
else
widget:set_markup()
baticon:set_image(theme.widget_ac)
end
end
})
-- Net
local neticon = wibox.widget.imagebox(theme.widget_net)
local net = lain.widget.net({
settings = function()
widget:set_markup(markup.fontfg(theme.font, "#FEFEFE", " " .. net_now.received .. " ↓↑ " .. net_now.sent .. " "))
end
})
-- Brigtness
local brighticon = wibox.widget.imagebox(theme.widget_brightness)
-- If you use xbacklight, comment the line with "light -G" and uncomment the line bellow
-- local brightwidget = awful.widget.watch('xbacklight -get', 0.1,
local brightwidget = awful.widget.watch('light -G', 0.1,
function(widget, stdout, stderr, exitreason, exitcode)
local brightness_level = tonumber(string.format("%.0f", stdout))
widget:set_markup(markup.font(theme.font, " " .. brightness_level .. "%"))
end)
-- Separators
local arrow = separators.arrow_left
function theme.powerline_rl(cr, width, height)
local arrow_depth, offset = height/2, 0
-- Avoid going out of the (potential) clip area
if arrow_depth < 0 then
width = width + 2*arrow_depth
offset = -arrow_depth
end
cr:move_to(offset + arrow_depth , 0 )
cr:line_to(offset + width , 0 )
cr:line_to(offset + width - arrow_depth , height/2 )
cr:line_to(offset + width , height )
cr:line_to(offset + arrow_depth , height )
cr:line_to(offset , height/2 )
cr:close_path()
end
local function pl(widget, bgcolor, padding)
return wibox.container.background(wibox.container.margin(widget, dpi(16), dpi(16)), bgcolor, theme.powerline_rl)
end
function theme.at_screen_connect(s)
-- Quake application
s.quake = lain.util.quake({ app = awful.util.terminal })
-- If wallpaper is a function, call it with the screen
local wallpaper = theme.wallpaper
if type(wallpaper) == "function" then
wallpaper = wallpaper(s)
end
gears.wallpaper.maximized(wallpaper, s, true)
-- Tags
awful.tag(awful.util.tagnames, s, awful.layout.layouts)
-- Create a promptbox for each screen
s.mypromptbox = awful.widget.prompt()
-- Create an imagebox widget which will contains an icon indicating which layout we're using.
-- We need one layoutbox per screen.
s.mylayoutbox = awful.widget.layoutbox(s)
s.mylayoutbox:buttons(my_table.join(
awful.button({}, 1, function () awful.layout.inc( 1) end),
awful.button({}, 2, function () awful.layout.set( awful.layout.layouts[1] ) end),
awful.button({}, 3, function () awful.layout.inc(-1) end),
awful.button({}, 4, function () awful.layout.inc( 1) end),
awful.button({}, 5, function () awful.layout.inc(-1) end)))
-- Create a taglist widget
s.mytaglist = awful.widget.taglist(s, awful.widget.taglist.filter.all, awful.util.taglist_buttons)
-- Create a tasklist widget
s.mytasklist = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, awful.util.tasklist_buttons)
-- Create the wibox
s.mywibox = awful.wibar({ position = "top", screen = s, height = dpi(16), bg = theme.bg_normal, fg = theme.fg_normal })
-- Add widgets to the wibox
s.mywibox:setup {
layout = wibox.layout.align.horizontal,
{ -- Left widgets
layout = wibox.layout.fixed.horizontal,
--spr,
s.mytaglist,
s.mypromptbox,
spr,
},
s.mytasklist, -- Middle widget
{ -- Right widgets
layout = wibox.layout.fixed.horizontal,
wibox.widget.systray(),
wibox.container.margin(scissors, dpi(4), dpi(8)),
arrow(theme.bg_normal, theme.bg_normal),
wibox.container.background(wibox.container.margin(wibox.widget { volicon, theme.volume.widget, layout = wibox.layout.align.horizontal }, dpi(4), dpi(7)), theme.bg_focus),
arrow(theme.bg_normal, "#343434"),
wibox.container.background(wibox.container.margin(wibox.widget { mailicon, theme.mail and theme.mail.widget, layout = wibox.layout.align.horizontal }, dpi(4), dpi(7)), "#343434"),
arrow("#343434", theme.bg_normal),
wibox.container.background(wibox.container.margin(wibox.widget { mpdicon, theme.mpd.widget, layout = wibox.layout.align.horizontal }, dpi(3), dpi(6)), theme.bg_focus),
arrow(theme.bg_normal, "#343434"),
wibox.container.background(wibox.container.margin(task, dpi(3), dpi(7)), "#343434"),
arrow("#343434", "#777E76"),
wibox.container.background(wibox.container.margin(wibox.widget { memicon, mem.widget, layout = wibox.layout.align.horizontal }, dpi(2), dpi(3)), "#777E76"),
arrow("#777E76", "#4B696D"),
wibox.container.background(wibox.container.margin(wibox.widget { cpuicon, cpu.widget, layout = wibox.layout.align.horizontal }, dpi(3), dpi(4)), "#4B696D"),
arrow("#4B696D", "#4B3B51"),
wibox.container.background(wibox.container.margin(wibox.widget { tempicon, temp.widget, layout = wibox.layout.align.horizontal }, dpi(4), dpi(4)), "#4B3B51"),
arrow("#4B3B51", "#CB755B"),
wibox.container.background(wibox.container.margin(wibox.widget { fsicon, theme.fs and theme.fs.widget, layout = wibox.layout.align.horizontal }, dpi(3), dpi(3)), "#CB755B"),
arrow("#CB755B", "#8DAA9A"),
wibox.container.background(wibox.container.margin(wibox.widget { baticon, bat.widget, layout = wibox.layout.align.horizontal }, dpi(3), dpi(3)), "#8DAA9A"),
arrow("#8DAA9A", "#C0C0A2"),
wibox.container.background(wibox.container.margin(wibox.widget { nil, neticon, net.widget, layout = wibox.layout.align.horizontal }, dpi(3), dpi(3)), "#C0C0A2"),
arrow("#C0C0A2", "#777E76"),
--wibox.container.background(wibox.container.margin(binclock.widget, dpi(4), dpi(8)), "#777E76"),
wibox.container.background(wibox.container.margin(convclock, dpi(4), dpi(8)), "#777E76"),
arrow("#777E76", "alpha"),
--
s.mylayoutbox,
},
}
end
return theme

View file

@ -0,0 +1,21 @@
[global_config]
enabled_plugins = SaveLastSessionLayout, LaunchpadBugURLHandler, LaunchpadCodeURLHandler, APTURLHandler
[keybindings]
[profiles]
[[default]]
background_darkness = 0.8
background_type = transparent
font = JetBrainsMono Nerd Font Mono 10
scrollback_lines = 15000
palette = "#282828:#cc241d:#98971a:#d79921:#458588:#b16286:#689d6a:#a89984:#928374:#fb4934:#b8bb26:#fabd2f:#83a598:#d3869b:#8ec07c:#ebdbb2"
use_system_font = False
copy_on_selection = True
[layouts]
[[default]]
[[[window0]]]
type = Window
parent = ""
[[[child1]]]
type = Terminal
parent = window0
[plugins]

View file

@ -0,0 +1,3 @@
setxkbmap -layout us -variant alt-intl
picom --daemon
exec awesome

View file

@ -0,0 +1,9 @@
#!/usr/bin/env zsh
APP_BIN="{{ application }}"
if [ ! -x "${APP_BIN}" ]; then
printf "%s: target not found" "${APP_BIN}" >&2
fi
exec "${APP_BIN}" "$@"

View file

@ -0,0 +1,105 @@
#############################################
# FILES #
#############################################
Section "Files"
ModulePath "/usr/lib/xorg/modules"
FontPath "/usr/share/fonts/X11/misc"
FontPath "/usr/share/fonts/X11/100dpi/:unscaled"
FontPath "/usr/share/fonts/X11/75dpi/:unscaled"
FontPath "/usr/share/fonts/X11/Type1"
FontPath "/usr/share/fonts/X11/100dpi"
FontPath "/usr/share/fonts/X11/75dpi"
FontPath "built-ins"
EndSection
#############################################
# MODULES #
#############################################
Section "Module"
Load "glx"
EndSection
#############################################
# INPUTDEVICES #
#############################################
Section "InputDevice"
Identifier "Mouse0"
Driver "mouse"
Option "Protocol" "auto"
Option "Device" "/dev/input/mice"
Option "ZAxisMapping" "4 5 6 7"
Option "CorePointer"
EndSection
Section "InputDevice"
Identifier "Keyboard0"
Driver "keyboard"
Option "CoreKeyboard"
Option "XkbRules" "xorg"
EndSection
#############################################
# DEVICES #
#############################################
Section "Device"
Identifier "Card0"
Driver "amdgpu"
Option "Monitor-HDMI-A-0" "VG248"
Option "HDMI-A-0" "VG248"
Option "Monitor-DisplayPort-2" "LG UWQHD"
Option "DisplayPort-2" "LG UWQHD"
Option "Monitor-DisplayPort-0" "LG ULTRAWIDE"
Option "DisplayPort-0" "LG ULTRAWIDE"
EndSection
#############################################
# SERVER LAYOUT #
#############################################
Section "ServerLayout"
Identifier "Layout0"
Screen 0 "Screen0" 0 0
InputDevice "Keyboard0" "CoreKeyboard"
InputDevice "Mouse0" "CorePointer"
Option "Xinerama" "0"
EndSection
#############################################
# SCREENS #
#############################################
Section "Screen"
Identifier "Screen0"
Device "Card0"
EndSection
#############################################
# MONITORS #
#############################################
Section "Monitor"
Identifier "VG248"
ModelName "VG248"
VendorName "ACI"
Option "DPMS" "on"
Option "PreferredMode" "1920x1080"
Option "LeftOf" "DisplayPort-2"
Option "Position" "0 520"
EndSection
Section "Monitor"
Identifier "LG UWQHD"
ModelName "LG UWQHD"
VendorName "GSM"
Option "LeftOf" "DisplayPort-0"
Option "DPMS" "on"
Option "PreferredMode" "3840x1600"
Option "Primary" "true"
Option "Position" "1920 0"
EndSection
Section "Monitor"
Identifier "LG ULTRAWIDE"
ModelName "LG ULTRAWIDE"
VendorName "GSM"
Option "DPMS" "on"
Option "PreferredMode" "3440x1440"
Option "Position" "5760 160"
EndSection

View file

@ -0,0 +1,3 @@
ansible_become_password: "{{ vault_root_pass }}"
ansible_host: "{{ vault_ansible_host }}"
ansible_user: "{{ vault_ssh_user }}"

View file

@ -0,0 +1,19 @@
ansible_become_password: "{{ vault_root_pass }}"
ansible_host: "{{ vault_ansible_host }}"
ansible_connection: local
custom_sysctl:
'fs.inotify.max_user_watches': 1048576
'vm.swappiness': 1
common_apt_packages:
- pcscd
- pinentry-curses
- radeontop
common_gitconfig_enable: true
common_gitconfig_username: "{{ vault_common_gitconfig_username }}"
common_gitconfig_email: "{{ vault_common_gitconfig_email }}"
common_gitconfig_force_sign: true
common_gitconfig_signingkey: "{{ vault_common_gitconfig_signingkey }}"
common_install_fonts: true

View file

@ -0,0 +1,19 @@
ansible_become_password: "{{ vault_root_pass }}"
ansible_host: "{{ vault_ansible_host }}"
ansible_connection: local
custom_sysctl:
'fs.inotify.max_user_watches': 1048576
'vm.swappiness': 1
common_apt_packages:
- pcscd
- pinentry-curses
- radeontop
common_gitconfig_enable: true
common_gitconfig_username: "{{ vault_common_gitconfig_username }}"
common_gitconfig_email: "{{ vault_common_gitconfig_email }}"
common_gitconfig_force_sign: true
common_gitconfig_signingkey: "{{ vault_common_gitconfig_signingkey }}"
common_install_fonts: true

View file

@ -1,2 +1,14 @@
all: all:
hosts: hosts:
external:
hosts:
main_desktop:
hosts:
internal:
hosts:
server:
hosts:

View file

@ -1,2 +1,3 @@
vault_custom_base_user_account: vault_custom_base_user_account: ""
vault_custom_github_token: vault_custom_github_token: ""
vault_custom_dockerhub_password: ""

View file

@ -1 +1,9 @@
--- ---
- name: setup internal infrastructure
hosts: internal
gather_facts: false
vars_files: ../inventory/vault.yml
tasks:
- include_vars: ../inventory/vault.yml
- ansible.builtin.import_playbook: nullified.infrastructure.main_desktop

View file

@ -12,7 +12,7 @@ USAGE
\$> ./$(basename "$0") [option [option...]] [file_path] \$> ./$(basename "$0") [option [option...]] [file_path]
ARGUMENTS ARGUMENTS
file_path file path where the file will be saved; defaults to '\$HOME/.ansible.cfg'; file_path file path where the file will be saved;
if neither this argument nor the environment variable \$ANSIBLE_CFG_PATH is provided, the if neither this argument nor the environment variable \$ANSIBLE_CFG_PATH is provided, the
content will be printed on stdout. content will be printed on stdout.