How to perform suite change

I have recently updated Debian Buster from testing to stable, so it is worth to note how to perform such update non-interactively.

Issue

By default you will be asked to confirm the change of suite name.

$ sudo apt update
Get:1 http://deb.debian.org/debian buster InRelease [118 kB]
Get:2 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Version' value from '' to '10'
E: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.
Do you want to accept these changes and continue updating from this repository? [y/N] ^C

Command-line solution

Use an additional parameter to allow change of release information contained in the repository.

$ sudo apt update --allow-releaseinfo-change
Get:1 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://deb.debian.org/debian buster InRelease [118 kB]
Get:3 http://security.debian.org/debian-security buster/updates/main Sources [27.3 kB]
Get:4 http://security.debian.org/debian-security buster/updates/main amd64 Packages [56.6 kB]              
Get:5 http://deb.debian.org/debian buster/main Sources [7,827 kB]                    
Get:6 http://deb.debian.org/debian buster/main amd64 Packages [7,897 kB]
Fetched 15.8 MB in 7s (2,120 kB/s)                                                                                                                                                
Reading package lists... Done
Building dependency tree       
Reading state information... Done
85 packages can be upgraded. Run 'apt list --upgradable' to see them.
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Version' value from '' to '10'
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Version' value from '' to '10.0'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Suite' value from 'testing' to 'stable'

You can be more specific and allow only change of suite name, you can also allow changes for other fields like origin, label and codename.

Version number changes with each release, you can skip it for now.
$ sudo apt update --allow-releaseinfo-change-suite
Get:1 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://deb.debian.org/debian buster InRelease [118 kB]
Get:3 http://security.debian.org/debian-security buster/updates/main Sources [27.3 kB]
Get:4 http://security.debian.org/debian-security buster/updates/main amd64 Packages [56.6 kB]
Get:5 http://deb.debian.org/debian buster/main Sources [7,827 kB]
Get:6 http://deb.debian.org/debian buster/main amd64 Packages [7,897 kB]
Fetched 15.5 MB in 7s (2,202 kB/s)                                                                                                                                                
Reading package lists... Done
Building dependency tree       
Reading state information... Done
85 packages can be upgraded. Run 'apt list --upgradable' to see them.
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Version' value from '' to '10'
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Version' value from '' to '10.0'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Suite' value from 'testing' to 'stable'

Set APT configuration option to achieve the same outcome.

$ sudo apt -o Acquire::AllowReleaseInfoChange::Suite=true update
Get:1 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://deb.debian.org/debian buster InRelease [118 kB]
Get:3 http://security.debian.org/debian-security buster/updates/main Sources [27.3 kB]
Get:4 http://security.debian.org/debian-security buster/updates/main amd64 Packages [56.6 kB]
Get:5 http://deb.debian.org/debian buster/main Sources [7,827 kB]
Get:6 http://deb.debian.org/debian buster/main amd64 Packages [7,897 kB]
Fetched 10.7 MB in 5s (1,977 kB/s)                          
Reading package lists... Done
Building dependency tree       
Reading state information... Done
85 packages can be upgraded. Run 'apt list --upgradable' to see them.
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Version' value from '' to '10'
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Version' value from '' to '10.0'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Suite' value from 'testing' to 'stable'

System-wide solution

Define system-wide APT options to allow the change of suite name and hide version transition.

$ echo 'Acquire::AllowReleaseInfoChange::Suite "true";' | sudo tee    /etc/apt/apt.conf.d/99releaseinfochange
$ echo 'quiet::ReleaseInfoChange::Version "true";'      | sudo tee -a /etc/apt/apt.conf.d/99releaseinfochange
$ sudo apt update
Get:1 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://deb.debian.org/debian buster InRelease [118 kB]
Get:3 http://security.debian.org/debian-security buster/updates/main Sources [27.3 kB]
Get:4 http://deb.debian.org/debian buster/main Sources [7,827 kB]                           
Get:5 http://security.debian.org/debian-security buster/updates/main amd64 Packages [56.6 kB]
Get:6 http://deb.debian.org/debian buster/main amd64 Packages [7,897 kB]
Fetched 15.8 MB in 7s (2,178 kB/s)                                                                                                                                                
Reading package lists... Done
Building dependency tree       
Reading state information... Done
85 packages can be upgraded. Run 'apt list --upgradable' to see them.
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Suite' value from 'testing' to 'stable'

Define system-wide APT options to allow the change of suite name and hide suite and version transition.

$ echo 'Acquire::AllowReleaseInfoChange::Suite "true";' | sudo tee    /etc/apt/apt.conf.d/99releaseinfochange
$ echo 'quiet::ReleaseInfoChange::Suite"true";'         | sudo tee -a /etc/apt/apt.conf.d/99releaseinfochange
$ echo 'quiet::ReleaseInfoChange::Version "true";'      | sudo tee -a /etc/apt/apt.conf.d/99releaseinfochange
$ sudo apt update
Get:1 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://deb.debian.org/debian buster InRelease [118 kB]
Get:3 http://deb.debian.org/debian buster/main Sources [7,827 kB]    
Get:4 http://security.debian.org/debian-security buster/updates/main Sources [27.3 kB]
Get:5 http://security.debian.org/debian-security buster/updates/main amd64 Packages [56.6 kB]
Get:6 http://deb.debian.org/debian buster/main amd64 Packages [7,897 kB]
Fetched 15.8 MB in 7s (2,355 kB/s)                                                                                                                                                
Reading package lists... Done
Building dependency tree       
Reading state information... Done
85 packages can be upgraded. Run 'apt list --upgradable' to see them.

Interactive confirmation

Interactive confirmation of the release information change is controlled by APT configuration option.

$ apt-config dump | grep ReleaseInfoChange
Binary::apt::APT::Get::Update::InteractiveReleaseInfoChanges "1";

You can override this option.

$ echo 'Binary::apt::APT::Get::Update::InteractiveReleaseInfoChanges "false";' | sudo tee /etc/apt/apt.conf.d/99interactivereleaseonfochanges

You will not be asked to confirm the change of suite and by default apt utility will fail as expected.

$ sudo apt update
Get:1 http://security.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://deb.debian.org/debian buster InRelease [118 kB]
Reading package lists... Done
N: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Version' value from '' to '10'
E: Repository 'http://security.debian.org/debian-security buster/updates InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.
N: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Version' value from '' to '10.0'
E: Repository 'http://deb.debian.org/debian buster InRelease' changed its 'Suite' value from 'testing' to 'stable'
N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.

Additional information

Inspect apt/apt-pkg/acquire-item.cc source file for detailed information.

[...]
   // changed info potentially breaks user config like pinning
   if (TransactionManager->LastMetaIndexParser != nullptr)
   {
      std::vector<pkgAcquireStatus::ReleaseInfoChange> Changes;
      auto const AllowInfoChange = _config->FindB("Acquire::AllowReleaseInfoChange", false);
      auto const quietInfoChange = _config->FindB("quiet::ReleaseInfoChange", false);
      struct {
	 char const * const Type;
	 bool const Allowed;
	 decltype(&metaIndex::GetOrigin) const Getter;
      } checkers[] = {
	 { "Origin", AllowInfoChange, &metaIndex::GetOrigin },
	 { "Label", AllowInfoChange, &metaIndex::GetLabel },
	 { "Version", true, &metaIndex::GetVersion }, // numbers change all the time, that is okay
	 { "Suite", AllowInfoChange, &metaIndex::GetSuite },
	 { "Codename", AllowInfoChange, &metaIndex::GetCodename },
	 { nullptr, false, nullptr }
      };
      auto const CheckReleaseInfo = [&](char const * const Type, bool const AllowChange, decltype(checkers[0].Getter) const Getter) {
	 std::string const Last = (TransactionManager->LastMetaIndexParser->*Getter)();
	 std::string const Now = (TransactionManager->MetaIndexParser->*Getter)();
	 if (Last == Now)
	    return;
	 auto const Allow = _config->FindB(std::string("Acquire::AllowReleaseInfoChange::").append(Type), AllowChange);
	 if (Allow == true && _config->FindB(std::string("quiet::ReleaseInfoChange::").append(Type), quietInfoChange) == true)
	    return;
	 std::string msg;
	 strprintf(msg, _("Repository '%s' changed its '%s' value from '%s' to '%s'"),
	       Desc.Description.c_str(), Type, Last.c_str(), Now.c_str());
	 Changes.push_back({Type, std::move(Last), std::move(Now), std::move(msg), Allow});
      };
      for (short i = 0; checkers[i].Type != nullptr; ++i)
	 CheckReleaseInfo(checkers[i].Type, checkers[i].Allowed, checkers[i].Getter);

      {
	 auto const Last = TransactionManager->LastMetaIndexParser->GetDefaultPin();
	 auto const Now = TransactionManager->MetaIndexParser->GetDefaultPin();
	 if (Last != Now)
	 {
	    auto const Allow = _config->FindB("Acquire::AllowReleaseInfoChange::DefaultPin", AllowInfoChange);
	    if (Allow == false || _config->FindB("quiet::ReleaseInfoChange::DefaultPin", quietInfoChange) == false)
	    {
	       std::string msg;
	       strprintf(msg, _("Repository '%s' changed its default priority for %s from %hi to %hi."),
		     Desc.Description.c_str(), "apt_preferences(5)", Last, Now);
	       Changes.push_back({"DefaultPin", std::to_string(Last), std::to_string(Now), std::move(msg), Allow});
	    }
	 }
      }
      if (Changes.empty() == false)
      {
	 auto const notes = TransactionManager->MetaIndexParser->GetReleaseNotes();
	 if (notes.empty() == false)
	 {
	    std::string msg;
	    // TRANSLATOR: the "this" refers to changes in the repository like a new release or owner change
	    strprintf(msg, _("More information about this can be found online in the Release notes at: %s"), notes.c_str());
	    Changes.push_back({"Release-Notes", "", std::move(notes), std::move(msg), true});
	 }
	 if (std::any_of(Changes.begin(),Changes.end(),[](pkgAcquireStatus::ReleaseInfoChange const &c) { return c.DefaultAction == false; }))
	 {
	    std::string msg;
	    // TRANSLATOR: %s is the name of the manpage in question, e.g. apt-secure(8)
	    strprintf(msg, _("This must be accepted explicitly before updates for "
		     "this repository can be applied. See %s manpage for details."), "apt-secure(8)");
	    Changes.push_back({"Confirmation", "", "", std::move(msg), true});
	 }

      }
[...]

You can also see Debian Bug report logs #931566 Don't complain about suite changes (Acquire::AllowReleaseInfoChange::Suite should be "true").