Skip to content

Commit

Permalink
Add a git_repo role
Browse files Browse the repository at this point in the history
This role accepts a single complex data structure to
clone a git repository with multiple remotes and branches.
  • Loading branch information
wbclark authored and evgeni committed Mar 13, 2024
1 parent f252111 commit e060947
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 0 deletions.
85 changes: 85 additions & 0 deletions roles/git_repo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
This role clones a set of git repositories, handling multiple remotes and managing different branches for each remote. It also provides basic support for bundle installs for Ruby projects, and pip installs within a virtual environment for Python projects. The role aims to present a single, simple interface in Ansible variables for managing a collection of locally cloned git repositories.

Role Variables
--------------

The main data structure for the role is the list of `git_repositories`. Each repository in the list requires the following attributes:

- `name`: The local name of the repository.
- `dir`: The parent directory to clone the repository from.
- `remotes`: List of repository remotes, each with `name`, `url`, and `branches`.

Repositories may also have the following optional attributes:

- `install_gems`: Boolean to indicate if Ruby gems should be installed. This assumes the cloned repository provides a Gemfile.
- `python_packages`: A list of python packages to install in a venv within the repository.

Each remote requires the following attributes:

- `name`: Name of the remote.
- `url`: URL of the remote.
- `branches`: A list of branches in the remote repository that will be created in the local clone.

Examples
--------

Basic example of the `git_repositories` variable, configuring two repositories, each with a single remote and a single branch:

```yaml
git_repositories:
- name: 'foreman'
dir: '/home/vagrant'
remotes:
- name: 'origin'
url: '[email protected]/my_github_user/foreman.git'
branches:
- 'develop'
- name: 'katello'
dir: '/home/vagrant'
remotes:
- name: 'origin'
url: '[email protected]/my_github_user/katello.git'
branches:
- 'master'
```
Example configuring a repository with multiple remotes and branches and installing gems from the project's Gemfile. The checkout will be of the first branch on the first remote when performing the bundle install:
```yaml
git_repositories:
- name: 'foreman'
dir: '/home/vagrant'
remotes:
- name: 'myfork'
url: '[email protected]/my_github_user/foreman.git'
branches:
- 'develop'
- 'exciting-new-feature'
- 'fix-annoying-bug'
- name: 'upstream'
url: '[email protected]/theforeman/foreman.git'
branches:
- '3.9-stable'
- '3.8-stable'
install_gems: true
```
Installing Python Packages in a Virtual Environment:
```yaml
git_repositories:
- name: 'rpm-packaging'
dir: '/home/vagrant'
remotes:
- name: 'downstream'
url: '[email protected]/systems_management/rpm-packaging.git'
branches:
- 'STREAM'
- 'VERSION-1'
- 'VERSION-2'
- 'VERSION-3'
python_packages:
- 'ansible'
- 'obal'
- 'tito'
```
3 changes: 3 additions & 0 deletions roles/git_repo/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
dependencies:
- role: git
5 changes: 5 additions & 0 deletions roles/git_repo/tasks/branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: "Create local repository branch tracking specified remote branch"
ansible.builtin.command:
cmd: "git branch -f {{ branch }} {{ remote.name }}/{{ branch }}"
chdir: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"
15 changes: 15 additions & 0 deletions roles/git_repo/tasks/clone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
- name: "Clone repository"
ansible.builtin.git:
repo: "{{ remote.url }}"
remote: "{{ remote.name }}"
version: "{{ (remote.branches[0]) if (remote.branches is defined) else 'HEAD' }}"
dest: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"
accept_newhostkey: yes

- name: "Configure additional branches for initial remote"
ansible.builtin.include_tasks: branch.yml
when: (remote.branches is defined) and (remote.branches | length > 1)
loop: "{{ remote.branches[1:] }}"
loop_control:
loop_var: branch
32 changes: 32 additions & 0 deletions roles/git_repo/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
- name: "Configure git repository"
ansible.builtin.include_tasks: "{{ 'clone.yml' if ansible_loop.first else 'remote.yml' }}"
loop: "{{ git_repo_repository.remotes }}"
loop_control:
loop_var: remote
extended: yes
label: "{{ remote.name }}"

- name: "Local bundle config"
when: git_repo_repository.install_gems is defined and git_repo_repository.install_gems
block:
- name: "Create .bundle/gems"
ansible.builtin.file:
path: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}/.bundle/gems"
state: directory
- name: "Configure bundler to use .bundle/gems"
ansible.builtin.command:
cmd: "bundle config --local path .bundle/gems"
chdir: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"
- name: "Install Gems"
community.general.bundler:
state: present
chdir: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"

- name: "Create venv and install python packages"
ansible.builtin.pip:
name: "{{ git_repo_repository.python_packages }}"
virtualenv: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}/env"
virtualenv_command: "python3 -m venv"
virtualenv_site_packages: yes
when: (git_repo_repository.python_packages is defined) and (git_repo_repository.python_packages | length >= 1)
26 changes: 26 additions & 0 deletions roles/git_repo/tasks/remote.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
- name: "Configure additional remote for repository"
block:
- name: "Configure remote url"
community.general.git_config:
scope: local
repo: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"
name: "remote.{{ remote.name }}.url"
value: "{{ remote.url }}"
- name: "Configure remote fetch"
community.general.git_config:
scope: local
repo: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"
name: "remote.{{ remote.name }}.fetch"
value: '+refs/heads/*:refs/remotes/{{ remote.name }}/*'
- name: "Fetch remote"
ansible.builtin.command:
cmd: "git fetch {{ remote.name }}"
chdir: "{{ git_repo_repository.dir }}/{{ git_repo_repository.name }}"

- name: "Configure branches for additional remote"
ansible.builtin.include_tasks: branch.yml
when: remote.branches is defined
loop: "{{ remote.branches }}"
loop_control:
loop_var: branch

0 comments on commit e060947

Please sign in to comment.