diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a01e8b8..f86a1f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,3 +50,60 @@ of precedence: `syspkgs`, `flatpkgs`, `snappkgs`, `appimages`. In that order, `syspkgs` and `caskpkgs` have equal weight, as it applies to macOS. + +## Formatting rules + +- Use indentation explicitness. Lists should be indented: + +```yaml +# Good +my_good_list: + - my_list_item + +# Bad +my_bad_list: +- my_list_item +``` + +- Variables should be in snake case, separating words +- Short names are fine if they are explicit. ie `vers` can be used instead of `version` +- If more then one variable starts with the same words, put it into a dict if it makes sense: + +```yaml +# variables that should be a dict +cargo_locked: true +cargo_pkg: alacritty + +# better to just be +cargo: + locked: true + pkg: alacritty +``` +- Tasks **MUST** follow the convention in this example: + +```yaml +- name: Capitalize first letter of name + when: + - each condition has it's own line + - (brackets around conditions with) or + (to show they are separate) + become: "{{ not use_local }}" # must be able to be used with use_local + become_user: # this should not be needed, but always follows become if it is + loop: "{{ my_loopable_list }}" + loop_control: # **MUST** always use at least loop_var for any loop + loop_var: my_item + ansible.builtin.set_fact: # always use the full module name + ... +``` + +- `name`: Every task needs a name, and the first letter must be capitalized +- `when`: If a when clause exists, it follows the name + - Each clause must have it's own line, including and `or` clause, as seen above +- `become`: must follow the when clause if it exists, name otherwise + - Any other `become_` settings follow `become` in alphabetical order +- `loop`: must be defined just before the module invocation + - Every loop needs to rename the `loop_var` to something that makes sense + - `until` is consider the same as `loop` for the purposes of placement +- The last item must be the module invocation, using the fully qualified name + - For example, don't use `set_fact:`, use `ansible.builtin.set_fact:` + diff --git a/README.md b/README.md index e4bf622..8b75507 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Role Name -========= +# ansible_role_package Manage package installation for a number of packages that sometimes need special handling. Some are built from @@ -11,60 +10,162 @@ special instructions to properly install and use. I decided to simplify the management of those packages by creating a role with special handling. -Included packages: +Order of precedence for package installation: -- Desktop applications: alacritty, firefox, ghostty, kitty, neovide -- Shell applications: btop, carapace, nushell, fzf, jq, neovim, pipx, ripgrep, - stow, tmux, zoxide -- Infrastructure applications: ansible, consul, nomad, packer, terraform -- Development tools: cmake, git, go, nodejs, hugo, rust, tidy -- Misc: editorconfig, hashicorp package repository, nerdfonts, pandoc, - pytho3-pip, terra repository for fedora, zfs -- Cargo packages: dotenv-linter, eza, htmx-lsp, starship -- Go packages: air, buf, checkmake, glow, lazygit, revive, templ, gopls -- NPM packages: ansible-language-server, commitlint-cli, - commitlint-config-conventional, markdownlint-cli, quobix-vacuum, - tailwindcss-languageserver -- Pipx packages: cmakelang, sqlfluff +1. System package manager (dnf, apt, homebrew, etc.) +2. Appimage (Linux only) +3. Flatpak (Linux only) +4. Snap (Linux only), takes precedence over flatpak on Ubuntu based systems +5. Language package manager (`cargo install`, `go install`, `npm install`, etc.) +6. Build and install from source -Requirements ------------- +_This does not configure installed software, just installs it_ + +## Available Packages + +- air +- alacritty +- ansible-language-server +- ansible-lint +- ansible +- bashls +- bat +- bitwarden +- blender +- broot +- btop +- buf +- bufls +- carapace +- cheat +- checkmake +- choose +- clangd +- cmake +- cmakelang +- commitlint-cli +- commitlint-config-conventional +- consul +- cssls +- curlie +- dbeaver +- dockerls +- dotenv-linter +- duf +- dust +- editorconfig +- eslint +- eza +- fd +- firefox +- flatpak +- fzf +- ghostty +- git +- glow +- go +- godot +- gopls +- gping +- heroic +- htmlls +- htmx-lsp +- httpie +- hugo +- hyperfine +- intelephense +- jinja-lsp +- jq +- jsonls +- kitty +- lazygit +- libreoffice +- lua-language-server +- markdownlint-cli +- mcfly +- neovide +- neovim +- nerdfonts +- nextcloud +- nginxls +- nodejs +- nomad +- nushell +- packer +- pandoc +- pgadmin +- pipx +- podman +- pyright +- python3-pip +- quobix-vacuum +- revive +- ripgrep +- rust +- sd +- sqlfluff +- sqlls +- starship +- stow +- tailscale +- tailwindcss-languageserver +- templ +- terra_repo +- terraform +- terraformls +- thunderbird +- tidy +- tldr +- tmux +- vault +- xh +- yamlls +- zfs +- zig +- zls +- zoxide +- zsh + +## Requirements + +- + +## Role Variables + +- `use_local`: Boolean, default `true` + - When `true`, uses the following paths: + - `$HOME/.local` as the install prefix, placing binaries in `$HOME/.local/bin` + - `$HOME/.local/appimage` for saved appimages, which are then linked to `$HOME/.local/bin` + - `$HOME/.local/archive` for extracted archives linking binaries to `$HOME/.local/bin` + - `$HOME/.cache` for caching downloads + - `$HOME/.cargo` for cargo installations, placing binaries in `$HOME/.cargo/bin` + - When `false`, uses the following system wide paths: + - `/usr/local` as the install prefix, placing binaries in `/usr/local/bin` + - `/opt/appimage` for saved appimages, which are then linked to `/usr/local/bin` + - `/opt/archive` for extracted archives, linking binaries to `/usr/local/bin` + - `/opt/cache` for caching downloads + - `/opt/cargo` for rust cargo installations, placing binaries in `/opt/cargo/bin` +- `packages`: List of strings + - List of packages to install from the `Available Packages` list +- `prefer_method`: String, default `system` + - The preferred method of installation an application when multiple options exist + - Valid options are: + - `flatpak` - Flatpak, Linux only + - `langtool` - Using language tools such as `cargo`, `go`, `npm` or `pipx` + - `snap` - Snap, Linux only + - `source` - Prefer building from source. **Not recommended** + - `system` - Default, system package manager (dnf, apt, homebrew, etc.) + +## Dependencies -Role Variables --------------- - -- packages: - - List of packages to install -- assume_missing_is_syspkg: - - Bool. Default `true` - - Handle non-supported packages as package manager packages -- full_upgrade: - - Bool. Default `false` - - Do a full package upgrade first if `true` -- install_state: - - String. Default `present` - - Set to `latest` to update packages - - Set to `absent` to remove packages - - *currently only supports remove package manager installed packages* - - Valid choices: `present`, `latest`, `absent` -- pkgconfig: - - specific configuration for individual packages - -Dependencies ------------- +## Example Playbook -Example Playbook ----------------- - - -License -------- +## License MIT -Author Information ------------------- +## Author Information -- Matthew Stobbs +- Matthew Stobbs diff --git a/meta/argument_spec.yml b/meta/argument_spec.yml index 6aa26e5..1b573af 100644 --- a/meta/argument_spec.yml +++ b/meta/argument_spec.yml @@ -1,43 +1,40 @@ --- argument_specs: main: - short_description: install software easily + short_description: Install software using ansible description: - Make the installation of packages consistent on each platform. - - Installs for RedHat, Debian or Darwin based systems. + - Installs for RedHat, Debian and MacOS based systems. author: - Matthew Stobbs options: + use_local: + type: bool + default: true + required: false + description: + Install packages using the current users + `$HOME/.local` directory as the install prefix. + + WHen `false`, uses the system wide `/usr/local` + as the install prefix, and stores other parts + (appimages, extracted archives, cache) in the + appropriate path starting with `/opt`. packages: - type: "list" - elements: "str" + type: list + elements: str required: true description: | The list of packages to install by name. - syspkglist: - type: "list" - elements: "str" + The list must contain only values that exist in + `Available Packages` in `README.md` + prefer_method: + type: str required: false - description: | - Additional packages to install via the system - default package manager. Is combined with the - completed list of packages to install. - Only use this if you a package isn't available to the role. - install_state: - type: "str" + default: system choices: - - "present" - - "latest" - - "absent" - required: false - default: present - description: | - Desired state of the packages to install. - full_upgrade: - type: "bool" - required: false - default: false - description: | - Do a full system upgrade before installing - additional packages. This does not handle - rebooting a machine that has been upgraded. + - flatpak + - langtool + - snap + - source + - system diff --git a/meta/main.yml b/meta/main.yml index 53c5bd6..ca8f837 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -7,20 +7,20 @@ galaxy_info: # issue_tracker_url: http://example.com/issue/tracker license: MIT - min_ansible_version: 3.0 + min_ansible_version: "3.0" platforms: - name: Fedora versions: - all - - 40+ - - name: Enterprise Linux 9 (RHEL, rocky alma) + - "40" + - "41" + - name: EL versions: - - 9.4 - - 9.5 + - "9" - name: macOS version: - - 14.2+ + - ">=14.2" galaxy_tags: - linux @@ -28,6 +28,6 @@ galaxy_info: - package - software -dependencies: [] - # List your role dependencies here, one per line. Be sure to remove the '[]' above, - # if you add dependencies to this list. +dependencies: + - ansible.builtin + - community.general diff --git a/tasks/config/alacritty.yml b/tasks/config/alacritty.yml index 6aab3ff..3b8e511 100644 --- a/tasks/config/alacritty.yml +++ b/tasks/config/alacritty.yml @@ -1,4 +1,13 @@ # vim: set filetype=yaml.ansible : --- -version: 0.15.0 -install_prefix: "{{ default_install_prefix }}" +- name: Set alacritty configuration + ansible.builtin.set_fact: + alacritty: + deps: "{{ pkgconfig.alacritty[ansible_os_family].build_deps }}" + ver: "{{ pkgconfig.alacritty.version }}" + repo: "{{ pkgconfig.alacritty.git_repo }}" + pkgs: "{{ pkgconfig.alacritty[ansible_os_family].pkgs | default(omit) }}" + bin: "{{ paths.install }}/bin/alacritty" + cargo: + locked: true + pkg: alacritty diff --git a/tasks/main.yml b/tasks/main.yml index a298f1e..36be2ee 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,35 +1,56 @@ # vim: set filetype=yaml.ansible : --- +# create all the facts used throughout the role, but shouldn't be touched +# by the user - name: Set installation facts ansible.builtin.set_fact: - paths: "{% if use_local %}{{ localpaths }}{% else %}{{ syspaths }}{% endif %}" - appimages: [] # appimages to install - cargopkgs: [] # rust packages from cargo - cargoversioned: [] # versioned rust packages from cargo - caskpkgs: [] # homebrew casks - flatpkgs: [] # flatpaks - gopkgs: [] # go applications - npmpkgs: [] # npm commands - pipxpkgs: [] # pipx packages - srcpkgs: [] # packages built from source - syspkgs: [] # system package manager packages - tappkgs: [] # homebrew tap packages - brewtaps: [] # homebrew taps - fpremotes: # flatpak remotes + app_images: [] # app_images to install + cargo_pkgs: [] # rust packages from cargo + cask_pkgs: [] # homebrew casks + flatpaks: [] # flatpaks + go_pkgs: [] # go applications + npm_pkgs: [] # npm commands + pipx_pkgs: [] # pipx packages + src_pkgs: [] # packages built from source + sys_pkgs: [] # system package manager packages + tap_pkgs: [] # homebrew tap packages + brew_taps: [] # homebrew taps + flatpak_remotes: # flatpak remotes, includes flathub by default - name: flathub url: https://dl.flathub.org/repo/flathub.flatpakrepo +- name: Set facts based on use_local == true + when: + - use_local + ansible.builtin.set_fact: + paths: "{{ local_paths }}" + flatpak_method: user + +- name: Set facts based on use_local == false + when: + - not use_local + ansible.builtin.set_fact: + paths: "{{ sys_paths }}" + flatpak_method: system + +- name: Set macOS specific facts + when: + - ansible_distribution == 'MacOSX' + ansible.builtin.set_fact: + pipx_exec: /opt/homebrew/bin/pipx + + - name: Determine OS and set facts for it block: - name: Set macOS facts when: ansible_os_family == 'Darwin' ansible.builtin.set_fact: - syspkg_become: false + sys_pkg_become: false - name: Set Linux facts when: ansible_system == 'Linux' ansible.builtin.set_fact: - syspkg_become: true + sys_pkg_become: true - name: Generate package installation lists ansible.builtin.include_tasks: @@ -38,10 +59,10 @@ loop_control: loop_var: pkg -- name: Install syspkgs list - become: "{{ syspkg_become }}" +- name: Install sys_pkgs list + become: "{{ sys_pkg_become }}" ansible.builtin.package: - name: "{{ syspkgs | unique }}" + name: "{{ sys_pkgs | unique }}" state: present - name: Redhat based OS @@ -50,9 +71,9 @@ - name: Install dnf packages become: true when: - - syspkgs|length > 0 + - sys_pkgs|length > 0 ansible.builtin.dnf: - name: "{{ syspkgs | unique }}" + name: "{{ sys_pkgs | unique }}" state: present - name: Debian based OS @@ -60,11 +81,11 @@ block: - name: Install apt packages ansible.builtin.apt: - name: "{{ syspkgs | unique }}" + name: "{{ sys_pkgs | unique }}" state: "{{ install_state }}" become: true when: - - syspkgs|length > 0 + - sys_pkgs|length > 0 - name: Darwin/macOS based OS when: ansible_os_family == 'Darwin' @@ -77,62 +98,62 @@ - name: Tap homebrew taps community.general.homebrew_tap: - name: "{{ brewtaps | unique }}" + name: "{{ brew_taps | unique }}" state: present - when: brewtaps|length > 0 + when: brew_taps|length > 0 - name: Install homebrew packages community.general.homebrew: - name: "{{ syspkgs | unique }}" + name: "{{ sys_pkgs | unique }}" state: "{{ install_state }}" - when: syspkgs|length > 0 + when: sys_pkgs|length > 0 - name: Install homebrew casks community.general.homebrew_cask: - name: "{{ caskpkgs | unique }}" + name: "{{ cask_pkgs | unique }}" state: "{{ install_state }}" - when: caskpkgs|length > 0 + when: cask_pkgs|length > 0 - name: Workaround to install homebrew taps ansible.builtin.command: - cmd: "brew install {{ tappkg }}" - loop: "{{ tappkgs | unique }}" + cmd: "brew install {{ tap_pkg }}" + loop: "{{ tap_pkgs | unique }}" loop_control: - loop_var: tappkg + loop_var: tap_pkg # TODO: fix the need to have this workaround - name: Install flatpaks on Linux Systems when: - ansible_system == 'Linux' - - flatpkgs|length > 0 + - flatpaks|length > 0 block: - name: Add flatpak repos - become: true - loop: "{{ fpremotes | unique }}" + when: + - flatpak_remotes|length > 0 + - flatpaks|length > 0 + become: "{{ not use_local }}" + loop: "{{ flatpak_remotes | unique }}" loop_control: loop_var: remote - when: - - fpremotes|length > 0 - - flatpkgs|length > 0 community.general.flatpak_remote: enabled: true - method: system + method: "{{ flatpak_method }}" state: present flatpakrepo_url: "{{ remote.url }}" name: "{{ remote.name }}" - name: Install flatpaks become: true - loop: "{{ flatpkgs | unique }}" + loop: "{{ flatpaks | unique }}" loop_control: - loop_var: flatpkg + loop_var: flatpak when: - - flatpkgs|length > 0 + - flatpaks|length > 0 community.general.flatpak: state: latest - method: system - name: "{{ flatpkg.name }}" - remote: "{{ flatpkg.remote | default('flathub') }}" + method: "{{ flatpak_method }}" + name: "{{ flatpak.name }}" + remote: "{{ flatpak.remote | default('flathub') }}" - name: Ensure /usr/local/bin exists ansible.builtin.file: @@ -142,64 +163,56 @@ mode: '0755' become: true -- name: Install srcpkgs +- name: Install src_pkgs ansible.builtin.include_tasks: - file: "src/{{ srcpkg }}.yml" - loop: "{{ srcpkgs | unique }}" + file: "src/{{ src_pkg }}.yml" + loop: "{{ src_pkgs | unique }}" loop_control: - loop_var: srcpkg - when: srcpkgs|length > 0 + loop_var: src_pkg + when: src_pkgs|length > 0 - name: Install cargo packages at specific version - community.general.cargo: - name: "{{ cargopkg.name }}" - version: "{{ cargopkg.version }}" - path: "{{ cargopkg.path | default(default_install_prefix) }}" - locked: "{{ cargopkg.locked | default(false) }}" - become: true - when: cargoversioned|length > 0 - loop: "{{ cargoversioned | unique }}" + when: + - cargo_pkgs|length > 0 + become: "{{ not use_local }}" + loop: "{{ cargo_pkgs | unique }}" loop_control: - loop_var: cargopkg - -- name: Install cargo packages at latest version + loop_var: cargo_pkg community.general.cargo: - name: "{{ cargopkg }}" - state: latest - locked: "{{ cargopkg.locked | default(false) }}" - when: cargopkgs|length > 0 - loop: "{{ cargopkgs | unique }}" - loop_control: - loop_var: cargopkg + name: "{{ cargo_pkg.cargo.pkg }}" + version: "{{ cargo_pkg.ver }}" + path: "{{ paths.cargo }}" + locked: "{{ cargo_pkg.cargo.locked }}" - name: Install local go packages - loop: "{{ gopkgs | unique }}" + when: + - go_pkgs|length > 0 + loop: "{{ go_pkgs | unique }}" loop_control: - loop_var: gopkg - when: gopkgs|length > 0 + loop_var: go_pkg environment: - GOROOT: /usr/local/go - PATH: /usr/local/go/bin:$PATH + GOROOT: "{{ paths.install }}/go" + PATH: "{{ paths.install }}/go/bin:$PATH" ansible.builtin.command: - cmd: "go install {{ gopkg }}" - #TODO: figure out how to check if the gopkg is already installed + cmd: "go install {{ go_pkg }}" + # TODO: figure out how to check if the go_pkg is already installed - name: Install local npm packages - loop: "{{ npmpkgs | unique }}" + loop: "{{ npm_pkgs | unique }}" loop_control: - loop_var: npmpkg - when: npmpkgs|length > 0 + loop_var: npm_pkg + when: npm_pkgs|length > 0 community.general.npm: global: true - name: "{{ npmpkg }}" + name: "{{ npm_pkg }}" state: latest - name: Install python pipx packages for user - loop: "{{ pipxpkgs | unique }}" + loop: "{{ pipx_pkgs | unique }}" loop_control: - loop_var: pipxpkg - when: pipxpkgs|length > 0 + loop_var: pipx_pkg + when: pipx_pkgs|length > 0 community.general.pipx: - executable: "{% if ansible_os_family == 'Darwin' %}/opt/homebrew/bin/pipx{% else %}/usr/bin/pipx{% endif %}" - name: "{{ pipxpkg }}" + executable: "{{ pipx_exec }}" + name: "{{ pipx_pkg }}" state: latest diff --git a/tasks/pkgs/alacritty.yml b/tasks/pkgs/alacritty.yml index b5f7f08..8567896 100644 --- a/tasks/pkgs/alacritty.yml +++ b/tasks/pkgs/alacritty.yml @@ -1,25 +1,19 @@ # vim: set filetype=yaml.ansible : --- -- ansible.builtin.include_vars: - file: alacritty.yml - name: _alacritty -- ansible.builtin.set_fact: - pkgconfig_alacritty: "{{ _alacritty | ansible.builtin.combine(pkgconfig.alacritty) }}" - -- name: linux based installation +- name: Linux based installation when: ansible_system == 'Linux' block: - - name: install rust and cargo + - name: Install rust and cargo ansible.builtin.include_tasks: file: "pkgs/rust.yml" when: pkgconfig_rust is undefined - - name: append to pkgs - set_fact: - syspkgs: "{{ syspkgs + pkgconfig_alacritty.deps[ansible_distribution] }}" - srcpkgs: "{{ srcpkgs + [ 'alacritty' ] }}" + - name: Append to pkgs + ansible.builtin.set_fact: + syspkgs: "{{ syspkgs + alacritty.deps }}" + srcpkgs: "{{ cargopkgs + [alacritty] }}" -- name: append alacritty to caskpkgs +- name: Append alacritty to caskpkgs ansible.builtin.set_fact: - caskpkgs: "{{ caskpkgs + [ 'alacritty' ] }}" + caskpkgs: "{{ caskpkgs + alacritty.pkgs }}" when: ansible_system == 'Darwin' diff --git a/tasks/pkgs/hashicorp_repo.yml b/tasks/repos/hashicorp_repo.yml similarity index 100% rename from tasks/pkgs/hashicorp_repo.yml rename to tasks/repos/hashicorp_repo.yml diff --git a/tasks/src/alacritty.yml b/tasks/src/alacritty.yml index bedb5cc..afb3015 100644 --- a/tasks/src/alacritty.yml +++ b/tasks/src/alacritty.yml @@ -1,15 +1,7 @@ # vim: set filetype=yaml.ansible : --- - name: Build and install alacritty - become: true + become: "{{ not use_local }}" ansible.builtin.command: - creates: "{{ pkgconfig_alacritty.install_prefix }}/bin/alacritty" - cmd: - - cargo - - install - - --root - - "{{ pkgconfig_alacritty.install_prefix }}" - - --git - - "{{ pkgconfig_alacritty.git_repo }}" - - --tag - - "v{{ pkgconfig_alacritty.version }}" + creates: "{{ alacritty.bin }}" + cmd: "cargo install --root {{ paths.cargo }} alacritty@{{ alacritty.ver }}" diff --git a/vars/main.yml b/vars/main.yml index 90b4ce3..87014b7 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,31 +1,40 @@ # vim: set filetype=yaml.ansible : # variables used in ansible_role_package --- -localpaths: # all localpaths are prefixed with the users $HOME directory - instrall: .local - appimage: .local/appimage - archive: .local/archive - cache: .cache +use_local: true +prefer_method: system +packages: [] # list of packages to install -syspaths: # if installing at a system level (default) - install: /usr/local # executables get linked to {{ default_install_prefix }}/bin +local_paths: # all localpaths are prefixed with the users $HOME directory + appimage: .local/appimage # keep appimages here + archive: .local/archive # extract archives here + cache: .cache # cache downloads here + cargo: .cargo # cargo install location + install: .local # installation prefix. Binaries are placed in `{{ install }}/bin` + +sys_paths: # if installing at a system level (default) appimage: /opt/appimage # appimages are installed to {{ apimage_install_prefix }}// archive: /opt/archive # pre-compiled archives are extracted to {{ archive_install_prefix }}/ cache: /opt/archive/.cache # download archives here + cargo: /opt/cargo # cargo install location + install: /usr/local # executables get linked to {{ default_install_prefix }}/bin -packages: [] # list of packages to install pkgconfig: alacritty: - build_deps: - RedHat: + version: 0.15.0 + RedHat: + pkgs: [] + build_deps: - cmake - freetype-devel - fontconfig-devel - libxcb-devel - libxkbcommon-devel - g++ - Debian: + Debian: + pkgs: [] + build_deps: - cmake - pkg-config - libfreetype6-dev @@ -33,6 +42,9 @@ pkgconfig: - libxcb-xfixes0-dev - libxkbcommon-dev - python3 + Darwin: + pkgs: + - alacritty git_repo: https://github.com/alacritty/alacritty.git bitwarden: flatpak: