Fold a [`RawConfig`] into the canonical [`PersistedConfig`], returning the upgraded persisted state and a flag indicating whether the on-disk shape needed changes (in which case the caller should re-persist). Bails if the file was written by a newer schema version than this CLI knows about, to avoid silently downgrading it (which would drop any fields the newer schema added).
(raw: RawConfig)
| 164 | /// CLI knows about, to avoid silently downgrading it (which would |
| 165 | /// drop any fields the newer schema added). |
| 166 | fn migrate(raw: RawConfig) -> Result<(PersistedConfig, bool)> { |
| 167 | // Fast path: already at the current schema version. Legacy fields |
| 168 | // cannot exist in a v1 file we wrote, so anything stray is a hand |
| 169 | // edit and we deliberately ignore it. |
| 170 | if raw.version == Some(CURRENT_CONFIG_VERSION) { |
| 171 | let mut profiles = raw.profiles; |
| 172 | profiles.entry(DEFAULT_PROFILE_NAME.to_owned()).or_default(); |
| 173 | return Ok(( |
| 174 | PersistedConfig { |
| 175 | version: CURRENT_CONFIG_VERSION, |
| 176 | profiles, |
| 177 | }, |
| 178 | false, |
| 179 | )); |
| 180 | } |
| 181 | |
| 182 | let raw_version = raw.version.unwrap_or(0); |
| 183 | if raw_version > CURRENT_CONFIG_VERSION { |
| 184 | bail!( |
| 185 | "Config file was written by a newer version of CodSpeed (schema v{raw_version}, this CLI supports v{CURRENT_CONFIG_VERSION}). Upgrade the CLI to read it." |
| 186 | ); |
| 187 | } |
| 188 | let mut dirty = raw_version != CURRENT_CONFIG_VERSION; |
| 189 | |
| 190 | let mut profiles = raw.profiles; |
| 191 | |
| 192 | // v0 → v1: move legacy top-level auth.token into profiles.default |
| 193 | // (only if the profile slot is empty, so we don't clobber a value |
| 194 | // the user explicitly set per-profile). |
| 195 | if let Some(legacy_auth) = raw.auth |
| 196 | && let Some(token) = legacy_auth.token |
| 197 | { |
| 198 | dirty = true; |
| 199 | let default_profile = profiles.entry(DEFAULT_PROFILE_NAME.to_owned()).or_default(); |
| 200 | if default_profile.auth.token.is_none() { |
| 201 | default_profile.auth.token = Some(token); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // Ensure the default profile exists so consumers can rely on it. |
| 206 | profiles.entry(DEFAULT_PROFILE_NAME.to_owned()).or_default(); |
| 207 | |
| 208 | Ok(( |
| 209 | PersistedConfig { |
| 210 | version: CURRENT_CONFIG_VERSION, |
| 211 | profiles, |
| 212 | }, |
| 213 | dirty, |
| 214 | )) |
| 215 | } |
| 216 | |
| 217 | /// Write the canonical [`PersistedConfig`] to disk. |
| 218 | fn write_persisted(persisted: &PersistedConfig, config_name: Option<&str>) -> Result<()> { |
no outgoing calls