Skip to main content

anodizer_core/context/
mode.rs

1use super::*;
2
3impl Context {
4    pub fn is_dry_run(&self) -> bool {
5        self.options.dry_run
6    }
7
8    pub fn is_snapshot(&self) -> bool {
9        self.options.snapshot
10    }
11
12    /// Whether this run builds only a subset of the configured targets — either
13    /// a `--split` / `--targets` determinism shard (`partial_target`) or a
14    /// host-only `--single-target` build.
15    ///
16    /// A publisher whose eligible artifact is legitimately absent on a
17    /// restricted build (e.g. a Windows-only publisher on a Linux single-target
18    /// snapshot) must self-skip its schema validation rather than error: the
19    /// artifact lands on another target, not a misconfiguration. On a FULL build
20    /// the same absence IS a misconfiguration and must surface. `--single-target`
21    /// (`single_target`) is clap-exclusive with `--targets` / `--host-targets`
22    /// (which populate `partial_target`), but NOT with `--split` (a split shard
23    /// resolves its own `partial_target` from `partial.by` yet may still be
24    /// scoped to the host target), so both signals can be set at once; this OR
25    /// is the single "restricted build" predicate the per-publisher validators
26    /// gate their no-artifact skip on, correct whether one or both are set.
27    pub fn is_target_restricted_build(&self) -> bool {
28        self.options.partial_target.is_some() || self.options.single_target.is_some()
29    }
30
31    /// Whether this run is `anodizer release --publish-only` (publishing a
32    /// preserved dist rather than building from source).
33    ///
34    /// Build-time concerns (notably the `binary_signs:` per-binary signing
35    /// loop, whose output is embedded into archives at build time and has no
36    /// publish-time consumer) are gated off this in publish-only mode, where
37    /// the runner carries only publish-time credentials.
38    pub fn is_publish_only(&self) -> bool {
39        self.options.publish_only
40    }
41
42    pub fn is_strict(&self) -> bool {
43        self.options.strict
44    }
45
46    /// Effective preflight strictness: the global `--strict` or the
47    /// config-level `preflight.strict` — either one turns it on. Under
48    /// strict preflight, indeterminate probe outcomes (Unknown publisher
49    /// state, 5xx / rate-limit / network failure / undeterminable
50    /// permissions) become hard blockers instead of warnings. Definitive
51    /// failures keep their required→blocker / optional→warning severity
52    /// either way.
53    pub fn preflight_is_strict(&self) -> bool {
54        self.options.strict || self.config.preflight.strict
55    }
56
57    /// Toggle the runtime strict-render flag (see the `render_strict` field).
58    ///
59    /// The pre-publish guard calls this with `true` before its render pass and
60    /// restores the prior value after, so render-error swallowing is suppressed
61    /// only for that in-memory validation — production publish renders stay
62    /// lenient unless the user passed the global `--strict`. Returns the prior
63    /// value so the caller can restore it.
64    pub fn set_render_strict(&self, on: bool) -> bool {
65        self.render_strict.replace(on)
66    }
67
68    /// Whether template renders should propagate errors (strict) rather than
69    /// warn-and-fall-back-to-raw (lenient).
70    ///
71    /// True when EITHER the guard's transient `render_strict` flag is set OR the
72    /// user passed the global `--strict`, so a malformed publisher/announce
73    /// template fails loud under the guard and under `--strict` everywhere.
74    pub fn render_is_strict(&self) -> bool {
75        self.render_strict.get() || self.is_strict()
76    }
77
78    /// In strict mode, return an error. In normal mode, log a warning and continue.
79    /// Use this for any situation where a configured feature silently skips.
80    pub fn strict_guard(&self, log: &crate::log::StageLogger, msg: &str) -> anyhow::Result<()> {
81        if self.options.strict {
82            anyhow::bail!("{} (strict mode)", msg);
83        }
84        log.warn(msg);
85        Ok(())
86    }
87
88    /// Defense-in-depth helper for upload-style stages.
89    ///
90    /// Returns `true` (after logging the skip) when the context is in snapshot
91    /// mode. Stages that perform external uploads (registries, package indexes,
92    /// object storage, snap store, …) call this at entry so they no-op even
93    /// when invoked directly without the orchestration layer's auto-skip.
94    /// Centralising the check keeps every publish stage consistent and avoids
95    /// per-stage copy-paste.
96    pub fn skip_in_snapshot(&self, log: &crate::log::StageLogger, stage: &str) -> bool {
97        if self.is_snapshot() {
98            // The stage name stays in the line: this guard fires on direct
99            // stage invocation, where no pipeline section header has named
100            // the stage yet.
101            log.status(&format!("skipped {stage} — snapshot mode"));
102            true
103        } else {
104            false
105        }
106    }
107
108    pub fn is_nightly(&self) -> bool {
109        self.options.nightly
110    }
111
112    /// Set the `ReleaseURL` template variable.
113    ///
114    /// Should be called after a GitHub release is created, with the URL of
115    /// the created release (e.g. `https://github.com/owner/repo/releases/tag/v1.0.0`).
116    pub fn set_release_url(&mut self, url: &str) {
117        self.template_vars.set("ReleaseURL", url);
118    }
119}