Merge pull request #96 from corvus-migratorius/role-miniforge

Migrate the role
This commit is contained in:
Alexander Gorelyshev
2026-05-19 17:04:39 +04:00
committed by GitHub
13 changed files with 356 additions and 0 deletions

46
roles/miniforge/README.md Normal file
View File

@@ -0,0 +1,46 @@
Role Name
=========
Install the Miniforge distribution for each supplied user and, optionally, deploy Mamba environment from provided YAML files.
Requirements
------------
None
Role Variables
--------------
`miniforge_release`: Miniforge release version
`miniforge_script_path`: where to download the installation script
`miniforge_release_base_url`: base URL
`miniforge_create_envs`: whethere to create environments for each user or not
`miniforge_users`: a list of dictionaries representing user accounts that need Miniforge
`manifest_dir`: path to folder on the Ansible controller host where environment files are kept
`miniforge_condarc`: a dictionary that will be YAMLified into the condarc file
NOTE: `miniforge_users` items must have `name` string attribute and, optionally, a `mamba_files` list attribute (filenames must be available under the `manifest_dir`).
Dependencies
------------
None
Example Playbook
----------------
See `molecule` > `default` > `converge.yml`
----------------
License
-------
BSD
Author Information
------------------
corvus-migratorius@proton.me
masayganova@gmail.com

View File

@@ -0,0 +1,5 @@
---
miniforge_release: "26.3.2-2"
miniforge_script_path: "/tmp/{{ miniforge_install_script_name }}.sh"
miniforge_release_base_url: "https://github.com/conda-forge/miniforge/releases/download/"
miniforge_create_envs: false

View File

@@ -0,0 +1 @@
---

View File

@@ -0,0 +1,17 @@
---
galaxy_info:
role_name: miniforge
namespace: genlab
author: Alexander Gorelyshev
company: Genlab, LLC
description: Deploy the Miniforge distribution of the Mamba package manager
license: MIT
min_ansible_version: "2.1"
platforms:
- name: "Ubuntu"
versions: ["jammy", "noble"]
galaxy_tags: []
dependencies: []

View File

@@ -0,0 +1,29 @@
---
# you can use {{all_users | selectattr('name', 'in', mamba_users)}} to narrow down a larger list
- name: Converge
hosts: all
vars:
mamba_users:
- name: alice
mamba_files: [project-alpha.yml, project-beta.yml]
- name: bob
mamba_files: [project-alpha.yml, project-beta.yml]
- name: charlie
pre_tasks:
- name: "Create test users"
loop: [alice, bob, charlie]
ansible.builtin.user:
name: "{{ item }}"
state: present
create_home: true
shell: /bin/bash
roles:
- role: genlab.common.miniforge
miniforge_users: "{{ mamba_users }}"
manifest_dir: "manifests"
miniforge_create_envs: true
miniforge_condarc:
channels: [conda-forge]

View File

@@ -0,0 +1,10 @@
---
name: env_alpha
channels:
- conda-forge
dependencies:
- python~=3.12
- pip>=22.2
- stress-ng==0.17.03
- pip:
- humanfriendly

View File

@@ -0,0 +1,9 @@
---
name: cucubeta
channels:
- conda-forge
dependencies:
- python~=3.10
- pip>=22.2
- pip:
- arrow

View File

@@ -0,0 +1,27 @@
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: ubuntu
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest
pre_build_image: true
command: ${MOLECULE_DOCKER_COMMAND:-""}
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
cgroupns_mode: host
privileged: true
provisioner:
name: ansible
verifier:
name: ansible
lint: |
set -e
yamllint .
ansible-lint .

View File

@@ -0,0 +1,70 @@
---
- name: Verify
hosts: all
gather_facts: false
any_errors_fatal: true
pre_tasks:
- name: "Verify | include default vars"
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
extensions: ['yml']
tasks:
- name: "Verify | get the version of Miniforge installed"
become: true
become_user: alice
changed_when: false
register: miniforge_installed_version
ansible.builtin.command:
cmd: ~/miniforge3/bin/conda --version
- name: "Verify | check the installed version of Miniforge"
ansible.builtin.assert:
that: "'{{ miniforge_release | split('-') | first }}' in miniforge_installed_version.stdout"
success_msg: "OK, expected version installed: '{{ miniforge_installed_version.stdout }}'"
fail_msg: "FAIL: unexpected version installed: '{{ miniforge_installed_version.stdout }}'"
- name: "Verify | check Mamba installing correct tools from channels"
become: true
become_user: alice
changed_when: false
vars:
env: env_alpha
register: miniforge_tool_version
ansible.builtin.command:
cmd: ~/miniforge3/envs/{{ env }}/bin/stress-ng --version
- name: "Verify | check the version of the installed tool"
ansible.builtin.assert:
that: "'stress-ng, version 0.17.03' in miniforge_tool_version.stdout"
success_msg: "OK, expected version installed: '{{ miniforge_tool_version.stdout }}'"
fail_msg: "FAIL: unexpected version installed: '{{ miniforge_tool_version.stdout }}'"
- name: "Verify | check the deployed environments for Alice (pip)"
become: true
become_user: alice
loop:
- { env: 'env_alpha', module: 'humanfriendly' }
- { env: 'cucubeta', module: 'arrow' }
changed_when: false
ansible.builtin.command:
cmd: ~/miniforge3/envs/{{ item.env }}/bin/python -c 'import {{ item.module }}'
- name: "Verify | check the deployed environments for Bob"
become: true
become_user: bob
loop:
- { env: 'cucubeta', module: 'arrow' }
changed_when: false
ansible.builtin.command:
cmd: ~/miniforge3/envs/{{ item.env }}/bin/python -c 'import {{ item.module }}'
- name: "Verify | check that no environments were deployed for Charlie"
become: true
become_user: charlie
register: miniforge_charlie_envs
failed_when: miniforge_charlie_envs.stdout_lines | length > 0
changed_when: false
ansible.builtin.command:
cmd: "ls ~/miniforge3/envs"

View File

@@ -0,0 +1,15 @@
---
- name: "Download miniforge from '{{ miniforge_release_base_url }}'"
retries: 3
delay: 1
ansible.builtin.get_url:
timeout: 10
url: "{{ miniforge_release_base_url }}/{{ miniforge_release }}/{{ miniforge_install_script_name }}.sh"
dest: "{{ miniforge_script_path }}"
mode: "0664"
- name: "Deploy Mamba in a user loop"
loop: "{{ miniforge_users }}"
loop_control:
loop_var: user
ansible.builtin.include_tasks: "miniforge.yml"

View File

@@ -0,0 +1,24 @@
---
- name: "Manifests | get contents of the manifest file"
register: miniforge_manifest_content
ansible.builtin.slurp:
src: "{{ miniforge_homedir }}/{{ file }}"
# Since the file can be named whatever, we need to extract the environment name
# as that is what the actual environment folder name will be
- name: "Manifests | extract environment name from manifest file"
ansible.builtin.set_fact:
miniforge_env_name: '{{ (miniforge_manifest_content.content | b64decode | from_yaml).name }}'
- name: "Manifests | display the extracted environment name"
ansible.builtin.debug:
msg: "{{ miniforge_env_name }}"
- name: "Manifests | create environment: {{ miniforge_env_name }}"
become: true
become_user: "{{ user.name }}"
ansible.builtin.shell:
cmd: "{{ miniforge_homedir }}/miniforge3/bin/mamba env create -f {{ miniforge_homedir }}/{{ file }}"
executable: /bin/bash
# ensures that the task runs only when the environment doesn't exist
creates: "{{ miniforge_homedir }}/miniforge3/envs/{{ miniforge_env_name }}"

View File

@@ -0,0 +1,101 @@
---
# These variables are registered as host vars, for whatever deep reason
- name: "Miniforge | get current user entity info ({{ user.name }})"
ansible.builtin.getent:
database: passwd
key: "{{ user.name }}"
split: ":"
- name: "Miniforge | get current user home directory ({{ user.name }})"
ansible.builtin.set_fact:
miniforge_homedir: "{{ getent_passwd[user.name][4] }}"
- name: "Miniforge | set miniforge dir ({{ user.name }})"
ansible.builtin.set_fact:
miniforge_dir: "{{ miniforge_homedir }}/miniforge3"
- name: "Miniforge | build miniforge bin path ({{ user.name }})"
ansible.builtin.set_fact:
miniforge_mamba_bin_path: "{{ miniforge_dir }}/bin/mamba"
- name: "Miniforge | display miniforge script path ({{ user.name }})"
ansible.builtin.debug:
msg: "{{ miniforge_script_path }}"
- name: "Miniforge | install miniforge ({{ user.name }})"
become: true
become_user: "{{ user.name }}"
vars:
allow_world_readable_tmpfiles: true
block:
- name: "Miniforge | check Mamba availability ({{ user.name }})"
changed_when: false
ansible.builtin.command:
cmd: "{{ miniforge_mamba_bin_path }} info"
rescue:
- name: "Miniforge | install miniforge in a non-interactive mode ({{ user.name }})"
ansible.builtin.command:
cmd: "/bin/bash {{ miniforge_script_path }} -b"
creates: "{{ miniforge_homedir }}/miniforge3"
- name: "Miniforge | initialize Mamba/Conda ({{ user.name }})"
changed_when: false
ansible.builtin.command:
cmd: "{{ miniforge_mamba_bin_path }} shell init"
- name: "Miniforge | check if Mamba has become available ({{ user.name }})"
changed_when: false
ansible.builtin.command:
cmd: "{{ miniforge_mamba_bin_path }} info"
- name: "Miniforge | deploy default .condarc if it doesn't exist already ({{ user.name }})"
become: true
become_user: "{{ user.name }}"
block:
- name: "Miniforge | check if .condarc already exists ({{ user.name }})"
register: miniforge_condarc_stat
failed_when: miniforge_condarc_stat.stat.exists is false
ansible.builtin.stat:
path: "{{ miniforge_homedir }}/.condarc"
rescue:
- name: "Miniforge | set default .condarc from external file if provided ({{ user.name }})"
when: condarc_file is defined and condarc_file != ""
ansible.builtin.copy:
src: "{{ condarc_file }}"
dest: "{{ miniforge_homedir }}/.condarc"
mode: "0600"
- name: "Miniforge | set default content via .condarc ({{ user.name }})"
when: condarc_file is not defined or condarc_file == ""
ansible.builtin.copy:
dest: "{{ miniforge_homedir }}/.condarc"
content: "{{ miniforge_condarc | to_nice_yaml }}"
mode: "0600"
- name: "Miniforge | deploy manifest files for user: {{ user.name }}"
when:
- "manifest_dir is defined"
- "user.mamba_files is defined"
loop: "{{ user.mamba_files }}"
become: true
become_user: "{{ user.name }}"
ansible.builtin.copy:
src: "{{ manifest_dir }}/{{ item }}"
dest: "{{ miniforge_homedir }}/{{ item }}"
owner: "{{ user.name }}"
group: "{{ user.name }}"
mode: "0660"
# Variables from the outer scope (like 'user') will get passed down to the included tasks
- name: "Miniforge | create environments from deployed manifests for user: {{ user.name }}"
when:
- "miniforge_create_envs is true"
- "user.mamba_files is defined"
loop: "{{ user.mamba_files }}"
loop_control:
loop_var: file
ansible.builtin.include_tasks: "manifests.yml"

View File

@@ -0,0 +1,2 @@
---
miniforge_install_script_name: "Miniforge3-{{ ansible_system }}-{{ ansible_architecture }}"