Skip to content

Commit 7a34cc7

Browse files
committed
Add support for customizable base branch
This change removes hardcoded references to 'main' as the base branch and adds support for configuring and auto-detecting the default branch across TUF-on-CI components. ## Features - **Auto-detection**: Automatically detect the default branch from git remote using `git symbolic-ref refs/remotes/<remote>/HEAD`, falling back to "main" if detection fails - **Configuration support**: Allow users to configure base branch in signer settings file via `base-branch` option - **Action support**: Add `base-branch` input parameter to signing-event action for workflow customization ## Implementation ### Shared git utilities - Created `repo/tuf_on_ci/_git_utils.py` with shared `_git()` and `_get_base_branch()` functions - Centralized git command execution with consistent TUF-on-CI user configuration - Deduplicated git utility code across multiple modules ### Repository package changes - Updated `build_repository.py`, `create_signing_events.py`, `online_sign.py`, and `signing_event.py` to use shared git utilities - Replaced hardcoded "main" references with dynamic base branch detection ### Signer package changes - Inlined `_get_base_branch()` in signer to maintain package independence - Updated User class to detect/configure base branch - Modified signing event context manager to use configured base branch - Updated delegate command to respect base branch configuration ### GitHub Actions - Enhanced signing-event action to accept optional `base-branch` input - Implemented auto-detection fallback when input not provided - Fixed template injection vulnerability by using environment variables instead of direct template expansion in shell scripts ## Bug Fixes - Fixed line length lint error in `signer/_common.py` by extracting variable - Fixed ModuleNotFoundError by inlining shared utility to avoid cross-package dependency during signer tests - Added missing subprocess imports in repo package files - Fixed template injection security vulnerability in signing-event action (zizmor high-severity finding) ## Documentation - Updated SIGNER-SETUP.md with base-branch configuration option - Updated create-config-file.sh script with base-branch prompt
1 parent 4355d70 commit 7a34cc7

File tree

11 files changed

+134
-68
lines changed

11 files changed

+134
-68
lines changed

actions/signing-event/action.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ inputs:
2020
token:
2121
description: 'GitHub token'
2222
required: true
23+
base-branch:
24+
description: 'Base branch name (defaults to auto-detected default branch or "main")'
25+
required: false
26+
default: ''
2327

2428
runs:
2529
using: "composite"
@@ -30,6 +34,20 @@ runs:
3034
fetch-depth: 0
3135
persist-credentials: true
3236

37+
- id: detect-base-branch
38+
env:
39+
INPUT_BASE_BRANCH: ${{ inputs.base-branch }}
40+
run: |
41+
if [ -n "$INPUT_BASE_BRANCH" ]; then
42+
echo "base-branch=$INPUT_BASE_BRANCH" >> $GITHUB_OUTPUT
43+
else
44+
# Try to auto-detect default branch
45+
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
46+
echo "base-branch=$BASE_BRANCH" >> $GITHUB_OUTPUT
47+
echo "Auto-detected base branch: $BASE_BRANCH"
48+
fi
49+
shell: bash
50+
3351
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
3452
with:
3553
python-version: "3.14"
@@ -70,6 +88,7 @@ runs:
7088
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
7189
env:
7290
STATUS: ${{ steps.status.outputs.status }}
91+
BASE_BRANCH: ${{ steps.detect-base-branch.outputs.base-branch }}
7392
with:
7493
github-token: ${{ inputs.token }}
7594
script: |
@@ -92,7 +111,7 @@ runs:
92111
body: `Processing signing event ${process.env.GITHUB_REF_NAME}, please wait.`,
93112
draft: true,
94113
head: process.env.GITHUB_REF_NAME,
95-
base: "main",
114+
base: process.env.BASE_BRANCH,
96115
})
97116
pr = response.data.number
98117
console.log(`Created pull request #${pr}`)

docs/SIGNER-SETUP.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,8 @@ pull-remote = origin
6767
# push-remote: If you are allowed to push to the TUF repository, you can use the same value
6868
# as pull-remote. Otherwise use the remote name of your fork
6969
push-remote = origin
70+
71+
# base-branch: The base branch name (optional)
72+
# If not provided, tuf-on-ci-sign will auto-detect the default branch or use 'main'
73+
# base-branch = main
7074
```

repo/tuf_on_ci/_git_utils.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright 2023 Google LLC
2+
3+
"""Shared git utilities for TUF-on-CI"""
4+
5+
import logging
6+
import subprocess
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
def _git(cmd: list[str]) -> subprocess.CompletedProcess:
12+
"""Execute a git command with TUF-on-CI user configuration"""
13+
cmd = [
14+
"git",
15+
"-c",
16+
"user.name=TUF-on-CI",
17+
"-c",
18+
"user.email=41898282+github-actions[bot]@users.noreply.github.com",
19+
*cmd,
20+
]
21+
proc = subprocess.run(cmd, check=True, capture_output=True, text=True)
22+
logger.debug("%s:\n%s", cmd, proc.stdout)
23+
return proc
24+
25+
26+
def _get_base_branch(remote: str = "origin") -> str:
27+
"""Get base branch by auto-detecting from git remote
28+
29+
Args:
30+
remote: The git remote name (defaults to "origin")
31+
32+
Returns:
33+
The name of the base branch
34+
"""
35+
try:
36+
# Try to auto-detect default branch from git remote
37+
result = _git(["symbolic-ref", f"refs/remotes/{remote}/HEAD"])
38+
# Output is like 'refs/remotes/origin/main'
39+
ref = result.stdout.strip()
40+
branch = ref.split("/")[-1]
41+
logger.debug("Auto-detected base branch: %s", branch)
42+
return branch
43+
except (subprocess.CalledProcessError, IndexError):
44+
logger.debug("Failed to auto-detect base branch, defaulting to 'main'")
45+
return "main"

repo/tuf_on_ci/build_repository.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,19 @@
44

55
import logging
66
import os
7-
import subprocess
87
from datetime import UTC, datetime, timedelta
98
from urllib import parse
109

1110
import click
1211
from tuf.api.metadata import Root, Signed, Targets
1312

13+
from tuf_on_ci._git_utils import _git
1414
from tuf_on_ci._repository import CIRepository
1515
from tuf_on_ci._version import __version__
1616

1717
logger = logging.getLogger(__name__)
1818

1919

20-
def _git(cmd: list[str]) -> subprocess.CompletedProcess:
21-
cmd = [
22-
"git",
23-
"-c",
24-
"user.name=tuf-on-ci",
25-
"-c",
26-
"user.email=41898282+github-actions[bot]@users.noreply.github.com",
27-
*cmd,
28-
]
29-
proc = subprocess.run(cmd, check=True, capture_output=True, text=True)
30-
logger.debug("%s:\n%s", cmd, proc.stdout)
31-
return proc
32-
33-
3420
def build_description(repo: CIRepository) -> str:
3521
lines = [
3622
"## TUF Repository state",

repo/tuf_on_ci/create_signing_events.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,12 @@
88

99
import click
1010

11+
from tuf_on_ci._git_utils import _git
1112
from tuf_on_ci._repository import CIRepository
1213

1314
logger = logging.getLogger(__name__)
1415

1516

16-
def _git(cmd: list[str]) -> subprocess.CompletedProcess:
17-
cmd = [
18-
"git",
19-
"-c",
20-
"user.name=TUF-on-CI",
21-
"-c",
22-
"user.email=41898282+github-actions[bot]@users.noreply.github.com",
23-
*cmd,
24-
]
25-
proc = subprocess.run(cmd, check=True, capture_output=True, text=True)
26-
logger.debug("%s:\n%s", cmd, proc.stdout)
27-
return proc
28-
29-
3017
@click.command() # type: ignore[arg-type]
3118
@click.option("-v", "--verbose", count=True, default=0)
3219
@click.option("--push/--no-push", default=False)

repo/tuf_on_ci/online_sign.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,15 @@
33
"""Command line online signing tool for TUF-on-CI"""
44

55
import logging
6-
import subprocess
76

87
import click
98

9+
from tuf_on_ci._git_utils import _git
1010
from tuf_on_ci._repository import CIRepository
1111

1212
logger = logging.getLogger(__name__)
1313

1414

15-
def _git(cmd: list[str]) -> subprocess.CompletedProcess:
16-
cmd = [
17-
"git",
18-
"-c",
19-
"user.name=tuf-on-ci",
20-
"-c",
21-
"user.email=41898282+github-actions[bot]@users.noreply.github.com",
22-
*cmd,
23-
]
24-
proc = subprocess.run(cmd, check=True, text=True)
25-
logger.debug("%s:\n%s", cmd, proc.stdout)
26-
return proc
27-
28-
2915
@click.command() # type: ignore[arg-type]
3016
@click.option("-v", "--verbose", count=True, default=0)
3117
@click.option("--push/--no-push", default=False)

repo/tuf_on_ci/signing_event.py

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,12 @@
1212

1313
import click
1414

15+
from tuf_on_ci._git_utils import _get_base_branch, _git
1516
from tuf_on_ci._repository import CIRepository
1617

1718
logger = logging.getLogger(__name__)
1819

1920

20-
def _git(cmd: list[str]) -> subprocess.CompletedProcess:
21-
cmd = [
22-
"git",
23-
"-c",
24-
"user.name=TUF-on-CI",
25-
"-c",
26-
"user.email=41898282+github-actions[bot]@users.noreply.github.com",
27-
*cmd,
28-
]
29-
proc = subprocess.run(cmd, check=True, capture_output=True, text=True)
30-
logger.debug("%s:\n%s", cmd, proc.stdout)
31-
return proc
32-
33-
3421
def _find_changed_roles(known_good_dir: str, signing_event_dir: str) -> set[str]:
3522
# find the files that have changed or been added
3623
# TODO what about removed roles?
@@ -173,7 +160,8 @@ def update_targets(verbose: int, push: bool) -> None:
173160
sys.exit(1)
174161

175162
# Find the known-good commit
176-
merge_base = _git(["merge-base", "origin/main", "HEAD"]).stdout.strip()
163+
base_branch = _get_base_branch()
164+
merge_base = _git(["merge-base", f"origin/{base_branch}", "HEAD"]).stdout.strip()
177165
if head == merge_base:
178166
click.echo("This signing event contains no changes yet")
179167
sys.exit(1)
@@ -306,7 +294,8 @@ def status(verbose: int) -> None:
306294
sys.exit(1)
307295

308296
# Find the known-good commit
309-
merge_base = _git(["merge-base", "origin/main", "HEAD"]).stdout.strip()
297+
base_branch = _get_base_branch()
298+
merge_base = _git(["merge-base", f"origin/{base_branch}", "HEAD"]).stdout.strip()
310299
if head == merge_base:
311300
click.echo("This signing event contains no changes yet")
312301
sys.exit(1)

signer/create-config-file.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,7 @@ user-name = @${GITHUB_HANDLE}
4848
# Git remotes
4949
pull-remote = origin
5050
push-remote = origin
51+
52+
# Base branch name (optional, defaults to auto-detected default branch or 'main')
53+
# base-branch = main
5154
EOF

signer/tuf_on_ci_sign/_common.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ def signing_event(name: str, config: User) -> Generator[SignerRepository, None,
3939
try:
4040
git(["checkout", f"{config.pull_remote}/{name}"])
4141
except subprocess.CalledProcessError:
42-
click.echo("Remote branch not found: branching off from main")
43-
git_expect(["checkout", f"{config.pull_remote}/main"])
42+
click.echo(f"Remote branch not found: branching off from {config.base_branch}")
43+
git_expect(["checkout", f"{config.pull_remote}/{config.base_branch}"])
4444

4545
try:
4646
# checkout the base of this signing event in another directory
4747
with TemporaryDirectory() as temp_dir:
48-
base_sha = git_expect(["merge-base", f"{config.pull_remote}/main", "HEAD"])
48+
base_ref = f"{config.pull_remote}/{config.base_branch}"
49+
base_sha = git_expect(["merge-base", base_ref, "HEAD"])
4950
event_sha = git_expect(["rev-parse", "HEAD"])
5051
git_expect(["clone", "--quiet", toplevel, temp_dir])
5152
git_expect(["-C", temp_dir, "checkout", "--quiet", base_sha])

signer/tuf_on_ci_sign/_user.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import os
33
import platform
4+
import subprocess
45
import sys
56
from configparser import ConfigParser
67

@@ -25,6 +26,37 @@ def bold(text: str) -> str:
2526
return click.style(text, bold=True)
2627

2728

29+
def _get_base_branch_internal(remote: str = "origin") -> str:
30+
"""Get base branch by auto-detecting from git remote
31+
32+
Args:
33+
remote: The git remote name (defaults to "origin")
34+
35+
Returns:
36+
The name of the base branch
37+
"""
38+
try:
39+
# Try to auto-detect default branch from git remote
40+
cmd = [
41+
"git",
42+
"-c",
43+
"user.name=TUF-on-CI",
44+
"-c",
45+
"user.email=41898282+github-actions[bot]@users.noreply.github.com",
46+
"symbolic-ref",
47+
f"refs/remotes/{remote}/HEAD",
48+
]
49+
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
50+
# Output is like 'refs/remotes/origin/main'
51+
ref = result.stdout.strip()
52+
branch = ref.split("/")[-1]
53+
logger.debug("Auto-detected base branch: %s", branch)
54+
return branch
55+
except (subprocess.CalledProcessError, IndexError):
56+
logger.debug("Failed to auto-detect base branch, defaulting to 'main'")
57+
return "main"
58+
59+
2860
class User:
2961
"""Class that manages user configuration and manages the users signer cache"""
3062

@@ -69,6 +101,20 @@ def __init__(self, path: str):
69101
# signer cache gets populated as they are used the first time
70102
self._signers: dict[str, Signer] = {}
71103

104+
# detect or use configured base branch
105+
self.base_branch = self._get_base_branch()
106+
107+
def _get_base_branch(self) -> str:
108+
"""Get base branch from config or auto-detect"""
109+
try:
110+
# First try to get from config
111+
return self._config["settings"]["base-branch"]
112+
except KeyError:
113+
pass
114+
115+
# Auto-detect using shared utility
116+
return _get_base_branch_internal(remote=self.pull_remote)
117+
72118
def get_signer(self, key: Key) -> Signer:
73119
"""Returns a Signer for the given public key
74120

0 commit comments

Comments
 (0)