Skip to content

Commit

Permalink
New module git config info (#7587)
Browse files Browse the repository at this point in the history
Add new module git_config_info
  • Loading branch information
guenhter authored Nov 29, 2023
1 parent 512b2c7 commit 1b9d437
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .github/BOTMETA.yml
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,8 @@ files:
maintainers: russoz
$modules/git_config.py:
maintainers: djmattyg007 mgedmin
$modules/git_config_info.py:
maintainers: guenhter
$modules/github_:
maintainers: stpierre
$modules/github_deploy_key.py:
Expand Down
2 changes: 1 addition & 1 deletion plugins/modules/git_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
requirements: ['git']
short_description: Read and write git configuration
description:
- The M(community.general.git_config) module changes git configuration by invoking 'git config'.
- The M(community.general.git_config) module changes git configuration by invoking C(git config).
This is needed if you do not want to use M(ansible.builtin.template) for the entire git
config file (for example because you need to change just C(user.email) in
/etc/.git/config). Solutions involving M(ansible.builtin.command) are cumbersome or
Expand Down
187 changes: 187 additions & 0 deletions plugins/modules/git_config_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2023, Guenther Grill <grill.guenther@gmail.com>
#
# 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

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = '''
---
module: git_config_info
author:
- Guenther Grill (@guenhter)
version_added: 8.1.0
requirements: ['git']
short_description: Read git configuration
description:
- The M(community.general.git_config_info) module reads the git configuration
by invoking C(git config).
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module
options:
name:
description:
- The name of the setting to read.
- If not provided, all settings will be returned as RV(config_values).
type: str
path:
description:
- Path to a git repository or file for reading values from a specific repo.
- If O(scope) is V(local), this must point to a repository to read from.
- If O(scope) is V(file), this must point to specific git config file to read from.
- Otherwise O(path) is ignored if set.
type: path
scope:
description:
- Specify which scope to read values from.
- If set to V(global), the global git config is used. O(path) is ignored.
- If set to V(system), the system git config is used. O(path) is ignored.
- If set to V(local), O(path) must be set to the repo to read from.
- If set to V(file), O(path) must be set to the config file to read from.
choices: [ "global", "system", "local", "file" ]
default: "system"
type: str
'''

EXAMPLES = '''
- name: Read a system wide config
community.general.git_config_info:
name: core.editor
register: result
- name: Show value of core.editor
ansible.builtin.debug:
msg: "{{ result.config_value | default('(not set)', true) }}"
- name: Read a global config from ~/.gitconfig
community.general.git_config_info:
name: alias.remotev
scope: global
- name: Read a project specific config
community.general.git_config_info:
name: color.ui
scope: local
path: /etc
- name: Read all global values
community.general.git_config_info:
scope: global
- name: Read all system wide values
community.general.git_config_info:
- name: Read all values of a specific file
community.general.git_config_info:
scope: file
path: /etc/gitconfig
'''

RETURN = '''
---
config_value:
description: >
When O(name) is set, a string containing the value of the setting in name. If O(name) is not set, empty.
If a config key such as V(push.pushoption) has more then one entry, just the first one is returned here.
returned: success if O(name) is set
type: str
sample: "vim"
config_values:
description:
- This is a dictionary mapping a git configuration setting to a list of its values.
- When O(name) is not set, all configuration settings are returned here.
- When O(name) is set, only the setting specified in O(name) is returned here.
If that setting is not set, the key will still be present, and its value will be an empty list.
returned: success
type: dict
sample:
core.editor: ["vim"]
color.ui: ["auto"]
push.pushoption: ["merge_request.create", "merge_request.draft"]
alias.remotev: ["remote -v"]
'''

from ansible.module_utils.basic import AnsibleModule


def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(type="str"),
path=dict(type="path"),
scope=dict(required=False, type="str", default="system", choices=["global", "system", "local", "file"]),
),
required_if=[
("scope", "local", ["path"]),
("scope", "file", ["path"]),
],
required_one_of=[],
supports_check_mode=True,
)

# We check error message for a pattern, so we need to make sure the messages appear in the form we're expecting.
# Set the locale to C to ensure consistent messages.
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')

name = module.params["name"]
path = module.params["path"]
scope = module.params["scope"]

run_cwd = path if scope == "local" else "/"
args = build_args(module, name, path, scope)

(rc, out, err) = module.run_command(args, cwd=run_cwd, expand_user_and_vars=False)

if rc == 128 and "unable to read config file" in err:
# This just means nothing has been set at the given scope
pass
elif rc >= 2:
# If the return code is 1, it just means the option hasn't been set yet, which is fine.
module.fail_json(rc=rc, msg=err, cmd=" ".join(args))

output_lines = out.strip("\0").split("\0") if out else []

if name:
first_value = output_lines[0] if output_lines else ""
config_values = {name: output_lines}
module.exit_json(changed=False, msg="", config_value=first_value, config_values=config_values)
else:
config_values = text_to_dict(output_lines)
module.exit_json(changed=False, msg="", config_value="", config_values=config_values)


def build_args(module, name, path, scope):
git_path = module.get_bin_path("git", True)
args = [git_path, "config", "--includes", "--null", "--" + scope]

if scope == "file":
args.append(path)

if name:
args.extend(["--get-all", name])
else:
args.append("--list")

return args


def text_to_dict(text_lines):
config_values = {}
for value in text_lines:
k, v = value.split("\n", 1)
if k in config_values:
config_values[k].append(v)
else:
config_values[k] = [v]
return config_values


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later

output/
integration/inventory
7 changes: 7 additions & 0 deletions tests/integration/targets/git_config_info/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 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

azp/posix/3
skip/aix
destructive
11 changes: 11 additions & 0 deletions tests/integration/targets/git_config_info/files/gitconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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

[credential "https://some.com"]
username = yolo
[user]
name = foobar
[push]
pushoption = merge_request.create
pushoption = merge_request.draft
7 changes: 7 additions & 0 deletions tests/integration/targets/git_config_info/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
# 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

dependencies:
- setup_remote_tmp_dir
26 changes: 26 additions & 0 deletions tests/integration/targets/git_config_info/tasks/error_handling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# 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

- import_tasks: "setup_file.yml"

- name: getting all system configs
git_config_info:
scope: system
register: get_result1

- name: getting all system configs for a key
git_config_info:
name: user.name
scope: system
register: get_result2

- name: assert value is correct
assert:
that:
- get_result1.config_value == ""
- 'get_result1.config_values == {}'
- get_result2.config_value == ""
- 'get_result2.config_values == {"user.name": []}'
...
19 changes: 19 additions & 0 deletions tests/integration/targets/git_config_info/tasks/get_all_values.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
# 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

- include_tasks: "{{ item.import_file }}"

- name: getting all values (as list) for a file config
git_config_info:
scope: "{{ item.git_scope }}"
path: "{{ item.git_file | default(omit) }}"
register: get_result

- name: assert value is correct
assert:
that:
- get_result.config_value == ""
- 'get_result.config_values == {"credential.https://some.com.username": ["yolo"], "user.name": ["foobar"], "push.pushoption": ["merge_request.create", "merge_request.draft"]}'
...
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
# 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

- include_tasks: "{{ item.import_file }}"

- name: getting only a single value (as string) from an option with multiple values in the git config file
git_config_info:
name: push.pushoption
scope: "{{ item.git_scope }}"
path: "{{ item.git_file | default(omit) }}"
register: get_result

- name: assert value is correct
assert:
that:
- get_result.config_value == "merge_request.create"
- 'get_result.config_values == {"push.pushoption": ["merge_request.create", "merge_request.draft"]}'
...
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
# 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

- include_tasks: "{{ item.import_file }}"

- name: getting simple file value1
git_config_info:
name: user.name
scope: "{{ item.git_scope }}"
path: "{{ item.git_file | default(omit) }}"
register: get_result1

- name: getting simple file value2
git_config_info:
name: "credential.https://some.com.username"
scope: "{{ item.git_scope }}"
path: "{{ item.git_file | default(omit) }}"
register: get_result2

- name: getting not existing value
git_config_info:
name: "user.email"
scope: "{{ item.git_scope }}"
path: "{{ item.git_file | default(omit) }}"
register: get_result3

- name: assert value is correct
assert:
that:
- get_result1.config_value == "foobar"
- 'get_result1.config_values == {"user.name": ["foobar"]}'
- get_result2.config_value == "yolo"
- 'get_result2.config_values == {"credential.https://some.com.username": ["yolo"]}'
- get_result3.config_value == ""
- 'get_result3.config_values == {"user.email": []}'
...
33 changes: 33 additions & 0 deletions tests/integration/targets/git_config_info/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################

# test code for the git_config_info module
# 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: setup
import_tasks: setup.yml

- block:
- include_tasks: get_simple_value.yml
loop:
- { import_file: setup_global.yml, git_scope: 'global' }
- { import_file: setup_file.yml, git_scope: 'file', git_file: "{{ remote_tmp_dir }}/gitconfig_file" }

- include_tasks: get_multi_value.yml
loop:
- { import_file: setup_global.yml, git_scope: 'global' }
- { import_file: setup_file.yml, git_scope: 'file', git_file: "{{ remote_tmp_dir }}/gitconfig_file" }

- include_tasks: get_all_values.yml
loop:
- { import_file: setup_global.yml, git_scope: 'global' }
- { import_file: setup_file.yml, git_scope: 'file', git_file: "{{ remote_tmp_dir }}/gitconfig_file" }

- include_tasks: error_handling.yml
when: git_installed is succeeded and git_version.stdout is version(git_version_supporting_includes, ">=")
...
15 changes: 15 additions & 0 deletions tests/integration/targets/git_config_info/tasks/setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
# 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: verify that git is installed so this test can continue
command: which git
register: git_installed
ignore_errors: true

- name: get git version, only newer than {{git_version_supporting_includes}} has includes option
shell: "git --version | grep 'git version' | sed 's/git version //'"
register: git_version
ignore_errors: true
...
Loading

0 comments on commit 1b9d437

Please sign in to comment.