Skip to content

Commit

Permalink
Better app version number handling. (BrainiumLLC#67)
Browse files Browse the repository at this point in the history
* Add back missing closing delimiter to `app_dependencies_platform`

* Start working on supporting version numbers with N numbers

* Mode progress on Version Struct that supports N versions

* Let user specify a longer VersionNumber

* Progress on setting build version

* Remove Build number from Raw/Config

* Slight cleanup to getting app version in Archive

* Progress on setting app version for xcodebuild

* fix agvtool command

* Add more error logging for ios bundle versions

* Rename ret to result

* Rename  `WithCurrentDirError` to `WithWorkingDirError`

* remove std::path::* specifier

* Fix Typo

* Fix typo

* Rename IosVersionNumberError

* Use impure command for xcrun

* cleanup

* make InvalidVersionConfiguration error message more clear

* a bit more cleanup

* add `from_other_and_number` to VersionNumber

* simplify xcrun command

* Add back Pod use

* fix xcrun command

* roll back to pure  parse for xcrun

* better with_working_dir

* Make VersionNumber iOS only

* point-free

* Remove trivial `from_version_number` function

* some more point-frees

* err instead of cause

* map free once again

* small cleanup

* cleanup a bit more

* Use `push_extra` instead of `from_other_and_number`

* cleanup
  • Loading branch information
ArthurKValladares committed Mar 3, 2022
1 parent 35e419d commit cc755c4
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 41 deletions.
10 changes: 9 additions & 1 deletion src/apple/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub enum Command {
},
#[structopt(name = "archive", about = "Builds and archives for targets(s)")]
Archive {
#[structopt(long = "build-number")]
build_number: Option<u32>,
#[structopt(name = "targets", default_value = Target::DEFAULT_KEY, possible_values = Target::name_list())]
targets: Vec<String>,
#[structopt(flatten)]
Expand Down Expand Up @@ -305,6 +307,7 @@ impl Exec for Input {
}),
Command::Archive {
targets,
build_number,
profile: cli::Profile { profile },
} => with_config(non_interactive, wrapper, |config, _| {
version_check()?;
Expand All @@ -314,11 +317,16 @@ impl Exec for Input {
&detect_target_ok,
&env,
|target: &Target| {
let mut app_version = config.bundle_version().clone();
if let Some(build_number) = build_number {
app_version.push_extra(build_number);
}

target
.build(config, &env, noise_level, profile)
.map_err(Error::BuildFailed)?;
target
.archive(config, &env, noise_level, profile)
.archive(config, &env, noise_level, profile, Some(app_version))
.map_err(Error::ArchiveFailed)
},
)
Expand Down
94 changes: 79 additions & 15 deletions src/apple/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod raw;

pub use self::raw::*;

use super::version_number::{VersionNumber, VersionNumberError};
use crate::{
config::app::App,
util::{
Expand All @@ -16,7 +17,7 @@ use std::{
};

static DEFAULT_PROJECT_DIR: &str = "gen/apple";
const DEFAULT_BUNDLE_VERSION: VersionTriple = VersionTriple::new(1, 0, 0);
const DEFAULT_BUNDLE_VERSION: VersionNumber = VersionNumber::new(VersionTriple::new(1, 0, 0), None);
const DEFAULT_IOS_VERSION: VersionDouble = VersionDouble::new(9, 0);
const DEFAULT_MACOS_VERSION: VersionDouble = VersionDouble::new(11, 0);

Expand Down Expand Up @@ -193,6 +194,9 @@ pub enum Error {
BundleVersionInvalid(VersionTripleError),
IosVersionInvalid(VersionDoubleError),
MacOsVersionInvalid(VersionDoubleError),
IosVersionNumberInvalid(VersionNumberError),
IosVersionNumberMismatch,
InvalidVersionConfiguration,
}

impl Error {
Expand Down Expand Up @@ -221,19 +225,74 @@ impl Error {
msg,
format!("`{}.macos-version` invalid: {}", super::NAME, err),
),
Self::IosVersionNumberInvalid(err) => Report::error(
msg,
format!("`{}.app-version` invalid: {}", super::NAME, err),
),
Self::IosVersionNumberMismatch => Report::error(
msg,
format!(
"`{}.app-version` short and long version number don't match",
super::NAME
),
),
Self::InvalidVersionConfiguration => Report::error(
msg,
format!(
"`{}.app-version` `bundle-version-short` cannot be specified without also specifying `bundle-version`",
super::NAME
),
),
}
}
}

#[derive(Debug)]
pub(crate) struct VersionInfo {
pub version_number: Option<VersionNumber>,
pub short_version_number: Option<VersionTriple>,
}

impl VersionInfo {
pub(crate) fn from_raw(
version_string: &Option<String>,
short_version_string: &Option<String>,
) -> Result<Self, Error> {
let version_number = version_string
.as_deref()
.map(VersionNumber::from_str)
.transpose()
.map_err(Error::IosVersionNumberInvalid)?;
let short_version_number = short_version_string
.as_deref()
.map(VersionTriple::from_str)
.transpose()
.map_err(Error::BundleVersionInvalid)?;
if short_version_number.is_some() && version_number.is_none() {
return Err(Error::InvalidVersionConfiguration);
}
if let Some((version_number, short_version_number)) =
version_number.as_ref().zip(short_version_number)
{
if version_number.triple != short_version_number {
return Err(Error::IosVersionNumberMismatch);
}
}
Ok(Self {
version_number,
short_version_number,
})
}
}

#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct Config {
#[serde(skip_serializing)]
app: App,
development_team: String,
project_dir: String,
// TODO: Allow support for [3, inf) integers
bundle_version: VersionTriple,
bundle_version: VersionNumber,
bundle_version_short: VersionTriple,
ios_version: VersionDouble,
macos_version: VersionDouble,
Expand Down Expand Up @@ -276,23 +335,24 @@ impl Config {
Ok(DEFAULT_PROJECT_DIR.to_owned())
})?;

let bundle_version_short = raw
.bundle_version_short
.map(|str| VersionTriple::from_str(&str))
.transpose()
.map_err(Error::BundleVersionInvalid)?
.unwrap_or(DEFAULT_BUNDLE_VERSION);
let (bundle_version, bundle_version_short) =
VersionInfo::from_raw(&raw.bundle_version, &raw.bundle_version_short).map(|info| {
let bundle_version = info
.version_number
.clone()
.unwrap_or(DEFAULT_BUNDLE_VERSION);

let bundle_version_short =
info.short_version_number.unwrap_or(bundle_version.triple);

(bundle_version, bundle_version_short)
})?;

Ok(Self {
app,
development_team: raw.development_team,
project_dir,
bundle_version: raw
.bundle_version
.map(|str| VersionTriple::from_str(&str))
.transpose()
.map_err(Error::BundleVersionInvalid)?
.unwrap_or(bundle_version_short),
bundle_version,
bundle_version_short,
ios_version: raw
.ios_version
Expand Down Expand Up @@ -374,4 +434,8 @@ impl Config {
pub fn scheme(&self) -> String {
format!("{}_iOS", self.app.name())
}

pub fn bundle_version(&self) -> &VersionNumber {
&self.bundle_version
}
}
2 changes: 1 addition & 1 deletion src/apple/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'a> Device<'a> {
.map_err(RunError::BuildFailed)?;
println!("Archiving app...");
self.target
.archive(config, env, noise_level, profile)
.archive(config, env, noise_level, profile, None)
.map_err(RunError::ArchiveFailed)?;
println!("Exporting app...");
self.target
Expand Down
1 change: 1 addition & 0 deletions src/apple/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(crate) mod project;
pub(crate) mod system_profile;
mod target;
pub(crate) mod teams;
mod version_number;

use crate::util::{
self,
Expand Down
25 changes: 21 additions & 4 deletions src/apple/target.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use super::{
config::{Config, Metadata},
system_profile::{self, DeveloperTools},
version_number::VersionNumber,
};
use crate::{
env::{Env, ExplicitEnv as _},
opts::{self, ForceColor, NoiseLevel, Profile},
target::TargetTrait,
util::{
self,
cli::{Report, Reportable},
CargoCommand,
CargoCommand, WithWorkingDirError,
},
};
use once_cell_regex::exports::once_cell::sync::OnceCell;
Expand Down Expand Up @@ -94,11 +96,17 @@ impl Reportable for BuildError {
}

#[derive(Debug)]
pub struct ArchiveError(bossy::Error);
pub enum ArchiveError {
SetVersionFailed(WithWorkingDirError<bossy::Error>),
ArchiveFailed(bossy::Error),
}

impl Reportable for ArchiveError {
fn report(&self) -> Report {
Report::error("Failed to archive via `xcodebuild`", &self.0)
match self {
Self::SetVersionFailed(err) => Report::error("Failed to set app version number", err),
Self::ArchiveFailed(err) => Report::error("Failed to archive via `xcodebuild`", err),
}
}
}

Expand Down Expand Up @@ -295,7 +303,16 @@ impl<'a> Target<'a> {
env: &Env,
noise_level: opts::NoiseLevel,
profile: opts::Profile,
build_number: Option<VersionNumber>,
) -> Result<(), ArchiveError> {
if let Some(build_number) = build_number {
util::with_working_dir(config.project_dir(), || {
bossy::Command::pure_parse("xcrun agvtool new-version -all")
.with_arg(&build_number.to_string())
.run_and_wait()
})
.map_err(ArchiveError::SetVersionFailed)?;
}
let configuration = profile.as_str();
let archive_path = config.archive_dir().join(&config.scheme());
bossy::Command::pure("xcodebuild")
Expand All @@ -312,7 +329,7 @@ impl<'a> Target<'a> {
.with_arg("-archivePath")
.with_arg(&archive_path)
.run_and_wait()
.map_err(ArchiveError)?;
.map_err(ArchiveError::ArchiveFailed)?;
Ok(())
}

Expand Down
89 changes: 89 additions & 0 deletions src/apple/version_number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::util::{VersionTriple, VersionTripleError};
use serde::{ser::Serializer, Serialize};
use std::fmt::{self, Debug, Display};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum VersionNumberError {
#[error("Failed to parse version triple.")]
VersionTripleInvalid(#[from] VersionTripleError),
#[error("Failed to parse extra version from {version:?}: {source}")]
ExtraVersionInvalid {
version: String,
source: std::num::ParseIntError,
},
}

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct VersionNumber {
pub triple: VersionTriple,
pub extra: Option<Vec<u32>>,
}

impl Display for VersionNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.triple)?;
if let Some(extra) = &self.extra {
for number in extra {
write!(f, ".{}", number)?;
}
}
Ok(())
}
}

impl Serialize for VersionNumber {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}

impl VersionNumber {
pub fn new_from_triple(triple: VersionTriple) -> Self {
Self {
triple,
extra: None,
}
}

pub const fn new(triple: VersionTriple, extra: Option<Vec<u32>>) -> Self {
Self { triple, extra }
}

pub fn from_str(v: &str) -> Result<Self, VersionNumberError> {
match v.split(".").count() {
1 | 2 | 3 => {
let triple = VersionTriple::from_str(v)?;
Ok(Self {
triple,
extra: None,
})
}
// Even when splitting a string that does not contain the delimeter, we should always get at least 1 split
// (the full string, which could be the empty string)
0 => unreachable!(),
_ => {
let mut s = v.split(".");
let triple = VersionTriple::from_split(&mut s, v)?;
let extra = Some(
s.map(|s| {
s.parse()
.map_err(|source| VersionNumberError::ExtraVersionInvalid {
version: v.to_owned(),
source,
})
})
.collect::<Result<Vec<_>, _>>()?,
);
Ok(Self { triple, extra })
}
}
}

pub fn push_extra(&mut self, number: u32) {
self.extra.get_or_insert_with(Default::default).push(number);
}
}
Loading

0 comments on commit cc755c4

Please sign in to comment.