Skip to content

Repository files navigation

⚙️ Gerrit Server Container

Starts and runs Gerrit Code Review server(s) in Docker containers with automated pull-replication plugin configuration for mirroring production Gerrit instances in CI/CD workflows.

Features

  • 🚀 Multi-instance support - Start multiple Gerrit servers from a single JSON configuration
  • 🔄 Automatic replication - Built-in pull-replication plugin for syncing from production servers
  • 🔐 Flexible authentication - Support for SSH keys, HTTP basic auth, and bearer tokens
  • 🐳 Docker layer caching - Optimized performance with intelligent caching
  • 💾 Persistent containers - Containers remain available throughout workflow duration
  • 🧹 Automatic cleanup - Graceful shutdown and cleanup on workflow completion
  • 🏥 Health checking - Automated service availability verification
  • 📊 Comprehensive outputs - Full instance metadata and access URLs
  • 🔗 Gerrit to Platform - Optional gerrit_to_platform integration for dispatching GitHub Actions workflows from Gerrit events

Quick Start

steps:
  - name: "Start Gerrit mirror"
    id: gerrit
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: |
        [{
          "project": "ONAP",
          "slug": "onap",
          "gerrit": "gerrit.onap.org"
        }]
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      sync_on_startup: true

  - name: "Use Gerrit instance"
    run: |
      echo "Gerrit URL: ${{ steps.gerrit.outputs.gerrit_urls }}"
      # Access Gerrit at http://localhost:8080

Use Cases

Use this action in CI/CD pipelines that need to:

  • Test against production data - Mirror a production Gerrit server for integration testing
  • Develop offline - Work with Gerrit repositories without affecting production
  • Verify changes - Test plugin configurations, migrations, or upgrades
  • CI/CD automation - Automate workflows that require Gerrit repository access
  • Multi-site testing - Test against multiple Gerrit instances simultaneously

Inputs

Required Inputs

Name Required Default Description
gerrit_setup True JSON array defining Gerrit instances

Authentication Inputs (one method required)

Name Required Default Description
ssh_private_key False SSH private key for authentication (required if auth_type=ssh)
ssh_known_hosts False auto SSH known_hosts entries (auto-generated if not provided)
ssh_auth_keys False SSH public keys to add for container access (one key per line)
ssh_auth_username False Gerrit username to create for SSH access (uses admin if omitted)
http_username False HTTP basic auth username (required if auth_type=http_basic)
http_password False HTTP basic auth password (required if auth_type=http_basic)
bearer_token False Bearer token (required if auth_type=bearer_token)

Gerrit Configuration

Name Required Default Description
gerrit_version False 3.13.1-ubuntu24 Gerrit Docker image version tag
plugin_version False stable-3.13 Pull-replication plugin version/branch
base_http_port False 8080 Starting HTTP port (increments for multi-instance)
base_ssh_port False 29418 Starting SSH port (increments for multi-instance)
auth_type False ssh Authentication method: ssh, http_basic, or bearer_token
remote_ssh_user False gerrit SSH username for remote Gerrit servers (can override per-instance)
remote_ssh_port False 29418 SSH port for remote Gerrit servers (can override per-instance)

Replication Settings

Name Required Default Description
sync_on_startup False true Trigger replication after startup
fetch_every False 60s Interval for pull-replication polling (e.g., 60s, 5m, 0s to disable)
replication_timeout False 600 Timeout for initial replication sync (seconds)
replication_wait_timeout False 600 Max time to wait for replication to match expected repo count (seconds)
require_replication_success False true Fail workflow if replication verification fails
sync_refs False (all) Comma-separated refs to sync (e.g., +refs/heads/*:refs/heads/*)
replication_threads False 4 Number of replication threads per instance
max_projects False 800 Maximum projects to fetch when no filter specified (increase for large instances)

Container Management

Name Required Default Description
exit False true Stop containers when job completes
check_service False true Verify service availability after startup
enable_cache False true Enable Docker layer and dependency caching
cache_key_suffix False Extra suffix for cache keys (for cache isolation)

Advanced Options

Name Required Default Description
debug False false Enable debugging output
use_api_path False false Use source server's URL path (e.g., /r, /infra) for local container
additional_plugins False Comma-separated list of extra plugin URLs to install
gerrit_init_args False Extra arguments for gerrit.war init
skip_plugin_install False false Skip pull-replication plugin installation (testing)

External Tunnel Configuration

These inputs configure Gerrit with public tunnel URLs for remote access.

Name Required Default Description
tunnel_host False External tunnel hostname (e.g., bore.pub, Tailscale IP). Used for canonicalWebUrl and sshd.advertisedAddress
tunnel_ports False JSON mapping slugs to tunnel ports: {"slug": {"http": 12345, "ssh": 54321}}

Note: When using tunnels, start the tunnel before invoking this action. Some tunnel tools (like bore) don't require the local port to be listening—they connect on-demand when traffic arrives. Other tunnels (like Tailscale) provide a stable IP that can be used directly with the local ports.

G2P (Gerrit to Platform) Inputs

These inputs configure gerrit_to_platform inside running Gerrit containers, enabling them to dispatch GitHub Actions workflows in response to Gerrit events (patchset uploads, comment-added, change merges).

Name Required Default Description
g2p_enable False false Enable G2P integration
g2p_github_token False GitHub PAT for workflow dispatch (see G2P Token Scopes)
g2p_github_owner False Target GitHub organisation or user (required when g2p_enable is true)
g2p_remote_name_style False dash How Gerrit project names map to GitHub repos: dash, underscore, slash
g2p_remote_url False Override remote URL pattern (auto-generated from g2p_github_owner)
g2p_remote_auth_group False GitHub Replication Gerrit authGroup for the replication remote (must contain "github")
g2p_comment_mappings False {"recheck":"verify","reverify":"verify","remerge":"merge"} JSON mapping of comment keywords to workflow filters
g2p_hooks False patchset-created,comment-added,change-merged Comma-separated Gerrit hooks to enable
g2p_validation_mode False warn Behaviour when GitHub checks fail: error, warn, or skip
g2p_validate_workflows False true Check that the target org has matching Gerrit workflows
g2p_validate_repos False Comma-separated repos to verify exist in the target org
g2p_ssh_private_key False SSH private key for GitHub push-based replication
g2p_github_known_hosts False SSH known_hosts entries for github.com (auto-fetched if omitted)
g2p_org_setup False verify Org-level audit/provisioning mode: provision, verify, or skip (see G2P Org Setup)
g2p_org_token_map False Base64-encoded JSON mapping orgs to admin PATs (see G2P Org Setup)

Outputs

Name Description Example
container_ids JSON array of running container IDs ["abc123", "def456"]
container_ips JSON array of container IP addresses ["172.17.0.2", "172.17.0.3"]
instances JSON object mapping slug to instance details See Instances Output
gerrit_urls Comma-separated list of Gerrit HTTP URLs http://172.17.0.2:8080,http://172.17.0.3:8080
api_paths JSON object mapping slug to API path details {"onap": {"api_path": "/r", "api_url": "..."}}
ssh_host_keys JSON object mapping slug to SSH host keys {"onap": {"ssh_host_ed25519_key": "..."}}
g2p_enabled Whether G2P integration ran true / false
g2p_config_path Path to generated INI inside container /var/gerrit/.config/.../gerrit_to_platform.ini
g2p_hooks_enabled JSON array of hooks that got wrapper scripts ["patchset-created","comment-added"]
g2p_github_owner Configured GitHub owner modeseven-gerrit-onap
g2p_remote_name_style Configured repository name style dash
g2p_validation_results JSON array of GitHub check results [{"check_name":"token_valid","passed":true}]
g2p_token_provided Whether a GitHub token was supplied true / false
g2p_ssh_public_key Public key for downstream deploy-key setup ssh-ed25519 AAAA...
g2p_org_audit_results JSON array of org-level audit check results [{"check_name":"org_secrets","passed":true}]
g2p_org_provisioned Whether any org items got auto-provisioned true / false

Instances Output

{
  "onap": {
    "cid": "abc123def456",
    "ip": "172.17.0.2",
    "http_port": 8080,
    "ssh_port": 29418,
    "url": "http://172.17.0.2:8080",
    "gerrit_host": "gerrit.onap.org",
    "project": "ONAP"
  }
}

Configuration Format

The gerrit_setup input accepts a JSON array of instance configurations:

[
  {
    "slug": "onap",
    "gerrit": "gerrit.onap.org",
    "project": "",
    "api_path": "/r"
  },
  {
    "slug": "opendaylight",
    "gerrit": "git.opendaylight.org",
    "project": "regex:releng/.*",
    "api_path": "/gerrit",
    "ssh_user": "replication-bot",
    "ssh_port": "29418"
  }
]

Configuration Fields

  • slug (required) - Unique identifier for this instance (used in container naming and credential lookup)
  • gerrit (required) - Hostname of the source Gerrit server to replicate from
  • project (optional) - Project filter for replication:
    • Empty string ("") or omitted: Replicate all projects from the server
    • Literal name: "releng/lftools" - single project
    • Comma-separated: "releng/lftools,ci-management" - multiple projects
    • Regex pattern: "regex:releng/.*" or "regex:^infra/.*" - pattern matching (must use regex: prefix to avoid misclassifying literal names containing special characters like . or [)
  • api_path (optional) - API path prefix if Gerrit is not at the root (e.g., /infra, /r, /gerrit). Auto-detected if not provided.
  • ssh_user (optional) - SSH username for the remote Gerrit server. Overrides global remote_ssh_user. This is the account on the remote server that has your ssh_private_key's public key registered.
  • ssh_port (optional) - SSH port for the remote Gerrit server. Overrides global remote_ssh_port. Typically 29418 for Gerrit.

Project Filter Behavior

When the project field is empty or omitted, the action will:

  1. Query the remote Gerrit server's /projects/ API endpoint
  2. Fetch the list of all available projects
  3. Pre-create empty bare repositories for each project locally
  4. Configure pull-replication to sync all projects on startup

This enables full server mirroring without needing to specify individual project names.

Usage Examples

Example 1: Single Instance with SSH

steps:
  - name: "Start Gerrit mirror"
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: |
        [{
          "project": "ONAP",
          "slug": "onap",
          "gerrit": "gerrit.onap.org"
        }]
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      sync_on_startup: true

Example 2: Multi-Instance Setup

steps:
  - name: "Start multiple Gerrit mirrors"
    id: gerrit
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: ${{ vars.GERRIT_SETUP }}
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      base_http_port: 9000
      base_ssh_port: 30000

  - name: "Access instances"
    run: |
      echo "All URLs: ${{ steps.gerrit.outputs.gerrit_urls }}"
      echo "Instance data:"
      echo '${{ steps.gerrit.outputs.instances }}' | jq '.'

Example 3: HTTP Basic Authentication

steps:
  - name: "Start Gerrit with HTTP auth"
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: ${{ vars.GERRIT_SETUP }}
      auth_type: http_basic
      http_username: ${{ vars.GERRIT_USERNAME }}
      http_password: ${{ secrets.GERRIT_PASSWORD }}

Example 4: Persistent Container

steps:
  - name: "Start Gerrit (persistent)"
    id: gerrit
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: ${{ vars.GERRIT_SETUP }}
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      exit: false  # Keep containers running

  - name: "Run tests"
    run: |
      # Gerrit is available at http://localhost:8080
      curl http://localhost:8080/config/server/version

  - name: "More work with Gerrit"
    run: |
      # Container is still available
      git clone http://localhost:8080/my-project

  - name: "Manual cleanup"
    if: always()
    run: |
      # Cleanup containers manually
      docker kill ${{ fromJson(steps.gerrit.outputs.container_ids)[0] }}

Example 5: Custom Gerrit Version

steps:
  - name: "Start specific Gerrit version"
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: ${{ vars.GERRIT_SETUP }}
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      gerrit_version: "3.9.4-ubuntu24"
      plugin_version: "stable-3.9"

Example 6: Custom Replication Configuration

steps:
  - name: "Selective replication"
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: ${{ vars.GERRIT_SETUP }}
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      sync_refs: "+refs/heads/*:refs/heads/*,+refs/tags/*:refs/tags/*"
      replication_threads: 8
      sync_on_startup: true

Example 7: With Extra Plugins

steps:
  - name: "Start Gerrit with extra plugins"
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: ${{ vars.GERRIT_SETUP }}
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      additional_plugins: |
        https://example.com/plugins/my-plugin.jar,
        https://example.com/plugins/another-plugin.jar

Example 8: Public Access via External Tunnel (Bore)

This example shows how to expose Gerrit publicly using an external tunnel. The example uses bore, but other tunnel methods like Tailscale can also be used. Bore tunnels can be started before Gerrit since they connect on-demand.

steps:
  # Calculate the local ports that Gerrit will use
  - name: "Calculate local ports"
    id: ports
    run: |
      # These match gerrit-action defaults (BASE_PORT + index)
      echo "http_port=8080" >> "$GITHUB_OUTPUT"
      echo "ssh_port=29418" >> "$GITHUB_OUTPUT"

  # Install bore tunnel client
  - name: "Install bore"
    run: |
      BORE_VERSION="0.5.2"
      curl -sSL "https://github.com/ekzhang/bore/releases/download/v${BORE_VERSION}/bore-v${BORE_VERSION}-x86_64-unknown-linux-musl.tar.gz" | tar xz
      sudo mv bore /usr/local/bin/

  # Start tunnels BEFORE Gerrit (bore connects on-demand)
  - name: "Start bore tunnels"
    id: tunnels
    env:
      LOCAL_HTTP: ${{ steps.ports.outputs.http_port }}
      LOCAL_SSH: ${{ steps.ports.outputs.ssh_port }}
      SERVER_SLUG: my-gerrit  # Must match slug in gerrit_setup
    run: |
      # Start tunnels - local ports don't need to be listening yet!
      bore local "$LOCAL_HTTP" --to bore.pub > bore-http.log 2>&1 &
      bore local "$LOCAL_SSH" --to bore.pub > bore-ssh.log 2>&1 &

      # Wait for tunnels to establish
      sleep 10

      # Extract assigned public ports from logs
      HTTP_PORT=$(grep -oP 'listening at bore\.pub:\K\d+' bore-http.log || true)
      SSH_PORT=$(grep -oP 'listening at bore\.pub:\K\d+' bore-ssh.log || true)

      # Validate extracted ports before using them
      if [ -z "$HTTP_PORT" ] || [ -z "$SSH_PORT" ] || \
         ! [[ "$HTTP_PORT" =~ ^[0-9]+$ ]] || ! [[ "$SSH_PORT" =~ ^[0-9]+$ ]]; then
        echo "Error: Failed to extract valid bore tunnel ports from logs." >&2
        echo "HTTP_PORT='$HTTP_PORT', SSH_PORT='$SSH_PORT'" >&2
        echo "Check bore-http.log and bore-ssh.log for connection issues." >&2
        exit 1
      fi

      # Build tunnel_ports JSON for gerrit-action
      TUNNEL_PORTS=$(jq -n \
        --arg slug "$SERVER_SLUG" \
        --argjson http "$HTTP_PORT" \
        --argjson ssh "$SSH_PORT" \
        '{($slug): {http: $http, ssh: $ssh}}')

      echo "tunnel_ports=$TUNNEL_PORTS" >> "$GITHUB_OUTPUT"
      echo "Tunnels ready: bore.pub:$HTTP_PORT (HTTP), bore.pub:$SSH_PORT (SSH)"

  # Start Gerrit with tunnel URLs configured from the start
  - name: "Start Gerrit mirror"
    id: gerrit
    uses: lfreleng-actions/gerrit-server-action@main
    with:
      gerrit_setup: |
        [{
          "project": "my-project",
          "slug": "my-gerrit",
          "gerrit": "gerrit.example.org"
        }]
      ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
      sync_on_startup: true
      # Pass tunnel configuration - Gerrit uses public URLs from the start
      tunnel_host: bore.pub
      tunnel_ports: ${{ steps.tunnels.outputs.tunnel_ports }}

  # Gerrit is now accessible at the public bore.pub URLs!

Real-world example: See the tunnel workflow in test-deploy-gerrit for a complete working implementation with multiple tunnel options.

Using Repository Variables

You can store the Gerrit setup configuration as a repository variable:

Variable Name: GERRIT_SETUP

Variable Value:

[
  {
    "project": "ONAP",
    "slug": "onap",
    "gerrit": "gerrit.onap.org"
  },
  {
    "project": "OpenDaylight",
    "slug": "opendaylight",
    "gerrit": "git.opendaylight.org"
  }
]

In Workflow:

- uses: lfreleng-actions/gerrit-server-action@main
  with:
    gerrit_setup: ${{ vars.GERRIT_SETUP }}
    ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}

Authentication Methods

SSH Authentication (Recommended)

Generate an SSH key pair and add the public key to your Gerrit account:

ssh-keygen -t rsa -b 4096 -f gerrit_key -N ""

Add gerrit_key.pub to your Gerrit account's SSH keys, then add the private key as a GitHub secret:

with:
  auth_type: ssh
  ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}

HTTP Basic Authentication

with:
  auth_type: http_basic
  http_username: ${{ vars.GERRIT_USERNAME }}
  http_password: ${{ secrets.GERRIT_PASSWORD }}

Bearer Token Authentication

with:
  auth_type: bearer_token
  bearer_token: ${{ secrets.GERRIT_BEARER_TOKEN }}

Performance & Caching

This action includes intelligent caching for optimal performance:

Docker Layer Caching

  • Caches Gerrit container images for faster follow-up runs
  • Pre-pulls images to reduce startup time
  • Smart cache keys based on OS, version, and optional suffix

Plugin Download Caching

  • Caches downloaded plugins to avoid repeated downloads
  • Shares cache across workflows in the same repository

Performance Benefits

  • First run: Downloads and caches all necessary components
  • Follow-up runs: 50-80% faster startup using cached layers
  • Reduced bandwidth: Fewer external downloads
  • Lower costs: Reduced GitHub Actions minutes

Disabling Cache

with:
  enable_cache: false

Cache Isolation

Use different cache keys for different purposes:

with:
  cache_key_suffix: '-production'  # Separate cache for production tests

Accessing Gerrit

Once started, Gerrit instances are accessible via:

HTTP Access

# Using localhost (from host)
curl http://localhost:8080/config/server/version

# Using container IP (from other containers)
curl http://172.17.0.2:8080/config/server/version

SSH Access

# Port is configurable (default: 29418)
ssh -p 29418 admin@localhost gerrit version

Git Operations

# Clone a repository
git clone http://localhost:8080/my-project

# With authentication; git prompts for the Gerrit HTTP password
git clone http://admin@localhost:8080/my-project

Monitoring Replication

Check replication status via container logs:

- name: "Check replication"
  run: |
    docker logs ${{ fromJson(steps.gerrit.outputs.container_ids)[0] }} | grep replication

Check replicated repositories:

- name: "List replicated repositories"
  run: |
    docker exec ${{ fromJson(steps.gerrit.outputs.container_ids)[0] }} \
      find /var/gerrit/git -name '*.git' -type d

Troubleshooting

Gerrit Won't Start

Check container logs:

docker logs <container_id>

Common issues:

  • Port conflicts (change base_http_port or base_ssh_port)
  • Invalid Gerrit version tag
  • Insufficient memory

Replication Not Working

  1. Check plugin installation:
docker exec <container_id> ls -la /var/gerrit/plugins/
  1. Verify replication config:
docker exec <container_id> cat /var/gerrit/etc/replication.config
  1. Check authentication:

    • Verify SSH keys are valid
    • Ensure credentials have proper permissions
    • Check network connectivity to source Gerrit
  2. Check replication logs:

docker logs -f <container_id> | grep -i replication

Health Check Failures

Increase timeout if Gerrit takes longer to initialize:

with:
  replication_timeout: 900  # 15 minutes

Or disable health checks for debugging:

with:
  check_service: false

G2P Hooks Not Firing

If a Gerrit change is uploaded, reviewed, and merged but no GitHub Actions workflows run in the target organisation, the container is most likely missing the Gerrit hooks plugin. G2P relies on Gerrit invoking the POSIX-shell wrapper scripts this action installs under /var/gerrit/hooks/ (patchset-created, comment-added, change-merged); each wrapper exec()s the matching gerrit_to_platform console script and tees its stdout / stderr to /var/gerrit/logs/g2p-hooks.log. Without hooks.jar loaded as a plugin Gerrit never invokes the wrappers and those log entries never appear.

Check whether the plugin is present in the running container:

docker exec <container_id> ls -la /var/gerrit/plugins/hooks.jar

If the file is missing, the site was initialised without gerrit init --install-all-plugins. This action always passes --batch --install-all-plugins to gerrit init and the gerrit_init_args input only appends to that argv — those two flags cannot be suppressed by user input. The most likely remaining cause is an older cached image that pre-dates the --install-all-plugins change; re-run the deploy workflow with caching disabled (enable_cache: false) to force a fresh build and repopulate the bundled plugins.

The most direct signal for whether hooks are firing in real time is the dedicated G2P hook log written by the wrappers this action installs. Tail it while uploading a patchset:

docker exec <container_id> tail -F /var/gerrit/logs/g2p-hooks.log

Each invocation produces a structured [g2p-hook][<hook>][pid=…] header followed by [out] / [err] prefixed lines from the underlying gerrit_to_platform console script and a final end pid=… rc=… summary. No entries for the hook you expected means the Gerrit hooks plugin did not invoke the wrapper at all — typically because the plugin was not loaded (see the hooks.jar check above) or the event type is not enabled in g2p_hooks. Entries present but ending with non-zero rc= means the wrapper fired and the underlying console script failed; the [err] lines for the same pid= carry the cause.

For lower-level diagnostics (plugin loader output, JGit errors, SSH session activity) the standard Gerrit logs are still useful:

docker exec <container_id> tail -f /var/gerrit/logs/error_log \
  /var/gerrit/logs/sshd_log

Authentication Issues

Verify SSH key format:

ssh-keygen -l -f gerrit_key

Test SSH connection manually:

ssh -i gerrit_key -p 29418 git@gerrit.example.org

Gerrit to Platform (G2P) Integration

The gerrit_to_platform package enables a Gerrit instance to dispatch GitHub Actions workflows in response to Gerrit events. When g2p_enable is set to true, this action configures the deployed Gerrit container(s) with the files and wrapper scripts that gerrit_to_platform needs:

  • gerrit_to_platform.ini — application config with the GitHub token and comment-keyword-to-workflow-filter mappings
  • replication.config remote — a detection-only remote that tells gerrit_to_platform which platform, organisation, and repository naming convention to use
  • Gerrit hook wrapper scriptspatchset-created, comment-added, and change-merged installed as POSIX-shell wrappers in /var/gerrit/hooks/ that exec() the matching g2p console scripts and tee every invocation (with stdout / stderr) to /var/gerrit/logs/g2p-hooks.log
  • SSH configuration — keypair and known_hosts for github.com

G2P Token Scopes

The g2p_github_token input requires a GitHub Personal Access Token. Either a classic or fine-grained token can be used depending on your access requirements.

Classic Token (cross-organisation access)

Use a classic token when the Gerrit instance dispatches workflows across multiple organisations (fine-grained tokens are scoped to a single owner).

Scope Why it is needed
workflow Trigger workflow dispatch events — the core function of gerrit_to_platform (POST /repos/{owner}/{repo}/actions/workflows/{id}/dispatches). Selecting this scope automatically grants repo (full control of private repositories), which is also needed for listing workflows and repos across organisations.
read:org Read organisation and team membership — required for GET /orgs/{owner} during validation checks.

Note: The workflow scope automatically selects repo (full control of private repositories) in the GitHub UI. If the target organisation contains only public repositories, you can deselect repo and select only the public_repo sub-scope instead.

Classic token permissions

Fine-Grained Token (single organisation)

Use a fine-grained token when dispatching workflows within a single organisation. Set the resource owner to the target organisation and grant these permissions:

  • Actions — Read and write (workflow dispatch)
  • Contents — Read-only (list workflows and repositories)
  • Metadata — Read-only (granted automatically)
  • Organization administration — Read-only (validation checks)

G2P Usage Example

- name: "Start Gerrit with G2P"
  uses: lfreleng-actions/gerrit-action@main
  with:
    gerrit_setup: ${{ vars.GERRIT_SETUP }}
    ssh_private_key: ${{ secrets.GERRIT_SSH_KEY }}
    exit: false
    # G2P integration
    g2p_enable: true
    g2p_github_token: ${{ secrets.G2P_GITHUB_TOKEN }}
    g2p_github_owner: my-gerrit-org
    g2p_remote_name_style: dash
    g2p_validation_mode: warn

G2P Org Setup and Provisioning

The g2p_org_setup input controls whether the action audits — and optionally creates — the org-level GitHub Actions secrets and variables that downstream Gerrit-dispatched workflows need to run:

Mode Behaviour
provision Audit the target org and auto-create absent required secrets/vars
verify Audit and report only (default); make no changes
skip Skip org-level audits entirely

Required org-level items checked (and provisioned in provision mode):

  • Secret: GERRIT_SSH_PRIVKEY
  • Variables: GERRIT_SERVER, GERRIT_SSH_USER, GERRIT_KNOWN_HOSTS, GERRIT_URL

An optional GERRIT_SSH_PRIVKEY_G2G secret is recorded for visibility but never blocks the run and never appears as a warning. It is the SSH private key used by gerrit-to-gerrit (G2G) replication — when one Gerrit instance pushes changes to another Gerrit instance rather than to GitHub. Most LF deployments only mirror Gerrit → GitHub via the G2P flow this action configures, so the secret is irrelevant and its absence is expected. Orgs that do run G2G replication should populate the secret out of band; the audit reports it as found once present. This action does not configure or provision G2G replication itself.

Re-run behaviour (always overwrite)

In provision mode, every required secret and variable is always overwritten with the current run's values, even when the initial audit reports them as already present. This is deliberate: each fresh Gerrit container build produces a new ephemeral SSH key, and tunnel host/port assignments may change between runs. Without overwriting, stale GERRIT_SSH_PRIVKEY / GERRIT_SERVER / GERRIT_KNOWN_HOSTS / GERRIT_URL values on the GitHub side would silently diverge from the live Gerrit instance — workflows would dispatch successfully and then fail at SSH-push or HTTP-API time. The provisioner uses POST for absent items and PATCH for existing ones to keep variable history clean.

If you do not want overwrite-on-every-run semantics, set g2p_org_setup to verify (audit only) or skip.

Token requirements (provision mode)

Provisioning org-level secrets requires a token with elevated scope beyond the default g2p_github_token. Provide it via g2p_org_token_map (recommended) or fall back to g2p_github_token:

  • Classic PATadmin:org scope.
  • Fine-grained PATOrganization secrets: Read & write and Organization variables: Read & write on the target org.

g2p_org_token_map format

The input takes a Base64-encoded JSON array of org/token pairs so a single secret can carry credentials for multiple target orgs.

Pre-encoding JSON (single org):

[
  {"github_org": "modeseven-gerrit-onap",
   "token": "<github-pat>"}
]

Pre-encoding JSON (multiple orgs):

[
  {"github_org": "modeseven-gerrit-onap", "token": "<github-pat-onap>"},
  {"github_org": "modeseven-gerrit-other", "token": "<github-pat-other>"}
]

Encode it (whitespace tolerated; single-line or wrapped both work):

JSON='[{"github_org":"modeseven-gerrit-onap","token":"<github-pat>"}]'
printf '%s' "$JSON" | base64

Example resulting value (single line):

W3siZ2l0aHViX29yZyI6Im1vZGVzZXZlbi1nZXJyaXQtb25hcCIsInRva2VuIjoiPGdpdGh1Yi1wYXQ+In1d

Store the encoded string as a repo or org secret (for example G2P_ORG_TOKENS) and pass it through to the action:

- name: "Start Gerrit with G2P (provision mode)"
  uses: lfreleng-actions/gerrit-action@main
  with:
    gerrit_setup: ${{ vars.GERRIT_SETUP }}
    g2p_enable: true
    g2p_github_owner: modeseven-gerrit-onap
    g2p_github_token: ${{ secrets.G2P_GITHUB_TOKEN }}
    g2p_org_setup: provision
    g2p_org_token_map: ${{ secrets.G2P_ORG_TOKENS }}

When g2p_org_token_map has no entry for the resolved g2p_github_owner, the action falls back to g2p_github_token and emits a warning. See docs/GITHUB-ORG-VERIFY-CONFIG.md for the full design and error-handling matrix.

G2P Validation Modes

Mode Behaviour
error Fail the step if any GitHub-side check fails
warn Log warnings but continue (default)
skip Skip all GitHub-side checks entirely

How Platform Detection Works

gerrit_to_platform reads the Gerrit replication.config to discover which platform hosts the target repositories. The action appends a detection-only remote named github-g2p whose authGroup must contain the string github (case-insensitive) for detection to succeed. The remoteNameStyle setting (dash, underscore, or slash) controls how Gerrit project names (e.g. foo/bar) are converted to GitHub repository names (e.g. foo-bar).

Advanced Configuration

Custom Gerrit Configuration

You can mount custom configuration by modifying the instance before container start. This requires workflow customization beyond the basic action usage.

Database Configuration

By default, Gerrit uses H2 database. For production use, consider:

  • Mounting external database configuration
  • Using PostgreSQL or MySQL backends
  • Persisting data volumes between runs

Network Configuration

Containers use Docker's default bridge network. For custom networking:

  • Use Docker compose for complex setups
  • Configure custom networks in workflow
  • Use service discovery for multi-container scenarios

Security Considerations

  1. Secrets Management: Always use GitHub Secrets for sensitive data
  2. SSH Keys: Use dedicated keys with minimal permissions
  3. Network Isolation: Consider using private runners for sensitive data
  4. Authentication: Prefer SSH over HTTP basic auth
  5. Cleanup: Ensure containers stop to avoid data leakage
  6. G2P Tokens: Use a dedicated PAT with the minimum scopes listed in G2P Token Scopes; never commit tokens to source control

Limitations

  • Max of 10 instances per workflow (port exhaustion)
  • Replication time depends on repository size
  • SSH authentication requires pre-configured keys on source server
  • Container persistence limited to workflow duration
  • H2 database by default (not suitable for heavy production use)

Implementation Details

This action uses:

  • Official Gerrit Docker image: gerritcodereview/gerrit
  • Pull-replication plugin from GerritForge
  • Docker BuildKit for layer caching
  • Bash scripts for orchestration

Contributing

See the main repository for contribution guidelines.

License

Apache-2.0

Support

For issues and questions, please use the GitHub issue tracker.

About

Action to run Gerrit in a Docker container, sync with a production server/node

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages