ansible-gitea/source_control/gitea_repository.py

385 lines
13 KiB
Python

import json
import requests
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
}
DOCUMENTATION = '''
---
module: gitea_repository
short_description: Manage Gitea repositories
description:
- "Manage repositories (and deployment keys) in Gitea"
options:
state:
description:
- The desired state of the repository.
choices: ['present', 'absent']
default: present
required: no
name:
description:
- The name for your repository.
required: true
private:
description:
- Wether the repository is publically accessible
choices: ['yes', 'no']
default: yes
required: false
organization:
description:
- The organization account the repository belongs to.
One of organization and user must be set.
Only one of organization and user must be set.
required: false
user:
description:
- The user account the repository belongs to.
One of organization and user must be set.
Only one of organization and user must be set.
required: false
auto_init:
description:
- Wether to initialize the repository with default files.
Can only be given on creation.
required: false
choices: ['yes', 'no']
description:
description:
- The repositories description.
required: false
gitignores:
description:
- .gitignore file for the repository.
Can only be given on creation.
required: false
issue_labels:
description:
- Issue labels for the repository.
Can only be given on creation.
required: false
license:
description:
- license file for the repository.
Can only be given on creation.
required: false
readme:
description:
- README (in Markdown) for the repository.
Can only be given on creation.
required: false
deploy_key:
description:
- SSH deployment key that is to be set up for the repository
suboptions:
state:
description:
- The desired state of the deploy key.
choices: ['present', 'absent']
default: present
required: false
key:
description:
- The public key in SSH format (e.g. "ssh-rsa 12345....12345 identifier")
required: true
read_only:
description:
- Wether the deploy key should have read-only access to the repository.
choices: ['yes', 'no']
default: yes
title:
description:
- A title for identification of the supplied key
required: false
required: false
auth_token:
description:
- Authentification token for your gitea account
required: true
gitea_url:
description:
- Base URL of your gitea API instance (e.g. "https://git.zknt.org")
required: true
author:
- Chris Gebhardt <cg@zknt.org> (@hnrd)
'''
EXAMPLES = '''
gitea_repository:
auth_token: 1234...6789
gitea_url: https://git.example.com
name: 'testrepo'
organization: test123
state: present
deploy_key:
key: ssh-ed25519 AAAA...1234 jenkins
title: CI key
read_only: False
'''
RETURN = '''
return_code:
description: The HTTP return code from the Gitea API
type: int
returned: always
gitea_respone:
description: The JSON output message that Gitea returns
type: dict
'''
from ansible.module_utils.basic import AnsibleModule
def run_module():
deploy_key_spec = dict(
state=dict(type='str', default='present', choices=['present', 'absent']),
key=dict(type='str', required=True),
read_only=dict(type='bool', default=True),
title=dict(type='str', default=""),
)
module_args = dict(
state=dict(type='str', default='present', choices=['present', 'absent']),
auth_token=dict(type='str', required=True),
gitea_url=dict(type='str', required=True),
organization=dict(type="str", default=None),
user=dict(type="str", default=None),
auto_init=dict(type="bool", default=False),
description=dict(type="str", default=""),
gitignores=dict(type="str", default=""),
issue_labels=dict(type="str", default=""),
license=dict(type="str", default=""),
name=dict(type="str", default=""),
private=dict(type="bool", default=True),
readme=dict(type="str", default=""),
### TODO support multiple keys
deploy_key=dict(type="dict", default=None, options=deploy_key_spec),
)
result = dict(
changed=False,
return_code=0,
gitea_response={},
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
if (
('organization' in module.params and module.params['organization']) and
('user' in module.params and module.params['user'])
):
module.fail_json(msg="Either organization *or*" \
"user must be set", **result)
gitea_url = module.params['gitea_url']
reponame = module.params['name']
headers = {
"Content-Type": "application/json",
"Authorization": "token {}".format(module.params['auth_token']),
}
user_req = requests.get(gitea_url + '/api/v1/user', headers=headers)
logged_in = user_req.json().get('username')
if not logged_in == module.params['user']:
headers['Sudo'] = module.params['user']
if module.check_mode:
module.exit_json(**result)
# Prepare repo data
new_data = {
"auto_init": module.params['auto_init'],
"description": module.params['description'],
"gitignores": module.params['gitignores'],
"issue_labels": module.params['issue_labels'],
"license": module.params['license'],
"name": reponame,
"private": module.params['private'],
"readme": module.params['readme'],
}
if 'organization' in module.params and module.params['organization']:
owner = module.params['organization']
else:
owner = module.params['user']
original_repo = requests.get(
gitea_url + '/api/v1/repos/{owner}/{repo}'.format(
owner=owner,
repo=reponame,
),
headers=headers,
)
if module.params['state'] == 'present':
if original_repo.status_code == 200:
result['return_code'] = 200
result['gitea_response'] = original_repo.json()
old_data = original_repo.json()
# check if repo needs to be patched
if (
new_data['description'] != old_data['description'] or
new_data['private'] != old_data['private']
):
new_data.pop('name')
new_data.pop('auto_init')
new_data.pop('gitignores')
new_data.pop('issue_labels')
new_data.pop('readme')
req_patch = requests.patch(
gitea_url + '/api/v1/repos/{owner}/{repo}'.format(
owner=module.params['organization'],
repo=reponame,
),
headers=headers,
data=json.dumps(new_data),
)
result['return_code'] = req_patch.status_code
result['gitea_response'] = req_patch.json()
result['changed'] = True
# all is fine
result['state'] = 'present'
elif original_repo.status_code == 404:
if (
'organization' in module.params and
module.params['organization']
):
create_path = '/api/v1/org/{org}/repos'.format(
org=owner,
)
else:
create_path = '/api/v1/user/repos'
create_req = requests.post(
gitea_url + create_path,
headers=headers,
data=json.dumps(new_data),
)
result['return_code'] = create_req.status_code
result['gitea_response'] = create_req.json()
if create_req.status_code != 201:
module.fail_json(msg="Creation failed", **result)
result['changed'] = True
result['state'] = 'present'
if 'deploy_key' in module.params:
deploy_key = module.params['deploy_key']
repokeys_req = requests.get(
gitea_url + '/api/v1/repos/{owner}/{repo}/keys'.format(
owner=owner,
repo=reponame,
),
headers=headers,
)
repokeys = repokeys_req.json()
key_id = None
repokey = None
for repokey in repokeys:
if (
' '.join(repokey.get('key', '').split()[:-1]) ==
' '.join(deploy_key.get('key', '').split()[:-1])
):
key_id = repokey.get('key_id')
break
if deploy_key['state'] == "present":
if key_id:
if (
repokey.get('title', "") != deploy_key['title'] or
repokey.get('read_only') !=
deploy_key['read_only'] or
repokey.get('key', "") != deploy_key['key']
):
# there is no PATCH for keys, so delete and re-create
keydelete_req = requests.delete(
gitea_url +
'/api/v1/repos/{owner}/{repo}/keys/{id}'.format(
owner=owner,
repo=reponame,
id=key_id,
),
headers=headers,
)
requests.post(
gitea_url +
'/api/v1/repos/{owner}/{repo}/keys'.format(
owner=owner,
repo=reponame,
),
headers=headers,
data=json.dumps({
"key": deploy_key['key'],
"read_only": deploy_key['read_only'],
"title": deploy_key['title'],
}),
)
result['changed'] = True
else:
requests.post(
gitea_url +
'/api/v1/repos/{owner}/{repo}/keys'.format(
owner=owner,
repo=reponame,
),
headers=headers,
data=json.dumps({
"key": deploy_key['key'],
"read_only": deploy_key['read_only'],
"title": deploy_key['title'],
}),
)
result['changed'] = True
elif deploy_key['state'] == "absent":
if key_id:
keydelete_req = requests.delete(
gitea_url +
'/api/v1/repos/{owner}/{repo}/keys/{id}'.format(
owner=owner,
repo=reponame,
id=key_id,
),
headers=headers,
)
if keydelete_req.status_code != 204:
# deletion failed
result['gitea_response'] = keydelete_req.json()
module.fail_json(msg="Deletion failed", **result)
result['changed'] = True
if module.params['state'] == 'absent':
if original_repo.status_code == 404:
result['return_code'] = 404
result['gitea_response'] = original_repo.json()
result['state'] = 'absent'
elif original_repo.status_code == 200:
# repo should be abenst and needs to be deleted
delete_req = requests.delete(
gitea_url + '/api/v1/repos/{owner}/{repo}'.format(
owner=owner,
repo=reponame,
),
headers=headers,
)
result['return_code'] = delete_req.status_code
if delete_req.status_code != 204:
# deletion failed
result['gitea_response'] = delete_req.json()
module.fail_json(msg="Deletion failed", **result)
result['changed'] = True
result['state'] = 'absent'
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()