code: func fetchAllEmailTemplates(c context.Context, configService configInterface.Interface, projectId string) (map[string]*EmailTemplate, error) {
  	configSet := configInterface.ProjectSet(projectId)
  	files, err := configService.ListFiles(c, configSet)
  	if err != nil {
  		return nil, err
  	}
  
  	ret := map[string]*EmailTemplate{}
  
  	// This runs in a cron job. It is not performance critical, so we don't have
  	// to fetch files concurrently.
  	filenameRegexp := emailTemplateFilenameRegexp(c)
  	for _, f := range files {
  		m := filenameRegexp.FindStringSubmatch(f)
  		if m == nil {
  			// Not a template file or a template of another instance of luci-notify.
  			continue
  		}
  		templateName := m[1]
  
  		logging.Infof(c, "fetching email template from %s:%s", configSet, f)
  		config, err := configService.GetConfig(c, configSet, f, false)
  		if err != nil {
  			return nil, errors.Annotate(err, "failed to fetch %q", f).Err()
  		}
  
  		subject, body, err := splitEmailTemplateFile(config.Content)
  		if err != nil {
  			// Should not happen. luci-config should not have passed this commit in
  			// because luci-notify exposes its validation code to luci-conifg.
  			logging.Warningf(c, "invalid email template content in %q: %s", f, err)
  			continue
  		}
  
  		ret[templateName] = &EmailTemplate{
  			Name:                templateName,
  			SubjectTextTemplate: subject,
  			BodyHTMLTemplate:    body,
  			DefinitionURL:       config.ViewURL,
  		}
  	}
  	return ret, nil
  }
docs: // fetchAllEmailTemplates fetches all valid email templates of the project from
  // a config service. Returned EmailTemplate entities do not have ProjectKey set.