- name: configure sudoers lineinfile: path: /etc/sudoers line: "{{ user.name }} ALL=(ALL) NOPASSWD:ALL" regexp: "^{{ user.name }}\\s+" become: true become_user: root - name: set shell user: name: "{{ user.name }}" shell: /usr/bin/zsh become: true become_user: root - set_fact: user_groups: - adm - cdrom - sudo - dip - plugdev - docker - libvirt when: distro == 'ubuntu' tags: [always] - set_fact: user_groups: - libvirt - wheel - vboxusers - wireshark - docker - sudonopw - games - kvm when: distro == 'archlinux' tags: [always] - set_fact: user_group_name: "{{ user.group|default(user.name) }}" tags: [always] - name: create user group group: name: "{{ user_group_name }}" state: present become: true become_user: root - name: set groups user: name: "{{ user.name }}" groups: "{{ [user_group_name, 'dotfiles'] + user_groups }}" become: true become_user: root - name: create systemd directory file: state: directory path: "{{ item }}" owner: "{{ user.name }}" group: "{{ user_group_name }}" loop: - "/home/{{ user.name }}/.config/" - "/home/{{ user.name }}/.config/systemd/" - "/home/{{ user.name }}/.config/systemd/user/" # No way to use the `systemd` module here, as it needs a logind # session. So we have to handle the symlinks for masking ourselves. - name: disable and mask systemd user units file: state: link dest: "/home/{{ user.name }}/.config/systemd/user/{{ item }}" src: "/dev/null" with_items: - gpg-agent.socket - gpg-agent-browser.socket - gpg-agent-ssh.socket - gpg-agent-extra.socket - name: create directory for getty autologin file: state: directory path: /etc/systemd/system/getty@tty{{ user.vt }}.service.d owner: root group: root mode: '0755' become: true become_user: root - name: enable getty autologin copy: dest: /etc/systemd/system/getty@tty{{ user.vt }}.service.d/override.conf owner: root group: root mode: '0644' content: | [Service] ExecStart= ExecStart=-/sbin/agetty --autologin {{ user.name }} --noclear %I $TERM become: true become_user: root - block: - name: load dotfile list include_vars: file: dotfiles.yml - name: get state of empty directories stat: path: ~/{{ item.name }} register: empty_dir_stat with_items: "{{ empty_directories }}" check_mode: false - name: remove sysmlinks file: path: "{{ item.stat.path }}" state: absent when: item.stat.exists and item.stat.islnk with_items: "{{ empty_dir_stat.results }}" - name: create empty directories for dotfiles file: state: directory path: ~/{{ item.name }} mode: "{{ item.mode | default('0755') }}" with_items: "{{ empty_directories }}" - name: link this folder to ~/.dotfiles file: state: link force: true follow: false owner: "{{ user.name }}" group: "{{ user_group_name }}" path: "/home/{{ user.name }}/.dotfiles" src: "{{ playbook_dir }}" become: true become_user: root - name: get state of copy targets stat: path: ~/{{ item.to }} register: copy_stat when: not item.template|default(false) with_items: "{{ dotfiles }}" check_mode: false - name: remove invalid copy target (directories) file: path: "{{ item.stat.path }}" state: absent when: - not item.skipped is defined or not item.skipped - item.stat.exists - item.stat.isdir with_items: "{{ copy_stat.results }}" - name: make sure target directories exist file: state: directory path: "{{ (['/home', user.name, item.to]|join('/')) | dirname }}" owner: "{{ user.name }}" group: "{{ user_group_name }}" with_items: "{{ dotfiles }}" become: true become_user: root - name: link dotfiles file: state: link force: true follow: false path: "/home/{{ user.name }}/{{ item.to }}" src: /var/lib/dotfiles/{{ item.from }} owner: "{{ user.name }}" group: "{{ user_group_name }}" when: not item.template|default(false) with_items: "{{ dotfiles }}" become: true become_user: root - name: get state of template targets stat: path: ~/{{ item.to }} register: template_stat when: item.template|default(false) with_items: "{{ dotfiles }}" check_mode: false - name: remove invalid template target (directory or symlink) file: path: "{{ item.stat.path }}" state: absent when: - not item.skipped is defined or not item.skipped - item.stat.exists - not item.stat.isreg with_items: "{{ template_stat.results }}" - name: deploy dotfiles templates template: src: /home/{{ user.name }}/.dotfiles/{{ item.from }}.j2 dest: "/home/{{ user.name }}/{{ item.to }}" owner: "{{ user.name }}" group: "{{ user_group_name }}" force: true become: true become_user: root when: item.template|default(false) with_items: "{{ dotfiles }}" - name: create directories file: state: directory path: "{{ item }}" with_items: - ~/tmp - ~/.var/lib - ~/.var/log - ~/.var/run - ~/.usr/lib - name: stat ~/bin stat: path: "/home/{{ user.name }}/bin" register: bin_stat check_mode: false - name: remove ~/bin if not a link file: state: absent path: "/home/{{ user.name }}/bin" when: - bin_stat.stat.exists - not bin_stat.stat.islnk - name: create ~/.opt and ~/.optbin file: path: "{{ item }}" state: directory with_items: - ~/.opt/ - ~/.optbin/ - name: symlink opt programs file: state: link force: true follow: false path: "/home/{{ user.name }}/.optbin/{{ item.name }}" src: "/home/{{ user.name }}/.opt/{{ item.optpath }}" owner: "{{ user.name }}" group: "{{ user_group_name }}" with_items: - name: hugo optpath: hugo - name: drone optpath: drone - name: link bin directory file: state: link force: true follow: false path: "/home/{{ user.name }}/bin" src: /var/lib/dotfiles/bin owner: "{{ user.name }}" group: "{{ user_group_name }}" tags: - dotfiles - block: - name: create intermediate directories for vim-plug file: path: "{{ item }}" state: directory with_items: - ~/.local/ - ~/.local/share/ - ~/.local/share/nvim/ - ~/.local/share/nvim/site/ - ~/.local/share/nvim/site/autoload/ - ~/.vim/ - ~/.vim/autoload - name: install vim-plug copy: src: contrib/vim-plug/plug.vim dest: ~/.vim/autoload/plug.vim owner: "{{ user.name }}" group: "{{ user_group_name }}" mode: "0644" - name: symlink vim-plug for neovim file: state: link path: ~/.local/share/nvim/site/autoload/plug.vim src: ~/.vim/autoload/plug.vim force: true - name: get ycm version before update shell: | if ! [[ -d ~/.local/share/nvim/plugged/YouCompleteMe ]] ; then exit 200 fi cd ~/.local/share/nvim/plugged/YouCompleteMe git rev-parse HEAD args: executable: /bin/bash register: ycm_before_update changed_when: false failed_when: ycm_before_update.rc not in (0, 200) tags: [update] - name: install vim plugins command: sh -c 'PATH=/usr/local/go/bin:$PATH GOROOT=/usr/local/go GOPATH=/home/{{ user.name }}/.go /usr/bin/nvim --headless +PlugInstall +qall' register: vim_plugin_install changed_when: vim_plugin_install.stderr != "" - name: install go binaries for vim command: sh -c 'PATH=/usr/local/go/bin:$PATH GOROOT=/usr/local/go GOPATH=/home/{{ user.name }}/.go /usr/bin/nvim --headless +GoInstallBinaries +qall' changed_when: false - name: update vim plugins command: sh -c 'PATH=/usr/local/go/bin:$PATH GOROOT=/usr/local/go GOPATH=/home/{{ user.name }}/.go /usr/bin/nvim --headless +PlugUpdate +qall' changed_when: false register: vim_plugin_update changed_when: vim_plugin_update.stderr != "" tags: [update] - name: update go binaries for vim command: sh -c 'PATH=/usr/local/go/bin:$PATH GOROOT=/usr/local/go GOPATH=/home/{{ user.name }}/.go /usr/bin/nvim --headless +GoUpdateBinaries +qall' changed_when: false tags: [update] - name: get ycm version after update shell: | cd ~/.local/share/nvim/plugged/YouCompleteMe git rev-parse HEAD args: executable: /bin/bash register: ycm_after_update changed_when: false tags: [update] - name: compile youcompleteme # --force-sudo is required, as the script refuses to run in a sudo # environment (i.e. if the SUDO_USER env variable is set). But of course, # ansible uses that to assume the other user. It's fine. shell: | cd ~/.local/share/nvim/plugged/YouCompleteMe python3 ./install.py --force-sudo args: executable: /bin/bash register: ycm_compile_output failed_when: ycm_compile_output.rc not in (0, 200) when: > (ycm_before_update.rc == 200) or (ycm_before_update.stdout != ycm_after_update.stdout) tags: [update] tags: [vim-plugins] - block: - name: install rustup on ubuntu shell: curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path args: creates: ~/.cargo/bin/rustup when: distro == 'ubuntu' tags: [rust] - block: - name: create firefox directories firefox_profile: name: "{{ item.key }}" loop: "{{ user.firefox_profiles | dict2items }}" register: firefox_profile_names - set_fact: firefox_preferences: browser.aboutConfig.showWarning: false browser.download.useDownloadDir: false extensions.pocket.enabled: false toolkit.legacyUserProfileCustomizations.stylesheets: true browser.contentblocking.category: "strict" browser.newtabpage.enabled: false browser.shell.checkDefaultBrowser: false browser.startup.homepage: "about:blank" privacy.trackingprotection.enabled: true privacy.trackingprotection.socialtracking.enabled: true general.smoothScroll: false # Restore last session on startup # https://support.mozilla.org/de/questions/1235263 browser.startup.page: 3 browser.sessionstore.resume_from_crash: true # "Play DRM-controlled content" media.eme.enabled: true # "Recommend (extensions|features) as you browse" browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons: false browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features: false # "Ask to save logins and passwords for websites" signon.rememberSignons: false # "Allow Firefox to make personalized extension recommendations" browser.discovery.enabled: false # "Allow Firefox to install and run studies" app.shield.optoutstudies.enabled: false - include_role: name: firefox vars: firefox_profiles: "{{ {item.key: item.value} | combine({item.key: {'preferences': firefox_preferences}}, recursive=True) }}" loop: "{{ user.firefox_profiles | dict2items }}" - name: firefox - create target directory for passff file: path: ~/.mozilla/native-messaging-hosts owner: "{{ user.name }}" group: "{{ user_group_name }}" state: directory mode: '0755' - name: firefox - get passff host application get_url: url: https://github.com/passff/passff-host/releases/download/1.2.2/{{ item.name }} dest: ~/.mozilla/native-messaging-hosts/{{ item.name }} owner: "{{ user.name }}" group: "{{ user_group_name }}" mode: "{{ item.mode }}" loop: - name: passff.json mode: '0644' - name: passff.py mode: '0755' - name: firefox - configure path to passff lineinfile: path: ~/.mozilla/native-messaging-hosts/passff.json search_string: '"path": "PLACEHOLDER"' line: " \"path\": \"/home/{{ user.name }}/.mozilla/native-messaging-hosts/passff.py\"," - name: firefox - create chrome directory file: path: "{{ item.profile_path }}/chrome/" state: directory mode: '0755' with_items: "{{ firefox_profile_names.results }}" - name: firefox - configure firefox custom css copy: dest: "{{ item.profile_path }}/chrome/userChrome.css" content: | #TabsToolbar { visibility: collapse !important; } #titlebar { visibility: collapse !important; } #sidebar-header { visibility: collapse !important; } #sidebar { width: 400px !important; } when: user.firefox_profiles[item.profile_name].manage_css is sameas True with_items: "{{ firefox_profile_names.results }}" tags: - firefox - block: - name: set portfolio performance version set_fact: portfolio_performace_version: "0.55.0" - name: look of current installation stat: path: /home/{{ user.name }}/.opt/portfolio-performance-{{ portfolio_performace_version }} register: stat_portfolio_performance_installation - block: - name: create temporary directory tempfile: state: directory register: tempdir - name: download portfolio performance get_url: url: https://github.com/buchen/portfolio/releases/download/{{ portfolio_performace_version }}/PortfolioPerformance-{{ portfolio_performace_version }}-linux.gtk.x86_64.tar.gz dest: "{{ tempdir.path }}/PortfolioPerformance.{{ portfolio_performace_version }}.tar.gz" - name: create destination directory file: state: directory path: "{{ tempdir.path }}/PortfolioPerformance" when: not stat_portfolio_performance_installation.stat.exists - name: unpack portfolio performance unarchive: src: "{{ tempdir.path }}/PortfolioPerformance.{{ portfolio_performace_version }}.tar.gz" owner: "{{ user.name }}" group: "{{ user_group_name }}" mode: '0755' dest: "{{ tempdir.path }}/PortfolioPerformance" remote_src: true - name: install portfolio performance synchronize: src: "{{ tempdir.path }}/PortfolioPerformance" dest: /home/{{ user.name }}/.opt/portfolio-performance-{{ portfolio_performace_version }} recursive: true checksum: true delete: true - name: clean up temp directory file: path: "{{ tempdir.path }}" state: absent when: - not stat_portfolio_performance_installation.stat.exists - not ansible_check_mode - name: link portfolio performance file: src: /home/{{ user.name }}/.opt/portfolio-performance-{{ portfolio_performace_version }}/PortfolioPerformance/portfolio/PortfolioPerformance dest: /home/{{ user.name }}/.optbin/portfolio-performance owner: "{{ user.name }}" group: "{{ user_group_name }}" state: link force: true tags: - portfolio-performance - block: - name: set kubectl version set_fact: kubectl_version: v1.22.2 - name: get current stable version uri: url: https://storage.googleapis.com/kubernetes-release/release/stable.txt return_content: true register: kubectl_stable_version_api check_mode: false - set_fact: kubectl_stable_version: "{{ kubectl_stable_version_api.content|trim }}" - set_fact: kubectl_outdated: "{{ kubectl_version != kubectl_stable_version }}" - name: warn if not on stable version fail: msg: "installing kubectl {{ kubectl_version }}, stable version would be {{ kubectl_stable_version }}" when: kubectl_outdated is sameas true ignore_errors: True - name: get kubectl get_url: url: https://storage.googleapis.com/kubernetes-release/release/{{ kubectl_version }}/bin/linux/amd64/kubectl dest: /home/{{ user.name }}/.opt/kubectl-{{ kubectl_version }} owner: "{{ user.name }}" group: "{{ user_group_name }}" mode: '0755' - name: make kubectl executable file: path: /home/{{ user.name }}/.opt/kubectl-{{ kubectl_version }} mode: '0755' - name: link kubectl file: src: /home/{{ user.name }}/.opt/kubectl-{{ kubectl_version }} dest: /home/{{ user.name }}/.optbin/kubectl state: link tags: - kubectl - block: - name: set terraform version set_fact: terraform_version: 1.0.2 - name: stat current terraform binary stat: path: "/home/{{ user.name }}/.opt/terraform-v{{ terraform_version }}" register: terraform_binary - name: create temporary download directory for terraform tempfile: state: directory register: terraform_download_dir when: not terraform_binary.stat.exists - name: get terraform get_url: url: "https://releases.hashicorp.com/terraform/{{ terraform_version }}/terraform_{{ terraform_version }}_linux_amd64.zip" dest: "{{ terraform_download_dir.path }}/terraform.zip" when: not terraform_binary.stat.exists - name: unpack terraform zip unarchive: src: "{{ terraform_download_dir.path }}/terraform.zip" dest: "{{ terraform_download_dir.path }}/" remote_src: true when: not terraform_binary.stat.exists - name: install terraform command: mv "{{ terraform_download_dir.path }}/terraform" /home/{{ user.name }}/.opt/terraform-v{{ terraform_version }} when: not terraform_binary.stat.exists - name: clean up download directory file: path: "{{ terraform_download_dir.path }}" state: absent when: not terraform_binary.stat.exists - name: link terraform file: src: /home/{{ user.name }}/.opt/terraform-v{{ terraform_version }} dest: /home/{{ user.name }}/.optbin/terraform state: link - name: get terraform version info command: /home/{{ user.name }}/.optbin/terraform version -json register: terraform_version_output changed_when: false - name: parse terraform version output set_fact: terraform_outdated: "{{ (terraform_version_output.stdout | from_json()).terraform_outdated }}" changed_when: false - name: warn if terraform is outdated fail: msg: "current terraform v{{ terraform_version }} is out of date" when: terraform_outdated is sameas true ignore_errors: True tags: - terraform - name: handle autostart units block: - name: create systemd user directory file: state: directory path: ~/{{ item }} loop: - .config/ - .config/systemd/ - .config/systemd/user/ - name: link autostart service files file: state: link force: true follow: false path: "/home/{{ user.name }}/.config/systemd/user/{{ item | basename }}" src: "{{ item }}" owner: "{{ user.name }}" group: "{{ user_group_name }}" with_fileglob: /var/lib/dotfiles/autostart/services/* - name: get state of autostart.target stat: path: "/home/{{ user.name }}/.config/systemd/user/autostart.target" register: autostart_target_stat - name: remove invalid autostart.target file: path: "/home/{{ user.name }}/.config/systemd/user/autostart.target" state: absent when: - autostart_target_stat.stat.exists - not autostart_target_stat.stat.isreg - name: deploy autostart.target template: src: ./autostart/autostart.target.j2 dest: "/home/{{ user.name }}/.config/systemd/user/autostart.target" owner: "{{ user.name }}" group: "{{ user_group_name }}" force: true follow: false tags: - autostart - block: - name: import gpg key command: gpg --import ./gpgkeys/{{ user.gpg_key.email }}.gpg.asc register: gpg_import_output changed_when: not ("unchanged" in gpg_import_output.stderr) - name: trust gpg key shell: "gpg --import-ownertrust <<< {{ user.gpg_key.fingerprint }}:6" args: executable: /bin/bash # required for <<< register: gpg_trust_output changed_when: gpg_trust_output.stderr_lines|length > 0 when: user.gpg_key is defined tags: [gpg] - block: - name: add passwordstore init script copy: dest: /home/{{ user.name }}/.optbin/pass owner: "{{ user.name }}" group: "{{ user_group_name }}" mode: '0755' content: | #!/usr/bin/env bash if [[ ! -e ~/.password-store ]] ; then printf '%s\n' 'This is the first time running pass, initializting repository ...' git clone ssh://git@code.hkoerber.de:2222/hannes/passwordstore.git ~/.password-store printf '%s\n' 'Done' fi exec mypass "${@}" when: user.enable_passwordstore|default(false) is sameas true tags: [passwordstore]