diff options
-rw-r--r-- | include/deliverable.h | 36 | ||||
-rw-r--r-- | include/system.h | 5 | ||||
-rw-r--r-- | src/deliverable.c | 123 | ||||
-rw-r--r-- | src/main.c | 61 | ||||
-rw-r--r-- | src/system.c | 83 | ||||
-rw-r--r-- | src/utils.c | 6 |
6 files changed, 212 insertions, 102 deletions
diff --git a/include/deliverable.h b/include/deliverable.h index 135a14c..e699703 100644 --- a/include/deliverable.h +++ b/include/deliverable.h @@ -28,6 +28,12 @@ #define DEFER_CONDA 0 ///< Build conda packages #define DEFER_PIP 1 ///< Build python packages +struct Content { + unsigned type; + char *filename; + char *data; +}; + /*! \struct Delivery * \brief A structure describing a full delivery object */ @@ -49,6 +55,7 @@ struct Delivery { char *tmpdir; ///< Temporary storage area (within root) char *delivery_dir; ///< Delivery artifact output directory char *tools_dir; ///< Tools storage + char *mission_dir; ///< Mission data storage char *conda_install_prefix; ///< Path to install Conda char *conda_artifact_dir; ///< Base path to store compiled conda packages char *conda_staging_dir; ///< Base path to copy compiled conda packages @@ -123,6 +130,12 @@ struct Delivery { char *build_recipe; ///< Conda recipe to build (optional) struct Runtime runtime; ///< Environment variables specific to the test context } tests[1000]; ///< An array of tests + + struct Rule { + bool enable_final; ///< true=allow rc value replacement, false=keep rc value even if final release + char *release_fmt; ///< Release generator format string + struct Content content[1000]; + } rules; }; /** @@ -284,6 +297,29 @@ void delivery_conda_enable(struct Delivery *ctx, char *conda_install_dir); */ void delivery_install_conda(char *install_script, char *conda_install_dir); +/** + * Generate a formatted release string + * + * Formatters: + * %n = Delivery Name + * %c = Delivery Codename (HST mission, only) + * %m = Mission + * %R = Delivery Revision number (or "final") + * %r = Delivery Revision number + * %v = Delivery Version + * %P = Python version (i.e. 3.9.1) + * %p = Compact Python version (i.e. 3.9.1 -> 39) + * %a = System architecture name + * %o = System platform name + * %t = Delivery timestamp (Unix Epoch) + * + * @param ctx pointer to Delivery context + * @param dest NULL pointer to string, or initialized string + * @param fmt release format string + * @return 0 on success, -1 on error + */ +int delivery_format_str(struct Delivery *ctx, char **dest, const char *fmt); + // helper function void delivery_gather_tool_versions(struct Delivery *ctx); diff --git a/include/system.h b/include/system.h index 32c7c79..8ad613f 100644 --- a/include/system.h +++ b/include/system.h @@ -21,9 +21,8 @@ struct Process { int returncode; }; -int shell(struct Process *proc, char *args[]); -int shell2(struct Process *proc, char *args); -int shell_safe(struct Process *proc, char *args[]); +int shell(struct Process *proc, char *args); +int shell_safe(struct Process *proc, char *args); char *shell_output(const char *command, int *status); #endif //OMC_SYSTEM_H diff --git a/src/deliverable.c b/src/deliverable.c index e6704d9..b77502c 100644 --- a/src/deliverable.c +++ b/src/deliverable.c @@ -103,8 +103,8 @@ int delivery_init_tmpdir(struct Delivery *ctx) { goto l_delivery_init_tmpdir_fatal; } - ctx->storage.tmpdir = strdup(tmpdir); globals.tmpdir = strdup(tmpdir); + ctx->storage.tmpdir = globals.tmpdir; return unusable; l_delivery_init_tmpdir_fatal: @@ -407,6 +407,8 @@ int delivery_init(struct Delivery *ctx, struct INIFILE *ini, struct INIFILE *cfg getter(ini, ini->section[i]->key, "build_recipe", INIVAL_TYPE_STR) conv_str(ctx, tests[z].build_recipe) + getter(ini, ini->section[i]->key, "runtime", INIVAL_TO_LIST) + conv_strlist(ctx, tests[z].runtime.environ, "\n") z++; } } @@ -438,6 +440,67 @@ int delivery_init(struct Delivery *ctx, struct INIFILE *ini, struct INIFILE *cfg return 0; } +int delivery_format_str(struct Delivery *ctx, char **dest, const char *fmt) { + size_t fmt_len = strlen(fmt); + + if (!*dest) { + *dest = calloc(OMC_NAME_MAX, sizeof(**dest)); + if (!*dest) { + return -1; + } + } + + for (size_t i = 0; i < fmt_len; i++) { + if (fmt[i] == '%' && strlen(&fmt[i])) { + i++; + switch (fmt[i]) { + case 'n': // name + strcat(*dest, ctx->meta.name); + break; + case 'c': // codename + strcat(*dest, ctx->meta.codename); + break; + case 'm': // mission + strcat(*dest, ctx->meta.mission); + break; + case 'r': // revision + sprintf(*dest + strlen(*dest), "%d", ctx->meta.rc); + break; + case 'R': // "final"-aware revision + if (ctx->meta.final) + strcat(*dest, "final"); + else + sprintf(*dest + strlen(*dest), "%d", ctx->meta.rc); + break; + case 'v': // version + strcat(*dest, ctx->meta.version); + break; + case 'P': // python version + strcat(*dest, ctx->meta.python); + break; + case 'p': // python version major/minor + strcat(*dest, ctx->meta.python_compact); + break; + case 'a': // system architecture name + strcat(*dest, ctx->system.arch); + break; + case 'o': // system platform (OS) name + strcat(*dest, ctx->system.platform[DELIVERY_PLATFORM_RELEASE]); + break; + case 't': // unix epoch + sprintf(*dest + strlen(*dest), "%ld", ctx->info.time_now); + break; + default: // unknown formatter, write as-is + sprintf(*dest + strlen(*dest), "%c%c", fmt[i - 1], fmt[i]); + break; + } + } else { // write non-format text + sprintf(*dest + strlen(*dest), "%c", fmt[i]); + } + } + return 0; +} + void delivery_meta_show(struct Delivery *ctx) { printf("\n====DELIVERY====\n"); printf("%-20s %-10s\n", "Target Python:", ctx->meta.python); @@ -461,21 +524,29 @@ void delivery_conda_show(struct Delivery *ctx) { printf("%-20s %-10s\n", "Prefix:", ctx->storage.conda_install_prefix); puts("Native Packages:"); - for (size_t i = 0; i < strlist_count(ctx->conda.conda_packages); i++) { - char *token = strlist_item(ctx->conda.conda_packages, i); - if (isempty(token) || isblank(*token) || startswith(token, "-")) { - continue; + if (strlist_count(ctx->conda.conda_packages)) { + for (size_t i = 0; i < strlist_count(ctx->conda.conda_packages); i++) { + char *token = strlist_item(ctx->conda.conda_packages, i); + if (isempty(token) || isblank(*token) || startswith(token, "-")) { + continue; + } + printf("%21s%s\n", "", token); } - printf("%21s%s\n", "", token); + } else { + printf("%21s%s\n", "", "N/A"); } puts("Python Packages:"); - for (size_t i = 0; i < strlist_count(ctx->conda.pip_packages); i++) { - char *token = strlist_item(ctx->conda.pip_packages, i); - if (isempty(token) || isblank(*token) || startswith(token, "-")) { - continue; + if (strlist_count(ctx->conda.pip_packages)) { + for (size_t i = 0; i < strlist_count(ctx->conda.pip_packages); i++) { + char *token = strlist_item(ctx->conda.pip_packages, i); + if (isempty(token) || isblank(*token) || startswith(token, "-")) { + continue; + } + printf("%21s%s\n", "", token); } - printf("%21s%s\n", "", token); + } else { + printf("%21s%s\n", "", "N/A"); } } @@ -851,7 +922,12 @@ void delivery_install_conda(char *install_script, char *conda_install_dir) { // Proceed with the installation // -b = batch mode (non-interactive) - if (shell_safe(&proc, (char *[]) {find_program("bash"), install_script, "-b", "-p", conda_install_dir, NULL})) { + char cmd[255] = {0}; + snprintf(cmd, sizeof(cmd) - 1, "%s %s -b -p %s", + find_program("bash"), + install_script, + conda_install_dir); + if (shell_safe(&proc, cmd)) { fprintf(stderr, "conda installation failed\n"); exit(1); } @@ -1116,7 +1192,7 @@ void delivery_tests_run(struct Delivery *ctx) { // 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); + status = shell(&proc, cmd); if (status) { COE_CHECK_ABORT(!globals.continue_on_error, "Test failure") } @@ -1188,6 +1264,9 @@ int delivery_artifact_upload(struct Delivery *ctx) { char *password = getenv("OMC_JF_PASSWORD"); char *ssh_key_path = getenv("OMC_JF_SSH_KEY_PATH"); char *ssh_passphrase = getenv("OMC_JF_SSH_PASSPHRASE"); + char *client_cert_key_path = getenv("OMC_JF_CLIENT_CERT_KEY_PATH"); + char *client_cert_path = getenv("OMC_JF_CLIENT_CERT_PATH"); + char *repo = getenv("OMC_JF_REPO"); if (!url) { fprintf(stderr, "Artifactory URL is not configured:\n"); @@ -1214,11 +1293,25 @@ int delivery_artifact_upload(struct Delivery *ctx) { } auth_ctx.password = NULL; auth_ctx.access_token = NULL; + } else if (client_cert_key_path && client_cert_path) { + auth_ctx.user = NULL; + auth_ctx.password = NULL; + auth_ctx.access_token = NULL; + auth_ctx.ssh_key_path = NULL; + auth_ctx.client_cert_key_path = client_cert_key_path; + auth_ctx.client_cert_path = client_cert_path; } else { fprintf(stderr, "Artifactory authentication is not configured:\n"); fprintf(stderr, "set OMC_JF_USER and OMC_JF_PASSWORD\n"); - fprintf(stderr, "set OMC_JF_ACCESS_TOKEN\nor\n"); - fprintf(stderr, "set OMC_JF_SSH_KEY_PATH and OMC_JF_SSH_KEY_PASSPHRASE\n"); + fprintf(stderr, "or, set OMC_JF_ACCESS_TOKEN\n"); + fprintf(stderr, "or, set OMC_JF_SSH_KEY_PATH and OMC_JF_SSH_KEY_PASSPHRASE\n"); + fprintf(stderr, "or, set OMC_JF_CLIENT_CERT_KEY_PATH and OMC_JF_CLIENT_CERT_PATH\n"); + return -1; + } + + if (!repo) { + fprintf(stderr, "Artifactory destination reppsitory is not configured:\n"); + fprintf(stderr, "set OMC_JF_REPO to a remote repository path\n"); return -1; } @@ -18,7 +18,7 @@ const char *BANNER = "---------------------------------------------------------- " Delivery Generator \n" " v%s \n" "---------------------------------------------------------------------\n" - "Copyright (C) 2023 %s,\n" + "Copyright (C) 2023-2024 %s,\n" "Association of Universities for Research in Astronomy (AURA)\n"; struct OMC_GLOBAL globals = { @@ -190,7 +190,7 @@ int main(int argc, char *argv[], char *arge[]) { msg(OMC_MSG_L2, "Reading OMC global configuration: %s\n", config_input); cfg = ini_open(config_input); if (!cfg) { - msg(OMC_MSG_ERROR | OMC_MSG_L2, "Failed to read config file: %s, %s", delivery_input, strerror(errno)); + msg(OMC_MSG_ERROR | OMC_MSG_L2, "Failed to read config file: %s, %s\n", delivery_input, strerror(errno)); exit(1); } } @@ -198,7 +198,7 @@ int main(int argc, char *argv[], char *arge[]) { 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)); + msg(OMC_MSG_ERROR | OMC_MSG_L2, "Failed to read delivery file: %s, %s\n", delivery_input, strerror(errno)); exit(1); } @@ -224,6 +224,58 @@ int main(int argc, char *argv[], char *arge[]) { exit(1); } + // Expose variables for use with the template engine + tpl_register("meta.name", ctx.meta.name); + tpl_register("meta.version", ctx.meta.version); + tpl_register("meta.codename", ctx.meta.codename); + tpl_register("meta.mission", ctx.meta.mission); + tpl_register("meta.python", ctx.meta.python); + tpl_register("meta.python_compact", ctx.meta.python_compact); + tpl_register("info.release_name", ctx.info.release_name); + tpl_register("conda.installer_baseurl", ctx.conda.installer_baseurl); + tpl_register("conda.installer_name", ctx.conda.installer_name); + tpl_register("conda.installer_version", ctx.conda.installer_version); + tpl_register("conda.installer_arch", ctx.conda.installer_arch); + tpl_register("conda.installer_platform", ctx.conda.installer_platform); + tpl_register("system.arch", ctx.system.arch); + tpl_register("system.platform", ctx.system.platform[DELIVERY_PLATFORM_RELEASE]); + + char missionfile[PATH_MAX] = {0}; + if (getenv("OMC_SYSCONFDIR")) { + globals.sysconfdir = calloc(PATH_MAX, sizeof(*globals.sysconfdir)); + } else { + globals.sysconfdir = strdup(OMC_SYSCONFDIR); + } + if (!globals.sysconfdir) { + msg(OMC_MSG_ERROR | OMC_MSG_L1, "Unable to allocate memory for system configuration directory path\n"); + exit(1); + } + if (getenv("OMC_SYSCONFDIR")) { + sprintf(missionfile, "%s/%s/%s/%s.ini", getenv("OMC_SYSCONFDIR"), "mission", ctx.meta.mission, ctx.meta.mission); + } else { + sprintf(missionfile, "%s/%s/%s/%s.ini", OMC_SYSCONFDIR, "mission", ctx.meta.mission, ctx.meta.mission); + } + + struct INIFILE *cfg_mission; + cfg_mission = ini_open(missionfile); + if (!cfg_mission) { + msg(OMC_MSG_ERROR | OMC_MSG_L2, "Failed to read misson configuration: %s, %s\n", missionfile, strerror(errno)); + exit(1); + } + + /* + union INIVal x; + ini_getval(cfg_mission, "template:readme.md.in", "destination", INIVAL_TYPE_STR, &x); + char *output = tpl_render(x.as_char_p); + if (!output) { + fprintf(stderr, "Render failed!\n"); + exit(1); + } + puts(output); + tpl_free(); + + exit(0); + */ runtime_apply(ctx.runtime.environ); 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); @@ -406,10 +458,13 @@ int main(int argc, char *argv[], char *arge[]) { msg(OMC_MSG_L1, "Cleaning up\n"); ini_free(&ini); if (cfg) { + // optional extras ini_free(&cfg); } + ini_free(&cfg_mission); delivery_free(&ctx); globals_free(); + tpl_free(); msg(OMC_MSG_L1, "Done!\n"); return 0; diff --git a/src/system.c b/src/system.c index 52e354a..2a5302e 100644 --- a/src/system.c +++ b/src/system.c @@ -5,78 +5,7 @@ #include "system.h" #include "omc.h" -int shell(struct Process *proc, char *args[]) { - FILE *fp_out, *fp_err; - pid_t pid; - pid_t status; - status = 0; - errno = 0; - - pid = fork(); - if (pid == -1) { - fprintf(stderr, "fork failed\n"); - exit(1); - } else if (pid == 0) { - int retval; - if (proc != NULL) { - if (strlen(proc->stdout)) { - fp_out = freopen(proc->stdout, "w+", stdout); - } - - if (strlen(proc->stderr)) { - fp_err = freopen(proc->stderr, "w+", stderr); - } - - if (proc->redirect_stderr) { - if (fp_err) { - fclose(fp_err); - fclose(stderr); - } - dup2(fileno(stdout), fileno(stderr)); - } - } - - retval = execv(args[0], args); - fprintf(stderr, "# executing: "); - for (size_t x = 0; args[x] != NULL; x++) { - fprintf(stderr, "%s ", args[x]); - } - - if (proc != NULL && strlen(proc->stdout)) { - fflush(fp_out); - fclose(fp_out); - fflush(stdout); - fclose(stdout); - } - if (proc != NULL && strlen(proc->stderr)) { - fflush(fp_err); - fclose(fp_err); - fflush(stderr); - fclose(stderr); - } - exit(retval); - } else { - if (waitpid(pid, &status, WUNTRACED) > 0) { - if (WIFEXITED(status) && WEXITSTATUS(status)) { - if (WEXITSTATUS(status) == 127) { - fprintf(stderr, "execv failed\n"); - } - } else if (WIFSIGNALED(status)) { - fprintf(stderr, "signal received: %d\n", WIFSIGNALED(status)); - } - } else { - fprintf(stderr, "waitpid() failed\n"); - } - } - - - if (proc != NULL) { - proc->returncode = status; - } - return WEXITSTATUS(status); -} - -int shell2(struct Process *proc, char *args) { +int shell(struct Process *proc, char *args) { FILE *fp_out = NULL; FILE *fp_err = NULL; pid_t pid; @@ -166,16 +95,14 @@ int shell2(struct Process *proc, char *args) { return WEXITSTATUS(status); } -int shell_safe(struct Process *proc, char *args[]) { +int shell_safe(struct Process *proc, char *args) { FILE *fp; char buf[1024] = {0}; int result; - for (size_t i = 0; args[i] != NULL; i++) { - if (strpbrk(args[i], ";&|()")) { - args[i] = NULL; - break; - } + char *invalid_ch = strpbrk(args, ";&|()"); + if (invalid_ch) { + args = NULL; } result = shell(proc, args); diff --git a/src/utils.c b/src/utils.c index 85a7a74..9e941f1 100644 --- a/src/utils.c +++ b/src/utils.c @@ -347,7 +347,7 @@ int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) { sprintf(command, "%s clone --recursive %s", program, url); if (destdir && access(destdir, F_OK) < 0) { sprintf(command + strlen(command), " %s", destdir); - result = shell2(proc, command); + result = shell(proc, command); } if (destdir) { @@ -360,12 +360,12 @@ int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) { { memset(command, 0, sizeof(command)); sprintf(command, "%s fetch --all", program); - result += shell2(proc, command); + result += shell(proc, command); if (gitref != NULL) { memset(command, 0, sizeof(command)); sprintf(command, "%s checkout %s", program, gitref); - result += shell2(proc, command); + result += shell(proc, command); } popd(); } |