Ansible install node with nvm - node.js

I'm looking for a way to install a given version of node via ansible and nvm, the installation of nvm is working as expected because if I connect with the root user, I can execute the command nvm install 8.11.3 but this same command doesn't work with Ansible, I don't understand why.
---
- name: Install nvm
git: repo=https://github.com/creationix/nvm.git dest=~/.nvm version=v0.33.11
tags: nvm
- name: Source nvm in ~/.{{ item }}
lineinfile: >
dest=~/.{{ item }}
line="source ~/.nvm/nvm.sh"
create=yes
tags: nvm
with_items:
- bashrc
- profile
- name: Install node and set version
become: yes
become_user: root
shell: nvm install 8.11.3
...
error log
TASK [node : Install node and set version] *************************************************************************************
fatal: [51.15.128.164]: FAILED! => {"changed": true, "cmd": "nvm install 8.11.3", "delta": "0:00:00.005883", "end": "2018-12-03 15:05:10.394433", "msg": "non-zero return code", "rc": 127, "start": "2018-12-03 15:05:10.388550", "stderr": "/bin/sh: 1: nvm: not found", "stderr_lines": ["/bin/sh: 1: nvm: not found"], "stdout": "", "stdout_lines": []}
to retry, use: --limit .../.../ansible/stater-debian/playbook.retry

It's okay, here's the configuration that works
- name: Install node and set version
become: yes
become_user: root
shell: "source /root/.nvm/nvm.sh && nvm install 8.11.3"
args:
executable: /bin/bash

I think the clue in the output you need is:
"/bin/sh: 1: nvm: not found"
To run a command without including the full path to that command (i.e. nvm rather than /the/dir/nvm/is/installed/in/nvm), then the directory that contains the command, must be in the $PATH environment variable for the shell that runs the command.
In this case it looks like that is not present for the shell that Ansible spawns, versus the shell your interactive commands run in. Change:
- name: Install node and set version
become: yes
become_user: root
shell: nvm install 8.11.3
to
- name: Install node and set version
become: yes
become_user: root
shell: /full/path/to/nvm install 8.11.3
If you don't know what to put in place of '/full/path/to', try either:
which nvm
or
find / -name nvm

I will just post under here, because there are hundreds of these posts.
- name: Install node
become: true
become_user: root
shell: "source /root/.nvm/nvm.sh && nvm install {{ personal_node_version }} && nvm alias default {{ personal_node_version }}"
args:
executable: /bin/bash
worked for me.

This worked for me on Ubuntu 20.04 using nvm version 0.39.1:
- name: Install NVM
shell: >
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
args:
creates: "/root/.nvm/nvm.sh"
- name: Install Node Versions
shell: ". /root/.bashrc && nvm install {{item}}"
with_items:
- 'v10.24.1'
- 'v16.17.0'
- '--lts'
- 'node'

Based on all the posts found on stack and tweaked a little for my own needs - I found this solution worked perfectly for both installing NVM (the easy part) and creating a loop that allows you to insert 1 or many versions of Node as needed
# test if nvm has been installed by the user desired
- stat:
path: /home/yournonrootuser/.nvm
register: nvm_path
- name: Setup NodeVersionManager and install node version
become: yes
# Execute config files such as .profile (Ansible uses non-interactive login shells)
become_flags: -i
become_user: yournonrootuser
block:
- name: Install nvm
shell: >
curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
args:
executable: /bin/bash
chdir: "$HOME"
creates: "$HOME/.nvm/nvm.sh"
- name: Setup .profile of yournonrootuser
lineinfile:
path: ~/.profile
# This will make sure Node is on the users PATH
line: source ~/.nvm/nvm.sh
create: yes
become_flags: -i
when: nvm_path.stat.exists == false
# if we got here we already know node version manager is installed
- name: installing node versions using loop
command: sudo -iu yournonrootuser nvm install {{item}}
args:
executable: /bin/bash
chdir: "$HOME"
creates: "$HOME/.nvm/versions/node/v{{item}}"
loop:
- 14.18.3

Related

Can't change installed version via NVM for user

I am trying to install Node in eg. version 12.18.2 for user dev.
There is no issue when I'm doing it via command line.
Theoretically, I run the same commands from Ansible on a server and there is still the first version installed. I wonder how to make Ansible change version already installed on a server.
This is how I install nvm:
- name: Install nvm
ansible.builtin.shell: >
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.38.0/install.sh | bash
args:
executable: /bin/bash
chdir: "$HOME"
creates: "$HOME/.nvm/nvm.sh"
- name: Setup .profile
ansible.builtin.lineinfile:
path: ~/.profile
line: source ~/.nvm/nvm.sh
create: yes
- name: Install node
ansible.builtin.shell: |
source ~/.profile & nvm install {{ item }}
args:
executable: /bin/bash
chdir: "$HOME"
creates: "$HOME/.nvm/versions/node/v{{item}}"
loop:
- 12.18.2
When running this code first time, there is issue with /bin/bash: nvm: command not found, when running second time Ansible informs that it's skipping this installation step as it's already installed. I want user dev to use this installed version so I added this task that will use a specific version for a specific user:
- name: Change version
become_user: dev
ansible.builtin.shell: |
source ~/.profile & nvm use {{ item }}
args:
executable: /bin/bash
chdir: "$HOME"
creates: "$HOME/.nvm/versions/node/v{{item}}"
loop:
- 12.18.2
The output is the same as it's with install step:
ok: [my-server] => (item=12.18.2) => changed=false
ansible_loop_var: item
cmd: |-
source ~/.profile & nvm use 12.18.2
item: 12.18.2
rc: 0
stdout: skipped, since /home/dev/.nvm/versions/node/v12.18.2 exists
stdout_lines: <omitted>
When running the same command directly on a a server I get proper output:
dev#my-server:$ nvm use 12.18.2
Now using node v12.18.2 (npm v6.14.5)
Also the strange this is, when running install nvm step for other version like 12.18.1, I'm getting this:
TASK [nodejs : Install node] ***********************************************************************************************************************************************************************
task path: /server/nodejs/tasks/main.yml:120
Monday 05 December 2022 09:01:41 +0100 (0:00:00.870) 0:00:09.360 *******
failed: [my-server] (item=12.18.1) => changed=true
ansible_loop_var: item
cmd: |-
source ~/.profile & nvm install 12.18.1
delta: '0:00:00.452931'
end: '2022-12-05 09:01:43.287552'
item: 12.18.1
msg: non-zero return code
rc: 127
start: '2022-12-05 09:01:42.834621'
stderr: '/bin/bash: nvm: command not found'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
But I am sourcing .profile before installation source ~/.profile & nvm install {{ item }}
Of course when I use command line on a server there is no issue even without sourcing the .profile:
dev#my-server:$ nvm install 12.18.1
Downloading and installing node v12.18.1...
Downloading https://nodejs.org/dist/v12.18.1/node-v12.18.1-linux-x64.tar.xz...
############################################################################################################################################################################################# 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v12.18.1 (npm v6.14.5)
dev#my-server:$ node --version
v12.18.1
dev#my-server:$ nvm install 12.18.0
Downloading and installing node v12.18.0...
Downloading https://nodejs.org/dist/v12.18.0/node-v12.18.0-linux-x64.tar.xz...
############################################################################################################################################################################################# 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v12.18.0 (npm v6.14.4)
TLTR
When using nvm use Ansible skip step and inform it's already installed (Yes, it's installed, but I want to use it, not install it using nvm use).
When using nvm install for not installed version Ansible informs that /bin/bash: nvm: command not found even when sourcing ~/.profile before installation.
Doing the same via command line works perfectly. Can somebody help me to find what am I doing wrong via Ansible?
EDIT: Thanks #β.εηοιτ.βε for pointing that creates in Change version step that should be remove. Now looks like Ansible changed version, but not exactly:
TASK [nodejs : Change version] *********************************************************************
changed: [my-server] => (item=12.18.4) => changed=true
ansible_loop_var: item
cmd: |-
source ~/.profile && nvm use 12.18.4
delta: '0:00:01.658122'
end: '2022-12-05 09:40:57.817836'
item: 12.18.4
rc: 0
start: '2022-12-05 09:40:56.159714'
stderr: ''
stderr_lines: <omitted>
stdout: Now using node v12.18.4 (npm v6.14.6)
stdout_lines: <omitted>
TASK [nodejs : WHO] *********************************************************************
changed: [my-server] => changed=true
cmd: whoami
delta: '0:00:00.014623'
end: '2022-12-05 09:46:05.535453'
rc: 0
start: '2022-12-05 09:46:05.520830'
stderr: ''
stderr_lines: <omitted>
stdout: dev
stdout_lines: <omitted>
TASK [nodejs : debug] *********************************************************************
ok: [my-server] =>
msg: dev
TASK [nodejs : Check version] *********************************************************************
changed: [my-server] => changed=true
cmd: node --version
delta: '0:00:00.023858'
end: '2022-12-05 09:46:06.998710'
rc: 0
start: '2022-12-05 09:46:06.974852'
stderr: ''
stderr_lines: <omitted>
stdout: v12.18.2
stdout_lines: <omitted>
TASK [nodejs : debug] *********************************************************************
ok: [my-server] =>
msg: v12.18.2
The issue is that there is still the old version on a dev user when I'm checking. I'm pretty confused why Ansible thinks that version was changed for user dev and then when Ansible checks version for this user it's still the old one.

CircleCI forgetting node version on machine executor

I'm setting the node version to 10.15.1 with nvm and in the next run step it's back to 6.1.0. I've tried several variations including this one : https://www.cloudesire.com/how-to-upgrade-node-on-circleci-machine-executor/
Am I missing something obvious? I just need each run step to remember the node version I set in the first one so they will all use 10.15.1 in this case.
Here is the job in my workflow:
dev:
environment:
BASH_ENV: run/env/test/.env
machine:
image: circleci/classic:latest
steps:
- checkout
- run:
name: Install node#10
command: |
set +e
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
export NVM_DIR="/opt/circleci/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install 10
nvm alias default 10
rm -rf ~./node-gyp
node -v # prints 10.15.1 as expected
- run:
name: Install yarn and rsync
command: |
node -v # prints 6.1.0
export NVM_DIR="/opt/circleci/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
node -v # prints 6.1.0
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn rsync
- run:
name: Install node modules
command: |
node -v # prints 6.1.0
yarn install # this is what is failing because of the unexpected node version
- run:
name: Deploy to Dev Server
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then rsync -arhvz --exclude .git/ -e "ssh -o StrictHostKeyChecking=no" --progress \
./ ubuntu#xxx.xxx.xxx.xxx:/var/www/xxx/xxx/; fi
if [ "${CIRCLE_BRANCH}" == "master" ]; then ssh -o StrictHostKeyChecking=no ubuntu#xxx.xxx.xxx.xxx 'cd /var/www/xxx/xxx && pm2 restart all --update-env'; fi
#FelicianoTech is incorrect, every single build step runs in a fresh environment and forgets your nvm settings. You have to set NVM_DIR and source the nvm setup script in every single build step. Annoying, I know.
Despite that FelicianoTech is correct, that in the machine executor you have nvm preinstalled, it does not remember the actual state in the next run command. In order to make nvm remember its state you have to store it in the $BASH_ENV. Searching hours long, I finally found an answer in the CircleCI Disussion Board:
machine:
image: circleci/classic:latest
steps:
- checkout
- run:
command: |
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
- run: nvm install 10
- run: node -v
- run: npm -v
- run: npm i -g npm#6.9.0
- run: npm -v
- run: nvm alias default 10
- run: nvm use 10
- run: node -v
- run: npm -v
Then, and only then, I had success as well that even the last run command - run: node -v still remembers the node version properly.
The output on CircleCI
(Credits to phil-lgr on the discussion board)
Solved by adding installed node to $PATH
Example:
- run:
name: 'Install Project Node'
command: |
set +x
source ~/.bashrc
nvm install 12
NODE_DIR=$(dirname $(which node))
echo "export PATH=$NODE_DIR:\$PATH" >> $BASH_ENV
You're doing way too much here. All you need to do is run nvm install v10. nvm is already installed in the machine executor.

Ansible: source profile.d in ansible.config

I'm installing ruby from source and using template to export path. My code looks like this:
- name: clone rbenv
git: repo=git://github.com/sstephenson/rbenv.git dest=/usr/local/rbenv
become: yes
- template: src=templates/rbenv.sh.j2 dest=/etc/profile.d/rbenv.sh
become: true
- name: clone ruby-build repo
git: repo=git://github.com/sstephenson/ruby-build.git dest=~/ruby-build
- name: Install ruby-build
shell: ./ruby-build/install.sh
become: yes
- name: install jruby
shell: . /etc/profile.d/rbenv.sh && rbenv install jruby-9.0.5.0
become: yes
I want to use command "rbenv",
this works but in this way I have to source profile with every command.
Is there any way to source profile once in ansible.config file or something else and use it in the whole project without sourcing profile again.
Either add path in .bashrc
or
- name: install jruby
shell: . /etc/profile.d/rbenv.sh && rbenv install jruby-9.0.5.0
become: yes
args:
executable: /bin/bash -l
/bin/bash -l behaves as login shell

Ansible command module with nvm

I am trying to execute this command using Ansible:
- name: install node v5.5.0
sudo: yes
shell: nvm ls-remote
environment:
http_proxy: http://17.99.193.229:3128
https_proxy: http://17.99.193.229:3128
I tried shell and command.
When using command: I got this error:
"[Errno 2] No such file or directory", "rc": 2"
When using shell: I got this error:
"fatal: [gocdagent-dev-01.rno.apple.com]: FAILED! => {"changed": true, "cmd": "nvm ls-remote", "delta": "0:00:00.016365", "end": "2016-05-16 18:26:07.259729", "failed": true, "rc": 127, "start": "2016-05-16 18:26:07.243364", "stderr": "/bin/sh: nvm: command not found", "stdout": "", "stdout_lines": [], "warnings": []}"
I can execute nvm command directly on the system. Why can't Ansible run nvm?
Nvm is not a binary, you have to load it first :
- name: install node v5.5.0
shell: . <NVM_DIR>/.nvm/nvm.sh && nvm ls-remote
environment:
http_proxy: http://17.99.193.229:3128
https_proxy: http://17.99.193.229:3128
Because you are running it as root (sudo: yes) in Ansible script. Make sure nvm is in root's PATH or give /full/path/to/nvm in shell command.
I chased my tail for hours on this one. Olivier Lecrivain headed me in the right direction. This solved it. This also gives you the ability to install multiple versions at the same time
- name: installing node versions using loop
become: yes
# Execute config files such as .profile (Ansible uses non-interactive login shells)
become_flags: -i
become_user: yourdesireduser
# $HOME will become yourdesireduser's home directory
shell: ". $HOME/.nvm/nvm.sh && nvm install {{item}}"
args:
chdir: "$HOME/.nvm/"
# not needed as it is created automatically through the nvm install command
# creates: "$HOME/.nvm/versions/node/v{{item}}"
loop:
- 14.18.3

How to Install python3.4.3 with Ansible

I want to install python3.x by use pyenv with ansible.
- name: install pyenv
git: >
repo=https://github.com/pyenv/pyenv.git
dest=/home/www/.pyenv
accept_hostkey=yes
become: yes
become_user: www
- name: enable pyenv
shell: |
echo 'export PYENV_ROOT="/home/www/.pyenv"' >> /home/www/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> /home/www/.bashrc
echo 'eval "$(pyenv init -)"' >> /home/www/.bashrc
- name: install python
shell: pyenv install 3.4.3
How to install python3.x with ansible?
So here is what worked for me well to get any version of python installed with ansible and make it an alternative installation. I first ran configure and make, later compressed the result since this takes a while, then re-distributed the file using a mirror so I can run make altinstall on its own. Here is the recipe:
---
# Check the alt python3 version
- name: check alt python version
shell: /usr/local/bin/python3.6 --version
register: python3_version
ignore_errors: yes # If not installed
tags:
- python-alt
# Stuff I did manually to compile everything first by hand
# Python3 alt-install - steps to create binary:
# wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz
# tar xf Python-3.6.4.tgz
# mv Python-3.6.4 Python-3.6.4-binary && cd Python-3.6.4-binary
# ./configure --prefix=/usr/local --enable-optimizations
# cd .. && tar -zcvf Python-3.6.4-binary.tar.gz Python-3.6.4-binary (upload to mirror servers)
# make && sudo make altinstall UNINST=1
- name: download and unpack alternative python3
unarchive:
src: http://www.yourmirror.com/centos/python/Python-3.6.4-binary.tar.gz dest=/tmp/Python-3.6.4-binary.tar.gz
dest: /tmp
remote_src: yes
keep_newer: yes
when: python3_version['stderr'] != 'Python 3.6.4'
tags:
- python-alt
# Its possible to install (instead of altinstall) python3 here
- name: make install alt python3
make:
chdir: /tmp/Python-3.6.4-binary
target: altinstall
params:
UNINST: 1 # Replace
when: python3_version['stderr'] != 'Python 3.6.4'
become: yes
tags:
- python-alt
- name: download get-pip.py
get_url:
url: https://bootstrap.pypa.io/get-pip.py
dest: /tmp/get-pip.py
mode: 0664
tags:
- python-alt
- name: install pip for python3
shell: /usr/local/bin/python3.6 /tmp/get-pip.py
become: yes
tags:
- python-alt
# We need virtualenv installed under py3 for the virtualenv command to work
- pip:
name: virtualenv
executable: /usr/local/bin/pip3.6
become: True
tags:
- python-alt
If you want to compile everything on your server you could do the following before the altinstall step and also download the source code package instead of the pre-compiled tar. I don't recommend doing it this way because it does take up resources and you don't want to be doing it in prod. Using Python2.7.14 as an example:
---
# Build the default target
- debug:
var: python2_version
tags:
- python_alt
- make:
chdir: /tmp/Python-2.7.14-binary
when: python2_version['stderr'] != 'Python 2.7.14'
tags:
- python_alt
- name: configure target command
command: ./configure --prefix=/usr/local --enable-optimizations chdir=/tmp/Python-2.7.14-binary
when: python2_version['stderr'] != alt_python_version
tags:
- python_alt
Rather than using the shell module to set environment variables on the remote host, Ansible has the environment keyword, which can set per task or even per playbook.
Assuming the www user already exists I managed to get this working with some more specific path setting:
- name: enable pyenv and install python
shell: /home/www/.pyenv/bin/pyenv init - && /home/www/.pyenv/bin/pyenv install 3.4.3 chdir=/home/www
environment:
pyenv_root: /home/www/.pyenv
path: "{{ pyenv_root }}/bin:$PATH"
become: yes
become_user: www
You will need to run the playbook with:
ansible-playbook --ask-become-pass <playbook-name>
and supply the password for the www user on request.
If that doesn't work, you might have to post the whole playbook here for us to look at :)

Resources