use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubmitterAdvisory {
pub publisher: String,
pub message: String,
}
pub fn submitter_required_warnings(config: &Config) -> Vec<SubmitterAdvisory> {
fn advisory(location: &str, name: &str, publisher: &str) -> SubmitterAdvisory {
SubmitterAdvisory {
publisher: publisher.to_string(),
message: format!(
"{location}: publisher '{name}' submits to an external moderation queue; \
`required: true` fails the release when the submission itself fails, \
but the eventual moderation outcome happens outside the release run \
and cannot be gated."
),
}
}
let mut warnings = Vec::new();
for_each_crate_publish(config, |axis, publish| {
let loc = axis.location();
if publish.chocolatey().and_then(|c| c.required) == Some(true) {
warnings.push(advisory(&loc, "chocolatey", "chocolatey"));
}
if publish.winget().and_then(|w| w.required) == Some(true) {
warnings.push(advisory(&loc, "winget", "winget"));
}
if publish.aur_source().and_then(|a| a.required) == Some(true) {
warnings.push(advisory(&loc, "aur_source", "upstream-aur"));
}
});
if let Some(ref sources) = config.aur_sources {
for (idx, src) in sources.iter().enumerate() {
if src.required == Some(true) {
let loc = format!("top-level aur_sources[{idx}]");
warnings.push(advisory(&loc, "aur_source", "upstream-aur"));
}
}
}
warnings
}
pub fn apply_archive_legacy_aliases(_config: &mut Config) {
}
pub fn validate_no_docker_v1(raw_yaml: &serde_yaml_ng::Value) -> Result<(), String> {
if raw_yaml.get("dockers").is_some() {
return Err(
"config: legacy GoReleaser `dockers:` block is not supported — anodizer ships \
dockers_v2: only (multi-arch buildx flow). Port the config to `dockers_v2:` per \
https://anodize.dev/docs/migration/docker.html."
.to_string(),
);
}
Ok(())
}
pub fn warn_on_legacy_homebrew_formula(config: &Config) {
for msg in legacy_homebrew_formula_warnings(config) {
tracing::warn!("{}", msg);
}
}
pub(crate) fn legacy_homebrew_formula_warnings(config: &Config) -> Vec<String> {
fn formula_warning(location: &str) -> String {
format!(
"DEPRECATION: {location}: publish.homebrew (Homebrew Formula) is deprecated upstream \
in GoReleaser v2.16; migrate to homebrew_casks. Cask is now the canonical Homebrew \
distribution channel for pre-compiled binaries. See \
https://anodize.dev/docs/publish/homebrew-casks/ for migration."
)
}
let mut warnings = Vec::new();
for_each_crate_publish(config, |axis, publish| {
if publish.homebrew().is_some() {
warnings.push(formula_warning(&axis.location()));
}
});
warnings
}
pub fn warn_on_legacy_snapshot_name_template(raw_yaml: &serde_yaml_ng::Value) {
if let Some(snap) = raw_yaml.get("snapshot")
&& snap.get("name_template").is_some()
{
tracing::warn!(
"DEPRECATION: snapshot.name_template is deprecated; use \
snapshot.version_template instead. Both spellings are accepted \
but the legacy key will be removed in a future release."
);
}
}
pub fn warn_on_legacy_furies_alias(raw_yaml: &serde_yaml_ng::Value) {
if raw_yaml.get("furies").is_some() {
tracing::warn!(
"DEPRECATION: the top-level `furies:` config key is deprecated since GoReleaser \
Pro v2.14; rename it to `gemfury:`. Both spellings are accepted but the legacy \
key will be removed in a future release."
);
}
}
pub fn warn_on_legacy_nfpm_builds(raw_yaml: &serde_yaml_ng::Value) {
fn warn_for_nfpm_value(value: &serde_yaml_ng::Value) {
match value {
serde_yaml_ng::Value::Mapping(_) => {
if value.get("builds").is_some() {
tracing::warn!(
"DEPRECATION: nfpm `builds:` is deprecated; use `ids:` instead. \
Both spellings are accepted but the legacy key will be removed in \
a future release."
);
}
}
serde_yaml_ng::Value::Sequence(items) => {
for item in items {
warn_for_nfpm_value(item);
}
}
_ => {}
}
}
fn descend(value: &serde_yaml_ng::Value) {
match value {
serde_yaml_ng::Value::Mapping(map) => {
for (key, child) in map {
if matches!(key.as_str(), Some("nfpm") | Some("nfpms")) {
warn_for_nfpm_value(child);
}
descend(child);
}
}
serde_yaml_ng::Value::Sequence(items) => {
for item in items {
descend(item);
}
}
_ => {}
}
}
descend(raw_yaml);
}
pub fn warn_on_legacy_disable_alias(raw_yaml: &serde_yaml_ng::Value) {
for msg in legacy_disable_alias_warnings(raw_yaml) {
tracing::warn!("{}", msg);
}
}
pub(crate) fn legacy_disable_alias_warnings(raw_yaml: &serde_yaml_ng::Value) -> Vec<String> {
const ALLOWLIST: &[&str] = &[
"mcp",
"makeselfs",
"makeselves",
"install_scripts",
"appimages",
"msis",
"pkgs",
"nsis",
"dockerhub",
"release",
"dockers_v2",
"docker_v2",
"changelog",
"snapcrafts",
"npms",
"gemfury",
"furies",
"pypis",
"homebrew_cores",
"publishers",
"sboms",
"aur",
"aur_source",
"aur_sources",
"blobs",
"docker_digest",
"checksum",
"flatpaks",
];
fn disable_warning(path: &str) -> String {
format!(
"DEPRECATION: {path}: legacy `disable:` is deprecated; rename it to `skip:`. \
Both spellings are accepted but the legacy key will be removed in a future release."
)
}
fn descend(
value: &serde_yaml_ng::Value,
path: &str,
enclosing_block: Option<&str>,
warnings: &mut Vec<String>,
) {
match value {
serde_yaml_ng::Value::Mapping(map) => {
for (key, child) in map {
let Some(key) = key.as_str() else { continue };
let child_path = if path.is_empty() {
key.to_string()
} else {
format!("{path}.{key}")
};
if key == "disable"
&& enclosing_block.is_some_and(|block| ALLOWLIST.contains(&block))
{
warnings.push(disable_warning(&child_path));
}
descend(child, &child_path, Some(key), warnings);
}
}
serde_yaml_ng::Value::Sequence(items) => {
for (idx, item) in items.iter().enumerate() {
let item_path = format!("{path}[{idx}]");
descend(item, &item_path, enclosing_block, warnings);
}
}
_ => {}
}
}
let mut warnings = Vec::new();
descend(raw_yaml, "", None, &mut warnings);
warnings
}
pub fn validate_no_mcp_github(raw_yaml: &serde_yaml_ng::Value) -> Result<(), String> {
if raw_yaml.get("mcp").and_then(|m| m.get("github")).is_some() {
return Err(
"config: nested `mcp.github:` block is not supported — anodizer mirrors GoReleaser \
v2.13.1+ where registry metadata moved to top-level `mcp:` fields (`mcp.name`, \
`mcp.repository.url`, `mcp.repository.source`). Port the nested keys to the \
canonical surface."
.to_string(),
);
}
Ok(())
}
pub fn warn_on_legacy_docker_retry(config: &Config) {
for msg in legacy_docker_retry_warnings(config) {
tracing::warn!("{}", msg);
}
}
pub(crate) fn legacy_docker_retry_warnings(config: &Config) -> Vec<String> {
fn pipe_warning(location: &str, kind: &str) -> String {
format!(
"DEPRECATION: {location}: nested `{kind}.retry:` is deprecated since GoReleaser \
v2.15.3; move retry settings to the top-level `retry:` block. The per-pipe \
value still wins at resolve time for back-compat, but the legacy spelling will \
be removed in a future release."
)
}
let mut warnings = Vec::new();
let scan_crate = |krate: &CrateConfig, prefix: &str, warnings: &mut Vec<String>| {
if let Some(ref v2) = krate.dockers_v2 {
for (i, cfg) in v2.iter().enumerate() {
if cfg.retry.is_some() {
warnings.push(pipe_warning(
&format!("{prefix}.dockers_v2[{i}]"),
"dockers_v2",
));
}
}
}
if let Some(ref manifests) = krate.docker_manifests {
for (i, cfg) in manifests.iter().enumerate() {
if cfg.retry.is_some() {
warnings.push(pipe_warning(
&format!("{prefix}.docker_manifests[{i}]"),
"docker_manifests",
));
}
}
}
};
for krate in &config.crates {
scan_crate(krate, &format!("crates[{}]", krate.name), &mut warnings);
}
if let Some(ref workspaces) = config.workspaces {
for ws in workspaces {
for krate in &ws.crates {
scan_crate(
krate,
&format!("workspaces[{}].crates[{}]", ws.name, krate.name),
&mut warnings,
);
}
}
}
if let Some(ref defaults) = config.defaults
&& let Some(ref v2) = defaults.dockers_v2
&& v2.retry.is_some()
{
warnings.push(pipe_warning("defaults.dockers_v2", "dockers_v2"));
}
warnings
}
pub fn apply_homebrew_cask_legacy_singulars(config: &mut Config) {
fn fold_one(location: &str, cask: &mut HomebrewCaskConfig) -> Vec<String> {
let mut warnings = Vec::new();
if let Some(legacy) = cask.legacy_binary.take() {
let entry = HomebrewCaskBinary::Name(legacy.clone());
match cask.binaries {
Some(ref mut list) => list.insert(0, entry),
None => cask.binaries = Some(vec![entry]),
}
warnings.push(format!(
"DEPRECATION: {location}: singular `binary: {legacy}` is deprecated since \
GoReleaser v2.12.6; use the plural `binaries: [{legacy}]` form. The legacy \
value has been folded into binaries[0]."
));
}
if let Some(legacy) = cask.legacy_manpage.take() {
match cask.manpages {
Some(ref mut list) => list.push(legacy.clone()),
None => cask.manpages = Some(vec![legacy.clone()]),
}
warnings.push(format!(
"DEPRECATION: {location}: singular `manpage: {legacy}` is deprecated; \
use the plural `manpages: [{legacy}]` form. The legacy value has been \
folded into manpages."
));
}
warnings
}
let mut warnings = Vec::new();
if let Some(ref mut casks) = config.homebrew_casks {
for (i, cask) in casks.iter_mut().enumerate() {
warnings.extend(fold_one(&format!("homebrew_casks[{i}]"), cask));
}
}
for_each_crate_publish_mut(config, |axis, mut publish| {
if let Some(cask) = publish.homebrew_cask_mut() {
warnings.extend(fold_one(&axis.homebrew_cask_location(), cask));
}
});
for msg in warnings {
tracing::warn!("{}", msg);
}
}