diff options
| author | Joseph Hunkeler <jhunkeler@gmail.com> | 2023-11-20 08:49:43 -0500 | 
|---|---|---|
| committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2023-11-20 08:49:43 -0500 | 
| commit | 601681d88387dddda70db8601a98cb89edea1d78 (patch) | |
| tree | 2868f12cd952090d62600def59a1e512522b3495 | |
| parent | b9576b9bddc5887d912643f7822d13df7140c84a (diff) | |
| download | stasis-601681d88387dddda70db8601a98cb89edea1d78.tar.gz | |
Implements slightly improved error handling
* Return status from conda_ and delivery_ functions
* Caller prints meaningful error, and handles it
* Enable warnings during compilation
* Squelch format-truncation warning -- those strings are extremely unlikely to overflow (famous last words)
| -rw-r--r-- | include/conda.h | 8 | ||||
| -rw-r--r-- | include/deliverable.h | 7 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/conda.c | 59 | ||||
| -rw-r--r-- | src/deliverable.c | 75 | ||||
| -rw-r--r-- | src/ini.c | 25 | ||||
| -rw-r--r-- | src/main.c | 133 | 
7 files changed, 216 insertions, 92 deletions
| diff --git a/include/conda.h b/include/conda.h index bc74b94..3565bda 100644 --- a/include/conda.h +++ b/include/conda.h @@ -16,9 +16,9 @@ int pip_exec(const char *args);  int conda_exec(const char *args);  int conda_activate(const char *root, const char *env_name);  void conda_setup_headless(); -void conda_env_create_from_uri(char *name, char *uri); -void conda_env_create(char *name, char *python_version, char *packages); -void conda_env_remove(char *name); -void conda_env_export(char *name, char *output_dir, char *output_filename); +int conda_env_create_from_uri(char *name, char *uri); +int conda_env_create(char *name, char *python_version, char *packages); +int conda_env_remove(char *name); +int conda_env_export(char *name, char *output_dir, char *output_filename);  int conda_index(const char *path);  #endif //OMC_CONDA_H diff --git a/include/deliverable.h b/include/deliverable.h index 02c13e1..b585054 100644 --- a/include/deliverable.h +++ b/include/deliverable.h @@ -72,7 +72,6 @@ struct Delivery {          char *mission;             ///< hst, jwst, roman          char *codename;            ///< HST uses codenames          bool final;                ///< is this a final release? -        bool continue_on_error;    ///< keep going, or don't      } meta;      /*! \struct Info @@ -187,7 +186,7 @@ int delivery_index_wheel_artifacts(struct Delivery *ctx);   * @return header on success   * @return NULL on error   */ -char *delivery_get_spec_header(struct Delivery *ctx); +char *delivery_get_release_header(struct Delivery *ctx);  /**   * Finalizes a delivery artifact for distribution @@ -216,7 +215,7 @@ int delivery_copy_conda_artifacts(struct Delivery *ctx);   * Retrieve Conda installer   * @param installer_url URL to installation script   */ -void delivery_get_installer(char *installer_url); +int delivery_get_installer(char *installer_url);  /**   * Generate URL based on Delivery context @@ -237,7 +236,7 @@ void delivery_get_installer_url(struct Delivery *delivery, char *result);   * @param type INSTALL_PKG_PIP_DEFERRED   * @param manifest pointer to array of StrList (package list(s))   */ -void delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, char *env_name, int type, struct StrList *manifest[]); +int delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, char *env_name, int type, struct StrList *manifest[]);  /**   * Update "conda index" on Conda artifact storage diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f996f2..e22e749 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@  include_directories(${CMAKE_SOURCE_DIR}/include)  include_directories(${PROJECT_BINARY_DIR}) +add_compile_options(-Wall -Wextra -Wno-format-truncation)  add_executable(omc          main.c          str.c diff --git a/src/conda.c b/src/conda.c index 79e4949..a361267 100644 --- a/src/conda.c +++ b/src/conda.c @@ -5,6 +5,8 @@  #include <unistd.h>  #include "conda.h" +extern struct OMC_GLOBAL globals; +  int python_exec(const char *args) {      char command[PATH_MAX];      memset(command, 0, sizeof(command)); @@ -104,7 +106,7 @@ int conda_activate(const char *root, const char *env_name) {      // Parse the log file:      // 1. Extract the environment keys and values from the sub-shell -    // 2. Apply it to ohmycal's runtime environment +    // 2. Apply it to OMC's runtime environment      // 3. Now we're ready to execute conda commands anywhere      fp = fopen(proc.stdout, "r");      if (!fp) { @@ -115,7 +117,7 @@ int conda_activate(const char *root, const char *env_name) {      while (!feof(fp)) {          char buf[OMC_BUFSIZ] = {0};          int ch = 0; -        int z = 0; +        size_t z = 0;          while (z < sizeof(buf) && (ch = (int) fgetc(fp)) != 0) {              if (ch == EOF) {                  ch = 0; @@ -160,47 +162,50 @@ void conda_setup_headless() {      conda_exec("config --system --set rollback_enabled false");      conda_exec("config --system --set report_errors false"); -    // make this configurable -    //if (conda_exec("update --all")) { -    //    fprintf(stderr, "conda update was unsuccessful\n"); -    //    exit(1); -    //} +    if (globals.verbose) { +        char *rcfile = getenv("CONDARC"); +        if (rcfile) { +            msg(OMC_MSG_L1, "Dump %s\n", rcfile); +            char **condarc_contents = file_readlines(rcfile, 0, 0, NULL); +            for (size_t i = 0; condarc_contents[i] != NULL; i++) { +                msg(OMC_MSG_L2, "%s", condarc_contents[i]); +                free(condarc_contents[i]); +            } +            free(condarc_contents); +        } + +    } + +    if (globals.always_update_base_environment) { +        if (conda_exec("update --all")) { +            fprintf(stderr, "conda update was unsuccessful\n"); +            exit(1); +        } +    }  } -void conda_env_create_from_uri(char *name, char *uri) { +int conda_env_create_from_uri(char *name, char *uri) {      char env_command[PATH_MAX];      sprintf(env_command, "env create -n %s -f %s", name, uri); -    if (conda_exec(env_command)) { -        fprintf(stderr, "derived environment creation failed\n"); -        exit(1); -    } +    return conda_exec(env_command);  } -void conda_env_create(char *name, char *python_version, char *packages) { +int conda_env_create(char *name, char *python_version, char *packages) {      char env_command[PATH_MAX];      sprintf(env_command, "create -n %s python=%s %s", name, python_version, packages ? packages : ""); -    if (conda_exec(env_command)) { -        fprintf(stderr, "conda environment creation failed\n"); -        exit(1); -    } +    return conda_exec(env_command);  } -void conda_env_remove(char *name) { +int conda_env_remove(char *name) {      char env_command[PATH_MAX];      sprintf(env_command, "env remove -n %s", name); -    if (conda_exec(env_command)) { -        fprintf(stderr, "conda environment removal failed\n"); -        exit(1); -    } +    return conda_exec(env_command);  } -void conda_env_export(char *name, char *output_dir, char *output_filename) { +int conda_env_export(char *name, char *output_dir, char *output_filename) {      char env_command[PATH_MAX];      sprintf(env_command, "env export -n %s -f %s/%s.yml", name, output_dir, output_filename); -    if (conda_exec(env_command)) { -        fprintf(stderr, "conda environment export failed\n"); -        exit(1); -    } +    return conda_exec(env_command);  }  int conda_index(const char *path) { diff --git a/src/deliverable.c b/src/deliverable.c index f8e5145..cf25e25 100644 --- a/src/deliverable.c +++ b/src/deliverable.c @@ -420,7 +420,7 @@ static char *requirement_from_test(struct Delivery *ctx, const char *name) {      return result;  } -void delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, char *env_name, int type, struct StrList **manifest) { +int delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, char *env_name, int type, struct StrList **manifest) {      char cmd[PATH_MAX];      char pkgs[OMC_BUFSIZ];      char *env_current = getenv("CONDA_DEFAULT_ENV"); @@ -458,6 +458,9 @@ void delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, ch          for (size_t p = 0; p < strlist_count(manifest[x]); p++) {              name = strlist_item(manifest[x], p);              strip(name); +            if (!strlen(name)) { +                continue; +            }              if (INSTALL_PKG_PIP_DEFERRED & type) {                  //DIR *dp;                  //struct dirent *rec; @@ -499,11 +502,12 @@ void delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, ch                  }              }          } -        if (runner(cmd)) { -            fprintf(stderr, "failed to install package: %s\n", name); -            exit(1); +        int status = runner(cmd); +        if (status) { +            return status;          }      } +    return 0;  }  void delivery_get_installer_url(struct Delivery *delivery, char *result) { @@ -524,13 +528,18 @@ void delivery_get_installer_url(struct Delivery *delivery, char *result) {  } -void delivery_get_installer(char *installer_url) { -    if (access(path_basename(installer_url), F_OK)) { -        if (download(installer_url, path_basename(installer_url))) { -            fprintf(stderr, "download failed: %s\n", installer_url); -            exit(1); +int delivery_get_installer(char *installer_url) { +    char *script = path_basename(installer_url); +    if (access(script, F_OK)) { +        // Script doesn't exist +        if (download(installer_url, script)) { +            // download failed +            return -1;          } +    } else { +        msg(OMC_MSG_RESTRICT | OMC_MSG_L3, "Skipped, installer already exists\n", script);      } +    return 0;  }  int delivery_copy_conda_artifacts(struct Delivery *ctx) { @@ -542,8 +551,11 @@ int delivery_copy_conda_artifacts(struct Delivery *ctx) {      memset(subdir, 0, sizeof(subdir));      sprintf(conda_build_dir, "%s/%s", ctx->storage.conda_install_prefix, "conda-bld"); +    // One must run conda build at least once to create the "conda-bld" directory. +    // When this directory is missing there can be no build artifacts.      if (access(conda_build_dir, F_OK) < 0) { -        // Conda build was never executed +        msg(OMC_MSG_RESTRICT | OMC_MSG_WARN | OMC_MSG_L3, +            "Skipped: 'conda build' has never been executed.\n");          return 0;      } @@ -710,6 +722,21 @@ char *delivery_get_spec_header(struct Delivery *ctx) {  void delivery_rewrite_spec(struct Delivery *ctx, char *filename) {      char *package_name = NULL;      char output[PATH_MAX]; +    char *header = NULL; +    char *tempfile = NULL; +    FILE *tp = NULL; + +    header = delivery_get_release_header(ctx); +    if (!header) { +        msg(OMC_MSG_ERROR, "failed to generate release header string\n", filename); +        exit(1); +    } +    tempfile = xmkstemp(&tp); +    if (!tempfile || !tp) { +        msg(OMC_MSG_ERROR, "%s: unable to create temporary file\n", strerror(errno)); +        exit(1); +    } +    fprintf(tp, "%s", header);      sprintf(output, "  - %s", ctx->storage.conda_staging_url);      file_replace_text(filename, "  - local", output); @@ -782,16 +809,21 @@ void delivery_tests_run(struct Delivery *ctx) {              msg(OMC_MSG_L3, "Cloning %s\n", ctx->tests[i].repository);              git_clone(&proc, ctx->tests[i].repository, destdir, ctx->tests[i].version); -            if (pushd(destdir) && !ctx->meta.continue_on_error) { -                fprintf(stderr, "unable to enter repository directory\n"); -                exit(1); +            if (pushd(destdir)) { +                COE_CHECK_ABORT(!globals.continue_on_error, "Unable to enter repository directory\n");              } else {  #if 1 -                msg(OMC_MSG_L3, "Running\n"); +                int status; +                char cmd[PATH_MAX]; +                msg(OMC_MSG_L3, "Testing %s\n", ctx->tests[i].name);                  memset(&proc, 0, sizeof(proc)); -                if (shell2(&proc, ctx->tests[i].script) && !ctx->meta.continue_on_error) { -                    fprintf(stderr, "continue on error is not enabled. aborting.\n"); -                    exit(1); + +                // enable trace mode before executing each test script +                memset(cmd, 0, sizeof(cmd)); +                sprintf(cmd, "set -x ; %s", ctx->tests[i].script); +                status = shell2(&proc, cmd); +                if (status) { +                    COE_CHECK_ABORT(!globals.continue_on_error, "Test failure");                  }                  popd();  #else @@ -800,6 +832,15 @@ void delivery_tests_run(struct Delivery *ctx) {              }          }      } +} +void delivery_gather_tool_versions(struct Delivery *ctx) { +    // Extract version from tool output +    ctx->conda.tool_version = shell_output("conda --version"); +    if (ctx->conda.tool_version) +        strip(ctx->conda.tool_version); +    ctx->conda.tool_build_version = shell_output("conda build --version"); +    if (ctx->conda.tool_build_version) +        strip(ctx->conda.tool_version);  } @@ -56,7 +56,7 @@ struct INIData *ini_getall(struct INIFILE *ini, char *section_name) {      static size_t i = 0;      section = ini_section_search(&ini, section_name); -    if (section->data[i]) { +    if (section->data_count) {          result = section->data[i];          i++;      } else { @@ -138,8 +138,7 @@ int ini_data_record(struct INIFILE **ini, char *section_name, char *key, char *v      struct INIData **tmp = realloc(section->data, (section->data_count + 1) * sizeof(**section->data));      if (!tmp) { -        perror(__FUNCTION__); -        exit(1); +        return 1;      }      section->data = tmp;      if (!ini_data_get((*ini), section_name, key)) { @@ -157,7 +156,7 @@ int ini_data_record(struct INIFILE **ini, char *section_name, char *key, char *v          value_tmp = realloc(data->value, value_len_new + 2);          if (!value_tmp) {              perror(__FUNCTION__ ); -            exit(1); +            return -1;          }          data->value = value_tmp;           */ @@ -170,13 +169,19 @@ int ini_data_record(struct INIFILE **ini, char *section_name, char *key, char *v  void ini_section_record(struct INIFILE **ini, char *key) {      struct INISection **tmp = realloc((*ini)->section, ((*ini)->section_count + 1) * sizeof((*ini)->section));      if (!tmp) { -        perror(__FUNCTION__); -        exit(1); +        return 1;      }      (*ini)->section = tmp;      (*ini)->section[(*ini)->section_count] = calloc(1, sizeof(*(*ini)->section[0])); +    if (!(*ini)->section[(*ini)->section_count]) { +        return -1; +    }      (*ini)->section[(*ini)->section_count]->key = strdup(key); +    if (!(*ini)->section[(*ini)->section_count]->key) { +        return -1; +    }      (*ini)->section_count++; +    return 0;  }  void ini_show(struct INIFILE *ini) { @@ -238,6 +243,9 @@ struct INIFILE *ini_open(const char *filename) {      char current_section[OMC_BUFSIZ] = {0};      char *key_last = NULL;      struct INIFILE *ini = ini_init(); +    if (ini == NULL) { +        return NULL; +    }      ini_section_init(&ini); @@ -249,8 +257,9 @@ struct INIFILE *ini_open(const char *filename) {      // Open the configuration file for reading      fp = fopen(filename, "r");      if (!fp) { -        perror(filename); -        exit(1); +        ini_free(&ini); +        ini = NULL; +        return NULL;      }      // Read file @@ -1,10 +1,8 @@ -#define GNU_SOURCE 1 +#define GNU_SOURCE  #include <stdio.h>  #include <stdlib.h>  #include <string.h> -#include <unistd.h>  #include <limits.h> -#include <time.h>  #include <sys/utsname.h>  #include <getopt.h>  #include "omc.h" @@ -115,10 +113,10 @@ int main(int argc, char *argv[], char *arge[]) {              .stderr = "",              .redirect_stderr = 0,      }; -    char env_name[PATH_MAX]; -    char env_name_testing[PATH_MAX]; -    char *delivery_input = argv[1]; -    char *config_input = argv[2]; +    char env_name[255] = {0}; +    char env_name_testing[255] = {0}; +    char *delivery_input = NULL; // = argv[1]; +    char *config_input = NULL; // = argv[2];      char installer_url[PATH_MAX];      char python_override_version[NAME_MAX];      unsigned char arg_continue_on_error = 0; @@ -218,18 +216,38 @@ int main(int argc, char *argv[], char *arge[]) {      if (config_input) {          msg(OMC_MSG_L2, "Reading OMC global configuration: %s\n", config_input);          cfg = ini_open(config_input); -        //ini_show(cfg); +        if (!cfg) { +            msg(OMC_MSG_ERROR | OMC_MSG_L2, "Failed to read config file: %s, %s", delivery_input, strerror(errno)); +            exit(1); +        }      }      msg(OMC_MSG_L2, "Reading OMC delivery configuration: %s\n", delivery_input);      ini = ini_open(delivery_input); +    if (!ini) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "Failed to read delivery file: %s, %s", delivery_input, strerror(errno)); +        exit(1); +    }      printf(BANNER, VERSION, AUTHOR); -    delivery_init(&ctx, ini, cfg); +    if (delivery_init(&ctx, ini, cfg)) { +        msg(OMC_MSG_ERROR | OMC_MSG_L1, "Failed to initialize delivery context\n"); +        exit(1); +    } + +    globals.always_update_base_environment = arg_always_update_base_environment; +    globals.continue_on_error = arg_continue_on_error; + +    // Override Python version from command-line, if any +    if (strlen(python_override_version) && ctx.meta.python) { +        free(ctx.meta.python); +        ctx.meta.python = strdup(python_override_version); +    } +      runtime_apply(ctx.runtime.environ); -    sprintf(env_name, "%s", ctx.info.release_name); -    sprintf(env_name_testing, "%s_test", env_name); +    snprintf(env_name, sizeof(env_name_testing) - 1, "%s", ctx.info.release_name); +    snprintf(env_name_testing, sizeof(env_name) - 1, "%s_test", env_name);      msg(OMC_MSG_L1, "Overview\n");      delivery_meta_show(&ctx); @@ -239,7 +257,10 @@ int main(int argc, char *argv[], char *arge[]) {      msg(OMC_MSG_L1, "Conda setup\n");      delivery_get_installer_url(&ctx, installer_url);      msg(OMC_MSG_L2, "Downloading: %s\n", installer_url); -    delivery_get_installer(installer_url); +    if (delivery_get_installer(installer_url)) { +        msg(OMC_MSG_ERROR, "download failed: %s\n", installer_url); +        exit(1); +    }      // Unlikely to occur: this should help prevent rmtree() from destroying your entire filesystem      // if path is "/" then, die @@ -255,16 +276,51 @@ int main(int argc, char *argv[], char *arge[]) {      msg(OMC_MSG_L2, "Configuring: %s\n", ctx.storage.conda_install_prefix);      delivery_conda_enable(&ctx, ctx.storage.conda_install_prefix); +    char *pathvar = NULL; +    pathvar = getenv("PATH"); +    if (!pathvar) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "PATH variable is not set. Cannot continue.\n"); +        exit(1); +    } else { +        char pathvar_tmp[OMC_BUFSIZ]; +        sprintf(pathvar_tmp, "%s/bin:%s", ctx.storage.conda_install_prefix, pathvar); +        setenv("PATH", pathvar_tmp, 1); +    } + +    msg(OMC_MSG_L2, "Installing build tools\n"); +    if (conda_exec("install boa conda-build conda-verify")) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "conda-build installation failed\n"); +        exit(1); +    }      msg(OMC_MSG_L1, "Creating release environment(s)\n");      if (ctx.meta.based_on && strlen(ctx.meta.based_on)) { -        conda_env_remove(env_name); -        conda_env_create_from_uri(env_name, ctx.meta.based_on); +        if (conda_env_remove(env_name)) { +           msg(OMC_MSG_ERROR | OMC_MSG_L2, "failed to remove release environment: %s\n", env_name_testing); +           exit(1); +        } +        msg(OMC_MSG_L2, "Based on release: %s\n", ctx.meta.based_on); +        if (conda_env_create_from_uri(env_name, ctx.meta.based_on)) { +            msg(OMC_MSG_ERROR | OMC_MSG_L2, "unable to install release environment using configuration file\n"); +            exit(1); +        } -        conda_env_remove(env_name_testing); -        conda_env_create_from_uri(env_name_testing, ctx.meta.based_on); +        if (conda_env_remove(env_name_testing)) { +            msg(OMC_MSG_ERROR | OMC_MSG_L2, "failed to remove testing environment\n"); +            exit(1); +        } +        if (conda_env_create_from_uri(env_name_testing, ctx.meta.based_on)) { +            msg(OMC_MSG_ERROR | OMC_MSG_L2, "unable to install testing environment using configuration file\n"); +            exit(1); +        }      } else { -        conda_env_create(env_name, ctx.meta.python, NULL); -        conda_env_create(env_name_testing, ctx.meta.python, NULL); +        if (conda_env_create(env_name, ctx.meta.python, NULL)) { +            msg(OMC_MSG_ERROR | OMC_MSG_L2, "failed to create release environment\n"); +            exit(1); +        } +        if (conda_env_create(env_name_testing, ctx.meta.python, NULL)) { +            msg(OMC_MSG_ERROR | OMC_MSG_L2, "failed to create release environment\n"); +            exit(1); +        }      }      // Activate test environment @@ -274,15 +330,15 @@ int main(int argc, char *argv[], char *arge[]) {          exit(1);      } -    msg(OMC_MSG_L2, "Installing build tools\n"); -    if (conda_exec("install boa conda-build conda-verify")) { -        msg(OMC_MSG_ERROR | OMC_MSG_L2, "conda-build installation failed"); +    delivery_gather_tool_versions(&ctx); +    if (!ctx.conda.tool_version) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "Could not determine conda version\n"); +        exit(1); +    } +    if (!ctx.conda.tool_build_version) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "Could not determine conda-build version\n");          exit(1);      } - -    // TODO: extract version from tool output -    ctx.conda.tool_version = NULL; -    ctx.conda.tool_build_version = NULL;      if (pip_exec("install build")) {          msg(OMC_MSG_ERROR | OMC_MSG_L2, "'build' tool installation failed"); @@ -311,7 +367,7 @@ int main(int argc, char *argv[], char *arge[]) {      //    }      //} -    if (ctx.conda.conda_packages_defer) { +    if (ctx.conda.conda_packages_defer && strlist_count(ctx.conda.conda_packages_defer)) {          msg(OMC_MSG_L2, "Building Conda recipe(s)\n");          if (delivery_build_recipes(&ctx)) {              exit(1); @@ -328,11 +384,18 @@ int main(int argc, char *argv[], char *arge[]) {      // Populate the release environment      msg(OMC_MSG_L1, "Populating release environment\n"); -      msg(OMC_MSG_L2, "Installing conda packages\n"); -    delivery_install_packages(&ctx, ctx.storage.conda_install_prefix, env_name, INSTALL_PKG_CONDA, (struct StrList *[]) {ctx.conda.conda_packages, NULL}); -    msg(OMC_MSG_L3, "Installing deferred conda packages\n"); -    delivery_install_packages(&ctx, ctx.storage.conda_install_prefix, env_name, INSTALL_PKG_CONDA | INSTALL_PKG_CONDA_DEFERRED, (struct StrList *[]) {ctx.conda.conda_packages_defer, NULL}); +    if (delivery_install_packages(&ctx, ctx.storage.conda_install_prefix, env_name, INSTALL_PKG_CONDA, (struct StrList *[]) {ctx.conda.conda_packages, NULL})) { +        exit(1); +    } +    if (strlist_count(ctx.conda.conda_packages_defer)) { +        msg(OMC_MSG_L3, "Installing deferred conda packages\n"); +        if (delivery_install_packages(&ctx, ctx.storage.conda_install_prefix, env_name, INSTALL_PKG_CONDA | INSTALL_PKG_CONDA_DEFERRED, (struct StrList *[]) {ctx.conda.conda_packages_defer, NULL})) { +            exit(1); +        } +    } else { +        msg(OMC_MSG_L3, "No deferred conda packages\n"); +    }      msg(OMC_MSG_L2, "Installing pip packages\n");      delivery_install_packages(&ctx, ctx.storage.conda_install_prefix, env_name, INSTALL_PKG_PIP, (struct StrList *[]) {ctx.conda.pip_packages, NULL});      msg(OMC_MSG_L3, "Installing deferred pip packages\n"); @@ -342,10 +405,16 @@ int main(int argc, char *argv[], char *arge[]) {      msg(OMC_MSG_L1, "Creating release\n");      msg(OMC_MSG_L2, "Exporting %s\n", env_name_testing); -    conda_env_export(env_name_testing, ctx.storage.delivery_dir, env_name_testing); +    if (conda_env_export(env_name_testing, ctx.storage.delivery_dir, env_name_testing)) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "failed %s\n", env_name_testing); +        exit(1); +    }      msg(OMC_MSG_L2, "Exporting %s\n", env_name); -    conda_env_export(env_name, ctx.storage.delivery_dir, env_name); +    if (conda_env_export(env_name, ctx.storage.delivery_dir, env_name)) { +        msg(OMC_MSG_ERROR | OMC_MSG_L2, "failed %s\n", env_name); +        exit(1); +    }      // Rewrite release environment output (i.e. set package origin(s) to point to the deployment server, etc.)      char specfile[PATH_MAX]; | 
