Add per-project scheduling for scan execution policy
What does this MR do and why?
Add per-project scheduling for scan execution policy
Add Security::ScanExecutionPolicies::ScheduleWorker, a cron worker that runs every minute and enqueues CreatePipelineWorker for each project schedule whose next_run_at has passed.
This mirrors the PEP pattern from PipelineExecutionPolicies::ScheduleWorker.
The worker is gated behind the scan_execution_policy_per_project_scheduling feature flag, which should be enabled after all project schedule rows have been self-healed by the creation flag from the previous commit.
When the scheduling flag is enabled, RuleScheduleService stops enqueuing CreatePipelineWorker directly (the ScheduleWorker handles it), eliminating the need for perform_in with random delays.
References
- Persist per-project schedules for scan executio... (!227096 - merged)
-
👉 Add per-project scheduling for scan execution p... (!227101) - Draft: Sync schedules when policy changes (!227102)
Database
There are no records yet on production, this table is new.
SELECT "security_scan_execution_project_schedules".*
FROM "security_scan_execution_project_schedules"
WHERE "security_scan_execution_project_schedules"."next_run_at" < '2026-03-18 12:09:14.529049'
ORDER BY "security_scan_execution_project_schedules"."next_run_at" ASC,
"security_scan_execution_project_schedules"."id" ASC
Plan (from local): https://explain.depesz.com/s/TCqD
How to set up and validate locally
- First, enable the feature flag for SEP project schedule creation
Feature.enable(:scan_execution_policy_project_schedule_creation) - Create a project with a scan execution policy that runs on schedule
scan_execution_policy: - name: Secrets on schedule description: '' enabled: true actions: - scan: secret_detection template: latest variables: SECURE_ENABLE_LOCAL_CONFIGURATION: 'false' rules: - type: schedule cadence: 0 0 1 * * branch_type: default timezone: Europe/Zurich skip_ci: allowed: true - Trigger the schedule pipeline manually via rails console
schedule = Security::OrchestrationPolicyRuleSchedule.last schedule.update_columns(next_run_at: 1.day.ago) && Security::OrchestrationPolicyRuleScheduleWorker.new.perform - Verify that project schedule record was created with
schedule.project_schedules - Now, enable the feature flag for the scheduling based on SEP project schedules
Feature.enable(:scan_execution_policy_per_project_scheduling) - Update the project schedule created before to have
next_run_atin the pastps = Security::ScanExecutionProjectSchedule.last ps.update_columns(next_run_at: 1.day.ago) - Wait a minute and verify that a pipeline is created (same as the one before which was scheduled by
OrchestrationPolicyRuleScheduleWorker) - Now, with both feature flags enabled, verify that
OrchestrationPolicyRuleScheduleWorkerdoesn't trigger the pipeline for this projectschedule = Security::OrchestrationPolicyRuleSchedule.last schedule.update_columns(next_run_at: 1.day.ago) && Security::OrchestrationPolicyRuleScheduleWorker.new.perform
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.
Related to #592731