├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── defaults └── main.yml ├── handlers └── main.yml ├── meta └── main.yml ├── tasks ├── Darwin.yml ├── Debian.yml ├── RedHat.yml ├── custom_facts.yml ├── install.yml └── main.yml ├── templates ├── .pyenvrc.j2 ├── check-configure-options.py.j2 └── pyenv_python_installations.fact.j2 ├── test.yml ├── test_update.yml ├── tests └── test_update_vars.yml └── vars └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | #env: 5 | before_install: 6 | - sudo apt-get update -qq 7 | - sudo apt-get install -qq python-apt python-pycurl 8 | install: 9 | - pip install ansible==2.5.0 10 | script: 11 | - echo localhost > inventory 12 | - ansible-playbook -i inventory test.yml --syntax-check 13 | - ansible-playbook -i inventory test.yml --connection=local --sudo -vvv --diff 14 | - ansible-playbook -i inventory test_update.yml --syntax-check 15 | - ansible-playbook -i inventory test_update.yml --connection=local --sudo -vvv --diff 16 | #after_failure: 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Maxim Avanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | avanov.pyenv 2 | ============ 3 | 4 | [![Build Status](https://travis-ci.org/avanov/ansible-galaxy-pyenv.svg)](https://travis-ci.org/avanov/ansible-galaxy-pyenv) 5 | 6 | 7 | Ansible Galaxy role for [pyenv](https://github.com/yyuu/pyenv) on Ubuntu / RedHat / OSX. 8 | 9 | Install it with the following command: 10 | 11 | ```bash 12 | $ ansible-galaxy install avanov.pyenv 13 | ``` 14 | 15 | Requirements 16 | ------------ 17 | 18 | None 19 | 20 | Role Variables 21 | -------------- 22 | 23 | Here is the list of all variables and their default values: 24 | 25 | * ``pyenv_env: "user"`` (should be either `"user"` or `"system"`) 26 | * ``pyenv_path: "{% if pyenv_env == 'user' %}{{ ansible_env.HOME }}/pyenv{% else %}/usr/local/pyenv{% endif %}"`` 27 | * ``pyenv_owner: "{{ ansible_env.USER }}"`` 28 | * ``pyenv_python_versions: ["3.4.1"]`` 29 | * ``pyenv_virtualenvs: [{ venv_name: "latest", py_version: "3.4.1" }]`` 30 | * ``pyenv_global: "3.4.1"`` 31 | * ``pyenv_update_git_install: no`` 32 | * ``pyenv_enable_autocompletion: no`` 33 | * ``pyenv_setting_path: "{% if pyenv_env == 'user' %}~/.bashrc{% else %}/etc/profile.d/pyenv.sh{% endif %}"`` 34 | 35 | Dependencies 36 | ------------ 37 | 38 | None 39 | 40 | Example Playbook 41 | ------------------------- 42 | 43 | - hosts: servers 44 | roles: 45 | - role: avanov.pyenv 46 | pyenv_path: "{{ home }}/pyenv" 47 | pyenv_owner: "{{ instance_owner }}" 48 | pyenv_global: "3.6.5" 49 | pyenv_update_git_install: no 50 | pyenv_enable_autocompletion: no 51 | pyenv_python_versions: 52 | - "3.6.5" 53 | - "3.4.1" 54 | - "2.7.8" 55 | pyenv_virtualenvs: 56 | - venv_name: "latest_v3" 57 | py_version: "3.4.1" 58 | 59 | - venv_name: "latest_v2" 60 | py_version: "2.7.8" 61 | 62 | License 63 | ------- 64 | 65 | MIT 66 | 67 | Author Information 68 | ------------------ 69 | 70 | Maxim Avanov (https://maximavanov.com/) 71 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for pyenv 3 | pyenv_env: "user" 4 | pyenv_path: "{% if pyenv_env == 'user' %}{{ ansible_env.HOME }}/pyenv{% else %}/usr/local/pyenv{% endif %}" 5 | pyenv_owner: "{{ ansible_env.USER }}" 6 | pyenv_setting_path: "{% if pyenv_env == 'user' %}~/.bashrc{% else %}/etc/profile.d/pyenv.sh{% endif %}" 7 | pyenv_update_git_install: no 8 | pyenv_enable_autocompletion: no 9 | pyenv_python_versions: [] 10 | pyenv_virtualenvs: [] 11 | # For a system install, the shims dir will not be writable by users, disable rehashing 12 | pyenv_init_options: "{% if pyenv_env != 'user' %}--no-rehash{% endif %}" 13 | 14 | pyenv_update: no 15 | 16 | # additional options for the build process, e.g "--enable-shared" 17 | pyenv_python_configure_opts: "" 18 | pyenv_uninstall_python_w_wrong_configure_opts: no 19 | 20 | pyenv_debian_packages: 21 | - build-essential 22 | # On Ubuntu 12.04 build may fail with the following error: 23 | # python-build: wget (< 1.14) doesn't support Server Name Indication. 24 | # Please install curl (>= 7.18.1) and try again 25 | - curl 26 | - git 27 | - llvm 28 | - libbz2-dev 29 | - libffi-dev 30 | - liblzma-dev 31 | - libncurses5-dev 32 | - libncursesw5-dev 33 | - libreadline-dev 34 | - libsqlite3-dev 35 | - libssl-dev 36 | - python3-openssl 37 | - tk-dev 38 | - wget 39 | - xz-utils 40 | - zlib1g-dev 41 | pyenv_redhat_packages: 42 | - make 43 | - git 44 | - gcc 45 | - "{{ ( (ansible_facts.distribution_major_version | default(0) | int) < 8) | ternary('libselinux-python','python3-libselinux') }}" 46 | - zlib-devel 47 | - openssl-devel 48 | - bzip2-devel 49 | - readline-devel 50 | - libffi-devel 51 | - sqlite-devel 52 | - gdbm-devel 53 | pyenv_osx_packages: 54 | - readline 55 | - xz 56 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for pyenv 3 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Maxim Avanov 4 | description: Ansible Galaxy role for pyenv (https://github.com/yyuu/pyenv). 5 | company: 6 | # Some suggested licenses: 7 | # - BSD (default) 8 | # - MIT 9 | # - GPLv2 10 | # - GPLv3 11 | # - Apache 12 | # - CC-BY 13 | license: MIT 14 | min_ansible_version: 2.5 15 | # 16 | # Below are all platforms currently available. Just uncomment 17 | # the ones that apply to your role. If you don't see your 18 | # platform on this list, let us know and we'll get it added! 19 | # 20 | platforms: 21 | - name: macOS 22 | versions: 23 | - Sierra 24 | 25 | - name: EL 26 | versions: 27 | # - all 28 | # - 5 29 | - 6 30 | - 7 31 | #- name: GenericUNIX 32 | # versions: 33 | # - all 34 | # - any 35 | #- name: Fedora 36 | # versions: 37 | # - all 38 | # - 16 39 | # - 17 40 | # - 18 41 | # - 19 42 | # - 20 43 | #- name: opensuse 44 | # versions: 45 | # - all 46 | # - 12.1 47 | # - 12.2 48 | # - 12.3 49 | # - 13.1 50 | # - 13.2 51 | #- name: Amazon 52 | # versions: 53 | # - all 54 | # - 2013.03 55 | # - 2013.09 56 | #- name: GenericBSD 57 | # versions: 58 | # - all 59 | # - any 60 | #- name: FreeBSD 61 | # versions: 62 | # - all 63 | # - 8.0 64 | # - 8.1 65 | # - 8.2 66 | # - 8.3 67 | # - 8.4 68 | # - 9.0 69 | # - 9.1 70 | # - 9.1 71 | # - 9.2 72 | - name: Ubuntu 73 | versions: 74 | # - all 75 | # - lucid 76 | # - maverick 77 | # - natty 78 | # - oneiric 79 | # - precise 80 | # - quantal 81 | # - raring 82 | # - saucy 83 | - trusty 84 | - xenial 85 | - yakkety 86 | #- name: SLES 87 | # versions: 88 | # - all 89 | # - 10SP3 90 | # - 10SP4 91 | # - 11 92 | # - 11SP1 93 | # - 11SP2 94 | # - 11SP3 95 | #- name: GenericLinux 96 | # versions: 97 | # - all 98 | # - any 99 | #- name: Debian 100 | # versions: 101 | # - all 102 | # - etch 103 | # - lenny 104 | # - squeeze 105 | # - wheezy 106 | # 107 | # Below are all categories currently available. Just as with 108 | # the platforms above, uncomment those that apply to your role. 109 | # 110 | categories: 111 | #- cloud 112 | #- cloud:ec2 113 | #- cloud:gce 114 | #- cloud:rax 115 | #- clustering 116 | #- database 117 | #- database:nosql 118 | #- database:sql 119 | - development 120 | #- monitoring 121 | #- networking 122 | #- packaging 123 | - system 124 | #- web 125 | dependencies: [] 126 | # List your role dependencies here, one per line. Only 127 | # dependencies available via galaxy should be listed here. 128 | # Be sure to remove the '[]' above if you add dependencies 129 | # to this list. 130 | -------------------------------------------------------------------------------- /tasks/Darwin.yml: -------------------------------------------------------------------------------- 1 | - name: Install development packages necessary for building Python 2 | homebrew: 3 | name: "{{ pyenv_osx_packages }}" 4 | state: present 5 | become: false -------------------------------------------------------------------------------- /tasks/Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure apt cache is up to date 3 | apt: 4 | update_cache: yes 5 | cache_valid_time: "{{ 48 * 60 * 60 }}" # consider the cache to be valid within 48 hours 6 | become: true 7 | 8 | - name: Install development packages necessary for building Python 9 | apt: 10 | pkg: "{{ pyenv_debian_packages }}" 11 | state: present 12 | become: true 13 | -------------------------------------------------------------------------------- /tasks/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use action module which can interchangably use yum or dnf depending on what is available ( Eg. dnf on Fedora vs yum on Centos ) 3 | # Note: special variable ansible_pkg_mgr requires fact_gathering to be enabled 4 | # More: https://ansible-tips-and-tricks.readthedocs.io/en/latest/os-dependent-tasks/installing_packages/ 5 | - name: Install development packages necessary for building Python 6 | action: > 7 | {{ ansible_pkg_mgr }} name="{{ pyenv_redhat_packages }}" state=present 8 | become: true 9 | -------------------------------------------------------------------------------- /tasks/custom_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create folder for custom facts 3 | file: 4 | path: /etc/ansible/facts.d 5 | state: directory 6 | 7 | - name: Copy over check-configure-options.py 8 | template: 9 | src: templates/check-configure-options.py.j2 10 | dest: /etc/ansible/facts.d/check-configure-options.py 11 | owner: "{{ pyenv_owner }}" 12 | group: "{{ pyenv_owner }}" 13 | mode: "0755" 14 | 15 | - name: Copy over python_check fact file 16 | template: 17 | src: templates/pyenv_python_installations.fact.j2 18 | dest: /etc/ansible/facts.d/pyenv_python_installations.fact 19 | owner: "{{ pyenv_owner }}" 20 | group: "{{ pyenv_owner }}" 21 | mode: "0755" 22 | 23 | - name: Reload setup to gather custom facts 24 | setup: 25 | filter: ansible_local 26 | -------------------------------------------------------------------------------- /tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install PyEnv 3 | git: 4 | repo: https://github.com/pyenv/pyenv.git 5 | dest: "{{ pyenv_path }}" 6 | update: "{{ pyenv_update_git_install }}" 7 | 8 | - name: Install PyEnv-virtualenv plugin 9 | git: 10 | repo: https://github.com/yyuu/pyenv-virtualenv.git 11 | dest: "{{ pyenv_path }}/plugins/pyenv-virtualenv" 12 | update: "{{ pyenv_update_git_install }}" 13 | 14 | - name: Install PyEnv-update plugin 15 | git: 16 | repo: https://github.com/pyenv/pyenv-update.git 17 | dest: "{{ pyenv_path }}/plugins/pyenv-update" 18 | update: "{{ pyenv_update_git_install }}" 19 | when: pyenv_update 20 | 21 | - name: Install .pyenvrc 22 | template: 23 | src: ".pyenvrc.j2" 24 | dest: "{{ pyenv_path }}/.pyenvrc" 25 | owner: "{{ pyenv_owner }}" 26 | mode: "0644" 27 | 28 | - name: "Load pyenv env variables in {{ pyenv_setting_path }}" 29 | lineinfile: dest="{{ pyenv_setting_path }}" 30 | regexp="\.pyenvrc$" 31 | line="source {{ pyenv_path }}/.pyenvrc" 32 | state=present 33 | create=yes 34 | 35 | - name: "Add pyenv autocomplete in {{ pyenv_setting_path }}" 36 | lineinfile: dest="{{ pyenv_setting_path }}" 37 | regexp="pyenv\.bash$" 38 | line="source {{ pyenv_path }}/completions/pyenv.bash" 39 | state=present 40 | when: pyenv_enable_autocompletion 41 | 42 | - name: Update Pyenv interpreter list 43 | shell: . {{ pyenv_path }}/.pyenvrc && pyenv update 44 | when: pyenv_update 45 | 46 | - name: Uninstall existing Python interpreters w/ wrong compilation flags 47 | shell: ". {{ pyenv_path }}/.pyenvrc && pyenv uninstall -f {{ item }}" 48 | args: 49 | removes: "{{ pyenv_path }}/versions/{{ item }}/bin/python" 50 | loop: "{{ ansible_local['pyenv_python_installations']['to_reinstall'] | default([]) }}" 51 | when: pyenv_uninstall_python_w_wrong_configure_opts 52 | 53 | - name: Install Python interpreters "{{ pyenv_python_versions }}" 54 | shell: ". {{ pyenv_path }}/.pyenvrc && pyenv install {{ item }}" 55 | environment: 56 | PYTHON_CONFIGURE_OPTS: "{{ pyenv_python_configure_opts }}" 57 | args: 58 | creates: "{{ pyenv_path }}/versions/{{ item }}/bin/python" 59 | with_items: "{{ pyenv_python_versions }}" 60 | 61 | - name: Create virtual environments 62 | shell: . {{ pyenv_path }}/.pyenvrc && pyenv virtualenv {{ item.py_version }} {{ item.venv_name }} 63 | creates="{{ pyenv_path }}/versions/{{ item.py_version }}/envs/{{ item.venv_name }}/bin/python" 64 | with_items: "{{ pyenv_virtualenvs }}" 65 | 66 | - name: Set pyenv global 67 | shell: . {{ pyenv_path }}/.pyenvrc && pyenv global {{ pyenv_global }} && pyenv rehash 68 | when: pyenv_global is defined 69 | 70 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: custom_facts.yml 3 | become: true 4 | 5 | - include: Darwin.yml 6 | when: ansible_os_family == "Darwin" 7 | 8 | - include: Debian.yml 9 | when: ansible_os_family == "Debian" 10 | 11 | - include: RedHat.yml 12 | when: ansible_os_family == "RedHat" 13 | 14 | - include: install.yml 15 | become: true 16 | become_user: "{{ pyenv_owner }}" 17 | when: pyenv_env == "user" 18 | 19 | - include: install.yml 20 | become: true 21 | when: pyenv_env == "system" 22 | -------------------------------------------------------------------------------- /templates/.pyenvrc.j2: -------------------------------------------------------------------------------- 1 | # Include this file into your ~/.bashrc 2 | # ------------------------------------- 3 | export PYENV_ROOT="{{ pyenv_path }}" 4 | export PATH="$PYENV_ROOT/bin:$PATH" 5 | eval "$(pyenv init --path)" 6 | eval "$(pyenv init - {{ pyenv_init_options}})" -------------------------------------------------------------------------------- /templates/check-configure-options.py.j2: -------------------------------------------------------------------------------- 1 | import sysconfig 2 | 3 | config_args = sysconfig.get_config_var("CONFIG_ARGS") 4 | config_args = config_args.replace("'", "").split() 5 | required_args = {{ pyenv_python_configure_opts.split() }} 6 | print(all(s in config_args for s in required_args)) 7 | -------------------------------------------------------------------------------- /templates/pyenv_python_installations.fact.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | configure_opts_checker="/etc/ansible/facts.d/check-configure-options.py" 4 | 5 | declare -a installs_to_check=({{ " ".join(pyenv_python_versions) }}) 6 | declare -a to_reinstall=() 7 | declare -a to_install=() 8 | 9 | # Join array to string 10 | function join { local IFS="$1"; shift; echo "[$*]"; } 11 | 12 | for version in "${installs_to_check[@]}" 13 | do 14 | py_installation="{{ pyenv_path }}/versions/$version/bin/python" 15 | 16 | if [[ -x "$py_installation" ]]; then 17 | is_valid_install=$("$py_installation" "$configure_opts_checker") 18 | 19 | if [[ $is_valid_install =~ "False" ]]; then 20 | to_reinstall+=("\"$version\"") 21 | fi 22 | else 23 | to_install+=("\"$version\"") 24 | fi 25 | done 26 | 27 | to_reinstall_str=$(join , ${to_reinstall[@]}) 28 | to_install_str=$(join , ${to_install[@]}) 29 | printf "{ \"to_reinstall\": "$to_reinstall_str", \"to_install\": "$to_install_str" }\n" 30 | -------------------------------------------------------------------------------- /test.yml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | gather_facts: True 3 | vars_files: 4 | - 'defaults/main.yml' 5 | tasks: 6 | - debug: 7 | msg: "Owner: {{ pyenv_owner }}" 8 | - include: 'tasks/main.yml' 9 | -------------------------------------------------------------------------------- /test_update.yml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | gather_facts: True 3 | vars_files: 4 | - 'defaults/main.yml' 5 | - 'tests/test_update_vars.yml' 6 | tasks: 7 | - debug: 8 | msg: "Owner: {{ pyenv_owner }}" 9 | - include: 'tasks/main.yml' 10 | -------------------------------------------------------------------------------- /tests/test_update_vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pyenv_update: yes -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | --------------------------------------------------------------------------------