[fedora-scm-requests] - Migrate scm-request related stuff in toddlers to support the new forge #13370

Open
opened 2026-05-28 09:31:37 +00:00 by humaton · 16 comments
Owner

Create a set of actions that will react on ticket creation/status change try to reuse code from toddlers where possible.

Create a set of actions that will react on ticket [creation/status change](https://forgejo.org/docs/latest/user/actions/reference/#onissues) try to reuse code from toddlers where possible.
humaton added this to the Backlog project 2026-05-28 09:31:37 +00:00
patrikp self-assigned this 2026-06-01 13:15:21 +00:00
Member

Is there a strong reason that this needs to be reimplemented using Forgejo Actions? Storing a distgit admin token in Forgejo Actions seems risky, especially if the runners are shared with other projects on Forge (or access to the repo with the secrets isn't very tightly controlled). And if you're not careful, the Github/Forgejo Actions templating system makes it easy to run arbitrary code when handling user inputs (including issue titles, descriptions, and comments).

Is there a strong reason that this needs to be reimplemented using Forgejo Actions? Storing a distgit admin token in Forgejo Actions seems risky, especially if the runners are shared with other projects on Forge (or access to the repo with the secrets isn't very tightly controlled). And if you're not careful, the Github/Forgejo Actions templating system makes it easy to run arbitrary code when handling user inputs (including issue titles, descriptions, and comments).
Author
Owner

The runners are not really shared; each organization has its own. But it is a valid concern. The only reason for the implementation in actions is to drop toddlers' dependency.

The runners are not really shared; each organization has its own. But it is a valid concern. The only reason for the implementation in actions is to drop toddlers' dependency.
Member

I've been looking into this and I'd be grateful for input from more knowledgeable people about what the best way to do this is. Below is my current understanding and the options we have but I'm very likely missing/misunderstanding one or more things.
CC: @gotmax23 @jnsamyak @kevin

Option A: Forgejo Actions

Re-implement the processing logic as a Forgejo Actions workflow.

Pros:

  • Native to Forgejo, triggered directly by issue events.
  • Self-contained, workflow lives directly in the repo.
  • No external messaging infrastructure needed.
  • Modern approach, familiar to GitHub Actions users.
  • One more step towards reducing the dependency on Toddlers. Although, getting rid of Toddlers is news to me and I'm not aware of any discussion on the topic. Would be thankful if someone could point me to it, if it exists.

Cons:

  • Security concern: Dist-git Admin Token In Forgejo Actions
    • The token allows: creating repos, creating branches, changing ownership, modifying ACLs on any package in dist-git.
    • The risk: Anyone with write access to fedora-scm-requests can modify the workflow file. A malicious or compromised committer could add code that misuses the token.
    • Bottom line: With Actions, the workflow file and the secrets it accesses live in the same trust boundary. With Toddlers, they don't.
  • All of the business logic must be rewritten or extracted.
  • New infrastructure to learn and maintain (it could be argued that this is a pro, depending on the point of view).
  • Testing workflows is harder than testing Python functions.

Option B: Extend Toddlers

Continue using the existing toddler infrastructure, adapted for Forgejo.

Pros:

  • All business logic already exists (~1200 lines of validation, Bugzilla integration, FAS checks, etc.).
  • Secrets (dist-git admin token) remain on infra servers, not accessible to repo committers.
  • Team already knows how to deploy, monitor, and debug Toddlers.
  • Can run both Pagure and Forgejo backends during transition

Cons:

  • Forgejo doesn't publish Pagure-style AMQP messages to the messaging bus, we'd need a new trigger mechanism (such as a webhook).
  • Need to write a forgejo.py utility module parallel to the existing pagure.py.
  • Additional infra dependency (Fedora Messaging bus).
  • Another thing to get rid of in Toddlers later, since apparently that is the plan.

Option C: Hybrid Approach

Use a minimal Forgejo Action that only triggers the toddler, without holding sensitive secrets.

  • The idea is to split the work:
    • Forgejo Actions detects new issues, triggers processing, doesn't hold dist-git token.
    • Toddler: Validates and processes requests, creates repos/branches.
  • We could use a TRIGGER_TOKEN for this integration:
    • On the Forgejo side:
      • Created as a repository secret in fedora-scm-requests settings.
      • Stored encrypted, only exposed to Actions at runtime.
      • Can be rotated without code changes.
    • On the Toddlers infrastructure side:
      • The trigger endpoint validates incoming requests against a list of known tokens.
      • Typically stored in the Toddler's configuration.
      • Infra team manages this.
    • This token ONLY authorizes "please process issue #XXXXX" requests. It cannot read or write to dist-git, modify the repo, access any other Fedora systems, etc.

Pros:

  • Immediate triggering. No polling, no messaging bus needed for Forgejo events.
  • Secrets stay secure, the admin token never touches Forgejo Actions.
  • Minimal action; the workflow file wouldn't need to be long at all, almost nothing can go wrong.
  • Reuses existing logic, all of the validation code in scm_request_processor.py stays as-is.
  • Low risk of repo compromise. Attacker may get TRIGGER_TOKEN, but that can only trigger processing, nothing else.
  • Easy to audit as the workflow would be relatively simple.

Cons:

  • New endpoint to create and maintain.
  • Two systems to deploy, both the Action (in repo) and the Toddler (in infra).
I've been looking into this and I'd be grateful for input from more knowledgeable people about what the best way to do this is. Below is my current understanding and the options we have but I'm very likely missing/misunderstanding one or more things. CC: @gotmax23 @jnsamyak @kevin ### Option A: Forgejo Actions Re-implement the processing logic as a Forgejo Actions workflow. **Pros:** - Native to Forgejo, triggered directly by issue events. - Self-contained, workflow lives directly in the repo. - No external messaging infrastructure needed. - Modern approach, familiar to GitHub Actions users. - One more step towards reducing the dependency on Toddlers. Although, getting rid of Toddlers is news to me and I'm not aware of any discussion on the topic. Would be thankful if someone could point me to it, if it exists. **Cons:** - ❗_Security concern:_ Dist-git Admin Token In Forgejo Actions - The token allows: creating repos, creating branches, changing ownership, modifying ACLs on any package in dist-git. - The risk: Anyone with write access to `fedora-scm-requests` can modify the workflow file. A malicious or compromised committer could add code that misuses the token. - Bottom line: With Actions, the workflow file and the secrets it accesses live in the same trust boundary. With Toddlers, they don't. - All of the business logic must be rewritten or extracted. - New infrastructure to learn and maintain (it could be argued that this is a pro, depending on the point of view). - Testing workflows is harder than testing Python functions. ### Option B: Extend Toddlers Continue using the existing toddler infrastructure, adapted for Forgejo. **Pros:** - All business logic already exists (~1200 lines of validation, Bugzilla integration, FAS checks, etc.). - Secrets (dist-git admin token) remain on infra servers, not accessible to repo committers. - Team already knows how to deploy, monitor, and debug Toddlers. - Can run both Pagure and Forgejo backends during transition **Cons:** - Forgejo doesn't publish Pagure-style AMQP messages to the messaging bus, we'd need a new trigger mechanism (such as a webhook). - Need to write a `forgejo.py` utility module parallel to the existing `pagure.py`. - Additional infra dependency (Fedora Messaging bus). - Another thing to get rid of in Toddlers later, since apparently that is the plan. ### Option C: Hybrid Approach Use a minimal Forgejo Action that only triggers the toddler, without holding sensitive secrets. - The idea is to split the work: - Forgejo Actions detects new issues, triggers processing, doesn't hold dist-git token. - Toddler: Validates and processes requests, creates repos/branches. - We could use a `TRIGGER_TOKEN` for this integration: - On the Forgejo side: - Created as a repository secret in `fedora-scm-requests` settings. - Stored encrypted, only exposed to Actions at runtime. - Can be rotated without code changes. - On the Toddlers infrastructure side: - The trigger endpoint validates incoming requests against a list of known tokens. - Typically stored in the Toddler's configuration. - Infra team manages this. - This token ONLY authorizes "please process issue #XXXXX" requests. It cannot read or write to dist-git, modify the repo, access any other Fedora systems, etc. **Pros:** - Immediate triggering. No polling, no messaging bus needed for Forgejo events. - Secrets stay secure, the admin token never touches Forgejo Actions. - Minimal action; the workflow file wouldn't need to be long at all, almost nothing can go wrong. - Reuses existing logic, all of the validation code in `scm_request_processor.py` stays as-is. - Low risk of repo compromise. Attacker may get `TRIGGER_TOKEN`, but that can only trigger processing, nothing else. - Easy to audit as the workflow would be relatively simple. **Cons:** - New endpoint to create and maintain. - Two systems to deploy, both the Action (in repo) and the Toddler (in infra).
Owner

Forgejo doesn't publish Pagure-style AMQP messages to the messaging bus, we'd need a new trigger mechanism (such as a webhook).

It sure can. It's just not enabled by default (which I think we should change). See: https://docs.fedoraproject.org/en-US/forge-documentation/webhook_to_fedora_messaging/

definitely IMHO releng org should enable this at the org level.

IMHO, I think we should just choose the option B for now and then after that look at perhaps moving to Option A if we want later.

> Forgejo doesn't publish Pagure-style AMQP messages to the messaging bus, we'd need a new trigger mechanism (such as a webhook). It sure can. It's just not enabled by default (which I think we should change). See: https://docs.fedoraproject.org/en-US/forge-documentation/webhook_to_fedora_messaging/ definitely IMHO releng org should enable this at the org level. IMHO, I think we should just choose the option B for now and then after that look at perhaps moving to Option A if we want later.
Member

The risk: Anyone with write access to fedora-scm-requests can modify the workflow file. A malicious or compromised committer could add code that misuses the token.

Besides that, if the Forgejo Action is not configured properly (it's relatively easy to misconfigure due to the way the templating system in GHA/Forgejo Actions workflow configuration files work), an untrusted user who creates a ticket in the repository could inject code to exfiltrate the distgit API token or do other malicious things using the repo-scoped Forgejo API token that is also made available to steps in the workflow.

Also, I'd say provisioning a runner container and making a separate workflow run for each SCM request ticket is probably computationally more expensive than using the toddlers setup.

> The risk: Anyone with write access to fedora-scm-requests can modify the workflow file. A malicious or compromised committer could add code that misuses the token. Besides that, if the Forgejo Action is not configured properly (it's relatively easy to misconfigure due to the way the templating system in GHA/Forgejo Actions workflow configuration files work), an untrusted user who creates a ticket in the repository could inject code to exfiltrate the distgit API token or do other malicious things using the repo-scoped Forgejo API token that is also made available to steps in the workflow. Also, I'd say provisioning a runner container and making a separate workflow run for each SCM request ticket is probably computationally more expensive than using the toddlers setup.
Member

I recommend looking at https://docs.zizmor.sh/ to help with securing Actions workflow. Since the Forgejo actions are very close to GitHub I think it should work.

I recommend looking at https://docs.zizmor.sh/ to help with securing Actions workflow. Since the Forgejo actions are very close to GitHub I think it should work.
Author
Owner

@patrikp wrote in #13370 (comment):

Option B: Extend Toddlers

Continue using the existing toddler infrastructure, adapted for Forgejo.

Pros:

  • All business logic already exists (~1200 lines of validation, Bugzilla integration, FAS checks, etc.).
  • Secrets (dist-git admin token) remain on infra servers, not accessible to repo committers.
  • Team already knows how to deploy, monitor, and debug Toddlers.
  • Can run both Pagure and Forgejo backends during transition

Cons:

  • Forgejo doesn't publish Pagure-style AMQP messages to the messaging bus, we'd need a new trigger mechanism (such as a webhook).
  • Need to write a forgejo.py utility module parallel to the existing pagure.py.
  • Additional infra dependency (Fedora Messaging bus).
  • Another thing to get rid of in Toddlers later, since apparently that is the plan.

If we take this approach, and update the existing toddlers code base to point to new queue topics.
If we stick with similar payload, how much work are we talking about?

@patrikp wrote in https://forge.fedoraproject.org/releng/tickets/issues/13370#issuecomment-868461: > ### [](#option-b-extend-toddlers)Option B: Extend Toddlers > Continue using the existing toddler infrastructure, adapted for Forgejo. > > **Pros:** > > * All business logic already exists (~1200 lines of validation, Bugzilla integration, FAS checks, etc.). > * Secrets (dist-git admin token) remain on infra servers, not accessible to repo committers. > * Team already knows how to deploy, monitor, and debug Toddlers. > * Can run both Pagure and Forgejo backends during transition > > **Cons:** > > * Forgejo doesn't publish Pagure-style AMQP messages to the messaging bus, we'd need a new trigger mechanism (such as a webhook). > * Need to write a `forgejo.py` utility module parallel to the existing `pagure.py`. > * Additional infra dependency (Fedora Messaging bus). > * Another thing to get rid of in Toddlers later, since apparently that is the plan. > If we take this approach, and update the existing toddlers code base to point to new queue topics. If we stick with similar payload, how much work are we talking about?
Owner

As per the request, I'll try to work on this sprint.

As per the request, I'll try to work on this sprint.
Owner

@humaton good question, with your investigation in mind, @patrikp could you help with that?

@humaton good question, with your investigation in mind, @patrikp could you help with that?
Member

Here is the investigation I was able to do. It's by no means complete but should hopefully provide some more context about what changes are needed.

Toddlers repository URL:
https://forge.fedoraproject.org/apps/toddlers

The fedora-scm-requests tracker currently lives on Pagure and there are two toddlers plugins that process tickets from it automatically:

  • scm_request_processor.py — handles new repo and branch requests
  • unretire_packages.py — handles package unretirement (I will ignore this for the rest of this comment but it'd be nice if @amedvede was aware that changes will be needed there too)

The scm_request_processor.py plugin listens for Pagure messages via fedora-messaging, fetches ticket data from Pagure, validates requests, does the actual work, then closes the ticket with a comment. It already maintains two separate connections (pagure_io for tickets and dist_git for src.fp.o) so the architecture supports multiple connections and dist-git isn't moving (yet), so we just need to swap the ticket-side connection to Forgejo.

Here's roughly what will need to change:

New code:

  • toddlers/utils/forgejo.py
    • New API client that mirrors pagure.py structure
    • Methods needed: get_issue(), close_issue(), add_comment_to_issue(), get_project_contributors()
    • Authentication has to be done via API token
  • toddlers/exceptions/forgejo_error.py
    • Exception class for Forgejo API errors
    • Mirrors pagure_error.py
  • tests/utils/test_forgejo.py (at the very least a "nice to have")
    • Unit tests for the new util

Existing files to modify:

  • toddlers/plugins/scm_request_processor.py
    • This is currently tightly coupled with Pagure, not sure about exact changes that are needed
  • toddlers/utils/__init__.py
    • Add forgejo to imports
  • toddlers/exceptions/__init__.py
    • Add ForgejoError to imports
  • tests/plugins/test_scm_request_processor.py
    • Update mocks to use Forgejo client instead of Pagure for ticket operations
    • Update test message fixtures to match the Forgejo message format

Toddlers config file:

This is as far as I've been able to get so far, hopefully it is at least somewhat helpful in terms of the "bigger picture".

Now, as for "how much work" we're talking about, I am not entirely sure. In what metric? If it's points, I'd say either 3 or 5.

Here is the investigation I was able to do. It's by no means complete but should hopefully provide some more context about what changes are needed. **Toddlers repository URL:** https://forge.fedoraproject.org/apps/toddlers The `fedora-scm-requests` tracker currently lives on Pagure and there are two toddlers plugins that process tickets from it automatically: * `scm_request_processor.py` — handles new repo and branch requests * `unretire_packages.py` — handles package unretirement (I will ignore this for the rest of this comment but it'd be nice if @amedvede was aware that changes will be needed there too) The `scm_request_processor.py` plugin listens for Pagure messages via fedora-messaging, fetches ticket data from Pagure, validates requests, does the actual work, then closes the ticket with a comment. It already maintains two separate connections (`pagure_io` for tickets and `dist_git` for src.fp.o) so the architecture supports multiple connections and dist-git isn't moving (yet), so we just need to swap the ticket-side connection to Forgejo. Here's roughly what will need to change: **New code:** * `toddlers/utils/forgejo.py` * New API client that mirrors `pagure.py` structure * Methods needed: `get_issue()`, `close_issue()`, `add_comment_to_issue()`, `get_project_contributors()` * Authentication has to be done via API token * `toddlers/exceptions/forgejo_error.py` * Exception class for Forgejo API errors * Mirrors `pagure_error.py` * `tests/utils/test_forgejo.py` (at the very least a "nice to have") * Unit tests for the new util **Existing files to modify:** * `toddlers/plugins/scm_request_processor.py` * This is currently tightly coupled with Pagure, not sure about exact changes that are needed * `toddlers/utils/__init__.py` * Add `forgejo` to imports * `toddlers/exceptions/__init__.py` * Add `ForgejoError` to imports * `tests/plugins/test_scm_request_processor.py` * Update mocks to use Forgejo client instead of Pagure for ticket operations * Update test message fixtures to match the Forgejo message format **Toddlers config file:** * New config keys and secrets will have to be added somewhere, but I don't understand exactly how it's deployed * Possibly here?: https://forge.fedoraproject.org/infra/ansible/src/branch/main/roles/openshift-apps/poddlers/templates/fedora-messaging.toml This is as far as I've been able to get so far, hopefully it is at least somewhat helpful in terms of the "bigger picture". Now, as for "how much work" we're talking about, I am not entirely sure. In what metric? If it's points, I'd say either 3 or 5.
jnsamyak changed title from [fedora-scm-requests] - Create forgejo workflow for processing tickets to [fedora-scm-requests] - Migrate scm-request related stuff in toddlers to support the new forge 2026-07-14 08:18:48 +00:00
Owner

I'm not sure how to use that investigation into actionable items. I did give the code base a look, and I think in order to move scm-request using toddlers, we should follow wherw toddlerswill listens via webhook→fedora-messaging talks Forgejo for issue chattere and keeps the existing Pagure helper only for creating repos/branches.

fedpkg request-repo/branch
        │
Forgejo issues (releng/fedora-scm-requests)
        │  webhook (org-level)
webhook-to-fedora-messaging  →  fedora-messaging bus
        │
toddlers scm_request_processor
        │
        ├── normalize W2FM payload → internal issue shape
        ├── toddlers.utils.forgejo  → comment / close / labels  (Forge)
        └── toddlers.utils.pagure   → repo/branch/monitoring     (dist-git only)

I'm going to proceed the work with this, unless there is a strong No.

I'm not sure how to use that investigation into actionable items. I did give the code base a look, and I think in order to move scm-request using toddlers, we should follow wherw toddlerswill listens via webhook→fedora-messaging talks Forgejo for issue chattere and keeps the existing Pagure helper only for creating repos/branches. ``` fedpkg request-repo/branch │ Forgejo issues (releng/fedora-scm-requests) │ webhook (org-level) webhook-to-fedora-messaging → fedora-messaging bus │ toddlers scm_request_processor │ ├── normalize W2FM payload → internal issue shape ├── toddlers.utils.forgejo → comment / close / labels (Forge) └── toddlers.utils.pagure → repo/branch/monitoring (dist-git only) ``` I'm going to proceed the work with this, unless there is a strong No.
Member

The toddlers change looks good. That would just mean to replace pagure calls with forgejo ones for pagure.io interactions, which shouldn't be that difficult.

I'm giving +1 to this solution and having the toddlers.utils.forgejo in place will help with the dist-git move to forgejo as well in the future.

The toddlers change looks good. That would just mean to replace pagure calls with forgejo ones for pagure.io interactions, which shouldn't be that difficult. I'm giving +1 to this solution and having the `toddlers.utils.forgejo` in place will help with the dist-git move to forgejo as well in the future.
Author
Owner

@jnsamyak wrote in #13370 (comment):

I'm going to proceed the work with this, unless there is a strong No.

Sounds about right, we should enable the webhook as a first thing to see any difference in the data that forge is sending. I suspect that some of the logic in toddlers.utils.pagure could be simplified.

@jnsamyak wrote in https://forge.fedoraproject.org/releng/tickets/issues/13370#issuecomment-1062204: > I'm going to proceed the work with this, unless there is a strong No. Sounds about right, we should enable the webhook as a first thing to see any difference in the data that forge is sending. I suspect that some of the logic in toddlers.utils.pagure could be simplified.
Owner

Okay, so for an unretirement toddler, I think the same trick we used for scm_request. Stop listening to Pagure issue messages; listen to Forge forgejo.issues instead. Talk to Forgejo for comments/closes/labels; leave dist-git, koji, bodhi, and bugzilla alone.

Concrete bits:

  • Wire unretire_packages to the existing forgejo util + W2FM helper
  • Swap ticket config to forgejo_* (same repo/labels as scm_request)
  • Keep the “title must start with unretire” filter
  • Add title to the shared issue normalizer (unretire needs it)

I think I can leave the actual unretired working bits as they are (clone, revert, retire commit, koji/bodhi/bugzilla). I'll make these changes in the same PR.

CC: @amedvede @humaton @zlopez^

Okay, so for an unretirement toddler, I think the same trick we used for scm_request. Stop listening to Pagure issue messages; listen to Forge forgejo.issues instead. Talk to Forgejo for comments/closes/labels; leave dist-git, koji, bodhi, and bugzilla alone. Concrete bits: - Wire unretire_packages to the existing forgejo util + W2FM helper - Swap ticket config to forgejo_* (same repo/labels as scm_request) - Keep the “title must start with unretire” filter - Add title to the shared issue normalizer (unretire needs it) I think I can leave the actual unretired working bits as they are (clone, revert, retire commit, koji/bodhi/bugzilla). I'll make these changes in the same PR. CC: @amedvede @humaton @zlopez^
Owner

Sounds like a good plan @jnsamyak. I'll take a look at the changes you made (I believe its this PR apps/toddlers#429). So the fedora-messaging message structure will look the same, and we only need to create a Forgejo webhook to create a message based on a repo action?

Sounds like a good plan @jnsamyak. I'll take a look at the changes you made (I believe its this PR https://forge.fedoraproject.org/apps/toddlers/pulls/429). So the fedora-messaging message structure will look the same, and we only need to create a Forgejo webhook to create a message based on a repo action?
Owner

That's the plan

That's the plan
Sign in to join this conversation.
No milestone
No assignees
8 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
releng/tickets#13370
No description provided.