aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2023-12-10 00:32:20 -0500
committerJoseph Hunkeler <jhunkeler@gmail.com>2023-12-10 00:32:20 -0500
commit6ea9b0e2fa675bfdb74b9c3efece2086321f7fca (patch)
treebc8066ccabe63c53df194fbcb84ecb241316f5df /src
parent4c3594a243ca43bd3d0bfca3728d4606a6b4e980 (diff)
downloadstasis-6ea9b0e2fa675bfdb74b9c3efece2086321f7fca.tar.gz
Add JFrog CLI integration
Diffstat (limited to 'src')
-rw-r--r--src/artifactory.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/src/artifactory.c b/src/artifactory.c
new file mode 100644
index 0000000..48bc943
--- /dev/null
+++ b/src/artifactory.c
@@ -0,0 +1,394 @@
+#include "artifactory.h"
+
+extern struct OMC_GLOBAL globals;
+
+int artifactory_download_cli(char *dest,
+ char *jfrog_artifactory_base_url,
+ char *jfrog_artifactory_product,
+ char *cli_major_ver,
+ char *version,
+ char *os,
+ char *arch,
+ char *remote_filename) {
+ char url[PATH_MAX] = {0};
+ char path[PATH_MAX] = {0};
+ char os_ident[OMC_NAME_MAX] = {0};
+ char arch_ident[OMC_NAME_MAX] = {0};
+
+ // convert platform string to lower-case
+ strcpy(os_ident, os);
+ tolower_s(os_ident);
+
+ // translate OS identifier
+ if (!strcmp(os_ident, "darwin") || startswith(os_ident, "macos")) {
+ strcpy(os_ident, "mac");
+ } else if (!strcmp(os_ident, "linux")) {
+ strcpy(os_ident, "linux");
+ } else {
+ fprintf(stderr, "%s: unknown operating system: %s\n", __FUNCTION__, os_ident);
+ return -1;
+ }
+
+ // translate ARCH identifier
+ strcpy(arch_ident, arch);
+ if (startswith(arch_ident, "i") && endswith(arch_ident, "86")) {
+ strcpy(arch_ident, "386");
+ } else if (!strcmp(arch_ident, "amd64") || !strcmp(arch_ident, "x86_64") || !strcmp(arch_ident, "x64")) {
+ strcpy(arch_ident, "amd64");
+ } else if (!strcmp(arch_ident, "arm64") || !strcmp(arch_ident, "aarch64")) {
+ strcpy(arch_ident, "arm64");
+ } else {
+ fprintf(stderr, "%s: unknown architecture: %s\n", __FUNCTION__, arch_ident);
+ return -1;
+ }
+
+ snprintf(url, sizeof(url) - 1, "%s/%s/%s/%s/%s-%s-%s/%s",
+ jfrog_artifactory_base_url, // https://releases.jfrog.io/artifactory
+ jfrog_artifactory_product, // jfrog-cli
+ cli_major_ver, // v\d+(-jf)?
+ version, // 1.2.3
+ jfrog_artifactory_product, // ...
+ os_ident, // ...
+ arch_ident, // jfrog-cli-linux-x86_64
+ remote_filename); // jf
+ strcpy(path, dest);
+
+ if (mkdirs(path, 0755)) {
+ fprintf(stderr, "%s: %s: %s", __FUNCTION__, path, strerror(errno));
+ return -1;
+ }
+ sprintf(path + strlen(path), "/%s", remote_filename);
+ if (download(url, path)) {
+ fprintf(stderr, "%s: download failed: %s\n", __FUNCTION__, url);
+ }
+ chmod(path, 0755);
+ return 0;
+}
+
+void jfrt_register_opt_str(char *jfrt_val, const char *opt_name, struct StrList **opt_map) {
+ char data[OMC_BUFSIZ];
+ memset(data, 0, sizeof(data));
+
+ if (jfrt_val == NULL) {
+ // no data
+ return;
+ }
+ snprintf(data, sizeof(data) - 1, "--%s=%s", opt_name, jfrt_val);
+ strlist_append(*opt_map, data);
+}
+
+void jfrt_register_opt_bool(bool jfrt_val, const char *opt_name, struct StrList **opt_map) {
+ char data[OMC_BUFSIZ];
+ memset(data, 0, sizeof(data));
+
+ if (jfrt_val == false) {
+ // option will not be used
+ return;
+ }
+ snprintf(data, sizeof(data) - 1, "--%s", opt_name);
+ strlist_append(*opt_map, data);
+}
+
+void jfrt_register_opt_int(int jfrt_val, const char *opt_name, struct StrList **opt_map) {
+ char data[OMC_BUFSIZ];
+ memset(data, 0, sizeof(data));
+
+ if (jfrt_val == 0) {
+ // option will not be used
+ return;
+ }
+ snprintf(data, sizeof(data) - 1, "--%s=%d", opt_name, jfrt_val);
+ strlist_append(*opt_map, data);
+}
+
+void jfrt_register_opt_long(long jfrt_val, const char *opt_name, struct StrList **opt_map) {
+ char data[OMC_BUFSIZ];
+ memset(data, 0, sizeof(data));
+
+ if (jfrt_val == 0) {
+ // option will not be used
+ return;
+ }
+ snprintf(data, sizeof(data) - 1, "--%s=%ld", opt_name, jfrt_val);
+ strlist_append(*opt_map, data);
+}
+
+void jfrt_upload_set_defaults(struct JFRT_Upload *ctx) {
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->recursive = true;
+ ctx->threads = 3;
+ ctx->retries = 3;
+}
+
+static int auth_required(char *cmd) {
+ const char *modes[] = {
+ "rt build-collect-env",
+ NULL,
+ };
+ for (size_t i = 0; modes[i] != NULL; i++) {
+ if (!startswith(cmd, modes[i])) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int jfrog_cli(struct JFRT_Auth *auth, char *args) {
+ struct Process proc;
+ char cmd[OMC_BUFSIZ];
+ char cmd_redacted[OMC_BUFSIZ];
+ int status;
+
+ memset(&proc, 0, sizeof(proc));
+ memset(cmd, 0, sizeof(cmd));
+ memset(cmd_redacted, 0, sizeof(cmd_redacted));
+
+ struct StrList *arg_map = strlist_init();
+ if (!arg_map) {
+ return -1;
+ }
+
+ char *auth_args = NULL;
+ if (auth_required(args)) {
+ // String options
+ jfrt_register_opt_str(auth->url, "url", &arg_map);
+ jfrt_register_opt_str(auth->user, "user", &arg_map);
+ jfrt_register_opt_str(auth->access_token, "access-token", &arg_map);
+ jfrt_register_opt_str(auth->password, "password", &arg_map);
+ jfrt_register_opt_str(auth->ssh_key_path, "ssh-key-path", &arg_map);
+ jfrt_register_opt_str(auth->ssh_passphrase, "ssh-passphrase", &arg_map);
+ jfrt_register_opt_str(auth->client_cert_key_path, "client-cert-key-path", &arg_map);
+ jfrt_register_opt_str(auth->client_cert_path, "client-cert-path", &arg_map);
+ jfrt_register_opt_bool(auth->insecure_tls, "insecure-tls", &arg_map);
+ jfrt_register_opt_str(auth->server_id, "server-id", &arg_map);
+ }
+
+ auth_args = join(arg_map->data, " ");
+ if (!auth_args) {
+ return -1;
+ }
+
+ snprintf(cmd, sizeof(cmd) - 1, "jf %s %s", args, auth_args);
+ strcpy(cmd_redacted, cmd);
+
+ const char *redactable[] = {
+ "--access-token=",
+ "--ssh-key-path=",
+ "--ssh-passphrase=",
+ "--client-cert-key-path",
+ "--client-cert-path",
+ "--password=",
+ NULL,
+ };
+ for (size_t i = 0; redactable[i] != NULL; i++) {
+ char *thing = strstr(cmd_redacted, redactable[i]);
+ if (thing) {
+ thing += strlen(redactable[i]);
+ while (*thing != '\0' && !isspace(*thing)) {
+ *thing = 'x';
+ ++thing;
+ }
+ }
+ }
+ guard_free(auth_args)
+ guard_strlist_free(arg_map)
+
+ msg(OMC_MSG_L2, "Executing: %s\n", cmd_redacted);
+
+ if (!globals.verbose) {
+ strcpy(proc.stdout, "/dev/null");
+ strcpy(proc.stderr, "/dev/null");
+ }
+ status = shell2(&proc, cmd);
+ return status;
+}
+
+int jfrog_cli_rt(struct JFRT_Auth *auth, char *args) {
+ char cmd[OMC_BUFSIZ];
+ memset(cmd, 0, sizeof(cmd));
+ snprintf(cmd, sizeof(cmd) - 1, "rt %s", args);
+ return jfrog_cli(auth, args);
+}
+
+int jfrog_cli_rt_build_collect_env(struct JFRT_Auth *auth, char *build_name, long build_number) {
+ char cmd[OMC_BUFSIZ];
+ memset(cmd, 0, sizeof(cmd));
+ snprintf(cmd, sizeof(cmd) - 1, "rt build-collect-env %s %ld", build_name, build_number);
+ return jfrog_cli(auth, cmd);
+}
+
+int jfrog_cli_rt_build_publish(struct JFRT_Auth *auth, char *build_name, long build_number) {
+ char cmd[OMC_BUFSIZ];
+ memset(cmd, 0, sizeof(cmd));
+ snprintf(cmd, sizeof(cmd) - 1, "rt build-publish %s %ld", build_name, build_number);
+ return jfrog_cli(auth, cmd);
+}
+
+int jfrog_cli_rt_ping(struct JFRT_Auth *auth) {
+ char cmd[OMC_BUFSIZ];
+ memset(cmd, 0, sizeof(cmd));
+
+ snprintf(cmd, sizeof(cmd) - 1, "rt ping");
+ return jfrog_cli_rt(auth, cmd);
+}
+
+int jfrog_cli_rt_download(struct JFRT_Auth *auth, struct JFRT_Download *ctx, char *repo_path, char *dest) {
+ char cmd[OMC_BUFSIZ];
+ memset(cmd, 0, sizeof(cmd));
+
+ if (isempty(repo_path)) {
+ fprintf(stderr, "repo_path argument must be a valid artifactory repository path\n");
+ return -1;
+ }
+
+ // dest is an optional argument, therefore may be NULL or an empty string
+
+ struct StrList *arg_map = strlist_init();
+ if (!arg_map) {
+ return -1;
+ }
+
+ jfrt_register_opt_str(ctx->archive_entries, "archive-entries", &arg_map);
+ jfrt_register_opt_str(ctx->build, "build", &arg_map);
+ jfrt_register_opt_str(ctx->build_name, "build-name", &arg_map);
+ jfrt_register_opt_str(ctx->bundle, "bundle", &arg_map);
+ jfrt_register_opt_str(ctx->exclude_artifacts, "exclude-artifacts", &arg_map);
+ jfrt_register_opt_str(ctx->exclude_props, "exclude-props", &arg_map);
+ jfrt_register_opt_str(ctx->exclusions, "exclusions", &arg_map);
+ jfrt_register_opt_str(ctx->gpg_key, "gpg-key", &arg_map);
+ jfrt_register_opt_str(ctx->include_deps, "include-deps", &arg_map);
+ jfrt_register_opt_str(ctx->include_dirs, "include-dirs", &arg_map);
+ jfrt_register_opt_str(ctx->module, "module", &arg_map);
+ jfrt_register_opt_str(ctx->project, "project", &arg_map);
+ jfrt_register_opt_str(ctx->props, "props", &arg_map);
+ jfrt_register_opt_str(ctx->sort_by, "sort-by", &arg_map);
+ jfrt_register_opt_str(ctx->sort_order, "sort-order", &arg_map);
+ jfrt_register_opt_str(ctx->spec, "spec", &arg_map);
+ jfrt_register_opt_str(ctx->spec_vars, "spec-vars", &arg_map);
+
+ jfrt_register_opt_bool(ctx->detailed_summary, "detailed-summary", &arg_map);
+ jfrt_register_opt_bool(ctx->dry_run, "dry-run", &arg_map);
+ jfrt_register_opt_bool(ctx->explode, "explode", &arg_map);
+ jfrt_register_opt_bool(ctx->fail_no_op, "fail-no-op", &arg_map);
+ jfrt_register_opt_bool(ctx->flat, "flat", &arg_map);
+ jfrt_register_opt_bool(ctx->quiet, "quiet", &arg_map);
+ jfrt_register_opt_bool(ctx->recursive, "recursive", &arg_map);
+ jfrt_register_opt_bool(ctx->retries, "retries", &arg_map);
+ jfrt_register_opt_bool(ctx->retry_wait_time, "retry-wait-time", &arg_map);
+ jfrt_register_opt_bool(ctx->skip_checksum, "skip-checksum", &arg_map);
+
+ jfrt_register_opt_long(ctx->build_number, "build-number", &arg_map);
+ jfrt_register_opt_int(ctx->limit, "limit", &arg_map);
+ jfrt_register_opt_int(ctx->min_split, "min-split", &arg_map);
+ jfrt_register_opt_int(ctx->offset, "offset", &arg_map);
+ jfrt_register_opt_int(ctx->split_count, "split-count", &arg_map);
+ jfrt_register_opt_int(ctx->sync_deletes, "sync-deletes", &arg_map);
+ jfrt_register_opt_int(ctx->threads, "threads", &arg_map);
+ jfrt_register_opt_int(ctx->validate_symlinks, "validate-symlinks", &arg_map);
+
+ char *args = join(arg_map->data, " ");
+ if (!args) {
+ return -1;
+ }
+
+ snprintf(cmd, sizeof(cmd) - 1, "rt download %s '%s' %s", args, repo_path, dest ? dest : "");
+ guard_free(args)
+ guard_strlist_free(arg_map)
+
+ int status = jfrog_cli_rt(auth, cmd);
+ return status;
+}
+
+int jfrog_cli_rt_upload(struct JFRT_Auth *auth, struct JFRT_Upload *ctx, char *src, char *repo_path) {
+ char cmd[OMC_BUFSIZ];
+ memset(cmd, 0, sizeof(cmd));
+
+ if (isempty(src)) {
+ fprintf(stderr, "src argument must be a valid file system path\n");
+ return -1;
+ }
+
+ if (isempty(repo_path)) {
+ fprintf(stderr, "repo_path argument must be a valid artifactory repository path\n");
+ return -1;
+ }
+
+ struct StrList *arg_map = strlist_init();
+ if (!arg_map) {
+ return -1;
+ }
+
+ // String options
+ jfrt_register_opt_str(ctx->build_name, "build-name", &arg_map);
+ jfrt_register_opt_str(ctx->exclusions, "exclusions", &arg_map);
+ jfrt_register_opt_str(ctx->module, "module", &arg_map);
+ jfrt_register_opt_str(ctx->regexp, "regexp", &arg_map);
+ jfrt_register_opt_str(ctx->spec, "spec", &arg_map);
+ jfrt_register_opt_str(ctx->spec_vars, "spec-vars", &arg_map);
+ jfrt_register_opt_str(ctx->project, "project", &arg_map);
+ jfrt_register_opt_str(ctx->target_props, "target-props", &arg_map);
+
+ // Boolean options
+ jfrt_register_opt_bool(ctx->quiet, "quiet", &arg_map);
+ jfrt_register_opt_bool(ctx->ant, "ant", &arg_map);
+ jfrt_register_opt_bool(ctx->archive, "archive", &arg_map);
+ jfrt_register_opt_bool(ctx->deb, "deb", &arg_map);
+ jfrt_register_opt_bool(ctx->detailed_summary, "detailed-summary", &arg_map);
+ jfrt_register_opt_bool(ctx->dry_run, "dry-run", &arg_map);
+ jfrt_register_opt_bool(ctx->explode, "explode", &arg_map);
+ jfrt_register_opt_bool(ctx->fail_no_op, "fail-no-op", &arg_map);
+ jfrt_register_opt_bool(ctx->flat, "flat", &arg_map);
+ jfrt_register_opt_bool(ctx->include_dirs, "include-dirs", &arg_map);
+ jfrt_register_opt_bool(ctx->recursive, "recursive", &arg_map);
+ jfrt_register_opt_bool(ctx->symlinks, "symlinks", &arg_map);
+ jfrt_register_opt_bool(ctx->sync_deletes, "sync-deletes", &arg_map);
+
+ // Integer options
+ jfrt_register_opt_long(ctx->build_number, "build-number", &arg_map);
+ jfrt_register_opt_int(ctx->retries, "retries", &arg_map);
+ jfrt_register_opt_int(ctx->retry_wait_time, "retry-wait-time", &arg_map);
+ jfrt_register_opt_int(ctx->threads, "threads", &arg_map);
+
+ char *args = join(arg_map->data, " ");
+ if (!args) {
+ return -1;
+ }
+
+ char *new_src = NULL;
+ char *base = NULL;
+ if (ctx->workaround_parent_only) {
+ struct StrList *components = strlist_init();
+
+ strlist_append_tokenize(components, src, "/");
+ int max_components = (int) strlist_count(components);
+ for (int i = 0; i < max_components; i++) {
+ if (strstr(components->data[i], "*")) {
+ max_components = i;
+ break;
+ }
+ }
+ base = join(&components->data[max_components], "/");
+ guard_free(components->data[max_components]);
+ new_src = join(components->data, "/");
+ }
+
+ if (new_src) {
+ if (base) {
+ src = base;
+ } else {
+ strcat(src, "/");
+ }
+ pushd(new_src);
+ }
+
+ snprintf(cmd, sizeof(cmd) - 1, "rt upload %s '%s' %s", args, src, repo_path);
+ guard_free(args)
+ guard_strlist_free(arg_map)
+
+ int status = jfrog_cli_rt(auth, cmd);
+ if (new_src) {
+ popd();
+ }
+
+ return status;
+} \ No newline at end of file