Skip to content

fix(repository): support per-submodule git credentials - #4136

Open
Arielgordon123 wants to merge 2 commits into
semaphoreui:developfrom
Arielgordon123:fix/submodule-credentials-4134
Open

fix(repository): support per-submodule git credentials#4136
Arielgordon123 wants to merge 2 commits into
semaphoreui:developfrom
Arielgordon123:fix/submodule-credentials-4134

Conversation

@Arielgordon123

@Arielgordon123 Arielgordon123 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem Description

When a repository's .gitmodules references a submodule hosted on a different git server/repo that needs different credentials than the main repository, cloning fails with:

fatal: could not read Username for 'https://gitserver': terminal prompts disabled

Root cause: db.Repository/db.AccessKey only support one credential per repository, and both git client backends (cmd_git and go_git) reuse that single credential for the entire recursive clone. cmd_git runs git clone --recursive / git submodule update --init --recursive, and go_git's CloneOptions.RecurseSubmodules passes the same top-level Auth into every submodule update internally — so switching SEMAPHORE_GIT_CLIENT between the two does not fix this.

Closes #4134

Solution Implemented

  • New RepositorySubmoduleCredential entity: a per-repository list of {host, access_key} mappings, with a new migration (v2.20.2), store methods, and REST endpoints under /api/project/:id/repositories/:repoId/submodule_credentials. Creating/updating a mapping validates the access key belongs to the same project (no cross-project IDOR).
  • Both CmdGitClient and GoGitClient now clone/update each submodule individually (instead of relying on --recursive/RecurseSubmodules), resolving credentials per submodule by exact hostname match. A submodule whose host has no explicit mapping falls back to the repository's own key — today's behavior is unchanged for existing repos.
  • Threaded the new credentials through the full HA remote-runner wire protocol (JobData, access-key hydration, ExecutorProvider), so it also works when tasks are dispatched to remote runners, not just local execution.
  • Added a "Submodule Credentials" section to the Repository edit form (Vue), backed by the new endpoints.

Verification

  • New unit tests (db_lib/git_url_host_test.go, db_lib/submodule_credentials_test.go) reproduce the exact reported failure and prove the fix end-to-end for both git backends against a real authenticated git-over-HTTP server.
  • New API tests (api/projects/repository_submodule_credential_test.go) cover CRUD and the cross-project IDOR rejection.
  • go build ./..., go vet ./..., and go test ./... all pass; frontend builds and lints clean.
  • Ran the compiled server locally end-to-end (login, create project/repo/keys, create/list a submodule credential, verified the IDOR rejection) and confirmed the new UI section renders and calls the new endpoints correctly.

Summary by CodeRabbit

  • New Features
    • Configure separate access credentials for repository submodules by host and optional port.
    • Add, edit, view, and remove submodule credential mappings from repository settings.
    • Clone, browse, and update repositories with authenticated submodules, including nested submodules.
    • Support submodule credentials for repository-backed inventories and tasks.
    • Validate repository ownership, credential access, and host formats before saving mappings.
  • Bug Fixes
    • Prevent tasks from running when required submodule credentials cannot be decrypted.

Submodule clones previously reused only the main repository's single
access key, so submodules hosted on a different server/credentials
than the main repo failed with "could not read Username ... terminal
prompts disabled" (semaphoreui#4134). This affected both git client backends
(cmd_git and go_git).

Adds a per-repository list of host -> access key mappings
(RepositorySubmoduleCredential) that both git clients consult when
cloning/updating each submodule individually, falling back to the
repository's own key when a submodule's host has no explicit mapping.
Wired through the full HA remote-runner path so it also works when
tasks are dispatched to remote runners, plus a new "Submodule
Credentials" section on the Repository edit form.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bc624b4-3109-4951-8437-99d3a187ff19

📥 Commits

Reviewing files that changed from the base of the PR and between 10f97c5 and 7f340c6.

📒 Files selected for processing (19)
  • api/projects/repository.go
  • api/projects/repository_submodule_credential_test.go
  • api/router.go
  • api/runners/runners.go
  • db/Inventory.go
  • db/RepositorySubmoduleCredential.go
  • db_lib/CmdGitClient.go
  • db_lib/GoGitClient.go
  • db_lib/git_url_host.go
  • db_lib/git_url_host_test.go
  • db_lib/submodule_credentials_test.go
  • services/runners/executor_factory.go
  • services/runners/executor_factory_test.go
  • services/runners/job_pool.go
  • services/runners/types.go
  • services/server/inventory_svc.go
  • services/tasks/local_executor_inventory.go
  • web/src/components/RepositorySubmoduleCredentials.vue
  • web/src/lang/en.js
🚧 Files skipped from review as they are similar to previous changes (9)
  • db_lib/git_url_host_test.go
  • web/src/lang/en.js
  • db_lib/git_url_host.go
  • api/router.go
  • db_lib/GoGitClient.go
  • web/src/components/RepositorySubmoduleCredentials.vue
  • db_lib/submodule_credentials_test.go
  • db/RepositorySubmoduleCredential.go
  • db_lib/CmdGitClient.go

📝 Walkthrough

Walkthrough

Adds repository-scoped submodule credential storage and management. Credentials flow through inventory and task execution into command-line Git and go-git clients. The repository form supports credential configuration.

Changes

Submodule credential persistence and API

Layer / File(s) Summary
Credential model and database storage
db/RepositorySubmoduleCredential.go, db/sql/..., db/Migration.go, db/Store.go
Adds the credential model, host validation, migration table, project-scoped access-key checks, and CRUD operations.
HTTP handlers and routes
api/projects/repository_submodule_credential.go, api/router.go, api/projects/repository_submodule_credential_test.go
Adds authenticated list, create, update, and delete endpoints with repository scoping, event logging, and integration tests.

Task credential propagation

Layer / File(s) Summary
Task, inventory, and executor plumbing
services/tasks/*, services/runners/*, services/server/inventory_svc.go, db/Inventory.go
Loads and decrypts repository submodule credentials, hydrates their access keys, and passes them through job data and executor construction.

Credential-aware Git operations

Layer / File(s) Summary
Host resolution and Git clients
db_lib/GitRepository.go, db_lib/git_url_host.go, db_lib/CmdGitClient.go, db_lib/GoGitClient.go
Resolves credentials by normalized host, performs explicit recursive submodule updates, embeds HTTP credentials, and retries submodule updates once.
Git credential tests
db_lib/git_url_host_test.go, db_lib/submodule_credentials_test.go
Tests URL host parsing, credential fallback, and matched or unmatched credentials for both Git clients.

Repository configuration UI

Layer / File(s) Summary
Submodule credential editor
web/src/components/RepositoryForm.vue, web/src/components/RepositorySubmoduleCredentials.vue, web/src/lang/en.js
Adds repository-form integration, credential editing, host validation, API persistence, deletion, loading states, and English labels.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RepositoryForm
  participant RepositorySubmoduleCredentials
  participant API
  participant TaskRunner
  participant LocalExecutor
  participant GitClient
  RepositoryForm->>RepositorySubmoduleCredentials: configure host and access key
  RepositorySubmoduleCredentials->>API: persist repository submodule credential
  TaskRunner->>LocalExecutor: pass decrypted credentials
  LocalExecutor->>GitClient: clone or pull repository
  GitClient->>GitClient: resolve credential by submodule host
  GitClient-->>LocalExecutor: update recursive submodules
Loading

Suggested reviewers: fiftin

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: support for per-submodule Git credentials.
Linked Issues check ✅ Passed The changes address issue #4134 by selecting credentials for separately hosted submodules across both Git clients and execution paths.
Out of Scope Changes check ✅ Passed The database, API, UI, credential propagation, validation, and tests support the stated per-submodule credential objective.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (2)
db_lib/git_url_host_test.go (1)

39-71: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a test for the host-plus-port mismatch.

resolveSubmoduleAccessKey matches host[:port] exactly. A credential configured for gitserver does not match a submodule URL of https://gitserver:8443/.... Add a subtest for that case to lock in the intended behavior.

♻️ Proposed test addition
 	t.Run("falls back to main key when no credentials configured", func(t *testing.T) {
 		got := resolveSubmoduleAccessKey(mainKey, nil, "https://gitserver/group/submodule1")
 		assert.Equal(t, mainKey.ID, got.ID)
 	})
+
+	t.Run("does not match when the url carries a port and the credential does not", func(t *testing.T) {
+		got := resolveSubmoduleAccessKey(mainKey, creds, "https://gitserver:8443/group/submodule1")
+		assert.Equal(t, mainKey.ID, got.ID)
+	})
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@db_lib/git_url_host_test.go` around lines 39 - 71, Add a subtest to
TestResolveSubmoduleAccessKey covering a credential configured for “gitserver”
and a submodule URL using “gitserver:8443”; assert that
resolveSubmoduleAccessKey returns the main key because host-plus-port matching
is exact.
db_lib/submodule_credentials_test.go (1)

172-173: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the cause of the failure, not only that a failure happened.

Both "unmatched" tests accept any error. A broken fixture, a missing git binary, or a wrong branch name also produces an error, so the tests can pass while the reproduction no longer holds. Assert that the error text names the submodule path or an authentication failure.

Also applies to: 229-230

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@db_lib/submodule_credentials_test.go` around lines 172 - 173, Update both
unmatched-credentials tests around gitRepo.Clone() to assert that the returned
error identifies the expected submodule path or indicates an authentication
failure, rather than only checking assert.Error. Preserve the existing failure
assertion while validating the error cause in each affected test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@db_lib/CmdGitClient.go`:
- Around line 298-309: Limit recursive submodule traversal in both
updateSubmodules implementations by threading a depth counter through each
recursive call and returning an error when a fixed maximum depth is reached.
Apply this change in db_lib/CmdGitClient.go at lines 298-309 and
db_lib/GoGitClient.go at lines 183-208, preserving normal submodule updates
below the limit.
- Around line 286-296: Update the submodule URL configuration in the loop around
resolveSubmoduleAccessKey and runInDir so git config always receives a
credential-free URL. Move authentication to a short-lived GIT_ASKPASS helper or
temporary credential file scoped to the init/update operation, ensuring
credentials are not persisted in .git/config and are cleaned up afterward.

In `@db_lib/git_url_host.go`:
- Around line 48-58: Correct the doc comment for resolveSubmoduleAccessKey to
remove the inaccurate claim that credentials are never sent to unauthorized
hosts. Document that unmatched or hostless submodule URLs fall back to mainKey,
while preserving the existing implementation and its recursive-clone behavior.

In `@db_lib/submodule_credentials_test.go`:
- Around line 47-54: Update lastCommitHash to remove the trailing newline from
git rev-parse HEAD output using strings.TrimSpace instead of slicing to 40
bytes. Preserve the full hash for both SHA-1 and SHA-256 repositories and avoid
panics on shorter output; add the required strings import.

In `@db/RepositorySubmoduleCredential.go`:
- Around line 31-38: Update RepositorySubmoduleCredential.Validate to accept
only non-empty hostname-only Host values: reject URLs, paths, ports, queries,
fragments, wildcard values, and any leading or trailing whitespace. Preserve the
existing AccessKeyID validation, and add API tests covering each invalid host
form.

In `@services/tasks/local_executor.go`:
- Around line 1100-1106: Update GetRepositoryPlaybooks and cloneInventoryRepo to
load the repository’s corresponding credential mappings and set
SubmoduleCredentials when constructing GitRepository values. Ensure every
Clone/Pull path receives credentials, while leaving checkoutRepository unchanged
because it does not perform submodule updates.

In `@web/src/components/RepositorySubmoduleCredentials.vue`:
- Around line 150-152: Update RepositorySubmoduleCredentials so errors assigned
by loadCredentials() and deleteCredential() remain visible when editDialog is
closed: render formError in an alert near the credential list or route it
through the existing global notification mechanism, while preserving the current
dialog error display for errors occurring within the editor.
- Around line 22-27: Update the host validation in
RepositorySubmoduleCredentials to require a canonical hostname rather than
merely a non-empty value, rejecting schemes, paths, and other URL-form inputs
such as https://gitserver.example.com/org/repo. Normalize the accepted hostname
consistently with the API before saving so credential mappings match the exact
hostname extracted from submodule Git URLs.

---

Nitpick comments:
In `@db_lib/git_url_host_test.go`:
- Around line 39-71: Add a subtest to TestResolveSubmoduleAccessKey covering a
credential configured for “gitserver” and a submodule URL using
“gitserver:8443”; assert that resolveSubmoduleAccessKey returns the main key
because host-plus-port matching is exact.

In `@db_lib/submodule_credentials_test.go`:
- Around line 172-173: Update both unmatched-credentials tests around
gitRepo.Clone() to assert that the returned error identifies the expected
submodule path or indicates an authentication failure, rather than only checking
assert.Error. Preserve the existing failure assertion while validating the error
cause in each affected test.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53912cbb-d567-4a77-845f-1f584a27dcc5

📥 Commits

Reviewing files that changed from the base of the PR and between d1238fa and 10f97c5.

📒 Files selected for processing (26)
  • api/projects/repository_submodule_credential.go
  • api/projects/repository_submodule_credential_test.go
  • api/router.go
  • api/runners/runners.go
  • db/Migration.go
  • db/RepositorySubmoduleCredential.go
  • db/Store.go
  • db/sql/migrations/v2.20.2.err.sql
  • db/sql/migrations/v2.20.2.sql
  • db/sql/repository_submodule_credential.go
  • db_lib/CmdGitClient.go
  • db_lib/GitRepository.go
  • db_lib/GoGitClient.go
  • db_lib/git_url_host.go
  • db_lib/git_url_host_test.go
  • db_lib/submodule_credentials_test.go
  • services/runners/executor_factory.go
  • services/runners/types.go
  • services/tasks/TaskPool.go
  • services/tasks/TaskRunner.go
  • services/tasks/executor.go
  • services/tasks/local_executor.go
  • services/tasks/local_executor_provider.go
  • web/src/components/RepositoryForm.vue
  • web/src/components/RepositorySubmoduleCredentials.vue
  • web/src/lang/en.js

Comment thread db_lib/CmdGitClient.go Outdated
Comment on lines +286 to +296
for _, sm := range submodules {
key := resolveSubmoduleAccessKey(r.Repository.SSHKey, r.SubmoduleCredentials, sm.URL)

// Override the submodule's local URL before init/update runs, so an
// HTTPS credential is embedded exactly like the main repository's
// GetGitURL(false) does -- never touching the tracked .gitmodules file.
effectiveURL := gitURLWithCredentials(sm.URL, key)
if err := c.runInDir(r, dir, r.Repository.SSHKey,
"config", "submodule."+sm.Name+".url", effectiveURL); err != nil {
return err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how LogCmd renders arguments and whether any redaction exists.
rg -nP -C 8 'func .*LogCmd' --type=go
rg -n -e 'redact' -e 'GIT_ASKPASS' -e 'credential' --type=go -g '!**/*_test.go' | head -n 40

Repository: semaphoreui/semaphore

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(CmdGitClient|.*Logger|.*log.*)\.(go)$' | head -n 80
printf '%s\n' '--- symbols and credential handling ---'
rg -n -C 12 'LogCmd|runInDir|gitURLWithCredentials|updateSubmodules|submodule\..*\.url|effectiveURL|GIT_ASKPASS|credential' --type=go .
printf '%s\n' '--- target file sections ---'
wc -l db_lib/CmdGitClient.go
sed -n '90,155p;270,370p' db_lib/CmdGitClient.go

Repository: semaphoreui/semaphore

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -e
rg -n -C 15 'LogCmd' --glob '*.go' .
rg -n -C 10 'type .*Logger|interface .*Logger|func .*Log|LogCmd' --glob '*.go' .
rg -n -C 8 'func \(.*\) run|func .*runInDir|exec\.Command|CommandContext|CombinedOutput|Stdout|Stderr' db_lib --glob '*.go'

Repository: semaphoreui/semaphore

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -e
rg -n -C 12 'updateSubmodules\(|Clone\(|Pull\(|GetFullPath\(|SubmoduleCredentials|SSHKey' --glob '*.go' .
rg -n -C 15 'func gitURLWithCredentials|func resolveSubmoduleAccessKey|type .*Submodule|SubmoduleCredentials' --glob '*.go' .

Repository: semaphoreui/semaphore

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- repository layout and target references ---'
git ls-files db_lib | head -n 80
printf '%s\n' '--- target implementation ---'
sed -n '1,160p' db_lib/CmdGitClient.go
sed -n '160,380p' db_lib/CmdGitClient.go

Repository: semaphoreui/semaphore

Length of output: 11464


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target implementation ---'
sed -n '1,160p' db_lib/CmdGitClient.go
sed -n '160,380p' db_lib/CmdGitClient.go
printf '%s\n' '--- logger and command execution references ---'
rg -n -C 12 'LogCmd|runInDir|exec\.Command|CommandContext|CombinedOutput' --glob '*.go' .

Repository: semaphoreui/semaphore

Length of output: 47130


🏁 Script executed:

#!/bin/bash
set -e
sed -n '90,155p;270,370p' db_lib/CmdGitClient.go
rg -n -C 12 'LogCmd|runInDir|exec\.Command|CommandContext' --glob '*.go' .
rg -n -C 8 'gitURLWithCredentials|resolveSubmoduleAccessKey|updateSubmodules|GetFullPath' --glob '*.go' .

Repository: semaphoreui/semaphore

Length of output: 50377


Do not persist the password in .git/config.

effectiveURL contains HTTPS credentials, and git config submodule.<name>.url stores them in the shared working copy. Keep the configured URL credential-free and use a short-lived GIT_ASKPASS helper or credential file instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@db_lib/CmdGitClient.go` around lines 286 - 296, Update the submodule URL
configuration in the loop around resolveSubmoduleAccessKey and runInDir so git
config always receives a credential-free URL. Move authentication to a
short-lived GIT_ASKPASS helper or temporary credential file scoped to the
init/update operation, ensuring credentials are not persisted in .git/config and
are cleaned up afterward.

Comment thread db_lib/CmdGitClient.go Outdated
Comment thread db_lib/git_url_host.go
Comment thread db_lib/submodule_credentials_test.go
Comment thread db/RepositorySubmoduleCredential.go
Comment on lines 1100 to +1106
func (t *LocalExecutor) updateRepository() error {
repo := db_lib.GitRepository{
Logger: t.Logger,
TemplateID: t.Template.ID,
Repository: t.Repository,
Client: db_lib.CreateDefaultGitClient(t.KeyInstaller),
Logger: t.Logger,
TemplateID: t.Template.ID,
Repository: t.Repository,
SubmoduleCredentials: t.SubmoduleCredentials,
Client: db_lib.CreateDefaultGitClient(t.KeyInstaller),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all GitRepository composite literals and check for the SubmoduleCredentials field.
ast-grep run --pattern 'GitRepository{$$$}' --lang go .
rg -nP -C 6 '\bGitRepository\s*\{' --type=go -g '!**/*_test.go'

Repository: semaphoreui/semaphore

Length of output: 3865


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- GitRepository constructions ---'
rg -n -P -C 8 '\b(?:db_lib\.)?GitRepository\s*\{' --type go -g '!**/*_test.go' .
printf '%s\n' '--- LocalExecutor methods and GitRepository references ---'
rg -n -P -C 10 '\bGitRepository\b|SubmoduleCredentials|func \(t \*LocalExecutor\)' services/tasks/local_executor.go
printf '%s\n' '--- GitRepository definition and credential use ---'
rg -n -P -C 12 'type GitRepository struct|SubmoduleCredentials|func .*Update|func .*Checkout|func .*Clone' db_lib --type go -g '!**/*_test.go'

Repository: semaphoreui/semaphore

Length of output: 37879


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- checkout and repository operation flow ---'
sed -n '1136,1174p' services/tasks/local_executor.go
sed -n '104,220p' db_lib/GoGitClient.go
sed -n '146,320p' db_lib/CmdGitClient.go
printf '%s\n' '--- inventory executor context and initialization ---'
sed -n '1,105p' services/tasks/local_executor_inventory.go
rg -n -P -C 8 'SubmoduleCredentials|LocalExecutor\{|Inventory\s*:' --type go -g '!**/*_test.go' .
printf '%s\n' '--- relevant changed-file summary ---'
git diff --stat
git diff -- services/tasks/local_executor.go services/tasks/local_executor_inventory.go db_lib/GitRepository.go db_lib/GoGitClient.go db_lib/CmdGitClient.go

Repository: semaphoreui/semaphore

Length of output: 50035


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- API repository operation bodies ---'
sed -n '55,135p' api/projects/repository.go
sed -n '1,120p' services/schedules/SchedulePool.go
printf '%s\n' '--- Static operation classification for every non-test literal ---'
python3 - <<'PY'
from pathlib import Path
import re

files = [p for p in Path('.').rglob('*.go') if '_test.go' not in p.name]
literal = re.compile(r'\b(?:db_lib\.)?GitRepository\s*\{')
func = re.compile(r'func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)\s*\(')

for path in files:
    text = path.read_text(errors='replace')
    for m in literal.finditer(text):
        start = m.start()
        line = text.count('\n', 0, start) + 1
        # Find the enclosing function by the last function declaration.
        fm = list(func.finditer(text, 0, start))
        name = fm[-1].group(1) if fm else '<package scope>'
        # Extract the composite literal with a brace counter.
        brace = text.find('{', m.start())
        depth = 0
        end = brace
        while end < len(text):
            if text[end] == '{':
                depth += 1
            elif text[end] == '}':
                depth -= 1
                if depth == 0:
                    end += 1
                    break
            end += 1
        literal_text = text[m.start():end]
        # Inspect the enclosing function body for direct calls on the conventional repo variable.
        body_start = text.find('{', fm[-1].start()) if fm else 0
        body_end = len(text)
        if fm:
            depth = 0
            for i in range(body_start, len(text)):
                if text[i] == '{':
                    depth += 1
                elif text[i] == '}':
                    depth -= 1
                    if depth == 0:
                        body_end = i
                        break
        body = text[body_start:body_end]
        ops = sorted(set(re.findall(r'\brepo\.(Clone|Pull|Checkout|GetLastRemoteCommitHash|GetRemoteBranches|GetLastCommitHash|GetLastCommitMessage|CanBePulled)\s*\(', body)))
        print(f'{path}:{line} function={name} credentials={"yes" if "SubmoduleCredentials:" in literal_text else "no"} operations={",".join(ops) or "none"}')
PY

Repository: semaphoreui/semaphore

Length of output: 6154


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- RepositoryController dependencies and construction ---'
sed -n '1,65p' api/projects/repository.go
rg -n -P -C 8 'NewRepositoryController|RepositoryController\{' api --type go
printf '%s\n' '--- Git operations that update submodules ---'
rg -n -P -C 5 'func \(c (GoGitClient|CmdGitClient)\) (Clone|Pull|Checkout)|updateSubmodules\(' db_lib/GoGitClient.go db_lib/CmdGitClient.go
printf '%s\n' '--- Repository credential model and inventory repository loading ---'
rg -n -P -C 8 'type RepositorySubmoduleCredential|type Inventory struct|Repository\s+\*?db?\.Repository|Inventory\.Repository|GetRepositorySubmoduleCredentials' db services api --type go -g '!**/*_test.go'

Repository: semaphoreui/semaphore

Length of output: 35306


Propagate credentials to every Clone/Pull path

GetRepositoryPlaybooks and cloneInventoryRepo construct GitRepository values without credentials, although Clone and Pull always update submodules. Load mappings for the corresponding repository and pass them to these constructions. checkoutRepository only checks out or reads commits, so it does not require the field.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/tasks/local_executor.go` around lines 1100 - 1106, Update
GetRepositoryPlaybooks and cloneInventoryRepo to load the repository’s
corresponding credential mappings and set SubmoduleCredentials when constructing
GitRepository values. Ensure every Clone/Pull path receives credentials, while
leaving checkoutRepository unchanged because it does not perform submodule
updates.

Comment thread web/src/components/RepositorySubmoduleCredentials.vue
Comment thread web/src/components/RepositorySubmoduleCredentials.vue
…#4136

- Stop persisting embedded submodule HTTPS credentials in .git/config;
  apply them as a process-local `git -c` override instead.
- Bound submodule recursion depth to prevent unbounded traversal/disk
  fill from a cyclic submodule graph.
- Validate submodule credential Host as a bare hostname (optionally
  with a port), both server-side and in the Vue form.
- Propagate submodule credentials to the playbook-browsing and
  inventory-repository clone/pull paths, including full HA
  remote-runner wiring for the inventory case.
- Fix doc comment, SHA truncation in a test helper, and surface
  load/delete errors outside the credentials dialog.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem: Failed to clone submodule

1 participant