code: func (cat *catalog) validateProjectConfig(ctx *validation.Context, configSet, path string, content []byte) error {
  	var cfg messages.ProjectConfig
  	err := proto.UnmarshalText(string(content), &cfg)
  	if err != nil {
  		ctx.Error(err)
  		return nil
  	}
  
  	// AclSets.
  	ctx.Enter("acl_sets")
  	knownACLSets := acl.ValidateACLSets(ctx, cfg.GetAclSets())
  	ctx.Exit()
  
  	knownIDs := stringset.New(len(cfg.Job) + len(cfg.Trigger))
  	// Jobs.
  	ctx.Enter("job")
  	for _, job := range cfg.Job {
  		id := "(empty)"
  		if job.Id != "" {
  			id = job.Id
  		}
  		ctx.Enter(id)
  		if job.Id != "" && !knownIDs.Add(job.Id) {
  			ctx.Errorf("duplicate id %q", job.Id)
  		}
  		cat.validateJobProto(ctx, job)
  		acl.ValidateTaskACLs(ctx, knownACLSets, job.GetAclSets(), job.GetAcls())
  		ctx.Exit()
  	}
  	ctx.Exit()
  
  	// Triggers.
  	ctx.Enter("trigger")
  	allJobIDs := getAllJobIDs(&cfg)
  	for _, trigger := range cfg.Trigger {
  		id := "(empty)"
  		if trigger.Id != "" {
  			id = trigger.Id
  		}
  		ctx.Enter(id)
  		if trigger.Id != "" && !knownIDs.Add(trigger.Id) {
  			ctx.Errorf("duplicate id %q", trigger.Id)
  		}
  		cat.validateTriggerProto(ctx, trigger, allJobIDs, true)
  		acl.ValidateTaskACLs(ctx, knownACLSets, trigger.GetAclSets(), trigger.GetAcls())
  		ctx.Exit()
  	}
  	ctx.Exit()
  
  	return nil
  }
docs: // validateProjectConfig validates the content of a project config file.
  //
  // Validation errors are returned via validation.Context. Returns an error if
  // the validation itself fails for some reason.