Project webhook for expiring deploy token notifications

What does this MR do and why?

This MR adds webhook support for expiring project deploy token notifications, allowing external systems to be notified when project deploy tokens are about to expire. This MR depends on !195254 (merged) and should only be merged after that MR is merged.

Project webhook events are protected behind the feature flag project_deploy_token_expiring_notifications and will be rolled out in !555470 along with project deploy token email notifications !193155 (merged)

Webhook Payload Example

{
  "object_kind": "deploy_token",
  "project": {
    "id": 2,
    "name": "Gitlab Test",
    "description": "Voluptates sit architecto quos distinctio.",
    "web_url": "https://gitlab.example.com/gitlab-org/gitlab-test",
    "avatar_url": null,
    "git_ssh_url": "ssh://git@gitlab.example.com:2222/gitlab-org/gitlab-test.git",
    "git_http_url": "https://gitlab.example.com/gitlab-org/gitlab-test.git",
    "namespace": "Gitlab Org",
    "visibility_level": 10,
    "path_with_namespace": "gitlab-org/gitlab-test",
    "default_branch": "master",
    "ci_config_path": null,
    "homepage": "https://gitlab.example.com/gitlab-org/gitlab-test",
    "url": "ssh://git@gitlab.example.com:2222/gitlab-org/gitlab-test.git",
    "ssh_url": "ssh://git@gitlab.example.com:2222/gitlab-org/gitlab-test.git",
    "http_url": "https://gitlab.example.com/gitlab-org/gitlab-test.git"
  },
  "object_attributes": {
    "id": 79,
    "name": "seven-days-6days",
    "expires_at": "2025-08-03 07:57:25 UTC",
    "created_at": "2025-07-28 07:57:25 UTC",
    "revoked": false,
    "deploy_token_type": "project_type"
  },
  "event_name": "expiring_deploy_token"
}

Steps to validate:

  1. Enter the rails console: bundle exec rails c
  2. Enable the feature flag: Feature.enable(:project_deploy_token_expiring_notifications)
  3. Set the project: project = Project.first
  4. Create a project webhook with resource_deploy_token_events: true (refer: https://webhook.site/ to generate unique-url.)
webhook = project.hooks.create!(
  url: '<webhook.site-unique-url>',
  resource_deploy_token_events: true
)
  1. Create deploy tokens with different expiration duration.
Click to expand
token1 = DeployToken.create!(
  name: 'test1',
  username: 'gitlab+deploy-token-7days',
  expires_at: 5.days.from_now.iso8601,
  read_repository: true,
  read_registry: true,
  deploy_token_type: 'project_type'
)

ProjectDeployToken.create!(
  project: project,
  deploy_token: token1
)

token2 = DeployToken.create!(
  name: 'test2',
  username: 'gitlab+deploy-token-30days',
  expires_at: 15.days.from_now.iso8601,
  read_repository: true,
  read_registry: true,
  deploy_token_type: 'project_type'
)

ProjectDeployToken.create!(
  project: project,
  deploy_token: token2
)

token3 = DeployToken.create!(
  name: 'test3',
  username: 'gitlab+deploy-token-60days',
  expires_at: 45.days.from_now.iso8601,
  read_repository: true,
  read_registry: true,
  deploy_token_type: 'project_type'
)

ProjectDeployToken.create!(
  project: project,
  deploy_token: token3
)  
  1. Run the worker and monitor your webhook.site url. It should receive the webhook events with the payload data mentioned above.
worker = DeployTokens::ExpiringWorker.new
worker.perform

Something like this: image

Dependencies

This MR depends on !195254 (merged) and should only be merged after that MR is merged.

Relates to #512193

Edited by Pratibha Gupta

Merge request reports

Loading