At AcmeCI, a pipeline consists of named gates with directed dependencies and additional OR-rules. You must compute the smallest set of gates (and an execution order) needed to reach a given milestone.
Implement plan_devsecops_gates(gates, deps, milestone, rules) -> list[str].
gates: list of unique gate names.deps: list of pairs [a, b] meaning gate a must run before gate b.rules: list of rules [[opt1, opt2, ...], before_gate] meaning at least one option must run before before_gate.The plan must:
milestone.before_gate is included (including milestone if it appears as a before_gate).[] if impossible.gates=[checkout,build,sast,sca,container_scan,deploy_prod], deps=[[checkout,build],[build,container_scan],[container_scan,deploy_prod]], rules=[[[sast,sca],deploy_prod],[[container_scan],deploy_prod]], milestone=deploy_prod → [checkout, build, sca, container_scan, deploy_prod] (pick sca to satisfy the OR-rule).
gates=[a,b,c,deploy], deps=[[a,deploy],[b,deploy],[c,deploy]], rules=[[[b,a],deploy]], milestone=deploy → [a, deploy] (only one option is needed; minimal picks a).
1 <= len(gates) <= 600 <= len(deps) <= 2000 <= len(rules) <= 121 <= len(options) <= 12 for each rule[] if no valid plan exists.gates = ["checkout","build","sast","sca","container_scan","deploy_prod"], deps = [["checkout","build"],["build","container_scan"],["container_scan","deploy_prod"]], milestone = "deploy_prod", rules = [[["sast","sca"],"deploy_prod"], [["container_scan"],"deploy_prod"]]Output["checkout","build","sca","container_scan","deploy_prod"]WhyDependencies force checkout→build→container_scan→deploy_prod. The OR-rule for deploy_prod is satisfied by choosing sca (one gate instead of both).gates = ["a","b","c","deploy"], deps = [["a","deploy"],["b","deploy"],["c","deploy"]], milestone = "deploy", rules = [[["b","a"],"deploy"]]Output["a","deploy"]WhyOnly one of {a,b} must be before deploy. Minimal size is 2 gates; lexicographically smallest valid plan is [a, deploy].1 <= len(gates) <= 600 <= len(deps) <= 2000 <= len(rules) <= 121 <= len(options) <= 12 for each ruleAll gate names are unique stringsmilestone is in gatesReturn [] if no valid plan existsIf multiple minimal plans exist, return the lexicographically smallest plandef plan_devsecops_gates(gates: list[str], deps: list[list[str]], milestone: str, rules: list[list[object]]) -> list[str]: