-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Copilot Spec – git_identity Role (GitHub + GitLab with 1Password SSH signing)
Goal
Create an Ansible role git_identity that configures Git identity and SSH-based commit signing on daily driver machines.
-
Assume a fresh install but the role must be idempotent.
-
Use existing
ansible/roles/op_*roles to pull any sensitive values from 1Password (SSH keys, alt emails, etc.). -
Separate Git configuration for:
- Repos under
~/GitHub(personal, signed commits, GitHub). - Repos under
~/GitLab(work or other, possibly different identity/signing policy).
- Repos under
The role should leave other Git behavior untouched.
Role Name and Structure
Create:
ansible/roles/git_identity/meta/argument_specs.ymlansible/roles/git_identity/defaults/main.ymlansible/roles/git_identity/tasks/main.yml- (Optional later:
tasks/github.yml,tasks/gitlab.yml,tasks/validate.ymland import them frommain.ymlfor cleanliness.)
Follow these constraints:
- No inline comments in YAML or tasks.
- Use FQCNs for modules (
ansible.builtin.file,ansible.builtin.command, etc.). - Make tasks minimal, idempotent, and safe.
Inputs and Variables
Define the following variables in meta/argument_specs.yml and defaults/main.yml:
Required (no defaults):
-
git_identity_user_name
Example:"SRF-Audio" -
git_identity_user_email_github
Example:"srfaudioproductions@gmail.com" -
git_identity_user_email_gitlab
Can be same or different; allow separate config. -
git_identity_github_signing_pubkey_item
A 1Password item reference used by theop_*role to retrieve the SSH public key for GitHub signing.
Example:"vault-name/GitHub SSH Signing Key"
Optional (with sensible defaults):
-
git_identity_github_root(default"{{ ansible_env.HOME }}/GitHub") -
git_identity_gitlab_root(default"{{ ansible_env.HOME }}/GitLab") -
git_identity_allowed_signers_path
Default:"{{ ansible_env.HOME }}/.config/git/allowed_signers" -
git_identity_enable_github_signing(default:true) -
git_identity_enable_gitlab_signing(default:false)
All sensitive values (SSH keys, extra emails) must be fetched via op_ roles*, not hard-coded.
Dependencies (1Password integration)
Assume existing 1Password roles under ansible/roles/op_*. The role should:
-
Use
include_roleto call appropriateop_*role(s) to fetch:git_identity_github_signing_pubkey(SSH public key string).
-
Do not assume exact role names; but in the spec, tell Copilot to:
- Expect an included role to set
git_identity_github_signing_pubkeyto a single-line SSH public key string (ssh-ed25519 AAAA...). - Fail fast with
ansible.builtin.assertif that variable is missing or empty.
- Expect an included role to set
Example pattern in tasks (Copilot should fill real role name):
- name: Retrieve GitHub SSH signing public key from 1Password
ansible.builtin.include_role:
name: op_ssh_signing_key
vars:
op_item_ref: "{{ git_identity_github_signing_pubkey_item }}"
- name: Assert GitHub SSH signing key present
ansible.builtin.assert:
that:
- git_identity_github_signing_pubkey | length > 0The actual op_* role is expected to populate git_identity_github_signing_pubkey.
Behavior – Step by Step
1) Create ~/GitHub and ~/GitLab
Tasks:
-
Use
ansible.builtin.filewith:path: "{{ git_identity_github_root }}",state: directory,mode: "0755",owner/group= login user.- Same for
git_identity_gitlab_root.
Idempotent: re-runs should not change if directories already exist.
2) Create / update top-level Git config (~/.gitconfig)
We want a top-level Git config that includes per-tree configs.
- Path:
{{ ansible_env.HOME }}/.gitconfig - Use
ansible.builtin.blockinfileoransible.builtin.lineinfileto ensure these stanzas exist:
[includeIf "gitdir:~/GitHub/"]
path = ~/.gitconfig-github
[includeIf "gitdir:~/GitLab/"]
path = ~/.gitconfig-gitlabRequirements:
- Do not clobber existing content in
~/.gitconfig. - Ensure both includeIf blocks are present and not duplicated.
- Prefer
blockinfilekeyed with a clear marker (e.g.# GIT_IDENTITY MANAGED BLOCK) to isolate the managed portion.
3) Create ~/.gitconfig-github
- Path:
{{ ansible_env.HOME }}/.gitconfig-github - Use
ansible.builtin.templateoransible.builtin.copywithcontentto create the whole file. - The file should contain at least:
[user]
name = {{ git_identity_user_name }}
email = {{ git_identity_user_email_github }}
signingkey = {{ git_identity_github_signing_pubkey }}
[gpg]
format = ssh
[gpg "ssh"]
allowedSignersFile = {{ git_identity_allowed_signers_path }}
[commit]
gpgsign = {{ 'true' if git_identity_enable_github_signing else 'false' }}Notes:
signingkeyshould be the literal SSH public key string, not a path.- Use an absolute path in
allowedSignersFile(no~). - If
git_identity_enable_github_signingisfalse,gpgsignshould befalse, but keep the rest so toggling later is trivial.
4) Create ~/.gitconfig-gitlab
- Path:
{{ ansible_env.HOME }}/.gitconfig-gitlab - Similar approach, but allow different behavior:
[user]
name = {{ git_identity_user_name }}
email = {{ git_identity_user_email_gitlab }}
[commit]
gpgsign = {{ 'true' if git_identity_enable_gitlab_signing else 'false' }}- For now, no SSH signing by default (
enable_gitlab_signing = false), but structure must support enabling later. - Do not configure
gpg.formathere unlessenable_gitlab_signingis true; we want GitLab behavior isolated and opt-in.
5) Create and populate allowed_signers file
- Path:
{{ git_identity_allowed_signers_path }} - Ensure parent dir:
{{ ansible_env.HOME }}/.config/gitexists (ansible.builtin.file). - File contents should include at least one line:
{{ git_identity_user_email_github }} {{ git_identity_github_signing_pubkey }}
Implementation detail:
- Use
ansible.builtin.lineinfilewithregexpbased on the email to ensure idempotency and avoid duplicate entries. - Mode:
"0600"; owner = login user.
6) Ensure Git sees the correct config
After the files are in place:
-
Use
ansible.builtin.command(FQCN:ansible.builtin.command) to run:git config --global --get-regexp '^includeIf\.'git config --file ~/.gitconfig-github --get user.emailgit config --file ~/.gitconfig-github --get user.signingkeygit config --file ~/.gitconfig-gitlab --get user.email
-
Set
changed_when: falsefor these validation commands. -
Register their outputs for assertions.
Use ansible.builtin.assert to enforce:
- GitHub email in
.gitconfig-githubequalsgit_identity_user_email_github. - GitLab email in
.gitconfig-gitlabequalsgit_identity_user_email_gitlab. includeIf "gitdir:~/GitHub/"andincludeIf "gitdir:~/GitLab/"show up in the global config.
7) Smoke-test signing in a temporary GitHub repo
Optionally (controlled by a var like git_identity_run_validation_tests, default true):
-
Create a temporary directory under
{{ git_identity_github_root }}/.git_identity_testusingansible.builtin.tempfileor a fixed name plusstate: directory. -
Initialize a repo and make a test commit:
git init .git config user.name "{{ git_identity_user_name }}"git config user.email "{{ git_identity_user_email_github }}"git commit --allow-empty -m "git_identity signing test"
-
Run:
git log --show-signature -1
-
Capture stdout and assert:
- If
git_identity_enable_github_signingistrue, output must contain"Good \"git\" signature"or at least"signature"and no error aboutgpg.ssh.allowedSignersFile.
- If
Implementation details:
- Use
ansible.builtin.commandwithchdirpointing to the temp repo. - Use
changed_when: false. - Use
ansible.builtin.assertto fail loudly if signing is expected but not present.
The test directory can be left in place or cleaned up (cleanup is nicer but optional; if cleaning, be idempotent and only remove the test dir).
Idempotency Requirements
Copilot must:
- Use
ansible.builtin.filewithstate: directoryand appropriate modes for directories. - Use
ansible.builtin.copy/templatein a way that does not churn files unnecessarily. - Use
ansible.builtin.lineinfileorblockinfilefor.gitconfigandallowed_signersto avoid duplicates. - Ensure validation commands have
changed_when: false.
Multiple runs of the role on the same machine must result in no further changes once everything is in the desired state.
Acceptance Criteria
-
After running
git_identityon a fresh daily driver machine:~/GitHuband~/GitLabexist with correct ownership and permissions.~/.gitconfigcontains theincludeIfblocks for both trees.~/.gitconfig-githuband~/.gitconfig-gitlabexist with correct user identity and (for GitHub) SSH signing configuration.{{ git_identity_allowed_signers_path }}exists and has a line forgit_identity_user_email_githubwith the SSH public key from 1Password.git config --file ~/.gitconfig-github --get user.signingkeyreturns the SSH public key string from 1Password.- A test commit in a repo under
~/GitHubshows a valid SSH signature ingit log --show-signaturewhen signing is enabled.
-
The role is safe to include in your “daily driver bootstrap” playbook and can be run repeatedly without noisy diffs.