@@ -343,7 +343,7 @@ class CiYaml {
343343 /// 5. [pb.Target] should not depend on self
344344 /// 6. [pb.Target] cannot have more than 1 dependency
345345 /// 7. [pb.Target] should depend on target that already exist in depedency graph, and already recorded in map [targetGraph]
346- /// 8. [pb.Target] has an empty runIf or the runIf includes `.ci.yaml` and `DEPS if on the engine repo .
346+ /// 8. [pb.Target] has an empty runIf or the runIf includes `.ci.yaml` `DEPS` , and `engine/**` if validating the framework .
347347 void _validate (pb.SchedulerConfig schedulerConfig, String branch, {pb.SchedulerConfig ? totSchedulerConfig}) {
348348 if (schedulerConfig.targets.isEmpty) {
349349 throw const FormatException ('Scheduler config must have at least 1 target' );
@@ -394,26 +394,18 @@ class CiYaml {
394394
395395 // Verify runIf includes foundational files.
396396 if (target.runIf.isNotEmpty) {
397- if (isFusion && type == CiType .fusionEngine) {
398- // Look in different locations if fusion && engine ci.yaml
399- if (! target.runIf.contains ('engine/src/flutter/.ci.yaml' )) {
400- exceptions.add (
401- 'ERROR: ${target .name } is missing `engine/src/flutter/.ci.yaml` in runIf' ,
402- );
403- }
404- if (! target.runIf.contains ('DEPS' )) {
405- exceptions.add ('ERROR: ${target .name } is missing `DEPS` in runIf' );
406- }
397+ if (isFusion) {
398+ _verifyTargetFusionRepo (target, exceptions);
407399 } else {
408- // not fusion or not engine in fusion.
409- if (! target.runIf.contains ('.ci.yaml' )) {
410- exceptions.add ('ERROR: ${target .name } is missing `.ci.yaml` in runIf' );
411- }
412- if (slug == Config .engineSlug && ! target.runIf.contains ('DEPS' )) {
413- exceptions.add ('ERROR: ${target .name } is missing `DEPS` in runIf' );
414- }
400+ _verifyTargetClassicRepo (target, exceptions);
415401 }
416402 }
403+
404+ // Verify runIfNot is never used, as it is a very confusing feature.
405+ // https://github.com/flutter/flutter/issues/147182
406+ if (target.runIfNot.isNotEmpty) {
407+ exceptions.add ('ERROR: ${target .name }: Do not use runIfNot.' );
408+ }
417409 }
418410
419411 /// Check the dependencies for the current target if it is viable and to
@@ -429,6 +421,61 @@ class CiYaml {
429421 _checkExceptions (exceptions);
430422 }
431423
424+ void _verifyTargetClassicRepo (pb.Target target, List <String > exceptions) {
425+ // These serve mostly as documentation.
426+ assert (! isFusion, 'Expected to be called in a non-monorepo' );
427+ assert (target.runIf.isNotEmpty, 'Expected to be called when non-empty' );
428+
429+ // 1. Every target must depend on .ci.yaml at the root of the repo.
430+ // TODO(matanlurey): Can this be inferred instead in the .ci.yaml parser?
431+ // See https://github.com/flutter/flutter/issues/160874.
432+ if (! target.runIf.contains ('.ci.yaml' )) {
433+ exceptions.add ('ERROR: ${target .name } is missing `.ci.yaml` in runIf' );
434+ }
435+
436+ // 2. The engine repo must additionally depend on DEPS.
437+ // TODO(matanlurey): Can this be inferred instead in the .ci.yaml parser?
438+ // See https://github.com/flutter/flutter/issues/160874.
439+ if (slug == Config .engineSlug && ! target.runIf.contains ('DEPS' )) {
440+ exceptions.add ('ERROR: ${target .name } is missing `DEPS` in runIf' );
441+ }
442+ }
443+
444+ void _verifyTargetFusionRepo (pb.Target target, List <String > exceptions) {
445+ // These serve mostly as documentation.
446+ assert (isFusion, 'Expected to be called in a fusion monorepo' );
447+ assert (target.runIf.isNotEmpty, 'Expected to be called when non-empty' );
448+ assert (slug == Config .flutterSlug, 'Expected to be in the combined repo' );
449+
450+ // 1. Every target must depend on .ci.yaml.
451+ // The path depends on whether the framework or engine are being validated;
452+ // while both belong in the same (mono)repo, they have separate .ci.yaml
453+ // files located in different paths.
454+ //
455+ // TODO(matanlurey): Can this be inferred instead in the .ci.yaml parser?
456+ // See https://github.com/flutter/flutter/issues/160874.
457+ final ciYamlPath = switch (type) {
458+ CiType .fusionEngine => 'engine/src/flutter/.ci.yaml' ,
459+ _ => '.ci.yaml' ,
460+ };
461+ if (! target.runIf.contains (ciYamlPath)) {
462+ exceptions.add ('ERROR: ${target .name } is missing `$ciYamlPath ` in runIf' );
463+ }
464+
465+ // 2. Every target must depend on DEPS.
466+ // TODO(matanlurey): Can this be inferred instead in the .ci.yaml parser?
467+ // See https://github.com/flutter/flutter/issues/160874.
468+ if (! target.runIf.contains ('DEPS' )) {
469+ exceptions.add ('ERROR: ${target .name } is missing `DEPS` in runIf' );
470+ }
471+
472+ // 3. The framework tree must depend on engine/**.
473+ final isFrameworkCiYaml = type != CiType .fusionEngine;
474+ if (isFrameworkCiYaml && ! target.runIf.contains ('engine/**' )) {
475+ exceptions.add ('ERROR: ${target .name } is missing `engine/**` in runIf' );
476+ }
477+ }
478+
432479 void _checkExceptions (List <String > exceptions) {
433480 if (exceptions.isNotEmpty) {
434481 final String fullException = exceptions.reduce ((String exception, _) => '$exception \n ' );
0 commit comments