add support for multiple creates, removes

Signed-off-by: Simon Leary <simon.leary42@proton.me>
This commit is contained in:
Simon Leary 2025-11-05 15:44:17 -05:00
parent a2dda41bb6
commit 6fa8035599
No known key found for this signature in database
3 changed files with 238 additions and 38 deletions

View file

@ -0,0 +1,3 @@
minor_changes:
- command - ``creates``, ``removes`` arguments now accept a list
- shell - ``creates``, ``removes`` arguments now accept a list

View file

@ -63,14 +63,16 @@ options:
- Only the string (free form) or the list (argv) form can be provided, not both. One or the other must be provided.
version_added: "2.6"
creates:
type: path
type: list
elements: str
description:
- A filename or (since 2.0) glob pattern. If a matching file already exists, this step B(will not) be run.
- A list of filenames or (since 2.0) glob patterns. If all exist, this step B(will not) be run.
- This is checked before O(removes) is checked.
removes:
type: path
type: list
elements: str
description:
- A filename or (since 2.0) glob pattern. If a matching file exists, this step B(will) be run.
- A list of filenames or (since 2.0) glob patterns. If none exist, this step B(will not) be run.
- This is checked after O(creates) is checked.
version_added: "0.8"
chdir:
@ -104,6 +106,7 @@ notes:
For instance, if you only want to run a command if a certain file does not exist, use this.
- Check mode is supported when passing O(creates) or O(removes). If running in check mode and either of these are specified, the module will
check for the existence of the file and report the correct changed status. If these are not supplied, the task will be skipped.
- C(changed_when) should not be specified when passing O(creates) or O(removes).
- The O(ignore:executable) parameter is removed since version 2.4. If you have a need for this parameter, use the M(ansible.builtin.shell) module instead.
- For Windows targets, use the M(ansible.windows.win_command) module instead.
- For rebooting systems, use the M(ansible.builtin.reboot) or M(ansible.windows.win_reboot) module.
@ -254,8 +257,8 @@ def main():
chdir=dict(type='path'),
executable=dict(),
expand_argument_vars=dict(type='bool', default=True),
creates=dict(type='path'),
removes=dict(type='path'),
creates=dict(type='list', elements='str'),
removes=dict(type='list', elements='str'),
# The default for this really comes from the action plugin
stdin=dict(required=False),
stdin_add_newline=dict(type='bool', default=True),
@ -311,17 +314,16 @@ def main():
# special skips for idempotence if file exists (assumes command creates)
if creates:
if glob.glob(creates):
r['msg'] = "%s not run command since '%s' exists" % (shoulda, creates)
r['stdout'] = "skipped, since %s exists" % creates # TODO: deprecate
if all(glob.glob(x) for x in creates):
r['msg'] = "%s not run command since '%s' all exist" % (shoulda, creates)
r['stdout'] = "skipped, since %s all exist" % creates # TODO: deprecate
r['rc'] = 0
# special skips for idempotence if file does not exist (assumes command removes)
if not r['msg'] and removes:
if not glob.glob(removes):
r['msg'] = "%s not run command since '%s' does not exist" % (shoulda, removes)
r['stdout'] = "skipped, since %s does not exist" % removes # TODO: deprecate
if (not any(glob.glob(x) for x in removes)):
r['msg'] = "%s not run command since none of '%s' exist" % (shoulda, removes)
r['stdout'] = "skipped, since none of %s exist" % removes # TODO: deprecate
r['rc'] = 0
if r['msg']:

View file

@ -112,7 +112,7 @@
# FIXME doesn't have the expected stdout.
#- name: execute the test.sh script with executable via command
# command: "{{remote_tmp_dir_test }}/test.sh executable={{ bash.stdout }}"
# command: "{{ remote_tmp_dir_test }}/test.sh executable={{ bash.stdout }}"
# register: command_result1
#
#- name: assert that the script executed correctly with command
@ -148,13 +148,13 @@
# creates
- name: verify that afile.txt is absent
- name: ensure that afile.txt is absent
file:
path: "{{ remote_tmp_dir_test }}/afile.txt"
state: absent
- name: create afile.txt with create_afile.sh via command (check mode)
command: "{{ remote_tmp_dir_test }}/create_afile.sh {{remote_tmp_dir_test }}/afile.txt"
command: "{{ remote_tmp_dir_test }}/create_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
args:
creates: "{{ remote_tmp_dir_test }}/afile.txt"
register: check_mode_result
@ -167,19 +167,30 @@
- name: verify that afile.txt still does not exist
stat:
path: "{{remote_tmp_dir_test}}/afile.txt"
path: "{{ remote_tmp_dir_test }}/afile.txt"
register: stat_result
failed_when: stat_result.stat.exists
- name: create afile.txt with create_afile.sh via command
command: "{{ remote_tmp_dir_test }}/create_afile.sh {{remote_tmp_dir_test }}/afile.txt"
command: "{{ remote_tmp_dir_test }}/create_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
args:
creates: "{{ remote_tmp_dir_test }}/afile.txt"
- name: verify that afile.txt is present
file:
stat:
path: "{{ remote_tmp_dir_test }}/afile.txt"
state: file
register: stat_result
failed_when: not stat_result.stat.exists
- name: create afile.txt with create_afile.sh via command (again)
command: "{{ remote_tmp_dir_test }}/create_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
args:
creates: "{{ remote_tmp_dir_test }}/afile.txt"
register: redundant_command_result
- name: verify that changed=false
assert:
that: "redundant_command_result is not changed"
- name: re-run previous command using creates with globbing (check mode)
command: "{{ remote_tmp_dir_test }}/create_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
@ -206,6 +217,11 @@
# removes
- name: ensure that afile.txt exists
file:
path: "{{ remote_tmp_dir_test }}/afile.txt"
state: touch
- name: remove afile.txt with remote_afile.sh via command (check mode)
command: "{{ remote_tmp_dir_test }}/remove_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
args:
@ -220,7 +236,7 @@
- name: verify that afile.txt still exists
stat:
path: "{{remote_tmp_dir_test}}/afile.txt"
path: "{{ remote_tmp_dir_test }}/afile.txt"
register: stat_result
failed_when: not stat_result.stat.exists
@ -230,7 +246,20 @@
removes: "{{ remote_tmp_dir_test }}/afile.txt"
- name: verify that afile.txt is absent
file: path={{remote_tmp_dir_test}}/afile.txt state=absent
stat:
path: "{{ remote_tmp_dir_test }}/afile.txt"
register: stat_result
failed_when: stat_result.stat.exists
- name: remove afile.txt with remove_afile.sh via command (again)
command: "{{ remote_tmp_dir_test }}/remove_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
args:
removes: "{{ remote_tmp_dir_test }}/afile.txt"
register: redundant_command_result
- name: verify that changed=false
assert:
that: "redundant_command_result is not changed"
- name: re-run previous command using removes with globbing (check mode)
command: "{{ remote_tmp_dir_test }}/remove_afile.sh {{ remote_tmp_dir_test }}/afile.txt"
@ -255,6 +284,170 @@
that:
- command_result4.changed != True
# creates (multiple)
- name: ensure that afile.txt, bfile.txt are absent
file:
path: "{{ item }}"
state: absent
loop:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: create afile.txt, bfile.txt via command (check mode)
command: touch {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
creates:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
register: check_mode_result
check_mode: yes
- assert:
that:
- check_mode_result.changed
- "'skipped' not in check_mode_result"
- name: verify that afile.txt, bfile.txt still do not exist
command: test ! -e {{ item }}
loop:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: create afile.txt, bfile.txt via command
command: touch {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
creates:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: verify that afile.txt, bfile.txt are present
command: test -e {{ item }}
loop:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: create afile.txt, bfile.txt via command (again)
command: touch {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
creates:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
register: redundant_command_result
- name: verify that changed=false
assert:
that: "redundant_command_result is not changed"
- name: re-run previous command using creates with globbing (check mode)
command: touch {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
creates: "{{ remote_tmp_dir_test }}/afile.*"
register: check_mode_result
check_mode: yes
- assert:
that:
- not check_mode_result.changed
- "'skipped' not in check_mode_result"
- name: re-run previous command using creates with globbing
command: touch {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
creates:
- "{{ remote_tmp_dir_test }}/afile.*"
- "{{ remote_tmp_dir_test }}/bfile.*"
register: command_result3
- name: assert that creates with globbing is working
assert:
that:
- command_result3 is not changed
# removes (multiple)
- name: ensure that afile.txt, bfile.txt exist
file:
path: "{{ item }}"
state: touch
loop:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: remove afile.txt, bfile.txt via command (check mode)
command: rm {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
removes:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
register: check_mode_result
check_mode: yes
- assert:
that:
- check_mode_result.changed
- "'skipped' not in check_mode_result"
- name: verify that afile.txt, bfile.txt still exist
command: test -e {{ item }}
loop:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: remove afile.txt, bfile.txt via command
command: rm {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
removes:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: verify that afile.txt, bfile.txt are absent
command: test ! -e {{ item }}
loop:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
- name: remove afile.txt, bfile.txt via command (again)
command: rm {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
removes:
- "{{ remote_tmp_dir_test }}/afile.txt"
- "{{ remote_tmp_dir_test }}/bfile.txt"
register: redundant_command_result
- name: verify that changed=false
assert:
that: "redundant_command_result is not changed"
- name: re-run previous command using removes with globbing (check mode)
command: rm {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
removes:
- "{{ remote_tmp_dir_test }}/afile.*"
- "{{ remote_tmp_dir_test }}/bfile.*"
register: check_mode_result
check_mode: yes
- assert:
that:
- not check_mode_result.changed
- "'skipped' not in check_mode_result"
- name: re-run previous command using removes with globbing
command: rm {{ remote_tmp_dir_test }}/afile.txt {{ remote_tmp_dir_test }}/bfile.txt
args:
removes:
- "{{ remote_tmp_dir_test }}/afile.*"
- "{{ remote_tmp_dir_test }}/bfile.*"
register: command_result4
- name: assert that removes with globbing is working
assert:
that:
- command_result4.changed != True
# end of removes
- name: pass stdin to cat via command
command: cat
args:
@ -328,7 +521,7 @@
# FIXME doesn't pass the expected stdout
#- name: execute the test.sh script
# shell: "{{remote_tmp_dir_test }}/test.sh executable={{ bash.stdout }}"
# shell: "{{ remote_tmp_dir_test }}/test.sh executable={{ bash.stdout }}"
# register: shell_result1
#
#- name: assert that the shell executed correctly
@ -358,9 +551,10 @@
# creates
- name: Verify that afile.txt is absent
file:
stat:
path: "{{ remote_tmp_dir_test }}/afile.txt"
state: absent
register: stat_result
failed_when: stat_result.stat.exists
- name: Execute the test.sh script with chdir
shell: "{{ remote_tmp_dir_test }}/test.sh > {{ remote_tmp_dir_test }}/afile.txt"
@ -369,9 +563,10 @@
creates: "{{ remote_tmp_dir_test }}/afile.txt"
- name: Verify that afile.txt is present
file:
stat:
path: "{{ remote_tmp_dir_test }}/afile.txt"
state: file
register: stat_result
failed_when: not stat_result.stat.exists
# multiline
@ -433,14 +628,14 @@
- shell_result7.stdout == 'One\n Two\n Three'
- name: execute a shell command with no trailing newline to stdin
shell: cat > {{remote_tmp_dir_test }}/afile.txt
shell: cat > {{ remote_tmp_dir_test }}/afile.txt
args:
stdin: test
stdin_add_newline: no
- name: make sure content matches expected
copy:
dest: "{{remote_tmp_dir_test }}/afile.txt"
dest: "{{ remote_tmp_dir_test }}/afile.txt"
content: test
register: shell_result7
failed_when:
@ -448,14 +643,14 @@
shell_result7 is changed
- name: execute a shell command with trailing newline to stdin
shell: cat > {{remote_tmp_dir_test }}/afile.txt
shell: cat > {{ remote_tmp_dir_test }}/afile.txt
args:
stdin: test
stdin_add_newline: yes
- name: make sure content matches expected
copy:
dest: "{{remote_tmp_dir_test }}/afile.txt"
dest: "{{ remote_tmp_dir_test }}/afile.txt"
content: |
test
register: shell_result8
@ -464,13 +659,13 @@
shell_result8 is changed
- name: execute a shell command with trailing newline to stdin, default
shell: cat > {{remote_tmp_dir_test }}/afile.txt
shell: cat > {{ remote_tmp_dir_test }}/afile.txt
args:
stdin: test
- name: make sure content matches expected
copy:
dest: "{{remote_tmp_dir_test }}/afile.txt"
dest: "{{ remote_tmp_dir_test }}/afile.txt"
content: |
test
register: shell_result9
@ -514,23 +709,23 @@
block:
- name: Create target folders
file:
path: '{{remote_tmp_dir}}/www_root/site'
path: '{{ remote_tmp_dir }}/www_root/site'
state: directory
- name: Create symlink
file:
path: '{{remote_tmp_dir}}/www'
path: '{{ remote_tmp_dir }}/www'
state: link
src: '{{remote_tmp_dir}}/www_root'
src: '{{ remote_tmp_dir }}/www_root'
- name: check parent using chdir
shell: dirname "$PWD"
args:
chdir: '{{remote_tmp_dir}}/www/site'
chdir: '{{ remote_tmp_dir }}/www/site'
register: parent_dir_chdir
- name: check parent using cd
shell: cd "{{remote_tmp_dir}}/www/site" && dirname "$PWD"
shell: cd "{{ remote_tmp_dir }}/www/site" && dirname "$PWD"
register: parent_dir_cd
- name: check expected outputs