Codified PC Setup.
source <(curl -s https://gist.githubusercontent.com/AlexAtkinson/27b12f4dfda31b1b74fcab3fc9a6d192/raw/init.sh)| [defaults] | |
| timeout = 60 | |
| inventory = ~/.ansible/hosts | |
| remote_tmp = ~/.ansible/tmp | |
| forks = 150 | |
| transport = smart | |
| gathering = smart | |
| roles_path = ~/.ansible/roles | |
| host_key_checking = False | |
| log_path = /var/log/ansible.log | |
| module_name = shell | |
| fact_caching = jsonfile | |
| fact_caching_connection = $HOME/.ansible/facts | |
| fact_caching_timeout = 600 | |
| retry_files_save_path = ~/.ansible/retry | |
| [ssh_connection] | |
| pipelining = True | |
| scp_if_ssh = True | |
| [accelerate] | |
| accelerate_port = 5099 | |
| accelerate_timeout = 30 | |
| accelerate_connect_timeout = 5.0 | |
| accelerate_daemon_timeout = 30 |
| - name: "Apt: Safe upgrade" | |
| ansible.builtin.apt: | |
| upgrade: safe | |
| update_cache: true | |
| cache_valid_time: 600 | |
| - name: "Apt: Install Packages" | |
| ansible.builtin.apt: | |
| pkg: | |
| - util-linux | |
| - vim | |
| - shellcheck | |
| - openssl | |
| - python3 | |
| - python3-pip | |
| - python3-pytest | |
| - curl | |
| - wget | |
| - whois | |
| - netcat-openbsd | |
| - tar | |
| - gzip | |
| - unzip | |
| - bat | |
| - bc | |
| - yq | |
| - jq | |
| - less | |
| - findutils | |
| - ca-certificates | |
| - apt-transport-https | |
| - software-properties-common | |
| - g++ | |
| - gccgo | |
| - pkg-config | |
| - make | |
| - cmake | |
| - check | |
| - valgrind | |
| - build-essential | |
| - libbz2-dev | |
| - libcurl4-openssl-dev | |
| - libjson-c-dev | |
| - libmilter-dev | |
| - libncurses5-dev | |
| - libpcre2-dev | |
| - libxml2-dev | |
| - libasound2-dev | |
| - mplayer | |
| - vlc | |
| - multitail | |
| - mise | |
| - nordvpn-gui | |
| - wipe | |
| - guake | |
| - tmux | |
| - btop | |
| - cursor | |
| - ansible | |
| - ansible-lint | |
| - dconf-cli | |
| - xclip | |
| - name: "Apt: Download Mise Key" | |
| ansible.builtin.get_url: | |
| url: https://mise.jdx.dev/gpg-key.pub | |
| dest: /tmp/mise-gpg-key.pub | |
| mode: '0644' | |
| when: "'/etc/apt/trusted.gpg.d/mise.gpg' is not exists" | |
| - name: "Apt: Dearmor Mise Key" | |
| ansible.builtin.shell: gpg --batch --dearmor -o /etc/apt/trusted.gpg.d/mise.gpg /tmp/mise-gpg-key.pub | |
| args: | |
| creates: /etc/apt/trusted.gpg.d/mise.gpg | |
| when: "'/etc/apt/trusted.gpg.d/mise.gpg' is not exists" | |
| - name: "Apt: Add Mise repository" | |
| ansible.builtin.apt_repository: | |
| repo: deb [signed-by=/etc/apt/trusted.gpg.d/mise.gpg arch=amd64] https://mise.jdx.dev/deb stable main | |
| state: present | |
| filename: mise | |
| - name: "NordVPN: Download Key" | |
| ansible.builtin.get_url: | |
| url: https://repo.nordvpn.com/gpg/nordvpn_public.asc | |
| dest: /etc/apt/trusted.gpg.d/nordvpn_public.asc | |
| mode: '0644' | |
| when: "'/etc/apt/trusted.gpg.d/nordvpn_public.asc' is not exists" | |
| - name: "NordVPN: Add Repository" | |
| ansible.builtin.apt_repository: | |
| repo: deb https://repo.nordvpn.com/deb/nordvpn/debian stable main | |
| state: present | |
| filename: nordvpn-app | |
| - name: "Opera: Check installed version" | |
| ansible.builtin.shell: opera --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' | |
| ignore_errors: true | |
| changed_when: false | |
| register: installed_opera_version | |
| - name: "Opera: Report version" | |
| ansible.builtin.debug: | |
| msg: "Installed: {{ installed_opera_version.stdout }}" | |
| - name: "Opera: Download" | |
| ansible.builtin.get_url: | |
| url: https://download5.operacdn.com/ftp/pub/opera/desktop/{{ opera_version }}/linux/opera-stable_{{ opera_version }}_amd64.deb | |
| dest: /tmp/opera-{{ opera_version }}_amd64.deb | |
| mode: '0644' | |
| when: opera_version != installed_opera_version.stdout | |
| - name: "Opera: Install" | |
| ansible.builtin.apt: | |
| deb: /tmp/opera-{{ opera_version }}_amd64.deb | |
| when: opera_version != installed_opera_version.stdout | |
| - name: "VS Code: Check Latest Version" | |
| shell: | | |
| curl -sSkIL "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64" | \ | |
| grep Content-Disposition | \ | |
| cut -d'_' -f2 | \ | |
| cut -d'-' -f1 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: vscode_latest_version | |
| - name: "VS Code: Check Installed Version" | |
| shell: | | |
| /usr/bin/code --version | head -n1 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: vscode_installed_version | |
| - name: "VS Code: Report Versions" | |
| debug: | |
| msg: "Installed: {{ vscode_installed_version.stdout }}, Latest: {{ vscode_latest_version.stdout }}" | |
| - name: "VS Code: Check Latest Version" | |
| shell: | | |
| curl -sSkIL "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64" | \ | |
| grep Content-Disposition | \ | |
| cut -d'_' -f2 | \ | |
| cut -d'-' -f1 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: vscode_latest_version | |
| - name: "VS Code: Check Installed Version" | |
| shell: | | |
| /usr/bin/code --no-sandbox --user-data-dir="$HOME/.vscode" --version | head -n1 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: vscode_installed_version | |
| - name: "VS Code: Report Versions" | |
| debug: | |
| msg: "Installed: {{ vscode_installed_version.stdout }}, Latest: {{ vscode_latest_version.stdout }}" | |
| - name: "VS Code: Download" | |
| ansible.builtin.get_url: | |
| url: https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64 | |
| dest: temp_assets_dir.path | |
| mode: '0644' | |
| register: vscode_deb_download | |
| when: vscode_installed_version.stdout != vscode_latest_version.stdout | |
| - name: "VS Code: Install" | |
| ansible.builtin.apt: | |
| deb: vscode_deb_download.dest | |
| when: vscode_installed_version.stdout != vscode_latest_version.stdout |
| # Bash # TODO: Re-enable once $- bug is resolved in 5.3 | |
| # - name: BASH - Check installed version | |
| # shell: /bin/bash -c "echo \$BASH_VERSION | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p'" | |
| # ignore_errors: yes | |
| # changed_when: false | |
| # register: installed_bash_version | |
| # - name: BASH - Report version | |
| # debug: | |
| # msg: "Installed Version: {{ installed_bash_version.stdout }}" | |
| # - name: BASH - Download bash-{{ bash_version_asset_label }}.tar.gz | |
| # ansible.builtin.get_url: | |
| # #url: https://ftp.gnu.org/gnu/bash/bash-{{ bash_version_asset_label }}.tar.gz | |
| # url: https://mirror.csclub.uwaterloo.ca/gnu/bash/bash-{{ bash_version_asset_label }}.tar.gz | |
| # dest: /tmp/ | |
| # register: bash_download | |
| # when: bash_version != installed_bash_version.stdout | |
| # - name: BASH - Unpack bash-{{ bash_version_asset_label }}.tar.gz | |
| # ansible.builtin.unarchive: | |
| # src: "{{ bash_download.dest }}" | |
| # dest: /tmp/ | |
| # when: bash_version != installed_bash_version.stdout | |
| # - name: BASH - Run ./configure | |
| # ansible.builtin.shell: ./configure | |
| # args: | |
| # chdir: /tmp/bash-{{ bash_version_asset_label }} | |
| # creates: Makefile | |
| # when: bash_version != installed_bash_version.stdout | |
| # - name: BASH - Run make | |
| # ansible.builtin.shell: make | |
| # args: | |
| # chdir: /tmp/bash-{{ bash_version_asset_label }} | |
| # creates: Makefile | |
| # when: bash_version != installed_bash_version.stdout | |
| # - name: BASH - Run make prefix=/ install | |
| # ansible.builtin.shell: make prefix=/ install | |
| # args: | |
| # chdir: /tmp/bash-{{ bash_version_asset_label }} | |
| # when: bash_version != installed_bash_version.stdout | |
| # Applications Directory | |
| # ---------------------- | |
| - name: Create {{ local_applications_dir }} directory | |
| ansible.builtin.file: | |
| path: "{{ local_applications_dir }}" | |
| state: directory | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| when: local_applications_dir is not exists | |
| # ffmpeg | |
| # ------ | |
| - name: Create ffmpeg directories | |
| ansible.builtin.file: | |
| path: "{{ item }}" | |
| state: directory | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| when: item is not exists and item is defined | |
| loop: "{{ ffmpeg_dirs }}" | |
| - name: ffmpeg - Check installed version | |
| shell: "cat {{ ffmpeg_dirs[0] }}/ffmpeg.version 2>/dev/null" | |
| ignore_errors: yes | |
| changed_when: false | |
| register: installed_ffmpeg_version | |
| - name: ffmpeg - Check latest version | |
| shell: | | |
| curl -Ss "https://api.github.com/repos/nwjs-ffmpeg-prebuilt/nwjs-ffmpeg-prebuilt/tags" \ | |
| | jq -r '.[].name' \ | |
| | grep -E "^[v]?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$" \ | |
| | head -n 1 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: latest_ffmpeg_version | |
| - name: ffmpeg - Report Versions | |
| debug: | |
| msg: "Installed: {{ installed_ffmpeg_version.stdout}}, Latest: {{ latest_ffmpeg_version.stdout }}" | |
| - name: Extract ffmpeg | |
| ansible.builtin.unarchive: | |
| src: "https://github.com/nwjs-ffmpeg-prebuilt/nwjs-ffmpeg-prebuilt/releases/download/{{ latest_ffmpeg_version.stdout }}/{{ latest_ffmpeg_version.stdout }}-linux-x64.zip" | |
| dest: "{{ item }}" | |
| remote_src: yes | |
| loop: "{{ ffmpeg_dirs }}" | |
| when: installed_ffmpeg_version.stdout != latest_ffmpeg_version.stdout | |
| - name: Create ffmpeg version files | |
| copy: | |
| dest: "{{ item }}/ffmpeg.version" | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| content: | | |
| {{ latest_ffmpeg_version.stdout }} | |
| loop: "{{ ffmpeg_dirs }}" | |
| when: installed_ffmpeg_version.stdout != latest_ffmpeg_version.stdout | |
| # yq | |
| # -- | |
| - name: yq - Check installed version | |
| shell: | | |
| yq --version | awk '{print $NF}' | |
| ignore_errors: yes | |
| changed_when: false | |
| register: installed_yq_version | |
| - name: yq - Check latest version | |
| shell: | | |
| curl -Ss "https://api.github.com/repos/mikefarah/yq/tags" \ | |
| | jq -r '.[].name' \ | |
| | grep -E "^[v]?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$" \ | |
| | head -n 1 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: latest_yq_version | |
| - name: yq - Report Versions | |
| debug: | |
| msg: "Installed: {{ installed_yq_version.stdout}}, Latest: {{ latest_yq_version.stdout }}" | |
| # Downloads multiple times... It's fine for this small package. For larger packages, dl to /tmp/foo. | |
| # Cause: Multiple '-C' args are not permitted with tar. | |
| - name: yq - Download and install yq | |
| ansible.builtin.unarchive: | |
| src: "https://github.com/mikefarah/yq/releases/download/{{ latest_yq_version.stdout }}/yq_{{ ansible_system|lower }}_{{ 'amd64' if ansible_architecture == 'x86_64' else ansible_architecture }}.tar.gz" | |
| dest: /usr/local/bin | |
| remote_src: yes | |
| extra_opts: | |
| - --transform | |
| - s/yq_linux_amd64/yq/ | |
| - -C | |
| - /usr/local/bin | |
| - ./yq_linux_amd64 | |
| when: installed_yq_version.stdout != latest_yq_version.stdout | |
| - name: yq - Download and install yq man page | |
| ansible.builtin.unarchive: | |
| src: "https://github.com/mikefarah/yq/releases/download/{{ latest_yq_version.stdout }}/yq_{{ ansible_system|lower }}_{{ 'amd64' if ansible_architecture == 'x86_64' else ansible_architecture }}.tar.gz" | |
| dest: /usr/share/man/man1 | |
| remote_src: yes | |
| extra_opts: | |
| - -C | |
| - /usr/share/man/man1 | |
| - yq.1 | |
| - name: Create ffmpeg version files | |
| copy: | |
| dest: "{{ item }}/ffmpeg.version" | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| content: | | |
| {{ latest_ffmpeg_version.stdout }} | |
| loop: "{{ ffmpeg_dirs }}" | |
| when: installed_ffmpeg_version.stdout != latest_ffmpeg_version.stdout | |
| # pCloud | |
| # ------ | |
| - name: Ensure {{ pcloud_local_share_dir }} directory exists | |
| ansible.builtin.file: | |
| path: "{{ pcloud_local_share_dir }}" | |
| state: directory | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| when: pcloud_local_share_dir is not exists | |
| - name: Get pCloud Download URL | |
| shell: | | |
| curl -sS 'https://api.pcloud.com/getpublinkdownload?code=XZNtR95ZctUIq8zYVD7eSKotwGMx7kDWVtzV' \ | |
| | jq -r '"https://" + .hosts[0] + .path' | |
| register: pcloud_dl_url | |
| - name: Report pCloud Download URL | |
| debug: | |
| msg: "URL: {{ pcloud_dl_url.stdout }}" | |
| - name: Download pCloud | |
| ansible.builtin.get_url: | |
| url: "{{ pcloud_dl_url.stdout }}" | |
| force: true | |
| dest: /usr/local/bin/pcloud | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: u=rwx,g=rwx,o=rx | |
| register: pcloud_download | |
| - name: Download pCloud Icon | |
| ansible.builtin.get_url: | |
| url: https://gist.githubusercontent.com/AlexAtkinson/86e7530f9a0b7c2744592e9fab54764b/raw/pcloud.svg | |
| dest: /home/{{ ansible_env.SUDO_USER }}/.local/share/pCloud/pcloud.svg | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: u=rw,g=rw,o=r | |
| # Defend against pcloud download process changes... | |
| - name: Check pCloud Download File Type | |
| shell: file -b {{ pcloud_download.dest }} | cut -d' ' -f1 | |
| register: pcloud_download_check | |
| failed_when: | |
| - '"ELF" not in pcloud_download_check.stdout' | |
| - name: Create pCloud Menu Entry | |
| copy: | |
| dest: /home/{{ ansible_env.SUDO_USER }}/.local/share/applications/pcloud.desktop | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| content: | | |
| [Desktop Entry] | |
| Icon=/home/{{ ansible_env.SUDO_USER }}/.local/share/pCloud/pcloud.svg | |
| Exec=/usr/local/bin/pcloud %u | |
| Version=1.0 | |
| Type=Application | |
| Categories=Network | |
| Name=pCloud | |
| StartupWMClass=pcloud | |
| MimeType=application/x-executable | |
| X-GNOME-Autostart-enabled=true | |
| StartupNotify=true | |
| X-GNOME-Autostart-Delay=10 | |
| X-MATE-Autostart-Delay=10 | |
| X-KDE-autostart-after=panel | |
| # qBittorrent | |
| # ----------- | |
| - name: qBittorrent - Check installed version | |
| shell: qBittorrent --version 2>/dev/null | cut -d' ' -f2 | |
| ignore_errors: yes | |
| changed_when: false | |
| register: installed_qbittorrent_version | |
| - name: qBittorrent - Check latest version | |
| shell: | | |
| curl -s https://www.qbittorrent.org/download | \ | |
| grep "Current version" | \ | |
| awk '{print $NF}' | \ | |
| cut -d'<' -f1 | \ | |
| sed 's/^v//' | |
| ignore_errors: yes | |
| changed_when: false | |
| register: latest_qbittorrent_version | |
| - name: qBittorrent - Report Versions | |
| debug: | |
| msg: "Installed: {{ installed_qbittorrent_version.stdout }}, Latest: {{ latest_qbittorrent_version.stdout }}" | |
| - name: Create {{ qbittorrent_dir }} directory | |
| ansible.builtin.file: | |
| path: "{{ qbittorrent_dir }}" | |
| state: directory | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| when: qbittorrent_dir is not exists | |
| - name: Download qBittorrent | |
| ansible.builtin.get_url: | |
| url: https://gigenet.dl.sourceforge.net/project/qbittorrent/qbittorrent-appimage/qbittorrent-{{ latest_qbittorrent_version.stdout }}/qbittorrent-{{ latest_qbittorrent_version.stdout }}_x86_64.AppImage | |
| dest: /usr/local/bin/qBittorrent | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: u=rwx,g=rwx,o=rx | |
| when: qbittorrent_version != installed_qbittorrent_version.stdout | |
| - name: Download qBittorrent Icon | |
| ansible.builtin.get_url: | |
| url: https://gist.githubusercontent.com/AlexAtkinson/f0bf8c89dda97ef42b5ba3575ab20363/raw/icon_qbittorrent.svg | |
| dest: "{{ qbittorrent_icon }}" | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: u=rw,g=rw,o=r | |
| when: qbittorrent_icon is not exists | |
| - name: Create qBittorrent Menu Entry | |
| copy: | |
| dest: "{{ qbittorrent_menu_entry }}" | |
| owner: "{{ ansible_env.SUDO_USER }}" | |
| group: "{{ ansible_env.SUDO_USER }}" | |
| mode: '0775' | |
| content: | | |
| [Desktop Entry] | |
| Icon="{{ qbittorrent_icon }}" | |
| Exec=/usr/local/bin/qBittorrent %u | |
| Version=1.0 | |
| Type=Application | |
| Categories=Network | |
| Name=qBittorrent | |
| StartupWMClass=qbittorrent | |
| MimeType=application/x-executable | |
| X-GNOME-Autostart-enabled=false | |
| StartupNotify=true | |
| X-GNOME-Autostart-Delay=10 | |
| X-MATE-Autostart-Delay=10 | |
| X-KDE-autostart-after=panel | |
| when: qbittorrent_menu_entry is not exists | |
| # Libre Office | |
| # ------------ | |
| - name: Libre Office - Check installed version | |
| shell: | | |
| libreoffice --version \ | |
| | head -n 1 \ | |
| | grep -oE "[v]?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*(\.(0|[1-9][0-9]*))?)" | |
| ignore_errors: yes | |
| changed_when: false | |
| register: installed_libreoffice_version | |
| - name: Libre Office - Check latest version | |
| shell: | | |
| curl -sS http://downloadarchive.documentfoundation.org/libreoffice/old/latest/deb/x86_64/ \ | |
| | grep -o \"LibreOffice_.*_Linux_x86-64_deb.tar.gz\" \ | |
| | tr -d \" \ | |
| | grep -oE '[v]?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*(\.(0|[1-9][0-9]*))?)' | |
| ignore_errors: yes | |
| changed_when: false | |
| register: latest_libreoffice_version | |
| - name: Libre Office - Report Versions | |
| debug: | |
| msg: "Installed: {{ installed_libreoffice_version.stdout }}, Latest: {{ latest_libreoffice_version.stdout }}" |
| --- | |
| - name: Install Packages | |
| hosts: localhost | |
| gather_facts: true | |
| vars: | |
| # The private config url may be any private repo or "secret" gist and must contain | |
| # a bash script which can be sourced. | |
| private_config_git_url: [email protected]:AlexAtkinson/private_config.git | |
| local_applications_dir: /home/{{ ansible_env.SUDO_USER }}/.local/share/applications | |
| bash_version: 5.3.0 | |
| bash_version_asset_label: 5.3 | |
| ffmpeg_dirs: | |
| - /usr/local/bin/ffmpeg | |
| - /usr/lib/chromium-browser | |
| - /usr/lib/x86_64-linux-gnu/opera | |
| opera_version: 125.0.5729.15 | |
| pcloud_local_share_dir: /home/{{ ansible_env.SUDO_USER }}/.local/share/pCloud | |
| qbittorrent_version: 5.3.0 | |
| qbittorrent_dir: /home/{{ ansible_env.SUDO_USER }}/.local/share/qBittorrent | |
| qbittorrent_icon: /home/{{ ansible_env.SUDO_USER }}/.local/share/qBittorrent/qBittorrent.svg | |
| qbittorrent_menu_entry: /home/{{ ansible_env.SUDO_USER }}/.local/share/applications/qbittorrent.desktop | |
| qbittorrent_icon_url: https://gist.githubusercontent.com/AlexAtkinson/f0bf8c89dda97ef42b5ba3575ab20363/raw/icon_qbittorrent.svg | |
| qbittorrent_dl_url: https://gigenet.dl.sourceforge.net/project/qbittorrent/qbittorrent-appimage/qbittorrent-{{ qbittorrent_version }}/qbittorrent-{{ qbittorrent_version }}_x86_64.AppImage | |
| tasks: | |
| - name: "Create temp directory for this build" | |
| ansible.builtin.tempfile: | |
| state: directory | |
| prefix: "ansible_{{ now(utc=true, fmt='%Y-%m-%dT%H-%M-%SZ') }}_" | |
| register: temp_dir | |
| - name: "Create temp assets directory for use across multiple builds" | |
| ansible.builtin.file: | |
| path: /tmp/ansible_assets_dir | |
| state: directory | |
| mode: '0755' | |
| register: temp_assets_dir | |
| - name: "Apt: Install Packages" | |
| ansible.builtin.include_tasks: | |
| file: ansible_apt.yml | |
| when: ansible_facts['os_family'] == 'Debian' | |
| - name: "Permissions" | |
| ansible.builtin.include_tasks: | |
| file: ansible_permissions.yml | |
| - name: "Custom Installs" | |
| ansible.builtin.include_tasks: | |
| file: ansible_custom_installs.yml | |
| roles: | |
| - geerlingguy.docker |
| - name: Ensure group 'docker' exists | |
| ansible.builtin.group: | |
| name: docker | |
| state: present | |
| - name: Add user '{{ ansible_env.SUDO_USER }}' to 'docker' group | |
| ansible.builtin.user: | |
| name: '{{ ansible_env.SUDO_USER }}' | |
| groups: docker | |
| append: true | |
| - name: Ensure group 'nordvpn' exists | |
| ansible.builtin.group: | |
| name: nordvpn | |
| state: present | |
| - name: Add user '{{ ansible_env.SUDO_USER }}' to 'nordvpn' group | |
| ansible.builtin.user: | |
| name: '{{ ansible_env.SUDO_USER }}' | |
| groups: nordvpn | |
| append: true |
| #!/usr/bin/env bash | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Clone the GIST and run setup.sh | |
| # | |
| # AUTHOR : Alex Atkinson | |
| # AUTHOR_EMAIL : | |
| # AUTHOR_GITHUB : https://github.com/AlexAtkinson | |
| # AUTHOR_LINKEDIN : https://www.linkedin.com/in/alex--atkinson | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| GIST='27b12f4dfda31b1b74fcab3fc9a6d192' | |
| TMP_DIR=$(mktemp -d) | |
| echo -e "TMP_DIR: $TMP_DIR" | |
| cd "$TMP_DIR" || exit 1 | |
| git clone "[email protected]:${GIST}.git" | |
| cd "$GIST" || exit 1 | |
| ./setup.sh |
| #!/usr/bin/env bash | |
| # shellcheck disable=2001 | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # | |
| # setup.sh | |
| # | |
| # SYNOPSIS | |
| # Setup a local linux environment. | |
| # | |
| # TODO | |
| # - Add Startup items | |
| # - Replace some bash with facts. `ansible localhost -m setup`` (~/.ansible_facts) | |
| # - ansible_system | |
| # - ansible_distribution | |
| # - ansible_architecture | |
| # - Cleanup articles from previously written scripts that hint at multi-os support. | |
| # - ffmpeg | |
| # - plex ( mask permissions fix...) | |
| # https://askubuntu.com/questions/150909/plex-wont-enter-my-home-directory-or-other-partitions | |
| # - libre office | |
| # https://www.libreoffice.org/download/download-libreoffice/ | |
| # sudo dpkg -i *.deb | |
| # - fonts | |
| # https://juliamono.netlify.app | |
| # - VPN | |
| # https://www.reddit.com/r/nordvpn/comments/1ccc2e5/split_tunnelling_on_linux_server | |
| # - ClamAV | |
| # https://docs.clamav.net/manual/Installing/Installing-from-source-Unix.html | |
| # https://docs.clamav.net/manual/Installing/Add-clamav-user.html | |
| # https://docs.clamav.net/faq/faq-freshclam.html | |
| # - Rustup | |
| # - ~/.config/tmux/tmux.conf | |
| # set -g default-terminal "tmux-256color" | |
| # set -ag terminal-overrides ",xterm-256color:RGB" | |
| # - ~/.vimrc | |
| # - Network Power Save | |
| # /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf Change 3 to 2 | |
| # [connection] | |
| # wifi.powersave = 2 | |
| # - Fonts | |
| # - Backup and restore | |
| # - pCloud content | |
| # - system dictionary | |
| # - gitleaks | |
| # - go install github.com/yannh/kubeconform/cmd/kubeconform@latest | |
| # | |
| # Disable hardware acceleration to stop site render problems opera://flags/#use-angle | |
| # install nerdfonts to ~/.local/share/fonts | |
| # Disable GPU Acceleration in Chromium browsers. | |
| # | |
| # - Opera opera://settings/system | |
| # | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # Traps | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| trap cd - || true EXIT | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # Environment Settings | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| set u | |
| set -o pipefail | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # Variables | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| THIS_SCRIPT="${0##*/}" | |
| DIR_NAME="${PWD##*/}" | |
| PARENT_DIR_PATH="${PWD%/*}" | |
| PARENT_DIR_NAME="${PARENT_DIR_PATH##*/}" | |
| LOG_TO_FILE="true" | |
| [[ "$LOG_TO_FILE" == "true" ]] && LOG_FILE="$HOME/pc_setup.log" | |
| ANSIBLE_LOCALHOST_WARNING=False | |
| ANSIBLE_INVENTORY_UNPARSED_WARNING=False | |
| CONFIG_REPO='[email protected]:AlexAtkinson/pc_configs_personal.git' | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # Functions | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Helper for `date` -- format for logging | |
| # Outputs: | |
| # - Date format compliant with ISO8601 + nano to the third | |
| # place in UTC. IE: 1970-01-01T00:00:00.000Z | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function dts() { date --utc +'%FT%T.%3NZ'; } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Syslog style error code handling with colors to improve | |
| # DX. | |
| # Notes: | |
| # - User friendly way of achieving consistent log and | |
| # script feedback. | |
| # - Named loggerx to avoid clobbering logger if present. | |
| # - There is no 9th severity level in RFC5424. | |
| # - Delimiter sequence: space-hyphen-space ( - ) | |
| # - Accepts multi-line logging. | |
| # IE: loggerx INFO "This is a | |
| # multi-line | |
| # log entry" | |
| # Globals: | |
| # LOG_TO_FILE | |
| # LOG_FILE | |
| # Arguments: | |
| # - $1 Log Level | |
| # - $2- Message | |
| # Depends On: | |
| # - function: dts | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # shellcheck disable=2034 | |
| function loggerx() { | |
| local MSG LOG RAW S C C_EMERGENCY C_ALERT C_CRITICAL \ | |
| C_ERROR C_WARNING C_NOTICE C_INFO C_DEBUG C_SUCCESS | |
| # Reverse lookup dict | |
| C_EMERGENCY='\e[01;30;41m' # EMERGENCY | |
| C_ALERT='\e[01;31;43m' # ALERT | |
| C_CRITICAL='\e[01;97;41m' # CRITICAL | |
| C_ERROR='\e[01;31m' # ERROR | |
| C_WARNING='\e[01;33m' # WARNING | |
| C_NOTICE='\e[01;30;107m' # NOTICE | |
| C_INFO='\e[01;39m' # INFO | |
| C_DEBUG='\e[01;97;46m' # DEBUG | |
| C_SUCCESS='\e[01;32m' # SUCCESS | |
| # Color lookup & spacing | |
| case $1 in | |
| "EMERGENCY") C="C_${1}"; S=$(printf "%-38s" '') ;; | |
| "ALERT") C="C_${1}"; S=$(printf "%-34s" '') ;; | |
| "CRITICAL") C="C_${1}"; S=$(printf "%-37s" '') ;; | |
| "ERROR") C="C_${1}"; S=$(printf "%-34s" '') ;; | |
| "WARNING") C="C_${1}"; S=$(printf "%-36s" '') ;; | |
| "NOTICE") C="C_${1}"; S=$(printf "%-35s" '') ;; | |
| "INFO") C="C_${1}"; S=$(printf "%-33s" '') ;; | |
| "DEBUG") C="C_${1}"; S=$(printf "%-34s" '') ;; | |
| "SUCCESS") C="C_${1}"; S=$(printf "%-36s" '') ;; | |
| esac | |
| # Final formatting | |
| MSG=$(echo -e "$(dts) - ${!C}${1}\e[0m - $(sed 's/ */ /g'<<<"${*:2}")") | |
| LOG=$(sed -z 's/\n$//g'<<<"${MSG}" | sed -z "s/\n/\n${S}/g") | |
| RAW="$THIS_SCRIPT - $1 - $(sed 's/ */ /g'<<<"${*:2}")" | |
| # Main Operation | |
| if [[ "$LOG_TO_FILE" == "true" ]]; then | |
| echo "$LOG" | tee -a "$LOG_FILE" | |
| else | |
| echo "$LOG" | |
| fi | |
| echo "$RAW" | logger | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # The `et` and `rc` functions are used to provide a simple | |
| # and consistent method of including basic exit code | |
| # validation and logging. | |
| # - et Echo Task | |
| # - rc Result Check | |
| # Arguments: | |
| # $TASK | |
| # Depends On: | |
| # - function: loggerx | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function et() { loggerx INFO "TASK START: $TASK..."; } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Arguments: | |
| # - $1 The expected exit code. | |
| # - $2 Passed in $? exit code. | |
| # - $3 If KILL is passed then exit with passed exit | |
| # code. | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function rc() { | |
| local EXIT_CODE=$? | |
| if [[ "$1" -eq "$EXIT_CODE" ]] ; then | |
| loggerx SUCCESS "TASK END: $TASK." | |
| else | |
| loggerx CRITICAL "TASK END: $TASK (exit code: $EXIT_CODE)" | |
| [[ "$3" == "KILL" ]] && exit "$2" | |
| fi | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Report the name of the OS distribution. | |
| # Outputs: | |
| # The NAME field from the /etc/*-release file | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function check_os_distro() { | |
| DISTRO=$(awk -F= '$1=="NAME" { gsub(/"/,"",$2); print $2 }' /etc/*-release) | |
| export DISTRO | |
| echo "$DISTRO" | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Report the architecture of the system. | |
| # Outputs: | |
| # Print the system architecture as defined by `uname -m` | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function check_os_arch() { | |
| case $(uname -m) in | |
| arm64|aarch64) ARCH="ARM64" ;; | |
| armhf|armv7*) ARCH="ARM32_COMPAT" ;; | |
| armv8*) ARCH="ARM64_COMPAT" ;; | |
| i*86*) ARCH="x86" ;; | |
| amd64|x86_64*) ARCH="x64" ;; | |
| *) ARCH="unknown: $ARCH" ;; | |
| esac | |
| export ARCH | |
| echo "$ARCH" | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Report the OS Type | |
| # Outputs: | |
| # The type of system as defined by the $OSTYPE variable. | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function check_os_type() { | |
| case "$OSTYPE" in | |
| solaris*) OS_TYPE="SOLARIS" ;; | |
| darwin*) OS_TYPE="OSX" ;; | |
| linux*) OS_TYPE="LINUX" ;; | |
| bsd*) OS_TYPE="BSD" ;; | |
| msys*) OS_TYPE="WINDOWS" ;; | |
| cygwin*) OS_TYPE="CYGWIN" ;; | |
| *) OS_TYPE="unknown: $OSTYPE" ;; | |
| esac; | |
| export OS_TYPE | |
| echo "$OS_TYPE" | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # DX friendly user prompts. | |
| # Arguments: | |
| # $1 Quoted prompt. IE: "Proceed?" | |
| # $2 Default Option. IE: Y | |
| # Outputs: | |
| # Exit Code matching response. | |
| # Example: | |
| # ask "Proceed?" Y | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function ask { | |
| while true; do | |
| if [ "${2:-}" = "Y" ]; then | |
| prompt="Y/n" | |
| default=Y | |
| elif [ "${2:-}" = "N" ]; then | |
| prompt="y/N" | |
| default=N | |
| elif [ "${2:-}" = "Range" ]; then | |
| prompt="${3:-}" | |
| default=0 | |
| else | |
| prompt="y/n" | |
| default= | |
| fi | |
| read -rp $"$1 [$prompt]: " reply | |
| if [ -z "$reply" ]; then | |
| reply=$default | |
| fi | |
| case "$reply" in | |
| Y*|y*|^[1-9][0-9]*$) return 0 ;; | |
| N*|n*|0*) return 1 ;; | |
| esac | |
| done | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Check SSH Authentication to Github | |
| # Notes: | |
| # - As a function to facilitate use with `while` | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function check_ssh_authentication_to_github() { | |
| TASK="Test SSH Authentication to GitHub"; et | |
| ssh -o "StrictHostKeyChecking accept-new" -T [email protected] 2>&1 | grep -q 'successfully authenticated' | |
| rc 0 | |
| } | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # Check SSH Authentication to CONFIG_REPO | |
| # Notes: | |
| # - As a function to facilitate use with `while` | |
| # Arguments: | |
| # $CONFIG_REPO | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| function check_ssh_authentication_to_config_repo() { | |
| TASK="Test SSH Authentication to Private Config Repo"; et | |
| git ls-remote "$CONFIG_REPO" >/dev/null 2>&1 | |
| rc 0 | |
| } | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| # Main Operations | |
| # ---------------------------------------------------------------------------------------------------------------------- | |
| TASK="Confirm script execution" | |
| if ! ask "This script configures a Debian based OS to the preferences of Alex Atkinson. Proceed?" Y; then | |
| false; rc 0 KILL | |
| fi | |
| # A | |
| TASK="Confirm installation of private configurations" | |
| if ! ask "Additionally install private configurations" Y; then | |
| false; rc 0 KILL | |
| else | |
| CONFIG_INSTALL=true | |
| TASK="Prompt for configuration archive password" | |
| read -rs -p "Enter configuration archive password: " CONFIG_ARCHIVE_PASS | |
| rc 0 | |
| fi | |
| TASK="Confirm sudo privilege" | |
| loggerx WARNING "SUDO privilege required. | |
| By proceeding you agree that you have reviewed the script content and trust the author. | |
| Note specifically that this program is provided without any warranty or guarantee." | |
| if ! ask "Confirm SUDO privilege" Y; then | |
| false; rc 0 KILL | |
| else | |
| sudo true; rc 0 | |
| fi | |
| TASK="Create SSH Key"; et | |
| if [[ ! -f "$HOME/.ssh/id_ed25519" ]]; then | |
| ssh-keygen -t ed25519 -C "[email protected]" -f "$HOME/.ssh/id_ed25519" -N "" -y | |
| rc 0 | |
| else | |
| loggerx NOTICE "SSH Key already exists. Skipping creation." | |
| true; rc 0 | |
| fi | |
| # Setup User bashrc | |
| # ------------ | |
| TASK="Detect target rc file"; et | |
| [[ -f $HOME/.bash_profile && ${SHELL##*/} =~ "bash" ]] && TARG="$HOME/.bash_profile" # MacOS Bash | |
| [[ -f $HOME/.zshrc && ${SHELL##*/} =~ "zsh" ]] && TARG="$HOME/.zshrc" # MacOS ZSH | |
| [[ -f $HOME/.bashrc && ${SHELL##*/} =~ "bash" ]] && TARG="$HOME/.bashrc" # Linux | |
| rc 0 KILL | |
| TARG_BACKUP="$TARG.$(date -u +"%FT%H-%M-%S").bak" | |
| cp "$TARG" "$TARG_BACKUP" \ | |
| && loggerx NOTICE "Your $TARG file has been backed up to $TARG_BACKUP!" | |
| find "$HOME/" -type f -name "\.bashrc*.bak" | sort -r | tail -n +5 | tr '\n' '\0' | xargs -0 rm -f -- | |
| loggerx NOTICE "Only the five most recent $TARG.*.bak files have been kept." | |
| TASK="Retrieve remote user bashrc file"; et | |
| wget -q https://gist.githubusercontent.com/AlexAtkinson/bc765a0c143ab2bba69a738955d90abd/raw/.bashrc -O ~/.bashrc_user_gist; rc 0 KILL | |
| START='# RCCTRLTEST - AUTOCONFIG START ----------------------------------------------------' | |
| CONTENT=() | |
| # shellcheck disable=SC2179 | |
| CONTENT+='# WARNING: Changes to this section will be overwritten.\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='# Last Updated: '"'$(date -u +"%FT%H-%M-%S")'"'\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='#\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='# NOTICE: Apply manual bashrc changes to ~\/.bashrc_user.\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='if \[ -f ~\/.bashrc_user \]; then\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+=' . ~\/.bashrc_user\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='fi\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='# NOTICE: Apply remotely maintained bashrc changes to ~\/.bashrc_user_gist.\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='# Replace with any remote source as needed.\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='if \[ -f ~\/.bashrc_user_gist \]; then\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+=' . ~\/.bashrc_user_gist\n' | |
| # shellcheck disable=SC2179 | |
| CONTENT+='fi\n' | |
| END='# RCCTRLTEST - AUTOCONFIG END ------------------------------------------------------' | |
| TASK="Add control flags if not already present"; et | |
| if [[ $(grep -c "$START" "$TARG") == 0 ]]; then | |
| echo -e "\n$START\n\n$END" >> "$TARG"; rc 0 | |
| else | |
| true; rc 0 | |
| fi | |
| # WARNING: This sed command is invalid on MacOS. Ensure gsed is installed and aliased to sed. | |
| # sed: 1: "...": unterminated substitute in regular expression | |
| sed -ni "/${START}/{p;:a;N;/${END}/!ba;s/.*\n/${CONTENT[*]}/};p" "$TARG"; rc 0 KILL | |
| loggerx SUCCESS "Your $TARG file has been updated!" | |
| loggerx INFO "Run 'source $TARG' to make your commands available in this terminal session." | |
| # Update Operating System | |
| # ----------------------- | |
| # Ensure this section runs only on linux hosts. | |
| if [[ $(check_os_type) != "LINUX" ]]; then | |
| loggerx ERROR "The remainder of this script supports LINUX only." | |
| exit 1 | |
| fi | |
| if [[ ! -f /etc/debian_version ]]; then | |
| loggerx ERROR "The remainder of this script supports Debian based distributions only." | |
| exit 1 | |
| fi | |
| TASK="System: apt update"; et | |
| sudo apt update; rc 0 KILL | |
| TASK="System: apt full-upgrade"; et | |
| sudo apt full-upgrade -y ; rc 0 KILL | |
| TASK="Update Operating System"; et | |
| true; rc 0 | |
| # Install Prerequisites | |
| # --------------- | |
| TASK="Install packages"; et | |
| sudo apt install \ | |
| ansible \ | |
| jq | |
| rc 0 KILL | |
| # Ansible | |
| # ------- | |
| TASK="Ansible: Install Roles"; et | |
| sudo ansible-galaxy role install geerlingguy.docker; rc 0 KILL | |
| TASK="Ansible: Install Apt Packages"; et | |
| sudo ansible-playbook ansible_main.yml; rc 0 | |
| # Private Configuration | |
| # --------------------- | |
| if [[ "$CONFIG_INSTALL" == "true" ]]; then | |
| while ! check_ssh_authentication_to_github; do | |
| loggerx ERROR "Could not authenticate to Github. | |
| Add you SSH key (~/.ssh/id_ed25519) to your GitHub account at https://github.com/settings/keys. | |
| REF: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account" | |
| ask "Retry?" Y || break | |
| done | |
| if check_ssh_authentication_to_config_repo; then | |
| TASK="Ansible: Install Private Configuration"; et | |
| sudo ansible-playbook ansible_private.yml; rc 0 KILL | |
| else | |
| loggerx ERROR "You do not have access to $CONFIG_REPO" | |
| fi | |
| fi | |
| # Cleanup | |
| # ------- | |
| loggerx INFO "Setup is complete." | |
| TASK="Confirm computer restart"; et | |
| if ask "Would you like to restart now" Y; then | |
| shutdown -r 0 | |
| else | |
| loggerx WARNING "Some applications may not function correctly without logging out/in, or restarting." | |
| false; rc 0 | |
| fi |