diff --git a/.github/workflows/ansible-test.yml b/.github/workflows/ansible-test.yml new file mode 100644 index 0000000..5f9a37d --- /dev/null +++ b/.github/workflows/ansible-test.yml @@ -0,0 +1,42 @@ +name: ansible-test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ansible_collections/ohioit/github + + steps: + - name: Clone the repo + uses: actions/checkout@v2 + with: + path: ansible_collections/ohioit/github + + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Install Ansible + run: pip install ansible==5.5 + + - name: Install PyGithub + run: pip install PyGithub==1.55 + + - name: Perform sanity testing with ansible-test on module files + run: ansible-test sanity --python 3.9 --skip-test import --skip-test ansible-doc --skip-test validate-modules plugins/modules/* + + - name: Perform unit testing with ansible-test + uses: ansible-community/ansible-test-gh-action@release/v1 + with: + ansible-core-version: stable-2.12 + pre-test-cmd: echo This runs before the ansible-test invocation + python-version: 3.9 + target-python-version: 3.9 + testing-type: units + test-deps: >- + ansible.netcommon + ansible.utils diff --git a/README.md b/README.md index 15ebd44..3eff8c0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,150 @@ # Ansible Collection for GitHub -The OHIO IT collection of Ansible modules for GitHub. + +### The OHIO IT collection of Ansible modules for GitHub. + +The collection includes a variety of Ansible modules to help automate the management of organizations, repositories, and user permissions in GitHub. + +--- + +## Version Compatibility + +Tested against: +`Ansible Version >=2.11` +`Python >=3.8` +`PyGitHub >=1.55` + +## Installation + +Install this collection via [Ansible Galaxy](https://galaxy.ansible.com/ohioit/github): + +```bash +ansible-galaxy collection install ohioit.github +``` + +Content in this collection requires the [PyGitHub package](https://github.com/PyGithub/PyGithub) to interact with [GitHub REST API](https://docs.github.com/en/rest). You can install it with: + +```bash +pip install PyGithub +``` + +## Included Content + +### Modules + +| Name | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | +| [repository_information](https://github.com/ohioit/ansible-collection-github/blob/main/docs/repository_information.rst) | Output repositories along with vital information from an (user specified) organization | +| [collaborator_information](https://github.com/ohioit/ansible-collection-github/blob/main/docs/collaborator_information.rst) | View and manage repository collaborators | +| [repository_webhooks](https://github.com/ohioit/ansible-collection-github/blob/main/docs/repository_webhooks.rst) | A module that manages a repository's webhooks | +| [branch_protection](https://github.com/ohioit/ansible-collection-github/blob/main/docs/branch_protection.rst) | A module that allows the modification of branch protections. | +| [general_repository](https://github.com/ohioit/ansible-collection-github/blob/main/docs/general_repository.rst) | A module that manages a repository in an organization. | + +## Usage + +### Repository Information + +``` + - name: "List GitHub repositories within a non-enterprise organization" + ohioit.github.repository_information: + token: "" + organization_name: "" + register: result + + - name: "List GitHub repositories within an enterprise organization" + ohioit.github.repository_information: + token: "" + organization_name: "" + enterprise_url: "https://github./api/v3/" + register: result +``` +### Collaborator Information + +``` + - name: "List the collaborators of a repository" + ohioit.github.collaborator_information: + access_token: "" + organization: "" + api_url: "https://github./api/v3/" + repository: "" + collaborator: "" + permission: + state: +``` +### Webhook +``` + - name: "Manage webhooks of a GitHub repository" + ohioit.github.repository_webhooks: + state: + access_token: "" + organization: "" + api_url: "https://github./api/v3/" + repository: "" + url: "" + events: + - "" + - "" + content_type: +``` +### Branch Protection + +``` + - name: "Manage the branch protections rules of a branch" + ohioit.github.branch_protection: + access_token: "" + organization: "" + api_url: "https://github./api/v3/" + repository: "" + branch: "" + state: "" + branch_protections: + strict: + contexts: ["", ...] + enforce_admins: + dismissal_users: ["", ...] + dismissal_teams: ["", ...] + dismiss_stale_reviews: + require_code_owner_reviews: + required_approving_review_count: + user_push_restrictions: ["", ...] + team_push_restrictions: ["", ...] +``` +### General Repository + +``` + - name: "Manage a repository within an enterprise organization" + ohioit.github.general_repository: + access_token: "" + organization: "" + api_url: "https://github./api/v3/" + repository: "" + private: + description: "" + homepage: "" + has_issues: + has_wiki: + has_downloads: + has_projects: + team_id: + auto_init: + license_template: "" + gitignore_template: "" + allow_squash_merge: + allow_merge_commit: + allow_rebase_merge: + delete_branch_on_merge: + state: +``` + +###### _**NOTE**: Tokens should be encrypted and only decrypted at runtime_ + +## Testing with 'ansible-test' + +Testing has been made available using the [ansible-test](https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html). These tests include [unit](https://github.com/senior-design-21-22/ansible-collection-github/blob/development/docs/unit_testing.rst), [sanity](https://github.com/senior-design-21-22/ansible-collection-github/tree/repo-information-module/unit/sanity), [integration](https://github.com/senior-design-21-22/ansible-collection-github/blob/development/docs/integration_testing.rst). + +The tests are runnable using the following commands: + +```bash +ansible-test units --python 3. --venv +ansible-test sanity --python 3. plugins/modules/* +ansible-test integration --python 3. +``` diff --git a/docs/integration_testing.rst b/docs/integration_testing.rst new file mode 100644 index 0000000..49091f2 --- /dev/null +++ b/docs/integration_testing.rst @@ -0,0 +1,58 @@ +.. _Integration_Testing: + + +******************** +Integration Testing +******************** + +**These steps may be taken to complete integration testing of ansible-collection-github modules** + + +Version added: 0.0.1 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This article contains the necessary requirements and steps to successfully run integration tests against the ansible-collection-github modules + +Requirements +------------ +- Regular Github token +- Enterprise Github token +- Organization name +- Directory ``/tests/integration/vars/args.yaml`` needs to be populated with: + +.. code-block:: yaml + + --- + token: "[regular_github_token]" + organization_name: "[regular_github_name]" + enterprise_token: "[enterprise_github_token]" + enterprise_organization_name: "[enterprise_organization_name]" + enterprise_url: "[enterprise_url]" + + +Running Integration Tests +---------- + +.. note:: +As of now, running the integration tests takes the same arguments needed to run the complete module + +#. Locally running the playbook with the command ``ansible-playbook integration_test.yaml`` + + +Status +------ + + +Authors +~~~~~~~ + +- Brad Golski (@bgolski) +- Jacob Eicher (@jacobeicher) +- Nolan Khounborin (@khounborinn) +- Tyler Zwolenik (@TylerZwolenik) diff --git a/docs/repository_information.rst b/docs/repository_information.rst new file mode 100644 index 0000000..0d7ee79 --- /dev/null +++ b/docs/repository_information.rst @@ -0,0 +1,371 @@ +.. _repository_information: + + +******************** +repository_information +******************** + +**With given user & organization, returns repository information.** + + +Version added: 0.0.1 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Retrieve GitHub organization repository information in the form of a list of dictionaries. + + + +Requirements +------------ +There are no further requirements needed to run this module. + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ access_token + +
+ string +
+
+ +
Token used to authenticate with the GitHub Rest API.
+
+
+ organization + +
+ string +
+
+ +
Organization provided by users.
+
+
+ api_url + +
+ string +
+
+
Unecessary in event of user token
+
+
An enterprise URL is necessary when a module is recieving an enterprise token. In the structure of the URL, it is vital that it includes the subdirectory path to the GitHub API as well as the correct version type. An template of this is:
+ https://github.<ENTERPRISE DOMAIN>/api/v3 +
+
+ + + + +Examples +-------- + +.. code-block:: yaml + + - name: "List GitHub repositories within non-enterprise organization" + ohioit.github.repository_information: + access_token: "" + organization: "" + register: result + + - name: "List GitHub repositories within enterprise organization" + ohioit.github.repository_information: + access_token: "" + organization: "" + api_url: "https://github./api/v3" + register: result + + + +Return Values +------------- +Common return values are documented `here `_, the following are the fields unique to this module: + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Key Returned Description
+
+ repos + +
+ List +
+
If provided GitHub API token connects. +
List contains dictionaries of repositories and their information.
+
+
+ repos.<ELEMENT INDEX> + +
+ Dict +
+
Only if at least one repo is contained within organization. +
Dictionary contains keys and values of a repository's information.
+
+
+ repos.<ELEMENT INDEX>.name + +
+ Str +
+
Only if at least one repo has been identified. +
Repository's name.
+
+
+ repos.<ELEMENT INDEX>.full_name + +
+ Str +
+
Only if at least one repo has been identified. +
Repository path name starting from organization.
+
+
+ repos.<ELEMENT INDEX>.owner + +
+ Str +
+
Only if at least one repo has been identified. +
Name of organization that owns the repository.
+
+
+ repos.<ELEMENT INDEX>.
description
+ +
+ Str +
+
Only if at least one repo has been identified. +
Description of the repository. This field will be null unless previously set.
+
+
+ repos.<ELEMENT INDEX>.private + +
+ Bool +
+
Only if at least one repo has been identified. +
Status whether the repository is private (true) or public (false).
+
+
+ repos.<ELEMENT INDEX>.archived + +
+ Bool +
+
Only if at least one repo has been identified. +
Status whether the repository is archived or not.
+
+
+ repos.<ELEMENT INDEX>.language + +
+ Str +
+
Only if at least one repo has been identified. +
Repository language. This can be any language listed here.
+
+
+ repos.<ELEMENT INDEX>.url + +
+ Str +
+
Only if at least one repo has been identified. +
URL for repository. The provided URL is the route used for the GitHub API to be connected to Ansible. Non-enterprise URLs will be structured as
https://api.github.com/repos/<ORGANIZATION NAME>/<REPO NAME>.
Enterprise URLs are structured as
https://github.<ENTERPRISE DOMAIN>/api/v3/repos/<ORGANIZATION NAME>/<REPO NAME>.
+
+
+ repos.<ELEMENT INDEX>.default_branch + +
+ Str +
+
Only if at least one repo has been identified. +
The branch that GitHub displays when anyone visits your repository.
+
+
+ repos.<ELEMENT INDEX>.hooks_url + +
+ Str +
+
Only if at least one repo has been identified. +
URL location where hooks are located within the repository when connected to the GitHub API. Non-enterprise URLs will be structured as
https://api.github.com/repos/<ORGANIZATION NAME>/<REPO NAME>/hooks.
Enterprise URLs are structured as
https://github.<ENTERPRISE DOMAIN>/api/v3/repos/<ORGANIZATION NAME>/<REPO NAME>/hooks.
+
+
+ repos.<ELEMENT INDEX>.clone_url + +
+ Str +
+
Only if at least one repo has been identified. +
URL location where repository will be accessible to be cloned. Non-enterprise URLs will be structured as
https://github.com/<ORGANIZATION NAME>/<REPO NAME>.git.
Enterprise URLs are structured as
https://github.<ENTERPRISE DOMAIN>/<ORGANIZATION NAME>/<REPO NAME>.git.
+
+
+ repos.<ELEMENT INDEX>.visibility + +
+ Str +
+
Only if at least one repo has been identified and organization is NOT part of an enterprise account. +
The repository visibility status will be 'public', 'internal', or 'private'.
+
+
+ repos.<ELEMENT INDEX>.is_template + +
+ Bool +
+
Only if at least one repo has been identified and organization is NOT part of an enterprise account. +
The repository template status will true or false.
+
+

+ + +Status +------ + + +Authors +~~~~~~~ + +- Brad Golski (@bgolski) +- Jacob Eicher (@jacobeicher) +- Nolan Khounborin (@khounborinn) +- Tyler Zwolenik (@TylerZwolenik) diff --git a/docs/unit_testing.rst b/docs/unit_testing.rst new file mode 100644 index 0000000..4549fca --- /dev/null +++ b/docs/unit_testing.rst @@ -0,0 +1,64 @@ +.. _Unit_Testing: + + +******************** +Unit Testing +******************** + +**These steps may be taken to complete unit testing of ansible-collection-github modules** + + +Version added: 0.0.1 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This article contains the necessary requirements and steps to successfully run unit tests against the ansible-collection-github modules + + + +Requirements +------------ +- A virtual development environment (venv or docker) +- Tests need to be ran with Python3 + +Setting Up Virtual Environment +---------- + +#. Clone the ansible repository: ``$ git clone https://github.com/ansible/ansible.git`` +#. Change your working directory: ``$ cd ansible`` +#. Virtual environment package should be downloaded: ``$ pip install virtualenv`` +#. Virtual environment needs to be created: ``$ python3 -m venv venv`` +#. Virtual environment needs to be activated: ``$ . venv/bin/activate`` +#. Requirements need to be installed: ``$ pip install -r requirements.txt`` +#. To obtain requirements for unit testing: ``$ pip install -r /test/units/requirements.txt`` +#. The environment seteup script for each new dev shell: ``$ . hacking/env-setup`` + +*If virtual environement has already been created, within* ``ansible`` *, run command* ``$ . venv/bin/activate && . hacking/env-setup`` + +Running Unit Tests +---------- + +#. Set up and start virtual environment (run ``$ . venv/bin/activate && . hacking/env-setup`` if virtual environment has been created previously) +#. Locate and change to the ``ansible-collection-github`` directory +#. Run the command ``ansible-test units --python 3.[YOUR PYTHON VERSION] --venv`` to run tests. + + + + + +Status +------ + + +Authors +~~~~~~~ + +- Brad Golski (@bgolski) +- Jacob Eicher (@jacobeicher) +- Nolan Khounborin (@khounborinn) +- Tyler Zwolenik (@TylerZwolenik) diff --git a/galaxy.yml b/galaxy.yml index 004ed7c..e844403 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -3,7 +3,11 @@ name: github version: 0.0.1 readme: README.md authors: - - Robert Foreman + - Robert Foreman + - Bradley Golski + - Jacob Eicher + - Tyler Zwolenik + - Nolan Khounborin description: Collection of modules for managing GitHub repository settings license_file: LICENSE tags: @@ -11,4 +15,4 @@ tags: repository: https://github.com/ohioit/ansible-collection-github documentation: https://github.com/ohioit/ansible-collection-github/blob/main/README.md homepage: https://github.com/ohioit/ansible-collection-github -issues: https://github.com/ohioit/ansible-collection-github/issues \ No newline at end of file +issues: https://github.com/ohioit/ansible-collection-github/issues diff --git a/plugins/modules/repository_information.py b/plugins/modules/repository_information.py new file mode 100644 index 0000000..6264325 --- /dev/null +++ b/plugins/modules/repository_information.py @@ -0,0 +1,220 @@ +#!/usr/bin/python + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import absolute_import, division, print_function + + +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: repository_information + +short_description: A module that returns information about GitHub repositories + +description: + + - "A module that fetches information about repositories + that a GitHub user has access to inside an organization." + +options: + access_token: + description: + - GitHub API token used to retrieve information + about repositories to which a user has access + required: true + type: str + api_url: + description: + - If using a token from a GitHub Enterprise account, + the user must pass an enterprise URL + required: false + type: str + organization: + description: + - The organization in which the query will be run. + required: true + type: str + +author: + - Jacob Eicher (@jacobeicher) + - Bradley Golski (@bgolski) + - Tyler Zwolenik (@TylerZwolenik) + - Nolan Khounborinn (@Khounborinn) +''' + +EXAMPLES = ''' +# Pass in an organization name and GitHub API token +- name: returns information about + repository_info: + organization: "senior-design-21-22" + access_token: "12345" + + +# Pass in an organization name, GitHub API token and enterprise URL +- name: returns information about + repository_info: + organization: "SSEP" + access_token: "12345" + api_url: "https://github./api/v3/repos//" +''' + +RETURN = ''' +repos: + description: List contains dictionaries of repositories and their information. + type: list + returned: if GitHub API token connects + +repos.: + description: Dictionary contains keys and values of a repository's information. + type: dict + returned: only if at least one repo is contained within organization + +repos..name: + description: Repository's name. + type: str + returned: only if organization contains a repository + +repos..full_name: + description: Repository path name starting from organization. + type: str + returned: only if organization contains a repository + +repos..owner: + description: Name of organization that owns the repository. + type: str + returned: only if organization contains a repository + +repos..description: + description: Description of the repository. This field will be null unless previously set. + type: str + returned: only if organization contains a repository + +repos..private: + description: Status whether the repository is private or public. + type: bool + returned: only if organization contains a repository + +repos..archived: + description: Status of whether the repository is archived or not. + type: bool + returned: only if organization contains a repository + +repos..language: + description: Repository language. This can be any language listed in 'https://github.com/github/linguist/blob/master/lib/linguist/languages.yml'. + type: str + returned: only if organization contains a repository + +repos..url: + description: URL for repository. The provided URL is the route used for the GitHub API to be connected to Ansible. + Non-enterprise URLs will be structured as 'https://api.github.com/repos//'. + Enterprise URLs are structured as 'https://github./api/v3/repos//'. + type: str + returned: only if organization contains a repository + +repos..default_branch: + description: The branch that GitHub displays when anyone visits your repository. + type: str + returned: only if organization contains a repository + +repos..hooks_url: + description: URL location where hooks are located within the repository when connected to the GitHub API. + Non-enterprise URLs should be structured as 'https://api.github.com/repos///hooks'. + Enterprise URLs should be structured as 'https://github./api/v3/repos///hooks'. + type: str + returned: only if organization contains a repository + +repos..clone_url: + description: URL location where repository will be accessible to be cloned. + Non-enterprise URLs should be structured as 'https://github.com//.git'. + Enterprise URLs should be structured as 'https://github.//.git'. + type: str + returned: only if organization contains a repository + +repos..visibility: + description: The repository visibility status will be 'public', 'internal', or 'private'. + type: str + returned: only if organization contains a repository and is not a part of an enterprise account + +repos..is_template: + description: The repository template status will true or false. + type: bool + returned: only if organization contains a repository and is not a part of an enterprise account +''' + +from ansible.module_utils.basic import AnsibleModule +from github import Github + + +def run_module(): + module_args = dict( + access_token=dict(type='str', required=True, no_log=True), + organization=dict(type='str', required=True), + api_url=dict(type='str', default=''), + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True + ) + + result = dict( + changed=False, + fact='' + ) + + if module.params['api_url'] == '': + g = Github(module.params['access_token']) + else: + g = Github(module.params['access_token'], + base_url=module.params['api_url']) + + output = [] + + org_name = module.params['organization'] + + for repo in g.get_organization(org_name).get_repos(): + current_repo_dict = { + "name": repo.name, + "full_name": repo.full_name, + "owner": repo.owner.login, + "description": repo.description, + "private": repo.private, + "archived": repo.archived, + "language": repo.language, + "url": repo.url, + "default_branch": repo.default_branch, + "hooks_url": repo.hooks_url, + "clone_url": repo.clone_url + } + if len(module.params["api_url"]) == 0: + current_repo_dict["visibility"] = repo.raw_data["visibility"] + current_repo_dict["is_template"] = repo.raw_data["is_template"] + + output.append(current_repo_dict) + + module.exit_json(repos=output) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/modules/test_github_repo_return.py b/tests/unit/modules/test_github_repo_return.py new file mode 100644 index 0000000..6a831c2 --- /dev/null +++ b/tests/unit/modules/test_github_repo_return.py @@ -0,0 +1,273 @@ +from ansible.module_utils import basic +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_bytes +import unittest +import json + + +class Repository: + def __init__(self): + self.archived = False + self.clone_url = 'https://api.github.com/github/Hello-World.git' + self.default_branch = "main" + self.description = "" + self.full_name = 'github/Hello-World' + self.hooks_url = 'https://api.github.com/repos/github/Hello-World/hooks' + self.language = None + self.name = "Hello-World" + self.owner = { + 'login': 'github' + } + self.private = True + self.url = "https://api.github.com/orgs/github/repos/Hello-World" + self.raw_data = { + 'visibility': 'public', + 'is_template': False + } + + def __init__(self, name): + self.archived = False + self.clone_url = 'https://api.github.com/github/' + name + '.git' + self.default_branch = "main" + self.description = "" + self.full_name = 'github/' + name + self.hooks_url = 'https://api.github.com/github/repos/' + name + '/hooks' + self.language = None + self.name = name + self.owner = { + 'login': 'github' + } + self.private = True + self.url = "https://api.github.com/orgs/github/repos/" + name + self.raw_data = { + 'visibility': 'public', + 'is_template': False + } + + +class Organization: + def __init__(self): + self.login = None + self.id = None + self.node_id = None + self.url = None + self.repos_url = None + self.description = None + self.name = None + self.type = None + + def __init__(self, name='default'): + self.name = name + self.login = "login" + self.id = 1 + self.node_id = -1 + self.url = "https://api.github.com/orgs/" + name + self.repos_url = "https://api.github.com/orgs/" + name + "/repos" + self.description = "A great organization" + self.type = "Organization" + + def get_repos(self): + repositories = [] + if self.name == 'github': + repositories = [ + Repository(name='Hello-World'), + Repository(name='Goodbye-Chat') + ] + elif self.name == 'Cloud': + repositories = [ + Repository(name='Cloud_repo') + ] + + return repositories + + +class Github: + def __init__(self, access_token, base_url=''): + self.access_token = access_token + self.base_url = base_url + + def get_organization(self, organization): + organization_output = Organization() + + if self.access_token == 'token': + organization_output = Organization(name=organization) + + if self.access_token == 'token' and self.base_url == 'api_url': + organization_output = Organization(name=organization) + + return organization_output + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +def run_module(): + module_args = dict( + access_token=dict(type='str', required=True, no_log=True), + organization=dict(type='str', required=True), + api_url=dict(type='str', default=''), + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True + ) + + result = dict( + changed=False, + fact='' + ) + + if module.params['api_url'] == '': + g = Github(module.params['access_token']) + else: + g = Github(module.params['access_token'], + base_url=module.params['api_url']) + + output = [] + + org_name = module.params['organization'] + + for repo in g.get_organization(org_name).get_repos(): + current_repo_dict = { + "name": repo.name, + "full_name": repo.full_name, + "owner": repo.owner['login'], + "description": repo.description, + "private": repo.private, + "archived": repo.archived, + "language": repo.language, + "url": repo.url, + "default_branch": repo.default_branch, + "hooks_url": repo.hooks_url, + "clone_url": repo.clone_url + } + if len(module.params["api_url"]) == 0: + current_repo_dict["visibility"] = repo.raw_data["visibility"] + current_repo_dict["is_template"] = repo.raw_data["is_template"] + + output.append(current_repo_dict) + + # module.exit_json(repos=output) + return output + + +class TestRepositoryInformationModule(unittest.TestCase): + def test_module_fail_when_required_args_missing(self): + set_module_args({}) + self.assertRaises(AnsibleFailJson) + + def test_module_return_repo(self): + set_module_args({ + 'access_token': 'token', + 'organization': 'github', + 'api_url': '' + }) + test = [ + { + "name": 'Hello-World', + "full_name": 'github/Hello-World', + "owner": 'github', + "description": "", + "private": True, + "archived": False, + "language": None, + "url": "https://api.github.com/orgs/github/repos/Hello-World", + "default_branch": 'main', + "hooks_url": 'https://api.github.com/github/repos/Hello-World/hooks', + "clone_url": 'https://api.github.com/github/Hello-World.git', + 'visibility': 'public', + 'is_template': False + }, + { + "name": 'Goodbye-Chat', + "full_name": 'github/Goodbye-Chat', + "owner": 'github', + "description": "", + "private": True, + "archived": False, + "language": None, + "url": "https://api.github.com/orgs/github/repos/Goodbye-Chat", + "default_branch": 'main', + "hooks_url": 'https://api.github.com/github/repos/Goodbye-Chat/hooks', + "clone_url": 'https://api.github.com/github/Goodbye-Chat.git', + 'visibility': 'public', + 'is_template': False + } + ] + + output = run_module() + assert test == output + + def test_fail_api_call(self): + set_module_args({'access_token': 'token', + 'organization': 'one_repo_org', + 'api_url': 'bad_url'}) + result = run_module() + assert result == [] + + def test_fail_access_token(self): + set_module_args({'access_token': 'bad_token', + 'organization': 'one_repo_org', + 'api_url': ''}) + result = run_module() + assert result == [] + + def test_module_return_empty_repo_api_url(self): + set_module_args({'access_token': 'empty token', + 'organization': 'no_repo_org', + 'api_url': ''}) + result = run_module() + test = [] + assert result == test + + def test_module_return_repo_cloud_org(self): + set_module_args({ + 'access_token': 'token', + 'organization': 'Cloud', + 'api_url': 'api_url' + }) + test = [ + { + "name": 'Cloud_repo', + "full_name": 'github/Cloud_repo', + "owner": 'github', + "description": "", + "private": True, + "archived": False, + "language": None, + "url": "https://api.github.com/orgs/github/repos/Cloud_repo", + "default_branch": 'main', + "hooks_url": 'https://api.github.com/github/repos/Cloud_repo/hooks', + "clone_url": 'https://api.github.com/github/Cloud_repo.git' + } + ] + + output = run_module() + assert test == output diff --git a/tests/unit/modules/utils.py b/tests/unit/modules/utils.py new file mode 100644 index 0000000..5239cab --- /dev/null +++ b/tests/unit/modules/utils.py @@ -0,0 +1,31 @@ +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs)