From e231c6ae7ae82c29625b7e096e1630691670462c Mon Sep 17 00:00:00 2001 From: Matthew Stobbs Date: Wed, 4 Feb 2026 22:26:15 -0700 Subject: [PATCH] updating documentation - giving things a better structure - better documentation with the way things need to be as a standard --- CONTRIBUTING.md | 155 ++++++++++------------- README.md | 23 ++-- tasks/helpers/add_repository.yml | 22 ++++ tasks/helpers/appimage.yml | 29 +++-- tasks/helpers/archive.yml | 78 ++++++------ tasks/helpers/cargo.yml | 13 +- tasks/helpers/cargo_build.yml | 16 +-- tasks/helpers/git.yml | 12 +- tasks/helpers/{go.yml => go_install.yml} | 0 tasks/helpers/npm.yml | 12 +- tasks/pkgs/air.yml | 14 +- tasks/pkgs/ansible.yml | 4 - tasks/pkgs/ansible_ls.yml | 16 ++- tasks/pkgs/go.yml | 10 +- vars/main.yml | 11 +- 15 files changed, 221 insertions(+), 194 deletions(-) create mode 100644 tasks/helpers/add_repository.yml rename tasks/helpers/{go.yml => go_install.yml} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f86a1f1..103eadc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,108 +2,83 @@ ## Package definition -Package defintions are just `yaml` tasks in the directory -`tasks/pkgs`, which handle how a package is installed. +A "package" is just a yaml task list that defines how to install a piece of software. +It does this through the use of `helpers` that manage; -Most can be installed using the `syspkgs` list, and are simply -appending the package name to said list based on the system. +- dependency installations +- building from source +- linking binaries to a usable `PATH` +- calling external tools when required +- installation via package managers on the system -If a package can be installed via an entry to `syspkgs` for some, -but not all platforms, handling is done to either inform the user -that the package is not available, or to append the package name -to another installation method such as: +## Anatomy of a Package Definition -- `appimages` to install the appimage of a package -- `cargopkgs` to install via the rust cargo package manager -- `cargoversioned` to install version lockec cargo packages -- `caskpkgs` to install a homebrew cask -- `flatpkgs` to install flatpaks -- `gopkgs` to install using the `go install` command -- `npmpkgs` to install packages from npm -- `pipxpkgs` to install packages from Python pip -- `srcpkgs` to build packages from source -- `tappkgs` to install packages from home brew taps +It starts with package metadata, as a comment block above the installation steps. +This should come after the line `# vim: set filetype=yaml.ansible :`, which sets +up the correct linter and language server when using neovim/vim. -Alternative sources of packages can be defined with entries to: +### Metadata -- `fpremotes` to add a flatpak remote -- `brewtaps` to add a homebrew tap - -### Adding system level repositories - -Many packages exist in their own external repository for the -given system, such as `/etc/yum.repos.d` for RedHat based linux -distros, `/etc/apt/sources.list.d` for Debian based distros and -others. Since you an add the package to `syspkgs` by enabling a -repo, the coresponding task in `tasks/pkgs` should take the -steps needed to enable the repository. - -### Packages with multiple instllation methods - -Many packages can be installed in different ways, like the -`bitwarden` package. `bitwarden` can be installed as a `syspkg` -on some machines, a `caskpkg` on macOS, an `appimage`, a `flatpak` -or even a `snap`. - -For such packages, a default is chosen to install, in the following order -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: +The metadata consists of at minimum 6 key: value pairs ```yaml -# Good -my_good_list: - - my_list_item - -# Bad -my_bad_list: -- my_list_item +# Package: +# Description: +# Version: +# Methods: +# Helpers: +# Maintainers: ``` +This list should match package defaults where it makes sense, for example, +the `Version:` should match the default installed version of the package. -- 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: +### Configuration acknowledgement + +The "Configuration acknowledgement" MUST be at the start of the yaml file, +and cover everything this package does. This is important for gating when +a package should be configured or not. + +It looks like this, using the `air` package: ```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 +- name: Start air configuration 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 - ... + - "'air' is not in __configured" + block: + - name: Configure air installation method + ... + + - name: Finalize air configuration + ansible.builtin.set_fact: + __air_configured: true ``` -- `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:` +or alternatively: + +```yaml +- name: Start air configuration + when: + - __configured['air'] is undefined +``` + +This way, if a package is included as part of another packages build, it only happens once. + +### Setting and adding package configuration to list + +Adding the configuration for a package is done by appending the configuration to the +appropriate list. Each helper has it's own installation block in `tasks/main.yml`. + +Depending on the package installation method, the package must be added to the appropriate list. + +#### Different lists, and when to use them + +The following is a list of the different lists that are used, and the +order they are run. + +- system: pkg_sys + - Only for packages installed from the system package manager + - If a package adds a repository to handle the system installation, it should be + done in the package configuration itself. For example, installing `ghostty` via + the system package manager requires adding a copr repo on fedora. This should be + done in a section named 'Adding copr repository scottames/ghostty' diff --git a/README.md b/README.md index a4826f9..fb99df0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# ansible_role_package +# Ansible package manager +## _A ports like system for ansible_ Manage package installation for a number of packages that sometimes need special handling. Some are built from @@ -10,14 +11,16 @@ special instructions to properly install and use. I decided to simplify the management of those packages by creating a role with special handling. +_The following are for general use. Not all packages have a candidate for system package managers_ + Order of precedence for package installation: 1. System package manager (dnf, apt, homebrew, etc.) 2. Source built - only in some situations, like for fast moving software (neovim, hyprland) -3. Appimage (Linux only) -4. Flatpak (Linux only) -5. Snap (Linux only), takes precedence over flatpak on Ubuntu based systems -6. Language package manager (`cargo install`, `go install`, `npm install`, etc.) + - Includes language package managers like `go install`, `cargo install`, and `npm install` +3. Flatpak (Linux only) +4. Snap (Linux only) +5. Appimage (Linux only) _This does not configure installed software, just installs it_ @@ -26,11 +29,11 @@ To install any of the available packages, they must be part of a list called ## Supported Operating Systems -- Fedora Linux -- MacOS -- RHEL based distributions *Experimental* *best effort* -- Debian *Experimental* -- Alpine linux *Experimental* *best effort) +- Fedora Linux _first class support_ +- MacOS _best effort_ +- RHEL based distributions _Experimental, best effort_ +- Debian _Experimental, best effort_ +- Alpine linux _Experimental, best effort_ ## Available Packages diff --git a/tasks/helpers/add_repository.yml b/tasks/helpers/add_repository.yml new file mode 100644 index 0000000..21a1c1f --- /dev/null +++ b/tasks/helpers/add_repository.yml @@ -0,0 +1,22 @@ +# vim: set filetype=yaml.ansible : +# Helper: external repository +--- +- name: Add copr repository + when: + - ansible_os_family == 'RedHat' + community.general.copr: + host: "{{ repo.host | default('copr.fedorainfracloud.org') }}" + state: "{{ repo.state | default('enabled') }}" + name: "@{{ repo.name }}" + include: "{{ repo.include | default(omit) }}" + exclude: "{{ repo.exclude | default(omit) }}" + +- name: Add apt ppa + when: + - ansible_os_family == 'Debian' + ansible.builtin.apt_repository: + codename: "{{ repo.codename | default(omit) }}" + filename: "{{ repo.filename | default(omit) }}" + install_python_apt: true + repo: "{{ repo.name }}" + state: "{{ repo.state | default('present') }}" diff --git a/tasks/helpers/appimage.yml b/tasks/helpers/appimage.yml index b8e78b1..3845f6e 100644 --- a/tasks/helpers/appimage.yml +++ b/tasks/helpers/appimage.yml @@ -1,26 +1,33 @@ # vim: set filetype=yaml.ansible : --- - name: Install appimage {{ pkg }} - become: "{{ ext_become }}" + become: "{{ install_become }}" + become_user: "{{ install_become_user }}" block: - name: Ensure appimage path exists ansible.builtin.file: - path: "{{ path.appimage }}/{{ appimage_link_name }}" - mode: '0755' + path: "{{ path_appimage }}/{{ pkg.name }}" + mode: "{{ pkg.mode | default('0755') }}" + owner: "{{ pkg.owner | default(ansible_user_id) }}" + group: "{{ pkg.group | default(ansible_user_gid) }}" state: directory - name: Fetch appimage - become: "{{ ext_become }}" + become: "{{ install_become }}" + become_user: "{{ install_become_user }}" ansible.builtin.get_url: - mode: '0755' + mode: "{{ pkg.mode | default('0755') }}" + owner: "{{ pkg.owner | default(ansible_user_id) }}" + group: "{{ pkg.group | default(ansible_user_gid) }}" + url: "{{ pkg.url }}" + dest: "{{ path_appimage }}/{{ pkg.name }}/{{ pkg.filename }}" decompress: false - backup: true - url: "{{ appimage_url }}" - dest: "{{ path.appimage }}/{{ appimage_link_name }}/{{ appimage_file }}" + backup: false - name: Link appimage to bin - become: "{{ ext_become }}" + become: "{{ install_become }}" + become_user: "{{ install_become_user }}" ansible.builtin.file: state: link - src: "{{ path.appimage }}/{{ appimage_link_name }}/{{ appimage_file }}" - path: "{{ path.bindir }}/{{ appimage_link_name }}" + src: "{{ path_appimage }}/{{ pkg.name }}/{{ pkg.filename }}" + path: "{{ path_bin }}/{{ pkg.name }}" diff --git a/tasks/helpers/archive.yml b/tasks/helpers/archive.yml index 9fa8b5b..6c884c0 100644 --- a/tasks/helpers/archive.yml +++ b/tasks/helpers/archive.yml @@ -1,42 +1,48 @@ # vim: set filetype=yaml.ansible : --- -- name: Extract archive to given path +- name: Ensure requirements met + when: + - pkg.extract_to is defined + - pkg.name is defined + - pkg.url is defined block: - - name: Ensure directory exists - become: "{{ install_become }}" - become_user: "{{ install_become_user }}" - ansible.bulitin.file: - state: directory - path: "{{ pkg.extract_path }}" - mode: "{{ pkg.install_prefix_mode | default('0755') }}" - owner: "{{ pkg.install_prefix_owner | default(ansible_user_id)}}" - group: "{{ pkg.install_prefix_group | default(ansible_user_gid) }}" + - name: Extract archive to given path + block: + - name: Ensure directory exists + become: "{{ install_become }}" + become_user: "{{ install_become_user }}" + ansible.bulitin.file: + state: directory + path: "{{ pkg.extract_to }}" + mode: "{{ pkg.mode | default('0755') }}" + owner: "{{ pkg.owner | default(ansible_user_id)}}" + group: "{{ pkg.group | default(ansible_user_gid) }}" - - name: Download archive to cache - ansible.builtin.get_url: - dest: "{{ d_cache }}/{{ pkg.archive_name }}" - url: "{{ pkg.archive_url }}" - checksum: "{{ pkg.archive_checksum | default(omit) }}" - decompress: false - mode: '0644' + - name: Download archive to cache + ansible.builtin.get_url: + dest: "{{ d_cache }}/{{ pkg.name }}" + url: "{{ pkg.url }}" + checksum: "{{ pkg.checksum | default(omit) }}" + decompress: false + mode: '0644' - - name: Extract archive - ansible.builtin.unarchive: - dest: "{{ pkg.extract_path }}" - src: "{{ d_cache }}/{{ pkg.archive_name }}" - remote_src: true - include: "{{ pkg.archive_include | default(omit) }}" - exclude: "{{ pkg.archive_exclude | default(omit) }}" + - name: Extract archive + ansible.builtin.unarchive: + dest: "{{ pkg.extract_to }}" + src: "{{ d_cache }}/{{ pkg.name }}" + remote_src: true + include: "{{ pkg.include | default(omit) }}" + exclude: "{{ pkg.exclude | default(omit) }}" - - name: Symlink archive files - when: - - pkg.link_bin is defined - - pkg.link_bin | length > 0 - loop: pkg.link_bin - loop_control: - loop_var: lnk - ansible.builtin.file: - state: link - path: "{{ lnk.to }}" - src: "{{ lnk.from }}" - force: "{{ lnk.force | default(omit) }}" + - name: Symlink archive files + when: + - pkg.bins is defined + - pkg.bins | length > 0 + loop: pkg.bins + loop_control: + loop_var: lnk + ansible.builtin.file: + state: link + path: "{{ lnk.to }}" + src: "{{ lnk.from }}" + force: "{{ lnk.force | default(omit) }}" diff --git a/tasks/helpers/cargo.yml b/tasks/helpers/cargo.yml index 25d51eb..629e62f 100644 --- a/tasks/helpers/cargo.yml +++ b/tasks/helpers/cargo.yml @@ -1,11 +1,10 @@ # vim: set filetype=yaml.ansible --- -- name: Install cargo {{ pkg }} - become: "{{ ext_become }}" - environment: - RUSTONIG_SYSTEM_LIBONIG: 1 +- name: Install cargo + become: "{{ install_become }}" + become_user: "{{ install_become_user }}" community.general.cargo: name: "{{ pkg.name | default(pkg) }}" - version: "{{ pkg.vers | default(omit) }}" - path: "{{ path.cargo | default(path.prefix) }}" - locked: "{{ pkg.locked | default(omit) }}" + version: "{{ pkg.version | default(omit) }}" + locked: "{{ pkg.locked | default(false) }}" + path: "{{ install_prefix }}" diff --git a/tasks/helpers/cargo_build.yml b/tasks/helpers/cargo_build.yml index 56ea3b0..59514d5 100644 --- a/tasks/helpers/cargo_build.yml +++ b/tasks/helpers/cargo_build.yml @@ -4,11 +4,11 @@ block: - name: Set build variables ansible.builtin.set_fact: - git_path: "{{ d_tempdir.path }}/{{ name }}" - git_repo: "{{ repo }}" - git_depth: "{{ depth | default(1) }}" - git_recursive: "{{ recursive | default(true) }}" - git_version: "{{ version | default(omit) }}" + git_path: "{{ d_tempdir.path }}/{{ pkg.name }}" + git_repo: "{{ pkg.repo }}" + git_depth: "{{ pkg.depth | default(1) }}" + git_recursive: "{{ pkg.recursive | default(true) }}" + git_version: "{{ pkg.version | default(omit) }}" - name: Fetch git repo ansible.builtin.include_tasks: @@ -16,9 +16,9 @@ - name: Build cargo release ansible.builtin.command: - creates: "{{ bin_output }}" - chdir: "{{ git_path }}" - argv: "{{ [cargo, build] + cargo_build_flags }}" + creates: "{{ pkg.bin_output }}" + chdir: "{{ pkg.path }}" + argv: "{{ [cargo, build] + pkg.build_flags }}" - name: Install cargo release block: diff --git a/tasks/helpers/git.yml b/tasks/helpers/git.yml index 4e8899e..575fe35 100644 --- a/tasks/helpers/git.yml +++ b/tasks/helpers/git.yml @@ -2,9 +2,9 @@ --- - name: Clone git repository ansible.builtin.git: - depth: "{{ git_depth | default(1) }}" - dest: "{{ git_path }}" - force: "{{ git_force | default(true) }}" - recursive: "{{ git_recursive | default(true) }}" - repo: "{{ git_repo }}" - version: "{{ git_version | default(omit) }}" + depth: "{{ git.depth | default(1) }}" + dest: "{{ git.path }}" + force: "{{ git.force | default(true) }}" + recursive: "{{ git.recursive | default(true) }}" + repo: "{{ git.repo }}" + version: "{{ git.version | default(omit) }}" diff --git a/tasks/helpers/go.yml b/tasks/helpers/go_install.yml similarity index 100% rename from tasks/helpers/go.yml rename to tasks/helpers/go_install.yml diff --git a/tasks/helpers/npm.yml b/tasks/helpers/npm.yml index 1afab30..ff42439 100644 --- a/tasks/helpers/npm.yml +++ b/tasks/helpers/npm.yml @@ -1,8 +1,10 @@ # vim: set filetype=yaml.ansible : --- -- name: Install npm {{ pkg }} - become: true +- name: Install npm + become: "{{ install_become }}" + become_user: "{{ install_become_user }}" community.general.npm: - global: true - name: "{{ pkg }}" - state: present + global: "{{ pkg.global | default(true) }}" + name: "{{ pkg.name }}" + version: "{{ pkg.version | default('latest') }}" + state: "{{ pkg.state | default('present') }}" diff --git a/tasks/pkgs/air.yml b/tasks/pkgs/air.yml index aae2976..11ebce5 100644 --- a/tasks/pkgs/air.yml +++ b/tasks/pkgs/air.yml @@ -1,8 +1,14 @@ # vim: set filetype=yaml.ansible : +# Package: air +# Description: application auto reload for go +# Version: latest +# Methods: source (go install) +# Helpers: go_install --- -- name: Configure air # {{{ +# {{{ Configure air +- name: Start air configuration when: - - __air_configured is undefined + - "'air' not in __configured" block: - name: Set air install method when: @@ -44,6 +50,6 @@ ansible.builtin.set_fact: pkg_go: "{{ pkg_go + [air_go_pkg] }}" - - name: Complete air configuration + - name: Finalize air configuration ansible.builtin.set_fact: - __air_configured: true + __configured: "{{ __configured | combine( { 'air': true } ) }}" diff --git a/tasks/pkgs/ansible.yml b/tasks/pkgs/ansible.yml index 7bf730d..d348b64 100644 --- a/tasks/pkgs/ansible.yml +++ b/tasks/pkgs/ansible.yml @@ -1,9 +1,5 @@ # vim: set filetype=yaml.ansible : --- -- name: Set maintainer - ansible.builtin.set_fact: - ansible_maintainers: - - Matthew Stobbs - name: Configure ansible when: - __ansible_configured is undefined or diff --git a/tasks/pkgs/ansible_ls.yml b/tasks/pkgs/ansible_ls.yml index 165d51e..1893de6 100644 --- a/tasks/pkgs/ansible_ls.yml +++ b/tasks/pkgs/ansible_ls.yml @@ -1,6 +1,6 @@ # vim: set filetype=yaml.ansible : --- -- name: Add ansible_ls +- name: Configure ansible_ls when: - __ansible_ls_configured is undefined block: @@ -10,9 +10,19 @@ ansible.builtin.set_fact: ansible_ls_install_method: "{% if install_method in ansible_ls_install_methods %}{{ install_method }}{% else %}{{ ansible_ls_install_methods[0] }}{% endif %}" - - name: Append ansible-language-server to pkg_npm + - name: Configure ansible_ls source npm installation + when: + - ansible_ls_install_method == 'source' ansible.builtin.set_fact: - pkg_npm: "{{ pkg_npm + ['@ansible/ansible-language-server'] }}" + ansible_npm_pkg: + name: '@ansible/ansible-language-server' + global: true + + - name: Append ansible-language-server to pkg_npm + when: + - ansible_ls_install_method == 'source' + ansible.builtin.set_fact: + pkg_npm: "{{ pkg_npm + [ansible_npm_pkg] }}" - name: Set ansible_ls_configured when: diff --git a/tasks/pkgs/go.yml b/tasks/pkgs/go.yml index 8ad4fa7..6cc754e 100644 --- a/tasks/pkgs/go.yml +++ b/tasks/pkgs/go.yml @@ -56,11 +56,11 @@ - name: Finalize go archive install ansible.builtin.set_fact: go_archive_install: - extract_path: "{{ go_extract_path }}" - archive_url: "{{ go_archive_url }}" - archive_name: "{{ go_archive }}" - archive_checksum: "{{ go_archive_sums[go_archive_version][ansible_system][go_arch] }}" - link_bin: + extract_to: "{{ go_extract_path }}" + url: "{{ go_archive_url }}" + name: "{{ go_archive }}" + checksum: "{{ go_archive_sums[go_archive_version][ansible_system][go_arch] }}" + bins: - from: "{{ go_archive_path }}/go" to: "{{ path_go }}" force: true diff --git a/vars/main.yml b/vars/main.yml index cf76903..81183b9 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -37,20 +37,21 @@ path_lib: "{{ install_prefix }}/lib" # System paths # empty lists that must be defined +pkg_appimage: [] # appimages to install pkg_archive: [] # packages installed via prebuilt archive pkg_cargo: [] # rust packages from cargo using `cargo install` pkg_cargo_build: [] # rust packages using `cargo build` before install +pkg_flatpak: [] # flatpak packages to install pkg_go: [] # go applications pkg_npm: [] # npm commands pkg_pipx: [] # pipx packages -pkg_sys_pip: [] # system pip packages +pkg_sh: [] # use shell commands to install a package +pkg_snap: [] # snpacraft.io packages pkg_src: [] # packages built from source pkg_sys: [] # system package manager packages +pkg_sys_pip: [] # system pip packages pkg_zig: [] # zig packages -pkg_sh: [] # use shell commands to install a package -pkg_appimage: [] # appimages to install -pkg_flatpak: [] # flatpak packages to install -pkg_snap: [] # snpacraft.io packages +__configured: [] # list of configured packages # Per package variables ## {{{ air