docs: def _lint(dxapp_json_filename, mode):
      """
      Examines the specified dxapp.json file and warns about any
      violations of app guidelines.
  
      Precondition: the dxapp.json file exists and can be parsed.
      """
  
      def _find_readme(dirname):
          for basename in ['README.md', 'Readme.md', 'readme.md']:
              if os.path.exists(os.path.join(dirname, basename)):
                  return os.path.join(dirname, basename)
          return None
  
      app_spec = json.load(open(dxapp_json_filename))
  
      dirname = os.path.basename(os.path.dirname(os.path.abspath(dxapp_json_filename)))
  
      if mode == "app":
          if 'title' not in app_spec:
              logger.warn('app is missing a title, please add one in the "title" field of dxapp.json')
  
          if 'summary' in app_spec:
              if app_spec['summary'].endswith('.'):
                  logger.warn('summary "%s" should be a short phrase not ending in a period' % (app_spec['summary'],))
          else:
              logger.warn('app is missing a summary, please add one in the "summary" field of dxapp.json')
  
          readme_filename = _find_readme(os.path.dirname(dxapp_json_filename))
          if 'description' in app_spec:
              if readme_filename:
                  raise dxpy.app_builder.AppBuilderException('Description was provided both in Readme.md '
                            'and in the "description" field of {file}. Please consolidate content in Readme.md '
                            'and remove the "description" field.'.format(file=dxapp_json_filename))
              if not app_spec['description'].strip().endswith('.'):
                  logger.warn('"description" field should be written in complete sentences and end with a period')
          else:
              if readme_filename is None:
                  logger.warn("app is missing a description, please supply one in README.md")
          if 'categories' in app_spec:
              for category in app_spec['categories']:
                  if category not in APP_CATEGORIES:
                      logger.warn('app has unrecognized category "%s"' % (category,))
                  if category == 'Import':
                      if 'title' in app_spec and not app_spec['title'].endswith('Importer'):
                          logger.warn('title "%s" should end in "Importer"' % (app_spec['title'],))
                  if category == 'Export':
                      if 'title' in app_spec and not app_spec['title'].endswith('Exporter'):
                          logger.warn('title "%s" should end in "Exporter"' % (app_spec['title'],))
  
      if 'name' in app_spec:
          if app_spec['name'] != app_spec['name'].lower():
              logger.warn('name "%s" should be all lowercase' % (app_spec['name'],))
          if dirname != app_spec['name']:
              logger.warn('app name "%s" does not match containing directory "%s"' % (app_spec['name'], dirname))
      else:
          logger.warn('app is missing a name, please add one in the "name" field of dxapp.json')
  
      if 'version' in app_spec:
          if not dxpy.executable_builder.GLOBAL_EXEC_VERSION_RE.match(app_spec['version']):
              logger.warn('"version" %s should be semver compliant (e.g. of the form X.Y.Z)' % (app_spec['version'],))
  
      # Note that identical checks are performed on the server side (and
      # will cause the app build to fail), but the checks here are printed
      # sooner and multiple naming problems can be detected in a single
      # pass.
      if 'inputSpec' in app_spec:
          for i, input_field in enumerate(app_spec['inputSpec']):
              if not re.match("^[a-zA-Z_][0-9a-zA-Z_]*$", input_field['name']):
                  logger.error('input %d has illegal name "%s" (must match ^[a-zA-Z_][0-9a-zA-Z_]*$)' % (i, input_field['name']))
      else:
          logger.warn("dxapp.json contains no input specification (inputSpec). Your applet will not be usable as an " +
                      "app, runnable from the GUI, or composable using workflows.")
      if 'outputSpec' in app_spec:
          for i, output_field in enumerate(app_spec['outputSpec']):
              if not re.match("^[a-zA-Z_][0-9a-zA-Z_]*$", output_field['name']):
                  logger.error('output %d has illegal name "%s" (must match ^[a-zA-Z_][0-9a-zA-Z_]*$)' % (i, output_field['name']))
      else:
          logger.warn("dxapp.json contains no output specification (outputSpec). Your applet will not be usable as an " +
                      "app, runnable from the GUI, or composable using workflows.")