/submodule-init

Action to manage submodule access: start an SSH agent with volatile keys and optionally rewrite HTTPS submodule URLs to SSH with auto cleanup.

Primary LanguageJavaScriptMIT LicenseMIT

Setup SSH for Git Submodules (Auto Cleanup)

A compact GitHub/Gitea Action to start an SSH agent, load one or more private keys (volatile — not persisted to disk), and optionally rewrite HTTPS submodule URLs to SSH for CI jobs.

Purpose

This action exists primarily to make working with private Git submodules easy and safe in CI. It avoids changing global Git configuration on the runner (for example, using a command like:

git config --global url."git@github.com:".insteadOf "https://github.com/"

) because global changes persist on a runner and can interfere with parallel jobs or subsequent workflow runs on personal/self-hosted runners. Preventing that runner-wide side effect is the main design goal of this repository.

Quick usage (required)

Use the auto-cleanup action (recommended) for most cases:

- name: Setup SSH for submodules
  uses: zcube/submodule-init@main
  with:
    https-host: 'git.example.com' # HTTPS host to match
    ssh-host: 'nas.example.com:2222' # SSH host[:port]
    ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

- name: Checkout with submodules
  uses: actions/checkout@v4
  with:
    submodules: recursive

If you only need a generic SSH agent (no URL rewriting):

- name: Setup SSH agent
  uses: zcube/submodule-init@main
  with:
    ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

- name: Use SSH
  run: git clone git@github.com:user/repo.git

For explicit (manual) cleanup control use the manual-cleanup action and the cleanup-action:

- name: Setup SSH (manual cleanup)
  id: ssh
  uses: zcube/submodule-init/manual-cleanup-action.yml@main
  with:
    https-host: 'git.example.com'
    ssh-host: 'nas.example.com:2222'
    ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

- name: Checkout
  uses: actions/checkout@v4
  with:
    submodules: recursive

- name: Cleanup SSH agent
  if: always()
  uses: zcube/submodule-init/cleanup-action.yml@main
  with:
    ssh-agent-pid: ${{ steps.ssh.outputs.ssh-agent-pid }}

Modes

  • URL rewriting (single): provide https-host + ssh-host.
  • URL rewriting (multiple): use url-mappings for multiple host mappings.
  • Generic SSH agent: omit https-host and ssh-host to only load keys.

Inputs

Name Description Required Default Example
ssh-private-key Private key(s); multiple keys separated by newlines. Yes - ---BEGIN RSA... (paste key)
https-host HTTPS hostname to match (used when rewriting). No - github.com
ssh-host SSH hostname (optionally with :port). No - github.com:22 or nas.example.com:2222
ssh-port SSH port used if ssh-host has no port. No 22 22
url-mappings Multi-line mappings. Format: https://<https-host>/=ssh://<user>@<ssh-host>[:port]/ (one per line). Example below. No - https://github.com/=ssh://git@github.com:22/
ssh-known-hosts Known hosts entries (recommended for production). No - github.com ssh-rsa AAAA... (see Creating secrets)
strict-host-key-checking 'yes' or 'no' (enable host key checking). No yes yes

Security (read before use)

Topic Notes
Secrets Provide keys only via repository or organization secrets.
Key persistence Keys are kept volatile (not persisted to disk) where possible.
Host verification For production, set ssh-known-hosts and strict-host-key-checking: 'yes' to reduce MITM risk.
Best practices Follow least-privilege and key rotation practices.

Examples

  • Auto cleanup: examples/basic-usage.yml, examples/custom-ssh-host-port.yml, examples/url-mappings-auto-cleanup.yml
  • Manual cleanup: examples/manual-cleanup-basic.yml, examples/manual-cleanup-custom.yml, examples/url-mappings-manual-cleanup.yml
  • Generic agent / multiple keys: examples/generic-ssh-agent.yml, examples/multiple-keys.yml

Troubleshooting

  • If submodules fail to clone, confirm https-host matches the submodule HTTPS URLs.
  • If host verification fails, populate ssh-known-hosts using ssh-keyscan and store it in secrets.
  • If your runner lacks Node.js 20 and you rely on auto-cleanup, use the manual-cleanup flow.

Creating secrets (ssh-known-hosts and private key)

To reduce MITM risk, populate ssh-known-hosts with the exact output of ssh-keyscan for your git host and store it as a secret (e.g., SSH_KNOWN_HOSTS). Example commands:

# Get known_hosts entries for github.com (use your actual host instead of github.com)
ssh-keyscan github.com > known_hosts

# Inspect and copy the file (the output below is a shortened sample; use the full output)
cat known_hosts
# Sample output (truncated):
# github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7... (truncated)
# github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM... (truncated)

# Set as a repository secret using GitHub CLI (replace owner/repo or run inside the repo)
gh secret set SSH_KNOWN_HOSTS --body "$(cat known_hosts)"

# Alternatively, paste the contents of known_hosts into the repository/organization secret

Also add your private key as SSH_PRIVATE_KEY (single key or multiple keys separated by newlines). For example, to set with gh:

gh secret set SSH_PRIVATE_KEY --body "$(cat ~/.ssh/id_rsa)"

Note: Always generate and use the current known_hosts output from your environment; do not rely on pasted example keys in production.

Example url-mappings format (concrete)

Provide one mapping per line. Each mapping rewrites HTTPS fetch URLs used by Git to SSH equivalents. Replace hosts and user names with your environment's values.

Example 1 — GitHub-like HTTPS submodules rewritten to a self-hosted SSH gateway:

url-mappings: |
  https://git.example.com/=ssh://git@nas.example.com:2222/

Example 2 — GitLab project host rewritten to same-host SSH (default port 22):

url-mappings: |
  https://gitlab.example.com/=ssh://git@gitlab.example.com:22/

Example 3 — Multiple mappings (each on its own line):

url-mappings: |
  https://git.example.com/=ssh://git@nas.example.com:2222/
  https://gitlab.example.com/=ssh://git@gitlab.example.com:22/

License

MIT