Add Dependency Management Service Account Service

What does this MR do and why?

Introduces DependencyManagement::ProvisionServiceAccountService and Project#dependency_management_service_account to establish a dedicated, project-scoped identity for the dependency management automation system.

Previously, !223849 (merged) used the security policy bot to trigger the updater workload pipeline. This bot is a Guest on the project and is shared with unrelated features (container scanning for registry, vulnerability state transitions, scan result policy audit events). Elevating it to Developer would silently affect all those features.

This MR replaces the bot with a project-scoped service account added as Developer. Developer access is required so the account can commit and push dependency file changes to the update branch server-side. Using a service account also avoids storing any persistent token in CI variables and removes the need to enable ci_push_repository_for_job_token_allowed on the customer's project.

The service account is provisioned once per project and reused across all subsequent scheduler runs. The lookup is exposed via Project#dependency_management_service_account.

Part of: #583114 — Implement the update scheduler worker and service

Stacked on: !223849 (merged) (workload creation service)


Stacked Diffs

How to set up and validate locally

End-to-end validation is not yet possible because the full pipeline flow (scheduler → workload → orchestrator → CreateMergeRequestService) is not wired together. However, the service can be exercised directly in a Rails console:

# In rails console on a GDK instance with a project that has security reports

project = Project.find_by_full_path('your-group/your-project')

result = DependencyManagement::ProvisionServiceAccountService
  .new(project: project)
  .execute

# Should return success and create a service account
puts result.success?
puts result.payload[:user].name        # => "GitLab Dependency Management"
puts result.payload[:user].user_type   # => "service_account"
puts project.member(result.payload[:user]).access_level  # => 30 (Developer)

# Calling it again should be idempotent — no new user created
result2 = DependencyManagement::ProvisionServiceAccountService
  .new(project: project)
  .execute

puts result2.payload[:user].id == result.payload[:user].id  # => true

Note: The service requires service_accounts licensed feature and an Ultimate license.

Queries

user.destroy:

DELETE FROM "users" WHERE "users"."id" = 12345;

Query Plan: https://console.postgres.ai/gitlab/gitlab-production-main/sessions/50345/commands/149450

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Albina Yusupova

Merge request reports

Loading