Sync A Git Repo On A Target

on this page

Use this guide when a target needs to clone or update a Git repository as part of a playbook run — for example, a museum kiosk pulling the latest exhibit content before it opens for the day. On Windows targets, the embedded preflight/git-sync action wraps the fetch, checkout, reset, and clean steps in a single idempotent task; on POSIX targets, run git through the shell module (see Sync On A POSIX Target).

Prerequisites

1. Sync A Repository

preflight/git-sync clones the repository into dest if it does not exist yet, or fetches and checks out ref if it does:

yaml
tasks:
  - name: Sync exhibit content
    uses: preflight/git-sync
    with:
      repo: https://github.com/example/content.git
      dest: C:\Exhibits\Content
      ref: main

Re-running the task leaves an up-to-date working tree at dest with ref checked out. Set create_parent: true if the parent of dest does not exist yet, and set_remote_url: true if repo may change between runs and the existing remote should be updated to match.

2. Pin To A Ref

ref accepts a branch, tag, or commit. Pin exhibit content to a tag so that a kiosk never picks up unreviewed commits on main:

yaml
tasks:
  - name: Sync exhibit content at a known-good tag
    uses: preflight/git-sync
    with:
      repo: https://github.com/example/content.git
      dest: C:\Exhibits\Content
      ref: v2026.03.01
      detach: true

detach: true checks out ref directly instead of creating or moving a local branch. Use local_branch to name the branch that tracks ref when you do want a branch checked out.

3. Reset And Clean The Working Tree

Exhibit content trees accumulate stray files over time: a kiosk application may write cache files, logs, or scratch state into the content directory, and a previous sync may leave behind files that no longer exist upstream. Set reset and clean so every sync starts from a pristine copy of ref:

yaml
tasks:
  - name: Sync and reset exhibit content
    uses: preflight/git-sync
    with:
      repo: https://github.com/example/content.git
      dest: C:\Exhibits\Content
      ref: main
      reset: true
      clean: true
      clean_ignored: true

reset: true hard-resets the working tree to the resolved ref, discarding local modifications. clean: true removes untracked files after checkout; add clean_ignored: true to also remove files that match .gitignore patterns, such as cached thumbnails an exhibit viewer generates locally.

Set fetch: true (the default when the repository already exists) to fetch remote updates before resolving ref, and prune: true alongside it to drop local references to branches deleted upstream. Use depth to limit fetch/clone to a shallow history when the full commit history of an exhibit content repository is not needed.

4. Run As The Exhibit Account

By default, preflight/git-sync runs as whichever account executes the task. If exhibit content should be owned by a dedicated kiosk account rather than the transport account, add become to the task. See Run Tasks As Another User for the full pattern; preflight/git-sync does not need any special handling beyond the standard become fields.

5. Understand Safe Directory Handling

preflight/git-sync adds dest to Git’s global safe.directory list before running sync checks, because safe_directory defaults to true. This avoids Git for Windows “dubious ownership” failures when the checkout is owned by a different Windows account than the one running the sync — a common situation when provisioning runs as an administrator but the exhibit app runs under a kiosk account. Set safe_directory: false only if your environment already manages the safe.directory list another way.

Sync On A POSIX Target

preflight/git-sync targets Windows. On a POSIX target over SSH, run git directly with the shell module, using creates: to make the clone idempotent:

yaml
tasks:
  - name: Clone content repository
    shell:
      cmd: git
      args:
        - clone
        - https://github.com/example/content.git
        - /opt/exhibits/content
      creates: /opt/exhibits/content/.git

  - name: Pull latest content
    shell:
      cmd: git
      args: ["-C", "/opt/exhibits/content", "pull", "--ff-only"]

The pull task runs on every apply; that is a stated tradeoff of the manual pattern, not a bug. Wrap it in when: or a powershell check_script if the extra network call matters.

Troubleshooting

Sync fails with a “dubious ownership” error even though safe_directory is enabled. Confirm you did not explicitly set safe_directory: false on the task, and that dest matches the exact path git reports as unsafe — a trailing slash or a different casing of a drive letter can produce a path git treats as distinct.

reset/clean removed content the kiosk generated at runtime. This is expected: reset and clean return the working tree to exactly what ref contains upstream. Store any state the kiosk application needs to keep across syncs outside of dest, such as in a sibling directory the action does not manage.

Task reports success but the target is still on the old commit. Check that fetch is not disabled and that ref resolves to the commit you expect on the remote; preflight/git-sync only fetches remote updates when fetch is enabled for an existing repository.