mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 02:25:53 +00:00
yarn: add Alpine Linux support in integration tests (#11943)
* test(yarn): add Alpine Linux support via apk Install nodejs and yarn via apk on Alpine, sharing the functional test block with the existing non-Alpine (pre-built binary) path. Extracts the test block into tests.yml to avoid duplication. Fixes #4270 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(yarn): skip Node.js runtime warnings in stderr processing Node.js 24 emits DeprecationWarning lines to stderr (e.g. for url.parse()) that are not JSON, causing _process_yarn_error to fail with "Unexpected stderr output from Yarn". Skip lines starting with "(node:" before attempting JSON parsing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(yarn): add changelog fragment for #11943 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(yarn): only JSON-parse lines starting with '{' in stderr Node.js 24 emits multi-line DeprecationWarnings to stderr (e.g. the hint line "(Use `node --trace-deprecation ...`") that are not JSON and were tripping the "Unexpected stderr output from Yarn" failure. Yarn's structured output always starts with '{', so skip any line that doesn't. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(yarn): install sqlite on Alpine to fix nodejs 22 symbol error On Alpine 3.21 nodejs 22 requires SQLite session extension symbols (sqlite3session_*) that are not present in sqlite-libs; installing the full sqlite package provides them. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(yarn): refresh apk cache and upgrade sqlite-libs before installing nodejs The CI Alpine container may have a stale sqlite-libs that lacks the session extension symbols (sqlite3session_*) required by nodejs 22+. Force a cache refresh and upgrade sqlite-libs to the latest revision. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(yarn): warn on non-JSON stderr lines instead of silently skipping Non-JSON lines in stderr (e.g. Node.js runtime DeprecationWarnings) are surfaced to the user via module.warn() rather than being silently ignored, since their content and meaning are not known in advance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * prefix yarn output line * Update changelogs/fragments/11943-yarn-nodejs-runtime-warnings.yml Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
d87a8a167f
commit
38d49d240e
6 changed files with 240 additions and 199 deletions
|
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- yarn - skip Node.js runtime warning lines (starting with ``(node:``) in stderr before JSON parsing, fixing failures with Node.js 24 which emits ``DeprecationWarning`` to stderr. The warnings are passed on to the user (https://github.com/ansible-collections/community.general/pull/11943).
|
||||
|
|
@ -187,8 +187,13 @@ class Yarn:
|
|||
|
||||
def _process_yarn_error(self, err):
|
||||
try:
|
||||
# We need to filter for errors, since Yarn warnings are included in stderr
|
||||
# We need to filter for errors, since Yarn warnings are included in stderr.
|
||||
# Non-JSON lines (e.g. Node.js runtime warnings) are surfaced via module.warn()
|
||||
# rather than treated as errors, since their meaning is unknown.
|
||||
for line in err.splitlines():
|
||||
if not line.startswith("{"):
|
||||
self.module.warn(f"yarn stderr: {line}")
|
||||
continue
|
||||
if json.loads(line)["type"] == "error":
|
||||
self.module.fail_json(msg=err)
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
# ============================================================
|
||||
|
||||
- include_tasks: run_alpine.yml
|
||||
when: ansible_facts.os_family == 'Alpine'
|
||||
|
||||
- include_tasks: run.yml
|
||||
vars:
|
||||
nodejs_version: '{{ item.node_version }}'
|
||||
|
|
@ -22,4 +25,4 @@
|
|||
with_items:
|
||||
- {node_version: 16.20.2, yarn_version: 1.22.22} # oldest node version with macOS arm64 support
|
||||
when:
|
||||
- not (ansible_facts.os_family == 'Alpine') # TODO
|
||||
- not (ansible_facts.os_family == 'Alpine')
|
||||
|
|
|
|||
|
|
@ -35,203 +35,8 @@
|
|||
path: '{{remote_tmp_dir}}/node_modules'
|
||||
state: absent
|
||||
|
||||
# Set vars for our test harness
|
||||
- vars:
|
||||
# node_bin_path: "/usr/local/lib/nodejs/node-v{{nodejs_version}}/bin"
|
||||
- include_tasks: tests.yml
|
||||
vars:
|
||||
node_bin_path: "/usr/local/lib/nodejs/{{ nodejs_path }}/bin"
|
||||
yarn_bin_path: "{{ remote_tmp_dir }}/yarn-v{{ yarn_version }}/bin"
|
||||
package: 'iconv-lite'
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ansible_facts.env.PATH}}"
|
||||
YARN_IGNORE_ENGINES: true
|
||||
block:
|
||||
|
||||
# Get the version of Yarn and register to a variable
|
||||
- shell: '{{ yarn_bin_path }}/yarn --version'
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_version
|
||||
|
||||
- name: 'Create dummy package.json'
|
||||
template:
|
||||
src: package.j2
|
||||
dest: '{{ remote_tmp_dir }}/package.json'
|
||||
|
||||
- name: 'Install all packages.'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
|
||||
- name: 'Install the same package from package.json again.'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: '{{ package }}'
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_install
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not (yarn_install is changed)
|
||||
|
||||
- name: 'Install all packages in check mode.'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
check_mode: true
|
||||
register: yarn_install_check
|
||||
|
||||
- name: verify test yarn global installation in check mode
|
||||
assert:
|
||||
that:
|
||||
- yarn_install_check.err is defined
|
||||
- yarn_install_check.out is defined
|
||||
- yarn_install_check.err is none
|
||||
- yarn_install_check.out is none
|
||||
|
||||
- name: 'Install package with explicit version (older version of package)'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: left-pad
|
||||
version: 1.1.0
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_install_old_package
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- yarn_install_old_package is changed
|
||||
|
||||
- name: 'Again but without explicit executable path'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
name: left-pad
|
||||
version: 1.1.0
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ yarn_bin_path }}:{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
|
||||
- name: 'Upgrade old package'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: left-pad
|
||||
state: latest
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_update_old_package
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- yarn_update_old_package is changed
|
||||
|
||||
- name: 'Remove a package'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: '{{ package }}'
|
||||
state: absent
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_uninstall_package
|
||||
|
||||
- name: 'Assert package removed'
|
||||
assert:
|
||||
that:
|
||||
- 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_facts.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_facts.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_facts.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_facts.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_facts.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_facts.env.PATH }}'
|
||||
register: yarn_global_uninstall_package
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- yarn_global_uninstall_package is changed
|
||||
|
|
|
|||
25
tests/integration/targets/yarn/tasks/run_alpine.yml
Normal file
25
tests/integration/targets/yarn/tasks/run_alpine.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
- name: Install nodejs and yarn via apk
|
||||
community.general.apk:
|
||||
name:
|
||||
- sqlite-libs
|
||||
- nodejs
|
||||
- yarn
|
||||
state: latest
|
||||
update_cache: true
|
||||
|
||||
# Clean up before running tests
|
||||
- name: Remove any previous Nodejs modules
|
||||
file:
|
||||
path: '{{ remote_tmp_dir }}/node_modules'
|
||||
state: absent
|
||||
|
||||
- include_tasks: tests.yml
|
||||
vars:
|
||||
node_bin_path: /usr/bin
|
||||
yarn_bin_path: /usr/bin
|
||||
package: 'iconv-lite'
|
||||
201
tests/integration/targets/yarn/tasks/tests.yml
Normal file
201
tests/integration/targets/yarn/tasks/tests.yml
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Expects: node_bin_path, yarn_bin_path, package
|
||||
|
||||
- environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_facts.env.PATH }}"
|
||||
YARN_IGNORE_ENGINES: true
|
||||
block:
|
||||
|
||||
# Get the version of Yarn and register to a variable
|
||||
- shell: '{{ yarn_bin_path }}/yarn --version'
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_installed_version
|
||||
|
||||
- name: 'Create dummy package.json'
|
||||
template:
|
||||
src: package.j2
|
||||
dest: '{{ remote_tmp_dir }}/package.json'
|
||||
|
||||
- name: 'Install all packages.'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
|
||||
- name: 'Install the same package from package.json again.'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: '{{ package }}'
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_install
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- not (yarn_install is changed)
|
||||
|
||||
- name: 'Install all packages in check mode.'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
check_mode: true
|
||||
register: yarn_install_check
|
||||
|
||||
- name: verify test yarn global installation in check mode
|
||||
assert:
|
||||
that:
|
||||
- yarn_install_check.err is defined
|
||||
- yarn_install_check.out is defined
|
||||
- yarn_install_check.err is none
|
||||
- yarn_install_check.out is none
|
||||
|
||||
- name: 'Install package with explicit version (older version of package)'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: left-pad
|
||||
version: 1.1.0
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_install_old_package
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- yarn_install_old_package is changed
|
||||
|
||||
- name: 'Again but without explicit executable path'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
name: left-pad
|
||||
version: 1.1.0
|
||||
state: present
|
||||
environment:
|
||||
PATH: '{{ yarn_bin_path }}:{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
|
||||
- name: 'Upgrade old package'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: left-pad
|
||||
state: latest
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_update_old_package
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- yarn_update_old_package is changed
|
||||
|
||||
- name: 'Remove a package'
|
||||
yarn:
|
||||
path: '{{ remote_tmp_dir }}'
|
||||
executable: '{{ yarn_bin_path }}/yarn'
|
||||
name: '{{ package }}'
|
||||
state: absent
|
||||
environment:
|
||||
PATH: '{{ node_bin_path }}:{{ ansible_facts.env.PATH }}'
|
||||
register: yarn_uninstall_package
|
||||
|
||||
- name: 'Assert package removed'
|
||||
assert:
|
||||
that:
|
||||
- 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_facts.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_facts.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_facts.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_facts.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_facts.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_facts.env.PATH }}'
|
||||
register: yarn_global_uninstall_package
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- yarn_global_uninstall_package is changed
|
||||
Loading…
Add table
Add a link
Reference in a new issue