Skip to content

Commit d0b4ef7

Browse files
committed
docs: update documentation for new features and add authentication guides
- Add step-by-step token creation guides for GitLab, GitHub, Bitbucket, CodeCommit, SSH - Document retry, exclude branches, parallel, and pre-check features - Add new workflow examples (exclude branches, parallel with retry) - Update project structure and coverage info in development guide - Update README with new features and inputs
1 parent f749cbe commit d0b4ef7

5 files changed

Lines changed: 369 additions & 44 deletions

File tree

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ A Go-based GitHub Action that mirrors repositories to multiple Git hosting provi
1515
- Multi-target mirroring in a single workflow step
1616
- Auto-detect provider from URL (GitLab, GitHub, Bitbucket, CodeCommit)
1717
- Selective branch mirroring or mirror all branches
18+
- Exclude specific branches from mirroring
1819
- Tag mirroring support
1920
- Force push option for exact replication
2021
- Multiple authentication methods (token, app password, SSH key)
21-
- Dry run mode for safe testing
22+
- Parallel mirroring to multiple targets concurrently
23+
- Retry logic with configurable count and delay
24+
- Dry run mode with remote connectivity pre-check (`git ls-remote`)
2225
- JSON result output for downstream steps
26+
- Credential masking in all log output
2327

2428
> For detailed documentation, see the [docs/](docs/) folder:
2529
> [Authentication](docs/AUTHENTICATION.md) |
@@ -166,7 +170,11 @@ jobs:
166170
| `mirror_branches` | Branches to mirror (comma-separated, or `all`) | No | `all` |
167171
| `mirror_tags` | Mirror tags | No | `true` |
168172
| `force_push` | Use force push | No | `true` |
169-
| `dry_run` | Log actions without pushing | No | `false` |
173+
| `dry_run` | Dry run mode with remote pre-check | No | `false` |
174+
| `retry_count` | Number of retry attempts on push failure | No | `0` |
175+
| `retry_delay` | Delay in seconds between retries | No | `5` |
176+
| `exclude_branches` | Branches to exclude (comma-separated) | No | `''` |
177+
| `parallel` | Mirror to targets in parallel | No | `false` |
170178
| `debug` | Enable debug logging | No | `false` |
171179

172180
<br/>
@@ -219,7 +227,8 @@ Many teams need to keep repository mirrors in sync across multiple Git providers
219227
│ │ ├── config.go # Configuration loading & target parsing
220228
│ │ └── config_test.go # Config tests
221229
│ ├── mirror/
222-
│ │ ├── mirror.go # Mirror logic & auth URL injection
230+
│ │ ├── mirror.go # Mirror logic, retry, parallel, auth URL injection
231+
│ │ ├── ssh.go # SSH key setup/cleanup
223232
│ │ └── mirror_test.go # Mirror tests
224233
│ └── output/
225234
│ └── output.go # GitHub Actions output writer

docs/AUTHENTICATION.md

Lines changed: 233 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Authentication
22

3-
Guide for configuring authentication for each supported Git provider.
3+
Guide for configuring authentication for each supported Git provider, including step-by-step token creation instructions.
44

55
<br/>
66

@@ -11,6 +11,7 @@ Guide for configuring authentication for each supported Git provider.
1111
- [Bitbucket](#bitbucket)
1212
- [AWS CodeCommit](#aws-codecommit)
1313
- [SSH Key](#ssh-key)
14+
- [Adding Secrets to GitHub Actions](#adding-secrets-to-github-actions)
1415
- [Security Best Practices](#security-best-practices)
1516

1617
<br/>
@@ -27,16 +28,42 @@ Uses OAuth2 token authentication. The token is injected into the HTTPS URL as `h
2728
gitlab_token: ${{ secrets.GITLAB_TOKEN }}
2829
```
2930
31+
<br/>
32+
3033
### Required Token Scopes
3134
32-
- `write_repository` — Push access to the target repository
33-
- `read_repository` — (optional) If the target repo is private
35+
| Scope | Purpose | Required |
36+
|-------|---------|----------|
37+
| `write_repository` | Push access to the target repository | Yes |
38+
| `read_repository` | Read access (for private repos) | Optional |
39+
40+
<br/>
41+
42+
### Step-by-Step: Creating a GitLab Personal Access Token
43+
44+
1. Log in to [GitLab](https://gitlab.com)
45+
2. Click your **avatar** (top-right) → **Preferences**
46+
3. In the left sidebar, click **Access Tokens**
47+
4. Click **Add new token**
48+
5. Configure:
49+
- **Token name**: `git-mirror-action`
50+
- **Expiration date**: Set an appropriate date (recommended: 1 year max)
51+
- **Scopes**: Check `write_repository`
52+
6. Click **Create personal access token**
53+
7. **Copy the token immediately** — it will not be shown again
54+
8. Add it as a GitHub secret named `GITLAB_TOKEN` ([how to add secrets](#adding-secrets-to-github-actions))
55+
56+
<br/>
57+
58+
### Alternative: GitLab Project Access Token
59+
60+
For organization-level access, use a Project Access Token instead:
3461

35-
### Creating a GitLab Token
62+
1. Go to your **GitLab project** → **Settings** → **Access Tokens**
63+
2. Create a token with `write_repository` scope and **Maintainer** role
64+
3. Use the token the same way as a personal access token
3665

37-
1. Go to **GitLab** > **Settings** > **Access Tokens**
38-
2. Create a token with `write_repository` scope
39-
3. Add the token as a GitHub secret (`GITLAB_TOKEN`)
66+
> **Note**: Project Access Tokens are available on GitLab Premium and higher. Free tier users should use Personal Access Tokens.
4067

4168
<br/>
4269

@@ -52,11 +79,45 @@ Uses x-access-token authentication. The token is injected as `https://x-access-t
5279
github_token: ${{ secrets.MIRROR_GITHUB_TOKEN }}
5380
```
5481

82+
<br/>
83+
5584
### Required Token Scopes
5685

57-
- `repo` — Full control of private repositories (or `public_repo` for public repos)
86+
| Scope | Purpose | Required |
87+
|-------|---------|----------|
88+
| `repo` | Full control of private repositories | Yes (private repos) |
89+
| `public_repo` | Push to public repositories only | Yes (public repos) |
90+
91+
> **Important**: Do NOT use `${{ secrets.GITHUB_TOKEN }}` for cross-repository mirroring. It only has access to the current repository. Use a Personal Access Token (PAT) instead.
92+
93+
<br/>
94+
95+
### Step-by-Step: Creating a GitHub Fine-Grained PAT (Recommended)
96+
97+
1. Log in to [GitHub](https://github.com)
98+
2. Click your **avatar** (top-right) → **Settings**
99+
3. Scroll down in the left sidebar → **Developer settings**
100+
4. Click **Personal access tokens** → **Fine-grained tokens**
101+
5. Click **Generate new token**
102+
6. Configure:
103+
- **Token name**: `git-mirror-action`
104+
- **Expiration**: Select an appropriate duration
105+
- **Resource owner**: Select the target organization or your account
106+
- **Repository access**: Select **Only select repositories** → choose the target repo
107+
- **Permissions** → **Repository permissions**:
108+
- **Contents**: `Read and write`
109+
7. Click **Generate token**
110+
8. **Copy the token immediately**
111+
9. Add it as a GitHub secret named `MIRROR_GITHUB_TOKEN`
112+
113+
<br/>
58114

59-
> **Note**: Do not use `${{ secrets.GITHUB_TOKEN }}` for cross-repository mirroring. It only has access to the current repository. Use a Personal Access Token (PAT) instead.
115+
### Alternative: Classic PAT
116+
117+
1. Go to **Settings** → **Developer settings** → **Personal access tokens** → **Tokens (classic)**
118+
2. Click **Generate new token (classic)**
119+
3. Check `repo` scope (or `public_repo` for public repos only)
120+
4. Generate and copy the token
60121

61122
<br/>
62123

@@ -73,30 +134,47 @@ Uses username and app password authentication. Credentials are injected as `http
73134
bitbucket_password: ${{ secrets.BITBUCKET_APP_PASSWORD }}
74135
```
75136

76-
### Creating a Bitbucket App Password
137+
<br/>
138+
139+
### Required Permissions
77140

78-
1. Go to **Bitbucket** > **Personal settings** > **App passwords**
79-
2. Create a password with **Repositories: Write** permission
80-
3. Add the username and app password as GitHub secrets
141+
| Permission | Purpose | Required |
142+
|------------|---------|----------|
143+
| Repositories: Write | Push access to repositories | Yes |
144+
| Repositories: Read | Read access (for private repos) | Optional |
81145

82146
> **Note**: Both `bitbucket_username` and `bitbucket_password` must be provided. If either is missing, the URL is used as-is.
83147

84148
<br/>
85149

86-
## AWS CodeCommit
150+
### Step-by-Step: Creating a Bitbucket App Password
151+
152+
1. Log in to [Bitbucket](https://bitbucket.org)
153+
2. Click your **avatar** (bottom-left) → **Personal settings**
154+
3. In the left sidebar under **Access management**, click **App passwords**
155+
4. Click **Create app password**
156+
5. Configure:
157+
- **Label**: `git-mirror-action`
158+
- **Permissions**: Check **Repositories** → **Write** (this also grants Read)
159+
6. Click **Create**
160+
7. **Copy the password immediately** — it will not be shown again
161+
8. Add two GitHub secrets:
162+
- `BITBUCKET_USERNAME`: Your Bitbucket username (not email)
163+
- `BITBUCKET_APP_PASSWORD`: The app password you just created
87164

88-
CodeCommit uses IAM-based authentication via the Git credential helper. The URL is used as-is without token injection.
165+
<br/>
89166

90-
```yaml
91-
- uses: somaz94/git-mirror-action@v1
92-
with:
93-
targets: |
94-
codecommit::https://git-codecommit.us-east-1.amazonaws.com/v1/repos/myrepo
95-
```
167+
### Finding Your Bitbucket Username
96168

97-
### IAM Configuration
169+
Your Bitbucket username is NOT your email. To find it:
170+
1. Go to **Personal settings**
171+
2. Your username is shown under **Atlassian account settings** or in the URL: `bitbucket.org/<username>/`
98172

99-
Ensure the runner has AWS credentials configured with `codecommit:GitPush` permissions. For GitHub Actions, use `aws-actions/configure-aws-credentials`:
173+
<br/>
174+
175+
## AWS CodeCommit
176+
177+
CodeCommit uses IAM-based authentication via the Git credential helper. The URL is used as-is without token injection.
100178

101179
```yaml
102180
- name: Configure AWS credentials
@@ -117,6 +195,60 @@ Ensure the runner has AWS credentials configured with `codecommit:GitPush` permi
117195

118196
<br/>
119197

198+
### Required IAM Permissions
199+
200+
```json
201+
{
202+
"Version": "2012-10-17",
203+
"Statement": [
204+
{
205+
"Effect": "Allow",
206+
"Action": [
207+
"codecommit:GitPush",
208+
"codecommit:CreateBranch",
209+
"codecommit:GetRepository"
210+
],
211+
"Resource": "arn:aws:codecommit:us-east-1:123456789012:myrepo"
212+
}
213+
]
214+
}
215+
```
216+
217+
<br/>
218+
219+
### Step-by-Step: Creating AWS IAM Credentials
220+
221+
#### Option A: IAM User with Access Keys
222+
223+
1. Log in to [AWS Console](https://console.aws.amazon.com) → **IAM**
224+
2. Click **Users** → **Create user**
225+
3. Enter username: `git-mirror-action`
226+
4. Click **Next** → **Attach policies directly**
227+
5. Click **Create policy** and paste the JSON above (adjust the Resource ARN)
228+
6. Attach the policy to the user
229+
7. Go to the user → **Security credentials** → **Create access key**
230+
8. Select **Third-party service** → **Create access key**
231+
9. **Copy both keys immediately**
232+
10. Add two GitHub secrets:
233+
- `AWS_ACCESS_KEY_ID`: The access key ID
234+
- `AWS_SECRET_ACCESS_KEY`: The secret access key
235+
236+
#### Option B: OIDC (Recommended for Production)
237+
238+
For keyless authentication, use GitHub OIDC provider:
239+
240+
```yaml
241+
- name: Configure AWS credentials (OIDC)
242+
uses: aws-actions/configure-aws-credentials@v4
243+
with:
244+
role-to-assume: arn:aws:iam::123456789012:role/git-mirror-role
245+
aws-region: us-east-1
246+
```
247+
248+
See [AWS docs](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) for OIDC setup.
249+
250+
<br/>
251+
120252
## SSH Key
121253

122254
For providers that support SSH, use the `ssh_private_key` input with the `generic` provider.
@@ -129,6 +261,8 @@ For providers that support SSH, use the `ssh_private_key` input with the `generi
129261
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
130262
```
131263

264+
<br/>
265+
132266
### How It Works
133267

134268
When `ssh_private_key` is provided, the action automatically:
@@ -138,11 +272,78 @@ When `ssh_private_key` is provided, the action automatically:
138272
3. Sets `GIT_SSH_COMMAND` to use the configured key
139273
4. Cleans up all SSH files after mirroring completes
140274

141-
### Setup
275+
<br/>
276+
277+
### Step-by-Step: Creating an SSH Key Pair
278+
279+
1. Generate an Ed25519 key pair (no passphrase):
280+
281+
```bash
282+
ssh-keygen -t ed25519 -C "git-mirror-action" -f mirror_key -N ""
283+
```
284+
285+
2. This creates two files:
286+
- `mirror_key` — Private key (add to GitHub secrets)
287+
- `mirror_key.pub` — Public key (add to target Git server)
142288

143-
1. Generate an SSH key pair: `ssh-keygen -t ed25519 -C "mirror-action"`
144-
2. Add the public key to the target Git server
145-
3. Add the private key as a GitHub secret (`SSH_PRIVATE_KEY`)
289+
3. **Add the public key** to the target Git server:
290+
- **GitLab**: Settings → Repository → Deploy keys → Add key (check "Grant write permissions")
291+
- **GitHub**: Settings → Deploy keys → Add deploy key (check "Allow write access")
292+
- **Bitbucket**: Repository settings → Access keys → Add key
293+
- **Custom server**: Add to `~/.ssh/authorized_keys` on the server
294+
295+
4. **Add the private key** as a GitHub secret:
296+
```bash
297+
# Copy the private key content
298+
cat mirror_key
299+
```
300+
Add this as `SSH_PRIVATE_KEY` in GitHub secrets
301+
302+
5. **Delete local key files** after setup:
303+
```bash
304+
rm mirror_key mirror_key.pub
305+
```
306+
307+
<br/>
308+
309+
## Adding Secrets to GitHub Actions
310+
311+
All credentials must be stored as GitHub secrets. Never hardcode them in workflow files.
312+
313+
<br/>
314+
315+
### Step-by-Step: Adding a Repository Secret
316+
317+
1. Go to your **GitHub repository**
318+
2. Click **Settings** → **Secrets and variables** → **Actions**
319+
3. Click **New repository secret**
320+
4. Enter:
321+
- **Name**: The secret name (e.g., `GITLAB_TOKEN`)
322+
- **Secret**: The token/password value
323+
5. Click **Add secret**
324+
325+
<br/>
326+
327+
### Step-by-Step: Adding an Organization Secret
328+
329+
1. Go to your **GitHub organization**
330+
2. Click **Settings** → **Secrets and variables** → **Actions**
331+
3. Click **New organization secret**
332+
4. Enter the name and value
333+
5. Under **Repository access**, select which repos can use this secret
334+
6. Click **Add secret**
335+
336+
<br/>
337+
338+
### Quick Reference: Required Secrets Per Provider
339+
340+
| Provider | Secrets Needed |
341+
|----------|---------------|
342+
| GitLab | `GITLAB_TOKEN` |
343+
| GitHub | `MIRROR_GITHUB_TOKEN` |
344+
| Bitbucket | `BITBUCKET_USERNAME`, `BITBUCKET_APP_PASSWORD` |
345+
| CodeCommit | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` |
346+
| SSH | `SSH_PRIVATE_KEY` |
146347

147348
<br/>
148349

@@ -153,10 +354,14 @@ When `ssh_private_key` is provided, the action automatically:
153354
- **Rotate tokens regularly** — Set expiration dates and rotate periodically
154355
- **Use separate tokens per target** — Avoid reusing a single token across providers
155356
- **Audit access** — Regularly review which tokens have access to your repositories
357+
- **Prefer fine-grained tokens** — GitHub Fine-Grained PATs and GitLab Project Access Tokens over classic tokens
358+
- **Use OIDC where possible** — For AWS CodeCommit, prefer OIDC over static access keys
359+
360+
<br/>
156361

157362
### Built-in Security Features
158363

159364
- **Credential URL encoding** — Special characters (`@`, `:`, `/`, etc.) in passwords are URL-encoded to prevent URL parsing issues
160-
- **Debug log masking** — Tokens and passwords are replaced with `***` in debug logs
365+
- **Log masking** — Tokens, passwords, and usernames are replaced with `***` in all log output (including git stderr)
161366
- **SSH key cleanup** — SSH key files are automatically removed after mirroring
162367
- **Config validation** — Warns when required credentials are missing for a target provider

0 commit comments

Comments
 (0)