How to Automate Git Pull/Push Operations on Remote Hosts Using Ansible Modules


2 views

When automating Git operations across distributed systems, Ansible users often hit a roadblock with direct git commands. The hanging issue typically occurs because:

  • SSH connections time out during lengthy git operations
  • Password prompts aren't handled properly
  • The script module doesn't manage interactive sessions well

Instead of raw scripts, consider these native approaches:

1. Using the git module for pull operations:

- name: Perform git pull on remote host
  ansible.builtin.git:
    repo: 'https://github.com/user/repo.git'
    dest: /path/to/repo
    version: main
    update: yes
    accept_hostkey: yes

2. For push operations (requires credential handling):

- name: Configure git credentials
  become: false
  ansible.builtin.shell: |
    git config --global credential.helper store
    echo "https://{{ git_username }}:{{ git_token }}@github.com" > ~/.git-credentials
  args:
    creates: ~/.git-credentials

- name: Execute git push
  ansible.builtin.shell: |
    cd /path/to/repo && \
    git add . && \
    git commit -m "Automated commit by Ansible" && \
    git push origin main

For private repositories using SSH keys:

- name: Deploy SSH key for git access
  ansible.builtin.copy:
    src: ~/.ssh/id_rsa
    dest: /home/remote_user/.ssh/id_rsa
    mode: '0600'

- name: Add github.com to known_hosts
  ansible.builtin.shell: |
    ssh-keyscan github.com >> ~/.ssh/known_hosts

- name: Pull via SSH
  ansible.builtin.git:
    repo: git@github.com:user/repo.git
    dest: /path/to/repo
    key_file: /home/remote_user/.ssh/id_rsa

If you still encounter hanging:

  • Add async and poll parameters to prevent timeouts
  • Set GIT_SSH_COMMAND="ssh -o ConnectTimeout=30" in environment variables
  • Consider using Ansible vault for credential security
- name: Git synchronization workflow
  hosts: webservers
  vars:
    repo_path: /var/www/app
  tasks:
    - name: Ensure repo directory exists
      ansible.builtin.file:
        path: "{{ repo_path }}"
        state: directory

    - name: Clone or update repository
      ansible.builtin.git:
        repo: '{{ git_repository }}'
        dest: "{{ repo_path }}"
        version: '{{ git_branch }}'
        update: yes
      register: git_result
      async: 300
      poll: 10

    - name: Notify on changes
      ansible.builtin.debug:
        msg: "Repository was updated"
      when: git_result.changed

When managing infrastructure as code, there are scenarios where you need to update git repositories across multiple servers. While Ansible has excellent modules for file manipulation and package management, native git operations require careful implementation.

The most common pitfall is using the command or shell module directly with git commands. This often hangs because:


# Problematic approach (may hang)
- name: Bad git pull example
  shell: git pull origin main

This fails because git might prompt for credentials or encounter SSH host verification.

Using the git Module Properly

Ansible's git module actually works well when configured correctly:


- name: Clone or update repository
  git:
    repo: 'git@github.com:user/repo.git'
    dest: /path/to/repo
    version: main
    accept_hostkey: yes
    key_file: /path/to/private_key
    update: yes

SSH Agent Forwarding Approach

For pushing changes, SSH agent forwarding is often the cleanest solution:


- name: Configure SSH agent forwarding
  set_fact:
    ansible_ssh_common_args: '-o ForwardAgent=yes'

- name: Git push changes
  shell: |
    cd /path/to/repo
    git config --global user.email "ci@example.com"
    git config --global user.name "CI Server"
    git add .
    git commit -m "Automated update"
    git push origin main

Deploy Key Alternative

For production environments, deploy keys provide better security:


- name: Set up deploy key
  copy:
    src: files/deploy_key
    dest: /home/user/.ssh/id_rsa
    mode: '0600'

- name: Update repository using deploy key
  git:
    repo: 'git@github.com:user/repo.git'
    dest: /path/to/repo
    version: main
    key_file: /home/user/.ssh/id_rsa
    force: yes

When dealing with submodules or complex workflows, consider:


- name: Recursive git operations
  shell: |
    cd /path/to/repo
    git submodule update --init --recursive
    git pull --recurse-submodules
  args:
    executable: /bin/bash

For large repositories, shallow clones can save time:


- name: Shallow clone
  git:
    repo: 'git@github.com:user/repo.git'
    dest: /path/to/repo
    depth: 1
    single_branch: yes