Skip to content

git

git keeps a checkout of a git repository at a revision: a tag, a branch or a commit. The first apply clones the repository; later ones move the checkout when the revision points somewhere else, such as a branch that got new commits. Use it to deploy an application or a site from its repository, or to keep a tool's source on a machine.

New in 0.2.0

Minimal example

examples/resources/git/minimal.rb
package "git"
git "/opt/sadame", repository: "https://github.com/enderkus/sadame.git", revision: "v0.1.0"

Options

Option Type Default Description
repository String required The URL to clone, as git clone takes it: HTTPS, SSH (git@github.com:example/app.git) or a local path.
revision String the remote's default branch A tag, a branch or a full 40-character commit.
user String root Run every git command as this user (through su), so the checkout belongs to them and their SSH keys and git configuration apply.
state :present or :absent :present :absent removes the checkout with rm -rf.
notifies [:action, "type[name]"], optionally with :immediately, or a list of them none Actions to run on other resources when this one changes. See Notifications.
subscribes [:action, "type[name]"], optionally with :immediately, or a list of them none Actions to run on this resource when another one changes.
tags Symbol, String, or an Array of them none Tags for running part of a recipe with --tags and --skip-tags. See Tags.

How it works

  • plan asks the remote which commit the revision names (git ls-remote; tags first, then branches, as git resolves a name) and compares it with the checkout's HEAD. A full commit needs no remote. This is why plan and drift need network access to the repository.
  • apply clones with git clone --no-checkout, or fetches into an existing checkout, then runs git checkout --force --detach COMMIT. The checkout is on that commit, not on a branch.
  • A checkout whose origin URL differs from repository gets the new URL (git remote set-url) and is fetched from there.
  • An empty directory, such as one a directory resource created for the user, is cloned into. A directory with other files in it is an error.

git must be installed: declare package "git" before. On a machine where it is not installed yet, plan shows the revision as you wrote it; apply resolves it once the package is in.

Git never asks for a password or passphrase during a run (GIT_TERMINAL_PROMPT=0). Private repositories need credentials that work without a prompt, such as a deploy key in the user's ~/.ssh and the server's host key in their ~/.ssh/known_hosts.

Scenarios

examples/resources/git/recipe.rb
# The sadame source at its 0.1.0 tag, cloned by a deploy user who owns the
# files. Once the checkout is at the tag, apply has nothing to do; a new
# revision moves it.
package "git"
package "shadow" if platform.family == "alpine" # useradd, for the user resource

user "deploy", shell: "/bin/sh"
directory "/srv/sadame", owner: "deploy", mode: "0755"

git "/srv/sadame",
    repository: "https://github.com/enderkus/sadame.git",
    revision: "v0.1.0",
    user: "deploy"

A tagged release, cloned by a deploy user

$ sudo sadame apply examples/resources/git
sadame 0.5.1 apply on local (Debian GNU/Linux 12 (bookworm))

changed    package[git]
           state: absent -> installed
changed    user[deploy]
           state: absent -> present
           shell: /bin/sh
changed    directory[/srv/sadame]
           state: absent -> present
           mode: 0755
           owner: deploy
changed    git[/srv/sadame]
           state: absent -> present
           repository: https://github.com/enderkus/sadame.git
           revision: d082e432ecc99ef03cf8f30fa4ecebb80defb5c3

Summary
  changed      4  package[git]
                  user[deploy]
                  directory[/srv/sadame]
                  git[/srv/sadame]
  unchanged    0
  failed       0
  skipped      0
  notified     0

Finished in 36.67s.

$ sudo sadame plan examples/resources/git
sadame 0.5.1 plan on local (Debian GNU/Linux 12 (bookworm))

Plan: 0 to create, 0 to update, 0 to remove, 0 to run, 4 unchanged.

$ sudo -u deploy git -C /srv/sadame describe --tags
v0.1.0

Following a branch, and restarting on a new commit

With a branch as the revision, every apply moves the checkout to the branch's latest commit, and a notification restarts the application only when it moved:

git "/srv/app",
    repository: "https://github.com/example/app.git",
    revision: "main",
    user: "deploy",
    notifies: [:restart, "service[app]"]

Pin a tag or a commit instead when every server must run the same code no matter when it was last applied.

Common mistakes

  • Local changes in the checkout. Moving the checkout uses checkout --force, which discards changes to tracked files. Files git does not track, such as uploads or a .env, are kept. Keep anything that must survive outside the checkout.
  • Credentials that need a prompt. A private HTTPS repository without stored credentials, or an SSH host key that is not in known_hosts, fails instead of waiting for an answer that never comes.
  • The wrong user for the key. Without user:, git runs as root and uses root's ~/.ssh. With user: "deploy", the key must be deploy's.
  • Submodules. They are not cloned or updated. Run git submodule update --init from an exec resource that the git resource notifies.

See also

  • package to install git.
  • user and directory for the user who owns the checkout and the directory it lives in.
  • service to restart the application when the checkout moves.