Repository: ansible/ansible

Issue:
creates option has no effect on unarchive module
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
unarchive

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.1.0
  config file = 
  configured module search path = Default w/o overrides
  python version = 3.5.3 (default, Apr  6 2017, 00:47:54) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Ubuntu 16.04.2 LTS

##### SUMMARY
<!--- Explain the problem briefly -->
creates option has no effect on unarchive module.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
Prerequisite: there are ~/test.tar.gz and ~/test2 directory.

<!--- Paste example playbooks or commands between quotes below -->
```yaml
unarchive:
  src: ~/test.tar.gz
  dest: ~/
  remote_src: yes
  creates: ~/test2
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
unarchive will be skipped.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
unarchive was executed.
<!--- Paste verbatim command output between quotes below -->
```
PLAY [ansible_test] ************************************************************

TASK [Gathering Facts] *********************************************************
ok: [ansible_test]

TASK [Unarchive test] **********************************************************
changed: [ansible_test]

PLAY RECAP *********************************************************************
ansible_test               : ok=2    changed=1    unreachable=0    failed=0   
```


Discussion:
cc @dagwieers @pileofrogs
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
The problem is that the _creates_ test currently doesn't handle tilde expansions, which feels a bit unintuitive when you can expand your _src_ and your _dest_ that way. If you instead check against `/full/path/to/test2` that ought to do the trick.

The tilde expansion is being broken by the quoting being added here.

https://github.com/ansible/ansible/blob/26d358c64dbbe9bf2a8133660567420edaa6a03d/lib/ansible/plugins/shell/__init__.py#L92

```python
cmd = ['test', '-e', shlex_quote(path)]
```

While the shell knows how to expand `~/foo` it treats `'~/foo'` as a literal path.
These being the earlier links in that test chain.

https://github.com/ansible/ansible/blob/26d358c64dbbe9bf2a8133660567420edaa6a03d/lib/ansible/plugins/action/unarchive.py#L65-L73
https://github.com/ansible/ansible/blob/26d358c64dbbe9bf2a8133660567420edaa6a03d/lib/ansible/plugins/action/__init__.py#L104-L109


Resolving patch:
diff --git a/lib/ansible/plugins/action/unarchive.py b/lib/ansible/plugins/action/unarchive.py
--- a/lib/ansible/plugins/action/unarchive.py
+++ b/lib/ansible/plugins/action/unarchive.py
@@ -66,6 +66,7 @@ def run(self, tmp=None, task_vars=None):
             # do not run the command if the line contains creates=filename
             # and the filename already exists. This allows idempotence
             # of command executions.
+            creates = self._remote_expand_user(creates)
             if self._remote_file_exists(creates):
                 result['skipped'] = True
                 result['msg'] = "skipped, since %s exists" % creates


Test patch: