aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/deliverable.h36
-rw-r--r--include/system.h5
-rw-r--r--src/deliverable.c123
-rw-r--r--src/main.c61
-rw-r--r--src/system.c83
-rw-r--r--src/utils.c6
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;
}
diff --git a/src/main.c b/src/main.c
index 7caa80c..1dccea2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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();
}