docs: def mod_repo(repo, saltenv='base', **kwargs):
      '''
      Modify one or more values for a repo.  If the repo does not exist, it will
      be created, so long as the definition is well formed.  For Ubuntu the
      ``ppa:<project>/repo`` format is acceptable. ``ppa:`` format can only be
      used to create a new repository.
  
      The following options are available to modify a repo definition:
  
      architectures
          A comma-separated list of supported architectures, e.g. ``amd64`` If
          this option is not set, all architectures (configured in the system)
          will be used.
  
      comps
          A comma separated list of components for the repo, e.g. ``main``
  
      file
          A file name to be used
  
      keyserver
          Keyserver to get gpg key from
  
      keyid
          Key ID or a list of key IDs to load with the ``keyserver`` argument
  
      key_url
          URL to a GPG key to add to the APT GPG keyring
  
      key_text
          GPG key in string form to add to the APT GPG keyring
  
          .. versionadded:: 2018.3.0
  
      consolidate : False
          If ``True``, will attempt to de-duplicate and consolidate sources
  
      comments
          Sometimes you want to supply additional information, but not as
          enabled configuration. All comments provided here will be joined
          into a single string and appended to the repo configuration with a
          comment marker (#) before it.
  
          .. versionadded:: 2015.8.9
  
      .. note::
          Due to the way keys are stored for APT, there is a known issue where
          the key won't be updated unless another change is made at the same
          time. Keys should be properly added on initial configuration.
  
      CLI Examples:
  
      .. code-block:: bash
  
          salt '*' pkg.mod_repo 'myrepo definition' uri=http://new/uri
          salt '*' pkg.mod_repo 'myrepo definition' comps=main,universe
      '''
      if 'refresh_db' in kwargs:
          salt.utils.versions.warn_until(
              'Neon',
              'The \'refresh_db\' argument to \'pkg.mod_repo\' has been '
              'renamed to \'refresh\'. Support for using \'refresh_db\' will be '
              'removed in the Neon release of Salt.'
          )
          refresh = kwargs['refresh_db']
      else:
          refresh = kwargs.get('refresh', True)
  
      _check_apt()
      # to ensure no one sets some key values that _shouldn't_ be changed on the
      # object itself, this is just a white-list of "ok" to set properties
      if repo.startswith('ppa:'):
          if __grains__['os'] in ('Ubuntu', 'Mint', 'neon'):
              # secure PPAs cannot be supported as of the time of this code
              # implementation via apt-add-repository.  The code path for
              # secure PPAs should be the same as urllib method
              if salt.utils.path.which('apt-add-repository') \
                      and 'ppa_auth' not in kwargs:
                  repo_info = get_repo(repo)
                  if repo_info:
                      return {repo: repo_info}
                  else:
                      env = None
                      http_proxy_url = _get_http_proxy_url()
                      if http_proxy_url:
                          env = {'http_proxy': http_proxy_url,
                                 'https_proxy': http_proxy_url}
                      if float(__grains__['osrelease']) < 12.04:
                          cmd = ['apt-add-repository', repo]
                      else:
                          cmd = ['apt-add-repository', '-y', repo]
                      out = _call_apt(cmd, env=env, scope=False, **kwargs)
                      if out['retcode']:
                          raise CommandExecutionError(
                              'Unable to add PPA \'{0}\'. \'{1}\' exited with '
                              'status {2!s}: \'{3}\' '.format(
                                  repo[4:],
                                  cmd,
                                  out['retcode'],
                                  out['stderr']
                              )
                          )
                      # explicit refresh when a repo is modified.
                      if refresh:
                          refresh_db()
                      return {repo: out}
              else:
                  if not HAS_SOFTWAREPROPERTIES:
                      _warn_software_properties(repo)
                  else:
                      log.info('Falling back to urllib method for private PPA')
  
                  # fall back to urllib style
                  try:
                      owner_name, ppa_name = repo[4:].split('/', 1)
                  except ValueError:
                      raise CommandExecutionError(
                          'Unable to get PPA info from argument. '
                          'Expected format "<PPA_OWNER>/<PPA_NAME>" '
                          '(e.g. saltstack/salt) not found.  Received '
                          '\'{0}\' instead.'.format(repo[4:])
                      )
                  dist = __grains__['lsb_distrib_codename']
                  # ppa has a lot of implicit arguments. Make them explicit.
                  # These will defer to any user-defined variants
                  kwargs['dist'] = dist
                  ppa_auth = ''
                  if 'file' not in kwargs:
                      filename = '/etc/apt/sources.list.d/{0}-{1}-{2}.list'
                      kwargs['file'] = filename.format(owner_name, ppa_name,
                                                       dist)
                  try:
                      launchpad_ppa_info = _get_ppa_info_from_launchpad(
                          owner_name, ppa_name)
                      if 'ppa_auth' not in kwargs:
                          kwargs['keyid'] = launchpad_ppa_info[
                              'signing_key_fingerprint']
                      else:
                          if 'keyid' not in kwargs:
                              error_str = 'Private PPAs require a ' \
                                          'keyid to be specified: {0}/{1}'
                              raise CommandExecutionError(
                                  error_str.format(owner_name, ppa_name)
                              )
                  except HTTPError as exc:
                      raise CommandExecutionError(
                          'Launchpad does not know about {0}/{1}: {2}'.format(
                              owner_name, ppa_name, exc)
                      )
                  except IndexError as exc:
                      raise CommandExecutionError(
                          'Launchpad knows about {0}/{1} but did not '
                          'return a fingerprint. Please set keyid '
                          'manually: {2}'.format(owner_name, ppa_name, exc)
                      )
  
                  if 'keyserver' not in kwargs:
                      kwargs['keyserver'] = 'keyserver.ubuntu.com'
                  if 'ppa_auth' in kwargs:
                      if not launchpad_ppa_info['private']:
                          raise CommandExecutionError(
                              'PPA is not private but auth credentials '
                              'passed: {0}'.format(repo)
                          )
                  # assign the new repo format to the "repo" variable
                  # so we can fall through to the "normal" mechanism
                  # here.
                  if 'ppa_auth' in kwargs:
                      ppa_auth = '{0}@'.format(kwargs['ppa_auth'])
                      repo = LP_PVT_SRC_FORMAT.format(ppa_auth, owner_name,
                                                      ppa_name, dist)
                  else:
                      repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist)
          else:
              raise CommandExecutionError(
                  'cannot parse "ppa:" style repo definitions: {0}'
                  .format(repo)
              )
  
      sources = sourceslist.SourcesList()
      if kwargs.get('consolidate', False):
          # attempt to de-dup and consolidate all sources
          # down to entries in sources.list
          # this option makes it easier to keep the sources
          # list in a "sane" state.
          #
          # this should remove duplicates, consolidate comps
          # for a given source down to one line
          # and eliminate "invalid" and comment lines
          #
          # the second side effect is removal of files
          # that are not the main sources.list file
          sources = _consolidate_repo_sources(sources)
  
      repos = [s for s in sources if not s.invalid]
      mod_source = None
      try:
          repo_type, \
              repo_architectures, \
              repo_uri, \
              repo_dist, \
              repo_comps = _split_repo_str(repo)
      except SyntaxError:
          raise SyntaxError(
              'Error: repo \'{0}\' not a well formatted definition'.format(repo)
          )
  
      full_comp_list = set(repo_comps)
      no_proxy = __salt__['config.option']('no_proxy')
  
      if 'keyid' in kwargs:
          keyid = kwargs.pop('keyid', None)
          keyserver = kwargs.pop('keyserver', None)
          if not keyid or not keyserver:
              error_str = 'both keyserver and keyid options required.'
              raise NameError(error_str)
          if not isinstance(keyid, list):
              keyid = [keyid]
  
          for key in keyid:
              if isinstance(key, int):  # yaml can make this an int, we need the hex version
                  key = hex(key)
              cmd = ['apt-key', 'export', key]
              output = __salt__['cmd.run_stdout'](cmd, python_shell=False, **kwargs)
              imported = output.startswith('-----BEGIN PGP')
              if keyserver:
                  if not imported:
                      http_proxy_url = _get_http_proxy_url()
                      if http_proxy_url and keyserver not in no_proxy:
                          cmd = ['apt-key', 'adv', '--batch', '--keyserver-options', 'http-proxy={0}'.format(http_proxy_url),
                                 '--keyserver', keyserver, '--logger-fd', '1', '--recv-keys', key]
                      else:
                          cmd = ['apt-key', 'adv', '--batch', '--keyserver', keyserver,
                                 '--logger-fd', '1', '--recv-keys', key]
                      ret = _call_apt(cmd, scope=False, **kwargs)
                      if ret['retcode'] != 0:
                          raise CommandExecutionError(
                              'Error: key retrieval failed: {0}'.format(ret['stdout'])
                          )
  
      elif 'key_url' in kwargs:
          key_url = kwargs['key_url']
          fn_ = __salt__['cp.cache_file'](key_url, saltenv)
          if not fn_:
              raise CommandExecutionError(
                  'Error: file not found: {0}'.format(key_url)
              )
          cmd = ['apt-key', 'add', fn_]
          out = __salt__['cmd.run_stdout'](cmd, python_shell=False, **kwargs)
          if not out.upper().startswith('OK'):
              raise CommandExecutionError(
                  'Error: failed to add key from {0}'.format(key_url)
              )
  
      elif 'key_text' in kwargs:
          key_text = kwargs['key_text']
          cmd = ['apt-key', 'add', '-']
          out = __salt__['cmd.run_stdout'](cmd, stdin=key_text,
                                           python_shell=False, **kwargs)
          if not out.upper().startswith('OK'):
              raise CommandExecutionError(
                  'Error: failed to add key:\n{0}'.format(key_text)
              )
  
      if 'comps' in kwargs:
          kwargs['comps'] = kwargs['comps'].split(',')
          full_comp_list |= set(kwargs['comps'])
      else:
          kwargs['comps'] = list(full_comp_list)
  
      if 'architectures' in kwargs:
          kwargs['architectures'] = kwargs['architectures'].split(',')
      else:
          kwargs['architectures'] = repo_architectures
  
      if 'disabled' in kwargs:
          kwargs['disabled'] = salt.utils.data.is_true(kwargs['disabled'])
  
      kw_type = kwargs.get('type')
      kw_dist = kwargs.get('dist')
  
      for source in repos:
          # This series of checks will identify the starting source line
          # and the resulting source line.  The idea here is to ensure
          # we are not returning bogus data because the source line
          # has already been modified on a previous run.
          repo_matches = source.type == repo_type and source.uri.rstrip('/') == repo_uri.rstrip('/') and source.dist == repo_dist
          kw_matches = source.dist == kw_dist and source.type == kw_type
  
          if repo_matches or kw_matches:
              for comp in full_comp_list:
                  if comp in getattr(source, 'comps', []):
                      mod_source = source
              if not source.comps:
                  mod_source = source
              if kwargs['architectures'] != source.architectures:
                  mod_source = source
              if mod_source:
                  break
  
      if 'comments' in kwargs:
          kwargs['comments'] = \
              salt.utils.pkg.deb.combine_comments(kwargs['comments'])
  
      if not mod_source:
          mod_source = sourceslist.SourceEntry(repo)
          if 'comments' in kwargs:
              mod_source.comment = kwargs['comments']
          sources.list.append(mod_source)
      elif 'comments' in kwargs:
          mod_source.comment = kwargs['comments']
  
      for key in kwargs:
          if key in _MODIFY_OK and hasattr(mod_source, key):
              setattr(mod_source, key, kwargs[key])
      sources.save()
      # on changes, explicitly refresh
      if refresh:
          refresh_db()
      return {
          repo: {
              'architectures': getattr(mod_source, 'architectures', []),
              'comps': mod_source.comps,
              'disabled': mod_source.disabled,
              'file': mod_source.file,
              'type': mod_source.type,
              'uri': mod_source.uri,
              'line': mod_source.line
          }
      }