Skip to content

Commit

Permalink
feat: implement nsis
Browse files Browse the repository at this point in the history
  • Loading branch information
amr-crabnebula committed Sep 6, 2023
1 parent da970b9 commit 28aecb3
Show file tree
Hide file tree
Showing 31 changed files with 1,432 additions and 29 deletions.
22 changes: 22 additions & 0 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,13 +516,35 @@ pub enum Resources {
Map(HashMap<String, String>),
}

/// Describes a shell command to be executed when a CLI hook is triggered.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(untagged)]
pub enum HookCommand {
/// Run the given script with the default options.
Script(String),
/// Run the given script with custom options.
ScriptWithOptions {
/// The script to execute.
script: String,
/// The working directory.
dir: Option<String>,
},
}

/// The packaging config.
#[derive(Deserialize, Serialize, Default, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Config {
/// The JSON schema for the config.
#[serde(rename = "$schema")]
pub schema: Option<String>,
/// Specify a command to run before starting to package an application.
#[serde(
default,
alias = "before-packaging-command",
alias = "before_packaging_command"
)]
pub before_packaging_command: Option<HookCommand>,
/// The log level.
#[serde(alias = "log-level", alias = "log_level")]
pub log_level: Option<LogLevel>,
Expand Down
41 changes: 41 additions & 0 deletions crates/packager/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@
"null"
]
},
"beforePackagingCommand": {
"description": "Specify a command to run before starting to package an application.",
"default": null,
"anyOf": [
{
"$ref": "#/definitions/HookCommand"
},
{
"type": "null"
}
]
},
"logLevel": {
"description": "The log level.",
"anyOf": [
Expand Down Expand Up @@ -245,6 +257,35 @@
},
"additionalProperties": false,
"definitions": {
"HookCommand": {
"description": "Describes a shell command to be executed when a CLI hook is triggered.",
"anyOf": [
{
"description": "Run the given script with the default options.",
"type": "string"
},
{
"description": "Run the given script with custom options.",
"type": "object",
"required": [
"script"
],
"properties": {
"script": {
"description": "The script to execute.",
"type": "string"
},
"dir": {
"description": "The working directory.",
"type": [
"string",
"null"
]
}
}
}
]
},
"LogLevel": {
"description": "An enum representing the available verbosity levels of the logger.",
"oneOf": [
Expand Down
13 changes: 13 additions & 0 deletions crates/packager/src/deb/main.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Desktop Entry]
Categories={{categories}}
{{#if comment}}
Comment={{comment}}
{{/if}}
Exec={{exec}}
Icon={{icon}}
Name={{name}}
Terminal=false
Type=Application
{{#if mime_type}}
MimeType={{mime_type}}
{{/if}}
2 changes: 2 additions & 0 deletions crates/packager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub enum Error {
NsisFailed(String),
#[error("Failed to get parent directory of a path")]
ParentDirNotFound,
#[error("{0} `{1}` failed with exit code {2}")]
HookCommandFailure(String, String, i32),
}

/// Convenient type alias of Result type for cargo-packager.
Expand Down
44 changes: 43 additions & 1 deletion crates/packager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ mod nsis;
target_os = "openbsd"
))]
mod rpm;
mod sign;
pub mod util;

use std::path::PathBuf;
use std::{path::PathBuf, process::Command};

use config::Config;
pub use config::PackageFormat;
Expand All @@ -76,6 +77,47 @@ pub fn package(config: &Config) -> Result<Vec<Package>> {
log:: warn!("Cross-platform compilation is experimental and does not support all features. Please use a matching host system for full compatibility.");
}

if let Some(hook) = &config.before_packaging_command {
let (mut cmd, script) = match hook {
cargo_packager_config::HookCommand::Script(script) => {
#[cfg(windows)]
let mut cmd = Command::new("cmd");
#[cfg(windows)]
cmd.arg("/S").arg("/C").arg(&script);
#[cfg(not(windows))]
let mut cmd = Command::new("sh");
#[cfg(not(windows))]
cmd.arg("-c").arg(&script);
(cmd, script)
}
cargo_packager_config::HookCommand::ScriptWithOptions { script, dir } => {
#[cfg(windows)]
let mut cmd = Command::new("cmd");
#[cfg(windows)]
cmd.arg("/S").arg("/C").arg(&script);
#[cfg(not(windows))]
let mut cmd = Command::new("sh");
#[cfg(not(windows))]
cmd.arg("-c").arg(&script);
if let Some(dir) = dir {
cmd.current_dir(dir);
}
(cmd, script)
}
};

log::info!(action = "Running"; "beforePackagingCommand `{}`", script);
let status = cmd.status()?;

if !status.success() {
return Err(crate::Error::HookCommandFailure(
"beforePackagingCommand".into(),
script.into(),
status.code().unwrap_or_default(),
));
}
}

let mut packages = Vec::new();

let formats = config
Expand Down
54 changes: 38 additions & 16 deletions crates/packager/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@ use clap::{ArgAction, CommandFactory, FromArgMatches, Parser};
use env_logger::fmt::Color;
use log::{log_enabled, Level};

fn load_configs_from_cwd(profile: &str, verbose: u8) -> Result<Vec<(PathBuf, Config)>> {
let metadata = cargo_metadata::MetadataCommand::new().exec()?;
fn load_configs_from_cwd(profile: &str, cli: &Cli) -> Result<Vec<(PathBuf, Config)>> {
let mut metadata_cmd = cargo_metadata::MetadataCommand::new();
if let Some(manifest_path) = &cli.manifest_path {
metadata_cmd.manifest_path(manifest_path);
}
let metadata = metadata_cmd.exec()?;
let mut configs = Vec::new();
for package in metadata.workspace_packages() {
// skip if this package was not specified in the explicit packages to build
if !cli
.packages
.as_ref()
.map(|packages| packages.contains(&package.name))
.unwrap_or(true)
{
continue;
}

if let Some(config) = package.metadata.get("packager") {
let mut config: Config = serde_json::from_value(config.to_owned())?;
if config.product_name.is_empty() {
Expand Down Expand Up @@ -41,7 +55,7 @@ fn load_configs_from_cwd(profile: &str, verbose: u8) -> Result<Vec<(PathBuf, Con
config.target_triple = util::target_triple()?;
}
if config.log_level.is_none() {
config.log_level.replace(match verbose {
config.log_level.replace(match cli.verbose {
0 => LogLevel::Info,
1 => LogLevel::Debug,
2.. => LogLevel::Trace,
Expand Down Expand Up @@ -79,34 +93,36 @@ fn try_run(cli: Cli) -> Result<()> {
use std::fmt::Write;

let profile = if cli.release {
"relase"
"release"
} else if let Some(profile) = &cli.profile {
profile.as_str()
} else {
"debug"
};

for (manifest_path, config) in load_configs_from_cwd(profile, cli.verbose)? {
let mut packages = Vec::new();
for (manifest_path, config) in load_configs_from_cwd(profile, &cli)? {
std::env::set_current_dir(
manifest_path
.parent()
.ok_or(cargo_packager::Error::ParentDirNotFound)?,
)?;

// create the packages
let packages = package(&config)?;

// print information when finished
let len = packages.len();
let pluralised = if len == 1 { "package" } else { "packages" };
let mut printable_paths = String::new();
for p in packages {
for path in &p.paths {
writeln!(printable_paths, " {}", util::display_path(path)).unwrap();
}
packages.extend(package(&config)?);
}

// print information when finished
let len = packages.len();
let pluralised = if len == 1 { "package" } else { "packages" };
let mut printable_paths = String::new();
for p in packages {
for path in &p.paths {
writeln!(printable_paths, " {}", util::display_path(path)).unwrap();
}
log::info!(action = "Finished"; "{} {} at:\n{}", len, pluralised, printable_paths);
}
log::info!(action = "Finished"; "{} {} at:\n{}", len, pluralised, printable_paths);

Ok(())
}

Expand Down Expand Up @@ -144,6 +160,12 @@ pub(crate) struct Cli {
/// Specify the cargo profile to use for packaging your app.
#[clap(long)]
profile: Option<String>,
/// Specify the packages to build.
#[clap(short, long)]
packages: Option<Vec<String>>,
/// Specify the manifest path to use for reading the configuration.
#[clap(long)]
manifest_path: Option<String>,
}

fn main() {
Expand Down
8 changes: 5 additions & 3 deletions crates/packager/src/nsis/installer.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,11 @@ Section Uninstall

; Delete app data
${If} $DeleteAppDataCheckboxState == 1
SetShellVarContext current
RmDir /r "$APPDATA\${BUNDLEID}"
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
!if "${BUNDLEID}" != ""
SetShellVarContext current
RmDir /r "$APPDATA\${BUNDLEID}"
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
!endif
${EndIf}

${GetOptions} $CMDLINE "/P" $R0
Expand Down
27 changes: 27 additions & 0 deletions crates/packager/src/nsis/languages/Arabic.nsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
LangString addOrReinstall ${LANG_ARABIC} "إضافة أو إزالة المكونات"
LangString alreadyInstalled ${LANG_ARABIC} "التطبيق مثبت بالفعل"
LangString alreadyInstalledLong ${LANG_ARABIC} "${PRODUCTNAME} ${VERSION} مثبت بالفعل. قم باختيار العملية التى تريدها ثم اضغط على التالى."
LangString appRunning ${LANG_ARABIC} "${PRODUCTNAME} مازال يعمل! من فضلك، قم بإغلاق التطبيق أولاً ثم حاول مرة أخرى."
LangString appRunningOkKill ${LANG_ARABIC} "${PRODUCTNAME} مازال يعمل!$\nاضغط OK لإغلاقه"
LangString chooseMaintenanceOption ${LANG_ARABIC} "قم باختيار نوع الصيانة التى تريدها."
LangString choowHowToInstall ${LANG_ARABIC} "قم باختيار طريقة تنصيب ${PRODUCTNAME}."
LangString createDesktop ${LANG_ARABIC} "اضف اختصار على سطح المكتب"
LangString dontUninstall ${LANG_ARABIC} "عدم إزالة"
LangString dontUninstallDowngrade ${LANG_ARABIC} "عدم إزالة (التخفيض بدون إزالة غير مسموح لهذا المثبت)"
LangString failedToKillApp ${LANG_ARABIC} "فشل فى غلف ${PRODUCTNAME}. من فضلك، قم بإغلاق التطبيق أولاً ثم حاول مرة أخرى."
LangString installingWebview2 ${LANG_ARABIC} "تنصيب WebView2..."
LangString newerVersionInstalled ${LANG_ARABIC} "يوجد نسخة جديدة من ${PRODUCTNAME} مثبتة بالغعل! لا ينصح بتنصيب نسخة اقدم من النسخة الحالية. اذا مازلت ترغب فى تنصيب النسخة الأقدم، فينصح بإزالة النسخة الحالية أولاً. قم باختيار العملية التى تريدها ثم اضغط على التالى للاستمرار."
LangString older ${LANG_ARABIC} "أقدم"
LangString olderOrUnknownVersionInstalled ${LANG_ARABIC} "نسخة $R4 من ${PRODUCTNAME} مثبتة بالفعل على نظامك. ينصح بإزالة النسخة الحالية قبل التنصيب. قم باختيار العملية التى تريدها ثم اضغط على التالى للاستمرار."
LangString silentDowngrades ${LANG_ARABIC} "التخفيض غير مسموح لهذا المثبت ولا يمكن الاستمرار فى الوضع الصامت. من فضلك، قم باستخدام الواجهة الرسومية."
LangString unableToUninstall ${LANG_ARABIC} "غير قادر على الإزالة!"
LangString uninstallApp ${LANG_ARABIC} "إزالة ${PRODUCTNAME}"
LangString uninstallBeforeInstalling ${LANG_ARABIC} "قم بإزالة التطبيق قبل التثبيت"
LangString unknown ${LANG_ARABIC} "غير معروفة"
LangString webview2AbortError ${LANG_ARABIC} "فشل فى تنصيب WebView2! لا يمكن تشغيل التطبيق من غيره. من فضلك، حاول إعادة تشغيل المثبت."
LangString webview2DownloadError ${LANG_ARABIC} "خطأ: فشل تنزيل WebView2 - $0"
LangString webview2DownloadSuccess ${LANG_ARABIC} "تم تنزيل WebView2 bootstrapper بنجاح"
LangString webview2Downloading ${LANG_ARABIC} "يتم تنزيل WebView2 bootstrapper..."
LangString webview2InstallError ${LANG_ARABIC} "خطأ: فشل فى تنصيب WebView2 بكود $1"
LangString webview2InstallSuccess ${LANG_ARABIC} "تم تنصيب WebView2 بنجاح"
LangString deleteAppData ${LANG_ARABIC} "مسح بيانات التطبيق"
27 changes: 27 additions & 0 deletions crates/packager/src/nsis/languages/Bulgarian.nsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
LangString addOrReinstall ${LANG_BULGARIAN} "Добавяне/Преинсталиране на компоненти"
LangString alreadyInstalled ${LANG_BULGARIAN} "Вече инсталиран"
LangString alreadyInstalledLong ${LANG_BULGARIAN} "${PRODUCTNAME} ${VERSION} е вече е инсталиран. Изберете операцията, която искате да извършите и натиснете Напред, за да продължите."
LangString appRunning ${LANG_BULGARIAN} "${PRODUCTNAME} е отворен! Моля, затворете го първо и опитайте отново."
LangString appRunningOkKill ${LANG_BULGARIAN} "${PRODUCTNAME} е отворен!$\nНатиснете ОК, за да го затворите."
LangString chooseMaintenanceOption ${LANG_BULGARIAN} "Изберете опция за поддръжка."
LangString choowHowToInstall ${LANG_BULGARIAN} "Изберете как искате да инсталирате ${PRODUCTNAME}."
LangString createDesktop ${LANG_BULGARIAN} "Създайте пряк път на работния плот"
LangString dontUninstall ${LANG_BULGARIAN} "Не деинсталирайте"
LangString dontUninstallDowngrade ${LANG_BULGARIAN} "Не деинсталирайте (Понижаването без деинсталация е забранено за този инсталатор)"
LangString failedToKillApp ${LANG_BULGARIAN} "Неуспешно прекратяване на ${PRODUCTNAME}. Моля, затворете го първо и опитайте отново."
LangString installingWebview2 ${LANG_BULGARIAN} "Инсталиране на WebView2..."
LangString newerVersionInstalled ${LANG_BULGARIAN} "Вече е инсталирана по-нова версия на ${PRODUCTNAME}! Не се препоръчва да инсталирате по-стара версия. Ако наистина желаете да инсталирате тази по-стара версия, по-добре е да деинсталирате текущата версия първо. Изберете операцията, която искате да извършите и натиснете Напред, за да продължите."
LangString older ${LANG_BULGARIAN} "по-стара"
LangString olderOrUnknownVersionInstalled ${LANG_BULGARIAN} "На вашата система е инсталирана $R4 версия на ${PRODUCTNAME}. Препоръчително е да деинсталирате текущата версия преди да инсталирате нова. Изберете операцията, която искате да извършите и натиснете Напред, за да продължите."
LangString silentDowngrades ${LANG_BULGARIAN} "Понижаванията не са позволени за този инсталатор. Не е възможно да продължите с безшумен инсталатор. Моля, използвайте графичния интерфейсен инсталатор."
LangString unableToUninstall ${LANG_BULGARIAN} "Неуспешна деинсталация!"
LangString uninstallApp ${LANG_BULGARIAN} "Деинсталиране на ${PRODUCTNAME}"
LangString uninstallBeforeInstalling ${LANG_BULGARIAN} "Деинсталирай преди инсталиране"
LangString unknown ${LANG_BULGARIAN} "неизвестно"
LangString webview2AbortError ${LANG_BULGARIAN} "Неуспешно инсталиране на WebView2! Приложението не може да работи без него. Опитайте да рестартирате инсталатора."
LangString webview2DownloadError ${LANG_BULGARIAN} "Грешка: Неуспешно изтегляне на WebView2 - $0"
LangString webview2DownloadSuccess ${LANG_BULGARIAN} "Стартиращият файл на WebView2 е изтеглен успешно"
LangString webview2Downloading ${LANG_BULGARIAN} "Изтегляне на стартиращят файл на WebView2..."
LangString webview2InstallError ${LANG_BULGARIAN} "Грешка: Инсталирането на WebView2 неуспешно с код на изход $1"
LangString webview2InstallSuccess ${LANG_BULGARIAN} "WebView2 инсталиран успешно"
LangString deleteAppData ${LANG_BULGARIAN} "Изтриване на данните на приложението"
Loading

0 comments on commit 28aecb3

Please sign in to comment.