1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-04-01 09:56:18 +00:00

composer - make create-project idempotent, add force parameter (#11689)

* composer - make create-project idempotent, add force parameter

Adds a check for an existing composer.json in working_dir before running
create-project, so the task is skipped rather than failing on second run.
A new force parameter allows bypassing this check when needed.

Fixes #725.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* changelog fragment: rename to PR number, add PR URL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexei Znamensky 2026-03-28 08:17:23 +13:00 committed by GitHub
parent 909458a661
commit a4bba99203
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 97 additions and 7 deletions

View file

@ -0,0 +1,6 @@
minor_changes:
- composer - add ``force`` parameter; when ``command=create-project``, the module now checks
whether a ``composer.json`` already exists in ``working_dir`` and skips the command if so,
making the task idempotent. Set ``force=true`` to always run the command regardless
(https://github.com/ansible-collections/community.general/issues/725,
https://github.com/ansible-collections/community.general/pull/11689).

View file

@ -98,6 +98,14 @@ options:
these.
default: false
type: bool
force:
description:
- When O(command) is V(create-project), the module checks whether a V(composer.json) already
exists in O(working_dir) and skips the command if it does, making the task idempotent.
- Set to V(true) to always run the command regardless.
default: false
type: bool
version_added: 12.6.0
composer_executable:
type: path
description:
@ -139,6 +147,7 @@ EXAMPLES = r"""
arguments: my/package
"""
import os
import re
import shlex
@ -212,6 +221,7 @@ def main():
optimize_autoloader=dict(default=True, type="bool"),
classmap_authoritative=dict(default=False, type="bool"),
ignore_platform_reqs=dict(default=False, type="bool"),
force=dict(default=False, type="bool"),
composer_executable=dict(type="path"),
),
required_if=[("global_command", False, ["working_dir"])],
@ -257,6 +267,12 @@ def main():
option = f"--{option}"
options.append(option)
working_dir = module.params["working_dir"]
if command == "create-project" and not module.params["force"]:
if working_dir and os.path.exists(os.path.join(working_dir, "composer.json")):
module.exit_json(changed=False, msg="composer.json already exists in working_dir, skipping create-project")
if module.check_mode:
if "dry-run" in available_options:
options.append("--dry-run")

View file

@ -9,6 +9,17 @@ from __future__ import annotations
from ansible_collections.community.general.plugins.modules import composer
from .uthelper import RunCommandMock, UTHelper
from .uthelper import RunCommandMock, TestCaseMock, UTHelper
UTHelper.from_module(composer, __name__, mocks=[RunCommandMock])
class OsPathExistsMock(TestCaseMock):
name = "os_path_exists"
def setup(self, mocker):
mocker.patch("os.path.exists", return_value=self.mock_specs.get("return_value", False))
def check(self, test_case, results):
pass
UTHelper.from_module(composer, __name__, mocks=[RunCommandMock, OsPathExistsMock])

View file

@ -3,6 +3,25 @@
# SPDX-License-Identifier: GPL-3.0-or-later
---
anchors:
help_install: &help_install
command: ["/testbin/php", "/testbin/composer", "help", "install", "--working-dir", "/var/www/foo", "--no-interaction", "--format=json"]
rc: 0
out: |
{"definition": {"options": ["a", "b", "c"]}}
err: ''
help_create_project: &help_create_project
command: ["/testbin/php", "/testbin/composer", "help", "create-project", "--working-dir", "/var/www/foo", "--no-interaction", "--format=json"]
rc: 0
out: |
{"definition": {"options": ["a", "b", "c"]}}
err: ''
run_create_project: &run_create_project
command: ["/testbin/php", "/testbin/composer", "create-project", "--working-dir", "/var/www/foo", "vendor/package"]
rc: 0
out: ''
err: ''
test_cases:
- id: composer
input:
@ -12,12 +31,50 @@ test_cases:
changed: true
mocks:
run_command:
- command: ["/testbin/php", "/testbin/composer", "help", "install", "--working-dir", "/var/www/foo", "--no-interaction", "--format=json"]
rc: 0
out: |
{"definition": {"options": ["a", "b", "c"]}}
err: ''
- *help_install
- command: ["/testbin/php", "/testbin/composer", "install", "--working-dir", "/var/www/foo"]
rc: 0
out: ''
err: ''
- id: create_project_runs
input:
command: create-project
arguments: "vendor/package"
working_dir: "/var/www/foo"
output:
changed: true
mocks:
os_path_exists:
return_value: false
run_command:
- *help_create_project
- *run_create_project
- id: create_project_skips_when_exists
input:
command: create-project
arguments: "vendor/package"
working_dir: "/var/www/foo"
output:
changed: false
mocks:
os_path_exists:
return_value: true
run_command:
- *help_create_project
- id: create_project_force
input:
command: create-project
arguments: "vendor/package"
working_dir: "/var/www/foo"
force: true
output:
changed: true
mocks:
os_path_exists:
return_value: true
run_command:
- *help_create_project
- *run_create_project