mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
yarn: Fix state=latest not working with global=true (#5829)
* Yarn module: fix state=latest not working with global=true * fix whitespace * add changelog fragment * add integration test cases * add only tests for this PR (install+upgrade) * fix assuming default global dir * fix list() not working when global=true and name a package with no binary * remove ignores * whitespace * Update changelogs/fragments/5829-fix-yarn-global.yml Co-authored-by: Felix Fontein <felix@fontein.de> * Update changelogs/fragments/5829-fix-yarn-global.yml Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --------- Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
This commit is contained in:
parent
cd0a414e9f
commit
4c4ef80ca9
8 changed files with 112 additions and 25 deletions
4
changelogs/fragments/5829-fix-yarn-global.yml
Normal file
4
changelogs/fragments/5829-fix-yarn-global.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
bugfixes:
|
||||||
|
- yarn - fix ``state=latest`` not working with ``global=true`` (https://github.com/ansible-collections/community.general/issues/5712).
|
||||||
|
- yarn - fix ``global=true`` to check for the configured global folder instead of assuming the default (https://github.com/ansible-collections/community.general/pull/5829)
|
||||||
|
- yarn - fix ``state=absent`` not working with ``global=true`` when the package does not include a binary (https://github.com/ansible-collections/community.general/pull/5829)
|
|
@ -163,8 +163,6 @@ from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
class Yarn(object):
|
class Yarn(object):
|
||||||
|
|
||||||
DEFAULT_GLOBAL_INSTALLATION_PATH = os.path.expanduser('~/.config/yarn/global')
|
|
||||||
|
|
||||||
def __init__(self, module, **kwargs):
|
def __init__(self, module, **kwargs):
|
||||||
self.module = module
|
self.module = module
|
||||||
self.globally = kwargs['globally']
|
self.globally = kwargs['globally']
|
||||||
|
@ -188,10 +186,12 @@ class Yarn(object):
|
||||||
elif self.name is not None:
|
elif self.name is not None:
|
||||||
self.name_version = self.name
|
self.name_version = self.name
|
||||||
|
|
||||||
def _exec(self, args, run_in_check_mode=False, check_rc=True):
|
def _exec(self, args, run_in_check_mode=False, check_rc=True, unsupported_with_global=False):
|
||||||
if not self.module.check_mode or (self.module.check_mode and run_in_check_mode):
|
if not self.module.check_mode or (self.module.check_mode and run_in_check_mode):
|
||||||
|
|
||||||
if self.globally:
|
with_global_arg = self.globally and not unsupported_with_global
|
||||||
|
|
||||||
|
if with_global_arg:
|
||||||
# Yarn global arg is inserted before the command (e.g. `yarn global {some-command}`)
|
# Yarn global arg is inserted before the command (e.g. `yarn global {some-command}`)
|
||||||
args.insert(0, 'global')
|
args.insert(0, 'global')
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ class Yarn(object):
|
||||||
|
|
||||||
# If path is specified, cd into that path and run the command.
|
# If path is specified, cd into that path and run the command.
|
||||||
cwd = None
|
cwd = None
|
||||||
if self.path and not self.globally:
|
if self.path and not with_global_arg:
|
||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
# Module will make directory if not exists.
|
# Module will make directory if not exists.
|
||||||
os.makedirs(self.path)
|
os.makedirs(self.path)
|
||||||
|
@ -233,24 +233,21 @@ class Yarn(object):
|
||||||
missing.append(self.name)
|
missing.append(self.name)
|
||||||
return installed, missing
|
return installed, missing
|
||||||
|
|
||||||
result, error = self._exec(cmd, True, False)
|
# `yarn global list` should be treated as "unsupported with global" even though it exists,
|
||||||
|
# because it only only lists binaries, but `yarn global add` can install libraries too.
|
||||||
|
result, error = self._exec(cmd, run_in_check_mode=True, check_rc=False, unsupported_with_global=True)
|
||||||
|
|
||||||
if error:
|
if error:
|
||||||
self.module.fail_json(msg=error)
|
self.module.fail_json(msg=error)
|
||||||
|
|
||||||
for json_line in result.strip().split('\n'):
|
for json_line in result.strip().split('\n'):
|
||||||
data = json.loads(json_line)
|
data = json.loads(json_line)
|
||||||
if self.globally:
|
if data['type'] == 'tree':
|
||||||
if data['type'] == 'list' and data['data']['type'].startswith('bins-'):
|
dependencies = data['data']['trees']
|
||||||
# This is a string in format: 'bins-<PACKAGE_NAME>'
|
|
||||||
installed.append(data['data']['type'][5:])
|
|
||||||
else:
|
|
||||||
if data['type'] == 'tree':
|
|
||||||
dependencies = data['data']['trees']
|
|
||||||
|
|
||||||
for dep in dependencies:
|
for dep in dependencies:
|
||||||
name, version = dep['name'].rsplit('@', 1)
|
name, version = dep['name'].rsplit('@', 1)
|
||||||
installed.append(name)
|
installed.append(name)
|
||||||
|
|
||||||
if self.name not in installed:
|
if self.name not in installed:
|
||||||
missing.append(self.name)
|
missing.append(self.name)
|
||||||
|
@ -276,9 +273,12 @@ class Yarn(object):
|
||||||
if not os.path.isfile(os.path.join(self.path, 'yarn.lock')):
|
if not os.path.isfile(os.path.join(self.path, 'yarn.lock')):
|
||||||
return outdated
|
return outdated
|
||||||
|
|
||||||
cmd_result, err = self._exec(['outdated', '--json'], True, False)
|
cmd_result, err = self._exec(['outdated', '--json'], True, False, unsupported_with_global=True)
|
||||||
if err:
|
|
||||||
self.module.fail_json(msg=err)
|
# the package.json in the global dir is missing a license field, so warnings are expected on stderr
|
||||||
|
for line in err.splitlines():
|
||||||
|
if json.loads(line)['type'] == 'error':
|
||||||
|
self.module.fail_json(msg=err)
|
||||||
|
|
||||||
if not cmd_result:
|
if not cmd_result:
|
||||||
return outdated
|
return outdated
|
||||||
|
@ -340,7 +340,8 @@ def main():
|
||||||
|
|
||||||
# When installing globally, use the defined path for global node_modules
|
# When installing globally, use the defined path for global node_modules
|
||||||
if globally:
|
if globally:
|
||||||
path = Yarn.DEFAULT_GLOBAL_INSTALLATION_PATH
|
_rc, out, _err = module.run_command([executable, 'global', 'dir'], check_rc=True)
|
||||||
|
path = out.strip()
|
||||||
|
|
||||||
yarn = Yarn(module,
|
yarn = Yarn(module,
|
||||||
name=name,
|
name=name,
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
package: 'iconv-lite'
|
package: 'iconv-lite'
|
||||||
environment:
|
environment:
|
||||||
PATH: "{{ node_bin_path }}:{{ansible_env.PATH}}"
|
PATH: "{{ node_bin_path }}:{{ansible_env.PATH}}"
|
||||||
|
YARN_IGNORE_ENGINES: true
|
||||||
block:
|
block:
|
||||||
|
|
||||||
# Get the version of Yarn and register to a variable
|
# Get the version of Yarn and register to a variable
|
||||||
|
@ -135,3 +136,89 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- yarn_uninstall_package is changed
|
- yarn_uninstall_package is changed
|
||||||
|
|
||||||
|
- name: 'Global install binary with explicit version (older version of package)'
|
||||||
|
yarn:
|
||||||
|
global: true
|
||||||
|
executable: '{{ yarn_bin_path }}/yarn'
|
||||||
|
name: prettier
|
||||||
|
version: 2.0.0
|
||||||
|
state: present
|
||||||
|
environment:
|
||||||
|
PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}'
|
||||||
|
register: yarn_global_install_old_binary
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- yarn_global_install_old_binary is changed
|
||||||
|
|
||||||
|
- name: 'Global upgrade old binary'
|
||||||
|
yarn:
|
||||||
|
global: true
|
||||||
|
executable: '{{ yarn_bin_path }}/yarn'
|
||||||
|
name: prettier
|
||||||
|
state: latest
|
||||||
|
environment:
|
||||||
|
PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}'
|
||||||
|
register: yarn_global_update_old_binary
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- yarn_global_update_old_binary is changed
|
||||||
|
|
||||||
|
- name: 'Global remove a binary'
|
||||||
|
yarn:
|
||||||
|
global: true
|
||||||
|
executable: '{{ yarn_bin_path }}/yarn'
|
||||||
|
name: prettier
|
||||||
|
state: absent
|
||||||
|
environment:
|
||||||
|
PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}'
|
||||||
|
register: yarn_global_uninstall_binary
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- yarn_global_uninstall_binary is changed
|
||||||
|
|
||||||
|
- name: 'Global install package with no binary with explicit version (older version of package)'
|
||||||
|
yarn:
|
||||||
|
global: true
|
||||||
|
executable: '{{ yarn_bin_path }}/yarn'
|
||||||
|
name: left-pad
|
||||||
|
version: 1.1.0
|
||||||
|
state: present
|
||||||
|
environment:
|
||||||
|
PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}'
|
||||||
|
register: yarn_global_install_old_package
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- yarn_global_install_old_package is changed
|
||||||
|
|
||||||
|
- name: 'Global upgrade old package with no binary'
|
||||||
|
yarn:
|
||||||
|
global: true
|
||||||
|
executable: '{{ yarn_bin_path }}/yarn'
|
||||||
|
name: left-pad
|
||||||
|
state: latest
|
||||||
|
environment:
|
||||||
|
PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}'
|
||||||
|
register: yarn_global_update_old_package
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- yarn_global_update_old_package is changed
|
||||||
|
|
||||||
|
- name: 'Global remove a package with no binary'
|
||||||
|
yarn:
|
||||||
|
global: true
|
||||||
|
executable: '{{ yarn_bin_path }}/yarn'
|
||||||
|
name: left-pad
|
||||||
|
state: absent
|
||||||
|
environment:
|
||||||
|
PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}'
|
||||||
|
register: yarn_global_uninstall_package
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- yarn_global_uninstall_package is changed
|
||||||
|
|
|
@ -24,6 +24,5 @@ plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
|
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
|
||||||
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
||||||
plugins/modules/yarn.py use-argspec-type-path
|
|
||||||
tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.6 # django generated code
|
tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.6 # django generated code
|
||||||
tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.7 # django generated code
|
tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.7 # django generated code
|
||||||
|
|
|
@ -19,4 +19,3 @@ plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
|
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
|
||||||
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
||||||
plugins/modules/yarn.py use-argspec-type-path
|
|
||||||
|
|
|
@ -19,4 +19,3 @@ plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
|
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
|
||||||
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
||||||
plugins/modules/yarn.py use-argspec-type-path
|
|
||||||
|
|
|
@ -21,4 +21,3 @@ plugins/modules/rax.py use-argspec-type-path
|
||||||
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
|
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
|
||||||
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
||||||
plugins/modules/yarn.py use-argspec-type-path
|
|
||||||
|
|
|
@ -21,4 +21,3 @@ plugins/modules/rax.py use-argspec-type-path
|
||||||
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
|
||||||
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
|
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
|
||||||
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
plugins/modules/xfconf.py validate-modules:return-syntax-error
|
||||||
plugins/modules/yarn.py use-argspec-type-path
|
|
||||||
|
|
Loading…
Reference in a new issue