diff options
| author | Joseph Hunkeler <jhunkeler@users.noreply.github.com> | 2024-12-19 10:04:35 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-19 10:04:35 -0500 | 
| commit | d117a9724e9335ba738fd5cb99c27d4793a4976d (patch) | |
| tree | 684451f6b834be5b32275cc360b587030bcf02b5 | |
| parent | 8186ad89bbd7cfab65700045eaa9b26f8f877bfb (diff) | |
| parent | 54ac1e2e256252ec75fbdfe668a3328fb5d02c31 (diff) | |
| download | stasis-d117a9724e9335ba738fd5cb99c27d4793a4976d.tar.gz | |
Merge pull request #78 from jhunkeler/remove-package-from-delivery
Allow removing packages from a delivery
| -rw-r--r-- | README.md | 20 | ||||
| -rw-r--r-- | src/cli/stasis/stasis_main.c | 30 | ||||
| -rw-r--r-- | src/lib/delivery/delivery_install.c | 58 | ||||
| -rw-r--r-- | src/lib/delivery/delivery_populate.c | 73 | ||||
| -rw-r--r-- | src/lib/delivery/include/delivery.h | 14 | 
5 files changed, 151 insertions, 44 deletions
| @@ -246,15 +246,17 @@ All configuration section names and keys are _case-sensitive_.  ### conda -| Key                | Type   | Purpose                    | Required | -|--------------------|--------|----------------------------|----------| -| installer_name     | String | Conda distribution name    | Y        | -| installer_version  | String | Conda distribution version | Y        | -| installer_platform | String | Conda target platform      | Y        | -| installer_arch     | String | Conda target architecture  | Y        | -| installer_baseurl  | String | Conda installer URL        | Y        | -| conda_packages     | List   | Conda packages to install  | N        | -| pip_packages       | List   | Pypi packages to install   | N        | +| Key                  | Type   | Purpose                    | Required | +|----------------------|--------|----------------------------|----------| +| installer_name       | String | Conda distribution name    | Y        | +| installer_version    | String | Conda distribution version | Y        | +| installer_platform   | String | Conda target platform      | Y        | +| installer_arch       | String | Conda target architecture  | Y        | +| installer_baseurl    | String | Conda installer URL        | Y        | +| conda_packages       | List   | Conda packages to install  | N        | +| conda_packages_purge | List   | Conda packages to remove   | N        | +| pip_packages         | List   | Pypi packages to install   | N        | +| pip_packages_purge   | List   | Conda packages to remove   | N        |  ### runtime diff --git a/src/cli/stasis/stasis_main.c b/src/cli/stasis/stasis_main.c index dc4e2d1..61f198e 100644 --- a/src/cli/stasis/stasis_main.c +++ b/src/cli/stasis/stasis_main.c @@ -390,6 +390,20 @@ int main(int argc, char *argv[]) {      }      if (!isempty(ctx.meta.based_on)) { +        if (ctx.conda.conda_packages_purge && strlist_count(ctx.conda.conda_packages_purge)) { +            msg(STASIS_MSG_L2, "Purging conda packages\n"); +            if (delivery_purge_packages(&ctx, env_name_testing, PKG_USE_CONDA)) { +                msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "unable to purge requested conda packages from %s\n", env_name_testing); +                exit(1); +            } +        } +        if (ctx.conda.pip_packages_purge && strlist_count(ctx.conda.pip_packages_purge)) { +            msg(STASIS_MSG_L2, "Purging pip packages\n"); +            if (delivery_purge_packages(&ctx, env_name_testing, PKG_USE_PIP)) { +                msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "unable to purge requested pip packages from %s\n", env_name_testing); +                exit(1); +            } +        }          msg(STASIS_MSG_L1, "Generating package overlay from environment: %s\n", env_name);          if (delivery_overlay_packages_from_env(&ctx, env_name)) {              msg(STASIS_MSG_L2 | STASIS_MSG_ERROR, "%s", "Failed to generate package overlay. Resulting environment integrity cannot be guaranteed.\n"); @@ -462,6 +476,22 @@ int main(int argc, char *argv[]) {          msg(STASIS_MSG_L3, "No deferred conda packages\n");      } +    if (ctx.conda.conda_packages_purge && strlist_count(ctx.conda.conda_packages_purge)) { +        msg(STASIS_MSG_L2, "Purging conda packages\n"); +        if (delivery_purge_packages(&ctx, env_name, PKG_USE_CONDA)) { +            msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "unable to purge requested conda packages\n"); +            exit(1); +        } +    } + +    if (ctx.conda.pip_packages_purge && strlist_count(ctx.conda.pip_packages_purge)) { +        msg(STASIS_MSG_L2, "Purging pip packages\n"); +        if (delivery_purge_packages(&ctx, env_name, PKG_USE_PIP)) { +            msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "unable to purge requested pip packages"); +            exit(1); +        } +    } +      msg(STASIS_MSG_L2, "Installing pip packages\n");      if (strlist_count(ctx.conda.pip_packages)) {          if (delivery_install_packages(&ctx, ctx.storage.conda_install_prefix, env_name, INSTALL_PKG_PIP, (struct StrList *[]) {ctx.conda.pip_packages, NULL})) { diff --git a/src/lib/delivery/delivery_install.c b/src/lib/delivery/delivery_install.c index a348346..77bd60b 100644 --- a/src/lib/delivery/delivery_install.c +++ b/src/lib/delivery/delivery_install.c @@ -122,6 +122,64 @@ int delivery_overlay_packages_from_env(struct Delivery *ctx, const char *env_nam      return 0;  } +int delivery_purge_packages(struct Delivery *ctx, const char *env_name, int use_pkg_manager) { +    int status = 0; +    char subcommand[100] = {0}; +    char package_manager[100] = {0}; +    typedef int (fnptr)(const char *); + +    const char *current_env = conda_get_active_environment(); +    if (current_env) { +        conda_activate(globals.conda_install_prefix, env_name); +    } + +    struct StrList *list = NULL; +    fnptr *fn = NULL; +    switch (use_pkg_manager) { +        case PKG_USE_CONDA: +            fn = conda_exec; +            list = ctx->conda.conda_packages_purge; +            strcpy(package_manager, "conda"); +            // conda is already configured for "always_yes" +            strcpy(subcommand, "remove"); +            break; +        case PKG_USE_PIP: +            fn = pip_exec; +            list = ctx->conda.pip_packages_purge; +            strcpy(package_manager, "pip"); +            // avoid user prompt to remove packages +            strcpy(subcommand, "uninstall -y"); +            break; +        default: +            SYSERROR("Unknown package manager: %d", use_pkg_manager); +            status = -1; +            break; +    } + +    for (size_t i = 0; i < strlist_count(list); i++) { +        char *package = strlist_item(list, i); +        char *command = NULL; +        if (asprintf(&command, "%s '%s'", subcommand, package) < 0) { +            SYSERROR("%s", "Unable to allocate bytes for removal command"); +            status = -1; +            break; +        } + +        if (fn(command)) { +            SYSERROR("%s removal operation failed", package_manager); +            guard_free(command); +            status = 1; +            break; +        } +    } + +    if (current_env) { +        conda_activate(globals.conda_install_prefix, current_env); +    } + +    return status; +} +  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[STASIS_BUFSIZ]; diff --git a/src/lib/delivery/delivery_populate.c b/src/lib/delivery/delivery_populate.c index c699545..e9aea89 100644 --- a/src/lib/delivery/delivery_populate.c +++ b/src/lib/delivery/delivery_populate.c @@ -74,6 +74,37 @@ int populate_delivery_cfg(struct Delivery *ctx, int render_mode) {      return 0;  } +static void normalize_ini_list(struct INIFILE **inip, struct StrList **listp, char * const section, char * const key, int render_mode) { +    struct INIFILE *ini = *inip; +    struct StrList *list = *listp; +    if (!list) { +        return; +    } +    char **data = list->data; +    if (!data) { +        return; +    } + +    if (data[0] && strpbrk(data[0], " \t")) { +        normalize_space(data[0]); +        replace_text(data[0], " ", LINE_SEP, 0); +        char *replacement = join(data, LINE_SEP); +        ini_setval(&ini, INI_SETVAL_REPLACE, section, key, replacement); +        guard_free(replacement); +        guard_strlist_free(&list); +        int err = 0; +        list = ini_getval_strlist(ini, section, key, LINE_SEP, render_mode, &err); +    } + +    for (size_t i = 0; i < strlist_count(list); i++) { +        char *pkg = strlist_item(list, i); +        if (strpbrk(pkg, ";#") || isempty(pkg)) { +            strlist_remove(list, i); +        } +    } +    (*inip) = ini; +    (*listp) = list; +}  int populate_delivery_ini(struct Delivery *ctx, int render_mode) {      struct INIFILE *ini = ctx->_stasis_ini_fp.delivery;      struct INIData *rtdata; @@ -118,42 +149,14 @@ int populate_delivery_ini(struct Delivery *ctx, int render_mode) {      ctx->conda.installer_platform = ini_getval_str(ini, "conda", "installer_platform", render_mode, &err);      ctx->conda.installer_arch = ini_getval_str(ini, "conda", "installer_arch", render_mode, &err);      ctx->conda.installer_baseurl = ini_getval_str(ini, "conda", "installer_baseurl", render_mode, &err); -    ctx->conda.conda_packages = ini_getval_strlist(ini, "conda", "conda_packages", " "LINE_SEP, render_mode, &err); - -    if (ctx->conda.conda_packages->data && ctx->conda.conda_packages->data[0] && strpbrk(ctx->conda.conda_packages->data[0], " \t")) { -        normalize_space(ctx->conda.conda_packages->data[0]); -        replace_text(ctx->conda.conda_packages->data[0], " ", LINE_SEP, 0); -        char *pip_packages_replacement = join(ctx->conda.conda_packages->data, LINE_SEP); -        ini_setval(&ini, INI_SETVAL_REPLACE, "conda", "conda_packages", pip_packages_replacement); -        guard_free(pip_packages_replacement); -        guard_strlist_free(&ctx->conda.conda_packages); -        ctx->conda.conda_packages = ini_getval_strlist(ini, "conda", "conda_packages", LINE_SEP, render_mode, &err); -    } - -    for (size_t i = 0; i < strlist_count(ctx->conda.conda_packages); i++) { -        char *pkg = strlist_item(ctx->conda.conda_packages, i); -        if (strpbrk(pkg, ";#") || isempty(pkg)) { -            strlist_remove(ctx->conda.conda_packages, i); -        } -    } - +    ctx->conda.conda_packages = ini_getval_strlist(ini, "conda", "conda_packages", LINE_SEP, render_mode, &err); +    normalize_ini_list(&ini, &ctx->conda.conda_packages, "conda", "conda_packages", render_mode); +    ctx->conda.conda_packages_purge = ini_getval_strlist(ini, "conda", "conda_packages_purge", LINE_SEP, render_mode, &err); +    normalize_ini_list(&ini, &ctx->conda.conda_packages_purge, "conda", "conda_package_purge", render_mode);      ctx->conda.pip_packages = ini_getval_strlist(ini, "conda", "pip_packages", LINE_SEP, render_mode, &err); -    if (ctx->conda.pip_packages->data && ctx->conda.pip_packages->data[0] && strpbrk(ctx->conda.pip_packages->data[0], " \t")) { -        normalize_space(ctx->conda.pip_packages->data[0]); -        replace_text(ctx->conda.pip_packages->data[0], " ", LINE_SEP, 0); -        char *pip_packages_replacement = join(ctx->conda.pip_packages->data, LINE_SEP); -        ini_setval(&ini, INI_SETVAL_REPLACE, "conda", "pip_packages", pip_packages_replacement); -        guard_free(pip_packages_replacement); -        guard_strlist_free(&ctx->conda.pip_packages); -        ctx->conda.pip_packages = ini_getval_strlist(ini, "conda", "pip_packages", LINE_SEP, render_mode, &err); -    } - -    for (size_t i = 0; i < strlist_count(ctx->conda.pip_packages); i++) { -        char *pkg = strlist_item(ctx->conda.pip_packages, i); -        if (strpbrk(pkg, ";#") || isempty(pkg)) { -            strlist_remove(ctx->conda.pip_packages, i); -        } -    } +    normalize_ini_list(&ini, &ctx->conda.pip_packages, "conda", "pip_packages", render_mode); +    ctx->conda.pip_packages_purge = ini_getval_strlist(ini, "conda", "pip_packages_purge", LINE_SEP, render_mode, &err); +    normalize_ini_list(&ini, &ctx->conda.pip_packages_purge, "conda", "pip_packages_purge", render_mode);      // Delivery metadata consumed      populate_mission_ini(&ctx, render_mode); diff --git a/src/lib/delivery/include/delivery.h b/src/lib/delivery/include/delivery.h index a3843f5..26a5499 100644 --- a/src/lib/delivery/include/delivery.h +++ b/src/lib/delivery/include/delivery.h @@ -141,8 +141,10 @@ struct Delivery {          char *tool_build_version;               ///< Installed version of "build" package          struct StrList *conda_packages;         ///< Conda packages to deliver          struct StrList *conda_packages_defer;   ///< Conda recipes to be built for delivery +        struct StrList *conda_packages_purge;   ///< Conda packages to remove from a delivery (for: based_on)          struct StrList *pip_packages;           ///< Python packages to install (pip)          struct StrList *pip_packages_defer;     ///< Python packages to be built for delivery +        struct StrList *pip_packages_purge;     ///< Python packages to remove from a delivery (for: based_on)          struct StrList *wheels_packages;        ///< Wheel packages built for delivery      } conda; @@ -445,4 +447,16 @@ int delivery_overlay_packages_from_env(struct Delivery *ctx, const char *env_nam   */  int delivery_series_sync(struct Delivery *ctx); +/** + * Remove packages from an environment + * @param ctx Delivery context + * @param env_name Name of conda environment + * @param use_pkg_manager PKG_USE_PIP + * @param use_pkg_manager PKG_USE_CONDA + * @returns -1 on error + * @returns 0 on success + * @returns >0 on failure + */ +int delivery_purge_packages(struct Delivery *ctx, const char *env_name, int use_pkg_manager); +  #endif //STASIS_DELIVERY_H | 
