1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

[PR #5829/4c4ef80c backport][stable-6] yarn: Fix state=latest not working with global=true (#5992)

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>
(cherry picked from commit 4c4ef80ca9)

Co-authored-by: Sargun Vohra <sargun.vohra@gmail.com>
This commit is contained in:
patchback[bot] 2023-02-13 22:07:12 +01:00 committed by GitHub
parent 53e5f51e57
commit 4f71c9384e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 112 additions and 25 deletions

View 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)

View file

@ -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,18 +233,15 @@ 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'] == 'list' and data['data']['type'].startswith('bins-'):
# This is a string in format: 'bins-<PACKAGE_NAME>'
installed.append(data['data']['type'][5:])
else:
if data['type'] == 'tree': if data['type'] == 'tree':
dependencies = data['data']['trees'] dependencies = data['data']['trees']
@ -276,8 +273,11 @@ 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:
# 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) self.module.fail_json(msg=err)
if not cmd_result: if not cmd_result:
@ -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,

View file

@ -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

View file

@ -24,6 +24,5 @@ plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
plugins/modules/rax.py use-argspec-type-path # fix needed plugins/modules/rax.py use-argspec-type-path # fix needed
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

View file

@ -19,4 +19,3 @@ plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
plugins/modules/rax.py use-argspec-type-path # fix needed plugins/modules/rax.py use-argspec-type-path # fix needed
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

View file

@ -19,4 +19,3 @@ plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
plugins/modules/rax.py use-argspec-type-path # fix needed plugins/modules/rax.py use-argspec-type-path # fix needed
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

View file

@ -21,4 +21,3 @@ plugins/modules/rax.py use-argspec-type-path # fix needed
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

View file

@ -21,4 +21,3 @@ plugins/modules/rax.py use-argspec-type-path # fix needed
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