Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PR #2386/17c3708f backport][stable-4] Bugfix: github_repo does not apply defaults on existing repos #3769

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- github_repo - ``private`` and ``description`` attributes should not be set to default values when the repo already exists (https://github.com/ansible-collections/community.general/pull/2386).
52 changes: 36 additions & 16 deletions plugins/modules/source_control/github/github_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,18 @@
description:
description:
- Description for the repository.
- Defaults to empty if I(force_defaults=true), which is the default in this module.
- Defaults to empty if I(force_defaults=false) when creating a new repository.
- This is only used when I(state) is C(present).
type: str
default: ''
required: false
private:
description:
- Whether the new repository should be private or not.
- Whether the repository should be private or not.
- Defaults to C(false) if I(force_defaults=true), which is the default in this module.
- Defaults to C(false) if I(force_defaults=false) when creating a new repository.
- This is only used when I(state) is C(present).
type: bool
default: no
required: false
state:
description:
Expand All @@ -72,6 +74,14 @@
type: str
default: 'https://api.github.com'
version_added: "3.5.0"
force_defaults:
description:
- Overwrite current I(description) and I(private) attributes with defaults if set to C(true), which currently is the default.
- The default for this option will be deprecated in a future version of this collection, and eventually change to C(false).
type: bool
default: true
required: false
version_added: 4.1.0
requirements:
- PyGithub>=1.54
notes:
Expand All @@ -92,6 +102,7 @@
description: "Just for fun"
private: yes
state: present
force_defaults: no
register: result

- name: Delete the repository
Expand All @@ -117,7 +128,7 @@

GITHUB_IMP_ERR = None
try:
from github import Github, GithubException
from github import Github, GithubException, GithubObject
from github.GithubException import UnknownObjectException
HAS_GITHUB_PACKAGE = True
except Exception:
Expand All @@ -135,7 +146,7 @@ def authenticate(username=None, password=None, access_token=None, api_url=None):
return Github(base_url=api_url, login_or_token=username, password=password)


def create_repo(gh, name, organization=None, private=False, description='', check_mode=False):
def create_repo(gh, name, organization=None, private=None, description=None, check_mode=False):
result = dict(
changed=False,
repo=dict())
Expand All @@ -151,16 +162,21 @@ def create_repo(gh, name, organization=None, private=False, description='', chec
except UnknownObjectException:
if not check_mode:
repo = target.create_repo(
name=name, private=private, description=description)
name=name,
private=GithubObject.NotSet if private is None else private,
description=GithubObject.NotSet if description is None else description,
)
result['repo'] = repo.raw_data

result['changed'] = True

changes = {}
if repo is None or repo.raw_data['private'] != private:
changes['private'] = private
if repo is None or repo.raw_data['description'] != description:
changes['description'] = description
if private is not None:
if repo is None or repo.raw_data['private'] != private:
changes['private'] = private
if description is not None:
if repo is None or repo.raw_data['description'] not in (description, description or None):
changes['description'] = description

if changes:
if not check_mode:
Expand Down Expand Up @@ -193,6 +209,10 @@ def delete_repo(gh, name, organization=None, check_mode=False):


def run_module(params, check_mode=False):
if params['force_defaults']:
params['description'] = params['description'] or ''
params['private'] = params['private'] or False

gh = authenticate(
username=params['username'], password=params['password'], access_token=params['access_token'],
api_url=params['api_url'])
Expand All @@ -216,17 +236,17 @@ def run_module(params, check_mode=False):

def main():
module_args = dict(
username=dict(type='str', required=False, default=None),
password=dict(type='str', required=False, default=None, no_log=True),
access_token=dict(type='str', required=False,
default=None, no_log=True),
username=dict(type='str'),
password=dict(type='str', no_log=True),
access_token=dict(type='str', no_log=True),
name=dict(type='str', required=True),
state=dict(type='str', required=False, default="present",
choices=["present", "absent"]),
organization=dict(type='str', required=False, default=None),
private=dict(type='bool', required=False, default=False),
description=dict(type='str', required=False, default=''),
private=dict(type='bool'),
description=dict(type='str'),
api_url=dict(type='str', required=False, default='https://api.github.com'),
force_defaults=dict(type='bool', default=True),
)
module = AnsibleModule(
argument_spec=module_args,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ def get_repo_mock(url, request):
return response(200, content, headers, None, 5, request)


@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="get")
def get_private_repo_mock(url, request):
match = re.search(
r"api\.github\.com(:[0-9]+)?/repos/(?P<org>[^/]+)/(?P<repo>[^/]+)", request.url)
org = match.group("org")
repo = match.group("repo")

# https://docs.github.com/en/rest/reference/repos#get-a-repository
headers = {'content-type': 'application/json'}
content = {
"name": repo,
"full_name": "{0}/{1}".format(org, repo),
"url": "https://api.github.com/repos/{0}/{1}".format(org, repo),
"private": True,
"description": "This your first repo!",
"default_branch": "master",
"allow_rebase_merge": True
}
content = json.dumps(content).encode("utf-8")
return response(200, content, headers, None, 5, request)


@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/orgs/.*/repos', method="post")
def create_new_org_repo_mock(url, request):
match = re.search(
Expand All @@ -83,8 +105,8 @@ def create_new_org_repo_mock(url, request):
content = {
"name": repo['name'],
"full_name": "{0}/{1}".format(org, repo['name']),
"private": repo['private'],
"description": repo['description']
"private": repo.get('private', False),
"description": repo.get('description')
}
content = json.dumps(content).encode("utf-8")
return response(201, content, headers, None, 5, request)
Expand All @@ -99,8 +121,8 @@ def create_new_user_repo_mock(url, request):
content = {
"name": repo['name'],
"full_name": "{0}/{1}".format("octocat", repo['name']),
"private": repo['private'],
"description": repo['description']
"private": repo.get('private', False),
"description": repo.get('description')
}
content = json.dumps(content).encode("utf-8")
return response(201, content, headers, None, 5, request)
Expand All @@ -120,8 +142,8 @@ def patch_repo_mock(url, request):
"name": repo,
"full_name": "{0}/{1}".format(org, repo),
"url": "https://api.github.com/repos/{0}/{1}".format(org, repo),
"private": body['private'],
"description": body['description'],
"private": body.get('private', False),
"description": body.get('description'),
"default_branch": "master",
"allow_rebase_merge": True
}
Expand Down Expand Up @@ -160,11 +182,34 @@ def test_create_new_org_repo(self):
"description": "Just for fun",
"private": False,
"state": "present",
"api_url": "https://api.github.com"
"api_url": "https://api.github.com",
"force_defaults": False,
})

self.assertEqual(result['changed'], True)
self.assertEqual(result['repo']['private'], False)
self.assertEqual(result['repo']['description'], 'Just for fun')

@with_httmock(get_orgs_mock)
@with_httmock(get_repo_notfound_mock)
@with_httmock(create_new_org_repo_mock)
def test_create_new_org_repo_incomplete(self):
result = github_repo.run_module({
'username': None,
'password': None,
"access_token": "mytoken",
"organization": "MyOrganization",
"name": "myrepo",
"description": None,
"private": None,
"state": "present",
"api_url": "https://api.github.com",
"force_defaults": False,
})

self.assertEqual(result['changed'], True)
self.assertEqual(result['repo']['private'], False)
self.assertEqual(result['repo']['description'], None)

@with_httmock(get_user_mock)
@with_httmock(get_repo_notfound_mock)
Expand All @@ -179,7 +224,8 @@ def test_create_new_user_repo(self):
"description": "Just for fun",
"private": True,
"state": "present",
"api_url": "https://api.github.com"
"api_url": "https://api.github.com",
"force_defaults": False,
})
self.assertEqual(result['changed'], True)
self.assertEqual(result['repo']['private'], True)
Expand All @@ -197,11 +243,31 @@ def test_patch_existing_org_repo(self):
"description": "Just for fun",
"private": True,
"state": "present",
"api_url": "https://api.github.com"
"api_url": "https://api.github.com",
"force_defaults": False,
})
self.assertEqual(result['changed'], True)
self.assertEqual(result['repo']['private'], True)

@with_httmock(get_orgs_mock)
@with_httmock(get_private_repo_mock)
def test_idempotency_existing_org_private_repo(self):
result = github_repo.run_module({
'username': None,
'password': None,
"access_token": "mytoken",
"organization": "MyOrganization",
"name": "myrepo",
"description": None,
"private": None,
"state": "present",
"api_url": "https://api.github.com",
"force_defaults": False,
})
self.assertEqual(result['changed'], False)
self.assertEqual(result['repo']['private'], True)
self.assertEqual(result['repo']['description'], 'This your first repo!')

@with_httmock(get_orgs_mock)
@with_httmock(get_repo_mock)
@with_httmock(delete_repo_mock)
Expand All @@ -215,7 +281,8 @@ def test_delete_org_repo(self):
"description": "Just for fun",
"private": False,
"state": "absent",
"api_url": "https://api.github.com"
"api_url": "https://api.github.com",
"force_defaults": False,
})
self.assertEqual(result['changed'], True)

Expand All @@ -232,7 +299,8 @@ def test_delete_user_repo(self):
"description": "Just for fun",
"private": False,
"state": "absent",
"api_url": "https://api.github.com"
"api_url": "https://api.github.com",
"force_defaults": False,
})
self.assertEqual(result['changed'], True)

Expand All @@ -249,7 +317,8 @@ def test_delete_org_repo_notfound(self):
"description": "Just for fun",
"private": True,
"state": "absent",
"api_url": "https://api.github.com"
"api_url": "https://api.github.com",
"force_defaults": False,
})
self.assertEqual(result['changed'], False)

Expand Down