Deprecate appcast.

This commit is contained in:
Markus Reiter 2023-03-29 20:49:29 +02:00
parent f4ee196f90
commit bdd6523ce8
No known key found for this signature in database
GPG Key ID: 245293B51702655B
23 changed files with 111 additions and 193 deletions

View File

@ -20,11 +20,11 @@ module Cask
attr_reader :cask, :download
attr_predicate :appcast?, :new_cask?, :strict?, :signing?, :online?, :token_conflicts?
attr_predicate :new_cask?, :strict?, :signing?, :online?, :token_conflicts?
def initialize(
cask,
appcast: nil, download: nil, quarantine: nil,
download: nil, quarantine: nil,
token_conflicts: nil, online: nil, strict: nil, signing: nil,
new_cask: nil, only: [], except: []
)
@ -34,15 +34,10 @@ module Cask
signing = new_cask if signing.nil?
token_conflicts = new_cask if token_conflicts.nil?
# `online` implies `appcast` and `download`
appcast = online if appcast.nil?
download = online if download.nil?
# `signing` implies `download`
download = signing if download.nil?
# `online` and `signing` imply `download`
download = online || signing if download.nil?
@cask = cask
@appcast = appcast
@download = Download.new(cask, quarantine: quarantine) if download
@online = online
@strict = strict
@ -292,22 +287,15 @@ module Cask
end
sig { void }
def audit_appcast_and_livecheck
def audit_appcast
return unless cask.appcast
if cask.livecheckable?
add_error "Cask has a `livecheck`, the `appcast` should be removed."
elsif new_cask?
add_error "New casks should use a `livecheck` instead of an `appcast`."
end
add_error "`appcast` should be replaced with a `livecheck`."
end
sig { void }
def audit_latest_with_appcast_or_livecheck
def audit_latest_with_livecheck
return unless cask.version.latest?
add_error "Casks with an `appcast` should not use `version :latest`." if cask.appcast
return unless cask.livecheckable?
return if cask.livecheck.skip?
@ -326,8 +314,10 @@ module Cask
sig { params(livecheck_result: T.any(NilClass, T::Boolean, Symbol)).void }
def audit_hosting_with_livecheck(livecheck_result: audit_livecheck_version)
return if cask.discontinued? || cask.version.latest?
return if block_url_offline? || cask.appcast || cask.livecheckable?
return if cask.discontinued?
return if cask.version.latest?
return if block_url_offline?
return if cask.livecheckable?
return if livecheck_result == :auto_detected
add_livecheck = "please add a livecheck. See #{Formatter.url(LIVECHECK_REFERENCE_URL)}"
@ -478,7 +468,7 @@ module Cask
end
sig { void }
def audit_appcast_unneeded_long_version
def audit_livecheck_unneeded_long_version
return unless cask.livecheck.strategy == :sparkle
return unless cask.version.csv.second
return if cask.url.to_s.include? cask.version.csv.second
@ -561,7 +551,7 @@ module Cask
sig { returns(T.any(NilClass, T::Boolean, Symbol)) }
def audit_livecheck_version
return unless appcast?
return unless online?
referenced_cask, = Homebrew::Livecheck.resolve_livecheck_reference(cask)
@ -581,16 +571,8 @@ module Cask
cask,
referenced_formula_or_cask: referenced_cask,
)&.fetch(:latest)
if cask.version.to_s == latest_version.to_s
if cask.appcast
add_error "Version '#{latest_version}' was automatically detected by livecheck; " \
"the appcast should be removed."
end
return :auto_detected
end
return :appcast if cask.appcast && !cask.livecheckable?
return :auto_detected if cask.version.to_s == latest_version.to_s
add_error "Version '#{cask.version}' differs from '#{latest_version}' retrieved by livecheck."
@ -643,33 +625,6 @@ module Cask
"and the cask defined #{min_os_symbol}"
end
sig { void }
def audit_appcast_contains_version
return unless appcast?
return if cask.appcast.to_s.empty?
return if cask.appcast.must_contain == :no_check
appcast_url = cask.appcast.to_s
begin
details = curl_http_content_headers_and_checksum(appcast_url, user_agent: HOMEBREW_USER_AGENT_FAKE_SAFARI)
appcast_contents = details[:file]
rescue
add_error "appcast at URL '#{Formatter.url(appcast_url)}' offline or looping"
return
end
version_stanza = cask.version.to_s
adjusted_version_stanza = cask.appcast.must_contain.presence || version_stanza.match(/^[[:alnum:].]+/)[0]
return if appcast_contents.blank?
return if appcast_contents.include?(adjusted_version_stanza)
add_error <<~EOS.chomp
appcast at URL '#{Formatter.url(appcast_url)}' does not contain \
the version number '#{adjusted_version_stanza}':
#{appcast_contents}
EOS
end
sig { void }
def audit_github_prerelease_version
return if cask.tap == "homebrew/cask-versions"
@ -808,8 +763,9 @@ module Cask
user_agents: [cask.url.user_agent], referer: cask.url&.referer)
end
if cask.appcast && appcast?
validate_url_for_https_availability(cask.appcast, "appcast URL", cask.token, cask.tap, check_content: true)
if cask.livecheckable? && !cask.livecheck.url.is_a?(Symbol)
validate_url_for_https_availability(cask.livecheck.url, "livecheck URL", cask.token, cask.tap,
check_content: true)
end
return unless cask.homepage
@ -841,7 +797,6 @@ module Cask
_, user, repo = *regex.match(cask.url.to_s)
_, user, repo = *regex.match(cask.homepage) unless user
_, user, repo = *regex.match(cask.appcast.to_s) unless user
return if !user || !repo
repo.gsub!(/.git$/, "")

View File

@ -17,7 +17,6 @@ module Cask
def initialize(
cask,
audit_download: nil,
audit_appcast: nil,
audit_online: nil,
audit_strict: nil,
audit_signing: nil,
@ -33,7 +32,6 @@ module Cask
)
@cask = cask
@audit_download = audit_download
@audit_appcast = audit_appcast
@audit_online = audit_online
@audit_new_cask = audit_new_cask
@audit_strict = audit_strict
@ -120,7 +118,6 @@ module Cask
def audit_cask_instance(cask)
audit = Audit.new(
cask,
appcast: @audit_appcast,
online: @audit_online,
strict: @audit_strict,
signing: @audit_signing,

View File

@ -16,7 +16,8 @@ module Cask
switch "--[no-]download",
description: "Audit the downloaded file"
switch "--[no-]appcast",
description: "Audit the appcast"
description: "Audit the appcast",
replacement: false
switch "--[no-]token-conflicts",
description: "Audit for token conflicts"
switch "--[no-]signing",
@ -49,7 +50,6 @@ module Cask
results = self.class.audit_casks(
*casks,
download: args.download?,
appcast: args.appcast?,
online: args.online?,
strict: args.strict?,
signing: args.signing?,
@ -73,7 +73,6 @@ module Cask
def self.audit_casks(
*casks,
download:,
appcast:,
online:,
strict:,
signing:,
@ -89,7 +88,6 @@ module Cask
)
options = {
audit_download: download,
audit_appcast: appcast,
audit_online: online,
audit_strict: strict,
audit_signing: signing,

View File

@ -11,7 +11,6 @@ require "cask/artifact_set"
require "cask/caskroom"
require "cask/exceptions"
require "cask/dsl/appcast"
require "cask/dsl/base"
require "cask/dsl/caveats"
require "cask/dsl/conflicts_with"
@ -211,7 +210,11 @@ module Cask
# @api public
def appcast(*args, **kwargs)
set_unique_stanza(:appcast, args.empty? && kwargs.empty?) { DSL::Appcast.new(*args, **kwargs) }
set_unique_stanza(:appcast, args.empty? && kwargs.empty?) do
# TODO: Remove the remaining audit for `appcast` usage when enabling this deprecation.
# odeprecated "the `appcast` stanza", "the `livecheck` stanza"
true
end
end
# @api public

View File

@ -1,27 +0,0 @@
# typed: true
# frozen_string_literal: true
module Cask
class DSL
# Class corresponding to the `appcast` stanza.
#
# @api private
class Appcast
attr_reader :uri, :parameters, :must_contain
def initialize(uri, **parameters)
@uri = URI(uri)
@parameters = parameters
@must_contain = parameters[:must_contain] if parameters.key?(:must_contain)
end
def to_yaml
[uri, parameters].to_yaml
end
def to_s
uri.to_s
end
end
end
end

View File

@ -156,7 +156,7 @@ module Homebrew
end
def switch(*names, description: nil, replacement: nil, env: nil, depends_on: nil,
method: :on, hidden: false)
method: :on, hidden: false, disable: false)
global_switch = names.first.is_a?(Symbol)
return if global_switch
@ -167,7 +167,7 @@ module Homebrew
description += " (disabled#{"; replaced by #{replacement}" if replacement.present?})"
end
@parser.public_send(method, *names, *wrap_option_desc(description)) do |value|
odisabled "the `#{names.first}` switch", replacement unless replacement.nil?
odeprecated "the `#{names.first}` switch", replacement, disable: disable if !replacement.nil? || disable
value = true if names.none? { |name| name.start_with?("--[no-]") }
set_switch(*names, value: value, from: :args)

View File

@ -30,10 +30,12 @@ module Homebrew
switch "--full",
description: "Convert a shallow clone to a full clone without untapping. Taps are only cloned as " \
"shallow clones if `--shallow` was originally passed.",
replacement: false
replacement: false,
disable: true
switch "--shallow",
description: "Fetch tap as a shallow clone rather than a full clone. Useful for continuous integration.",
replacement: false
replacement: false,
disable: true
switch "--[no-]force-auto-update",
description: "Auto-update tap even if it is not hosted on GitHub. By default, only taps " \
"hosted on GitHub are auto-updated (for performance reasons)."

View File

@ -49,7 +49,8 @@ module Homebrew
"for Homebrew. This should be used when creating new formula and implies " \
"`--strict` and `--online`."
switch "--[no-]appcast",
description: "Audit the appcast."
description: "Audit the appcast.",
replacement: false
switch "--[no-]signing",
description: "Audit for signed apps, which are required on ARM"
switch "--token-conflicts",
@ -246,8 +247,6 @@ module Homebrew
Cask::Cmd::Audit.audit_casks(
*audit_casks,
download: nil,
# No need for `|| nil` for `--[no-]appcast` because boolean switches are already `nil` if not passed
appcast: args.appcast?,
# No need for `|| nil` for `--[no-]signing` because boolean switches are already `nil` if not passed
signing: args.signing?,
online: args.online? || nil,

View File

@ -540,7 +540,6 @@ module Homebrew
urls << package_or_resource.head.url if package_or_resource.head
urls << package_or_resource.homepage if package_or_resource.homepage
when Cask::Cask
urls << package_or_resource.appcast.to_s if package_or_resource.appcast
urls << package_or_resource.url.to_s if package_or_resource.url
urls << package_or_resource.homepage if package_or_resource.homepage
when Resource

View File

@ -119,9 +119,6 @@ end
module Cask
class Audit
sig { returns(T::Boolean) }
def appcast?; end
sig { returns(T::Boolean) }
def new_cask?; end

View File

@ -70,11 +70,13 @@ describe Cask::Audit, :cask do
let(:except) { [] }
let(:strict) { nil }
let(:token_conflicts) { nil }
let(:signing) { nil }
let(:audit) do
described_class.new(cask, online: online,
strict: strict,
new_cask: new_cask,
token_conflicts: token_conflicts,
signing: signing,
only: only,
except: except)
end
@ -99,9 +101,13 @@ describe Cask::Audit, :cask do
context "when `online` is specified" do
let(:online) { true }
it "implies `appcast`" do
expect(audit.appcast?).to be true
it "implies `download`" do
expect(audit.download).to be_truthy
end
end
context "when `signing` is specified" do
let(:signing) { true }
it "implies `download`" do
expect(audit.download).to be_truthy
@ -761,7 +767,7 @@ describe Cask::Audit, :cask do
end
context "when the download is hosted on SourceForge and has a livecheck" do
let(:cask_token) { "sourceforge-with-appcast" }
let(:cask_token) { "sourceforge-with-livecheck" }
it { is_expected.not_to error_with(message) }
end
@ -774,48 +780,57 @@ describe Cask::Audit, :cask do
end
context "when the download is hosted on DevMate and has a livecheck" do
let(:cask_token) { "devmate-with-appcast" }
let(:cask_token) { "devmate-with-livecheck" }
it { is_expected.not_to error_with(message) }
end
context "when the download is hosted on DevMate and does not have a livecheck" do
let(:cask_token) { "devmate-without-appcast" }
let(:cask_token) { "devmate-without-livecheck" }
it { is_expected.to error_with(message) }
end
context "when the download is hosted on HockeyApp and has a livecheck" do
let(:cask_token) { "hockeyapp-with-appcast" }
let(:cask_token) { "hockeyapp-with-livecheck" }
it { is_expected.not_to error_with(message) }
end
context "when the download is hosted on HockeyApp and does not have a livecheck" do
let(:cask_token) { "hockeyapp-without-appcast" }
let(:cask_token) { "hockeyapp-without-livecheck" }
it { is_expected.to error_with(message) }
end
end
describe "latest with appcast checks" do
let(:only) { ["latest_with_appcast_or_livecheck"] }
let(:message) { "Casks with an `appcast` should not use `version :latest`." }
describe "latest with livecheck checks" do
let(:only) { ["latest_with_livecheck"] }
let(:message) { "Casks with a `livecheck` should not use `version :latest`." }
context "when the Cask is :latest and does not have an appcast" do
context "when the Cask is :latest and does not have a livecheck" do
let(:cask_token) { "version-latest" }
it { is_expected.not_to error_with(message) }
end
context "when the Cask is versioned and has an appcast" do
let(:cask_token) { "with-appcast" }
context "when the Cask is versioned and has a livecheck with skip information" do
let(:cask_token) { "latest-with-livecheck-skip" }
it { is_expected.not_to error_with(message) }
it { is_expected.to pass }
end
context "when the Cask is :latest and has an appcast" do
let(:cask_token) { "latest-with-appcast" }
context "when the Cask is versioned and has a livecheck" do
let(:cask_token) { "latest-with-livecheck" }
it { is_expected.to error_with(message) }
end
end
describe "appcast" do
context "when the Cask has an appcast" do
let(:cask_token) { "with-appcast" }
let(:message) { "`appcast` should be replaced with a `livecheck`." }
it { is_expected.to error_with(message) }
end

View File

@ -1,26 +0,0 @@
# typed: false
# frozen_string_literal: true
describe Cask::DSL::Appcast do
subject(:appcast) { described_class.new(url, **params) }
let(:url) { "https://brew.sh" }
let(:uri) { URI(url) }
let(:params) { {} }
describe "#to_s" do
it "returns the parsed URI string" do
expect(appcast.to_s).to eq("https://brew.sh")
end
end
describe "#to_yaml" do
let(:yaml) { [uri, params].to_yaml }
context "with empty parameters" do
it "returns an YAML serialized array composed of the URI and parameters" do
expect(appcast.to_yaml).to eq(yaml)
end
end
end
end

View File

@ -365,30 +365,6 @@ describe Cask::DSL, :cask do
end
end
describe "appcast stanza" do
let(:token) { "with-appcast" }
it "allows appcasts to be specified" do
expect(cask.appcast.to_s).to match(/^http/)
end
context "when multiple appcasts are defined" do
let(:token) { "invalid/invalid-appcast-multiple" }
it "raises an error" do
expect { cask }.to raise_error(Cask::CaskInvalidError, /'appcast' stanza may only appear once/)
end
end
context "when appcast URL is invalid" do
let(:token) { "invalid/invalid-appcast-url" }
it "refuses to load" do
expect { cask }.to raise_error(Cask::CaskInvalidError)
end
end
end
describe "depends_on stanza" do
let(:token) { "invalid/invalid-depends-on-key" }

View File

@ -167,7 +167,7 @@ describe Homebrew::Cleanup do
end
context "when given a `:latest` cask" do
let(:cask) { Cask::CaskLoader.load("latest-with-appcast") }
let(:cask) { Cask::CaskLoader.load("latest") }
it "does not remove the download for the latest version" do
download = Cask::Cache.path/"#{cask.token}--#{cask.version}"

View File

@ -208,9 +208,9 @@ describe Homebrew::Completions do
it "handles --[no]- options correctly" do
options = described_class.command_options("audit")
expect(options.key?("--appcast")).to be true
expect(options.key?("--no-appcast")).to be true
expect(options["--appcast"] == options["--no-appcast"]).to be true
expect(options.key?("--signing")).to be true
expect(options.key?("--no-signing")).to be true
expect(options["--signing"] == options["--no-signing"]).to be true
end
it "return an empty array if command is not found" do

View File

@ -1,11 +1,14 @@
cask "devmate-without-appcast" do
cask "devmate-with-livecheck" do
version "1.0"
sha256 "a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9"
# dl.devmate.com/com.my.fancyapp was verified as official when first introduced to the cask
url "https://dl.devmate.com/com.my.fancyapp/app_#{version}.zip"
name "DevMate"
homepage "https://www.brew.sh/"
livecheck do
url "https://updates.devmate.com/com.my.fancyapp.app.xml"
end
app "DevMate.app"
end

View File

@ -1,10 +1,8 @@
cask "devmate-with-appcast" do
cask "devmate-without-livecheck" do
version "1.0"
sha256 "a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9"
# dl.devmate.com/com.my.fancyapp was verified as official when first introduced to the cask
url "https://dl.devmate.com/com.my.fancyapp/app_#{version}.zip"
appcast "https://updates.devmate.com/com.my.fancyapp.app.xml"
name "DevMate"
homepage "https://www.brew.sh/"

View File

@ -1,11 +1,14 @@
cask "hockeyapp-without-appcast" do
cask "hockeyapp-with-livecheck" do
version "1.0,123"
sha256 "a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9"
# rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1 was verified as official when first introduced to the cask
url "https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1/app_versions/#{version.csv.second}?format=zip"
name "HockeyApp"
homepage "https://www.brew.sh/"
livecheck do
url "https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1"
end
app "HockeyApp.app"
end

View File

@ -1,10 +1,8 @@
cask "hockeyapp-with-appcast" do
cask "hockeyapp-without-livecheck" do
version "1.0,123"
sha256 "a69e7357bea014f4c14ac9699274f559086844ffa46563c4619bf1addfd72ad9"
# rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1 was verified as official when first introduced to the cask
url "https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1/app_versions/#{version.csv.second}?format=zip"
appcast "https://rink.hockeyapp.net/api/2/apps/67503a7926431872c4b6c1549f5bd6b1"
name "HockeyApp"
homepage "https://www.brew.sh/"

View File

@ -0,0 +1,13 @@
cask "latest-with-livecheck-skip" do
version :latest
sha256 :no_check
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
homepage "https://brew.sh/with-livecheck-skip"
livecheck do
skip "no version information available"
end
app "Caffeine.app"
end

View File

@ -0,0 +1,13 @@
cask "latest-with-livecheck" do
version :latest
sha256 :no_check
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
homepage "https://brew.sh/with-livecheck"
livecheck do
url "https://brew.sh/with-livecheck/changelog"
end
app "Caffeine.app"
end

View File

@ -1,9 +1,8 @@
cask "latest-with-appcast" do
cask "latest" do
version :latest
sha256 :no_check
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
appcast "https://brew.sh/appcast.xml"
homepage "https://brew.sh/with-appcast"
app "Caffeine.app"

View File

@ -1,7 +1,10 @@
cask "sourceforge-with-appcast" do
cask "sourceforge-with-livecheck" do
version "1.2.3"
url "https://downloads.sourceforge.net/something/Something-1.2.3.dmg"
appcast "https://sourceforge.net/projects/something/rss"
homepage "https://sourceforge.net/projects/something/"
livecheck do
url "https://sourceforge.net/projects/something/rss"
end
end