-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cargo
module install from source in a given directory (#8480)
* Fixes installed version for git/local. * Support latest determination with local source. * Adds docs. * Improves error message. * Setup for tests. * Updates copyright. * Align closer to #7895. * Adds changelog. * Check directory exists. * Stop using format strings. * Corrects directory arg type in docs. * Setup test repo dynamically. * Adds tests. * Adds version matching tests. * Update changelog fragment to match PR ID. * Updates copyright. * Import new directory tests.
- Loading branch information
1 parent
3314d5c
commit 69b72e4
Showing
4 changed files
with
184 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
minor_changes: | ||
- "cargo - add option ``directory``, which allows source directory to be specified (https://github.com/ansible-collections/community.general/pull/8480)." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
# Copyright (c) 2021 Radek Sprta <[email protected]> | ||
# Copyright (c) 2024 Colin Nolan <[email protected]> | ||
# 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 | ||
|
||
|
@@ -65,6 +66,13 @@ | |
type: str | ||
default: present | ||
choices: [ "present", "absent", "latest" ] | ||
directory: | ||
description: | ||
- Path to the source directory to install the Rust package from. | ||
- This is only used when installing packages. | ||
type: path | ||
required: false | ||
version_added: 9.1.0 | ||
requirements: | ||
- cargo installed | ||
""" | ||
|
@@ -98,8 +106,14 @@ | |
community.general.cargo: | ||
name: ludusavi | ||
state: latest | ||
- name: Install "ludusavi" Rust package from source directory | ||
community.general.cargo: | ||
name: ludusavi | ||
directory: /path/to/ludusavi/source | ||
""" | ||
|
||
import json | ||
import os | ||
import re | ||
|
||
|
@@ -115,6 +129,7 @@ def __init__(self, module, **kwargs): | |
self.state = kwargs["state"] | ||
self.version = kwargs["version"] | ||
self.locked = kwargs["locked"] | ||
self.directory = kwargs["directory"] | ||
|
||
@property | ||
def path(self): | ||
|
@@ -143,7 +158,7 @@ def get_installed(self): | |
|
||
data, dummy = self._exec(cmd, True, False, False) | ||
|
||
package_regex = re.compile(r"^([\w\-]+) v(.+):$") | ||
package_regex = re.compile(r"^([\w\-]+) v(\S+).*:$") | ||
installed = {} | ||
for line in data.splitlines(): | ||
package_info = package_regex.match(line) | ||
|
@@ -163,19 +178,53 @@ def install(self, packages=None): | |
if self.version: | ||
cmd.append("--version") | ||
cmd.append(self.version) | ||
if self.directory: | ||
cmd.append("--path") | ||
cmd.append(self.directory) | ||
return self._exec(cmd) | ||
|
||
def is_outdated(self, name): | ||
installed_version = self.get_installed().get(name) | ||
latest_version = ( | ||
self.get_latest_published_version(name) | ||
if not self.directory | ||
else self.get_source_directory_version(name) | ||
) | ||
return installed_version != latest_version | ||
|
||
def get_latest_published_version(self, name): | ||
cmd = ["search", name, "--limit", "1"] | ||
data, dummy = self._exec(cmd, True, False, False) | ||
|
||
match = re.search(r'"(.+)"', data) | ||
if match: | ||
latest_version = match.group(1) | ||
|
||
return installed_version != latest_version | ||
if not match: | ||
self.module.fail_json( | ||
msg="No published version for package %s found" % name | ||
) | ||
return match.group(1) | ||
|
||
def get_source_directory_version(self, name): | ||
cmd = [ | ||
"metadata", | ||
"--format-version", | ||
"1", | ||
"--no-deps", | ||
"--manifest-path", | ||
os.path.join(self.directory, "Cargo.toml"), | ||
] | ||
data, dummy = self._exec(cmd, True, False, False) | ||
manifest = json.loads(data) | ||
|
||
package = next( | ||
(package for package in manifest["packages"] if package["name"] == name), | ||
None, | ||
) | ||
if not package: | ||
self.module.fail_json( | ||
msg="Package %s not defined in source, found: %s" | ||
% (name, [x["name"] for x in manifest["packages"]]) | ||
) | ||
return package["version"] | ||
|
||
def uninstall(self, packages=None): | ||
cmd = ["uninstall"] | ||
|
@@ -191,16 +240,21 @@ def main(): | |
state=dict(default="present", choices=["present", "absent", "latest"]), | ||
version=dict(default=None, type="str"), | ||
locked=dict(default=False, type="bool"), | ||
directory=dict(default=None, type="path"), | ||
) | ||
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True) | ||
|
||
name = module.params["name"] | ||
state = module.params["state"] | ||
version = module.params["version"] | ||
directory = module.params["directory"] | ||
|
||
if not name: | ||
module.fail_json(msg="Package name must be specified") | ||
|
||
if directory is not None and not os.path.isdir(directory): | ||
module.fail_json(msg="Source directory does not exist") | ||
|
||
# Set LANG env since we parse stdout | ||
module.run_command_environ_update = dict( | ||
LANG="C", LC_ALL="C", LC_MESSAGES="C", LC_CTYPE="C" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
tests/integration/targets/cargo/tasks/test_directory.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
--- | ||
# Copyright (c) 2024 Colin Nolan <[email protected]> | ||
# 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: Create temp directory | ||
tempfile: | ||
state: directory | ||
register: temp_directory | ||
|
||
- name: Test block | ||
vars: | ||
manifest_path: "{{ temp_directory.path }}/Cargo.toml" | ||
package_name: hello-world-directory-test | ||
block: | ||
- name: Initialize package | ||
ansible.builtin.command: | ||
cmd: "cargo init --name {{ package_name }}" | ||
args: | ||
chdir: "{{ temp_directory.path }}" | ||
|
||
- name: Set package version (1.0.0) | ||
ansible.builtin.lineinfile: | ||
path: "{{ manifest_path }}" | ||
regexp: '^version = ".*"$' | ||
line: 'version = "1.0.0"' | ||
|
||
- name: Ensure package is uninstalled | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
state: absent | ||
directory: "{{ temp_directory.path }}" | ||
register: uninstall_absent | ||
|
||
- name: Install package | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
directory: "{{ temp_directory.path }}" | ||
register: install_absent | ||
|
||
- name: Change package version (1.0.1) | ||
ansible.builtin.lineinfile: | ||
path: "{{ manifest_path }}" | ||
regexp: '^version = ".*"$' | ||
line: 'version = "1.0.1"' | ||
|
||
- name: Install package again (present) | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
state: present | ||
directory: "{{ temp_directory.path }}" | ||
register: install_present_state | ||
|
||
- name: Install package again (latest) | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
state: latest | ||
directory: "{{ temp_directory.path }}" | ||
register: install_latest_state | ||
|
||
- name: Change package version (2.0.0) | ||
ansible.builtin.lineinfile: | ||
path: "{{ manifest_path }}" | ||
regexp: '^version = ".*"$' | ||
line: 'version = "2.0.0"' | ||
|
||
- name: Install package with given version (matched) | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
version: "2.0.0" | ||
directory: "{{ temp_directory.path }}" | ||
register: install_given_version_matched | ||
|
||
- name: Install package with given version (unmatched) | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
version: "2.0.1" | ||
directory: "{{ temp_directory.path }}" | ||
register: install_given_version_unmatched | ||
ignore_errors: true | ||
|
||
- name: Uninstall package | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
state: absent | ||
directory: "{{ temp_directory.path }}" | ||
register: uninstall_present | ||
|
||
- name: Install non-existant package | ||
community.general.cargo: | ||
name: "{{ package_name }}-non-existant" | ||
state: present | ||
directory: "{{ temp_directory.path }}" | ||
register: install_non_existant | ||
ignore_errors: true | ||
|
||
- name: Install non-existant source directory | ||
community.general.cargo: | ||
name: "{{ package_name }}" | ||
state: present | ||
directory: "{{ temp_directory.path }}/non-existant" | ||
register: install_non_existant_source | ||
ignore_errors: true | ||
|
||
always: | ||
- name: Remove temp directory | ||
file: | ||
path: "{{ temp_directory.path }}" | ||
state: absent | ||
|
||
- name: Check assertions | ||
assert: | ||
that: | ||
- uninstall_absent is not changed | ||
- install_absent is changed | ||
- install_present_state is not changed | ||
- install_latest_state is changed | ||
- install_given_version_matched is changed | ||
- install_given_version_unmatched is failed | ||
- uninstall_present is changed | ||
- install_non_existant is failed | ||
- install_non_existant_source is failed |