diff options
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | docs/Doxyfile | 16 | ||||
| -rw-r--r-- | include/artifactory.h | 55 | ||||
| -rw-r--r-- | include/copy.h | 42 | ||||
| -rw-r--r-- | include/deliverable.h | 23 | ||||
| -rw-r--r-- | include/docker.h | 70 | ||||
| -rw-r--r-- | include/download.h | 5 | ||||
| -rw-r--r-- | include/recipe.h | 62 | ||||
| -rw-r--r-- | include/template.h | 5 | ||||
| -rw-r--r-- | include/utils.h | 172 | ||||
| -rw-r--r-- | src/artifactory.c | 55 | ||||
| -rw-r--r-- | src/deliverable.c | 55 | ||||
| -rw-r--r-- | src/utils.c | 56 | 
13 files changed, 479 insertions, 139 deletions
| @@ -1,5 +1,3 @@ -# OH MY CAL -  OMC aims to consolidate the steps required to build, test, and deploy calibration pipelines and other software suites maintained by engineers at the Space Telescope Science Institute.  # Requirements diff --git a/docs/Doxyfile b/docs/Doxyfile index bef5a97..d6509e6 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -124,7 +124,7 @@ WARN_LOGFILE           =  #---------------------------------------------------------------------------  # Configuration options related to the input files  #--------------------------------------------------------------------------- -INPUT                  = src include +INPUT                  = README.md src include  INPUT_ENCODING         = UTF-8  INPUT_FILE_ENCODING    =  FILE_PATTERNS          = *.c \ @@ -190,7 +190,7 @@ INPUT_FILTER           =  FILTER_PATTERNS        =  FILTER_SOURCE_FILES    = NO  FILTER_SOURCE_PATTERNS = -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = README.md  FORTRAN_COMMENT_AFTER  = 72  #---------------------------------------------------------------------------  # Configuration options related to source browsing @@ -308,7 +308,7 @@ RTF_EXTENSIONS_FILE    =  #---------------------------------------------------------------------------  # Configuration options related to the man page output  #--------------------------------------------------------------------------- -GENERATE_MAN           = NO +GENERATE_MAN           = YES  MAN_OUTPUT             = man  MAN_EXTENSION          = .3  MAN_SUBDIR             = @@ -346,7 +346,7 @@ PERLMOD_MAKEVAR_PREFIX =  # Configuration options related to the preprocessor  #---------------------------------------------------------------------------  ENABLE_PREPROCESSING   = YES -MACRO_EXPANSION        = NO +MACRO_EXPANSION        = YES  EXPAND_ONLY_PREDEF     = NO  SEARCH_INCLUDES        = YES  INCLUDE_PATH           = @@ -366,14 +366,14 @@ EXTERNAL_PAGES         = YES  # Configuration options related to diagram generator tools  #---------------------------------------------------------------------------  HIDE_UNDOC_RELATIONS   = YES -HAVE_DOT               = NO +HAVE_DOT               = YES  DOT_NUM_THREADS        = 0  DOT_COMMON_ATTR        = "fontname=Helvetica,fontsize=10"  DOT_EDGE_ATTR          = "labelfontname=Helvetica,labelfontsize=10"  DOT_NODE_ATTR          = "shape=box,height=0.2,width=0.4"  DOT_FONTPATH           =  CLASS_GRAPH            = YES -COLLABORATION_GRAPH    = YES +COLLABORATION_GRAPH    = NO  GROUP_GRAPHS           = YES  UML_LOOK               = NO  UML_LIMIT_NUM_FIELDS   = 10 @@ -381,8 +381,8 @@ DOT_UML_DETAILS        = NO  DOT_WRAP_THRESHOLD     = 17  TEMPLATE_RELATIONS     = NO  INCLUDE_GRAPH          = YES -INCLUDED_BY_GRAPH      = YES -CALL_GRAPH             = NO +INCLUDED_BY_GRAPH      = NO +CALL_GRAPH             = YES  CALLER_GRAPH           = NO  GRAPHICAL_HIERARCHY    = YES  DIRECTORY_GRAPH        = YES diff --git a/include/artifactory.h b/include/artifactory.h index c8ce6d3..0883eef 100644 --- a/include/artifactory.h +++ b/include/artifactory.h @@ -130,10 +130,10 @@ int artifactory_download_cli(char *dest,   *   * ```c   * struct JFRT_Auth auth_ctx; - * memset(auth_ctx, 0, sizeof(auth_context);   * auth_ctx.user = strdup("myuser");   * auth_ctx.password = strdup("mypassword");   * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx);   *   * if (jfrog_cli(&auth_ctx, "ping") {   *     fprintf(stderr, "Failed to ping artifactory server: %s\n", auth_ctx.url); @@ -152,10 +152,10 @@ int jfrog_cli(struct JFRT_Auth *auth, char *args);   *   * ```c   * struct JFRT_Auth auth_ctx; - * memset(auth_ctx, 0, sizeof(auth_context);   * auth_ctx.user = strdup("myuser");   * auth_ctx.password = strdup("mypassword");   * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx);   *   * if (jfrog_cli_ping(&auth_ctx)) {   *     fprintf(stderr, "Failed to ping artifactory server: %s\n", auth_ctx.url); @@ -173,13 +173,13 @@ int jfrog_cli_rt_ping(struct JFRT_Auth *auth);   *   * ```c   * struct JFRT_Auth auth_ctx; - * memset(auth_ctx, 0, sizeof(auth_context);   * auth_ctx.user = strdup("myuser");   * auth_ctx.password = strdup("mypassword");   * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx);   *   * struct JFRT_Upload upload_ctx; - * jfrt_upload_set_defaults(&upload_ctx); + * jfrt_upload_init(&upload_ctx);   *   * if (jfrt_cli_rt_upload(&auth_ctx, &upload_ctx,   *     "local/files_*.ext", "repo_name/ext_files/")) { @@ -201,10 +201,10 @@ int jfrog_cli_rt_upload(struct JFRT_Auth *auth, struct JFRT_Upload *ctx, char *s   *   * ```c   * struct JFRT_Auth auth_ctx; - * memset(auth_ctx, 0, sizeof(auth_context);   * auth_ctx.user = strdup("myuser");   * auth_ctx.password = strdup("mypassword");   * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx);   *   * struct JFRT_Download download_ctx;   * memset(download_ctx, 0, sizeof(download_ctx)); @@ -229,10 +229,10 @@ int jfrog_cli_rt_download(struct JFRT_Auth *auth, struct JFRT_Download *ctx, cha   *   * ```c   * struct JFRT_Auth auth_ctx; - * memset(auth_ctx, 0, sizeof(auth_context);   * auth_ctx.user = strdup("myuser");   * auth_ctx.password = strdup("mypassword");   * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx);   *   * if (jfrog_cli_rt_build_collect_env(&auth_ctx, "mybuildname", "1.2.3+gabcdef")) {   *     fprintf(stderr, "Failed to collect runtime data for Artifactory build object\n"); @@ -252,10 +252,10 @@ int jfrog_cli_rt_build_collect_env(struct JFRT_Auth *auth, char *build_name, cha   *   * ```c   * struct JFRT_Auth auth_ctx; - * memset(auth_ctx, 0, sizeof(auth_context);   * auth_ctx.user = strdup("myuser");   * auth_ctx.password = strdup("mypassword");   * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx);   *   * if (jfrog_cli_rt_build_collect_env(&auth_ctx, "mybuildname", "1.2.3+gabcdef")) {   *     fprintf(stderr, "Failed to collect runtime data for Artifactory build object\n"); @@ -276,9 +276,48 @@ int jfrog_cli_rt_build_collect_env(struct JFRT_Auth *auth, char *build_name, cha  int jfrog_cli_rt_build_publish(struct JFRT_Auth *auth, char *build_name, char *build_number);  /** + * Configure JFrog CLI authentication according to OMC specs + * + * This function will use the OMC_JF_* environment variables to configure the authentication + * context. With this in mind, if an OMC_JF_* environment variable is not defined, the original value of + * the structure member will be used instead. + * + * Use OMC_JF_* variables to configure context + * + * ```c + * struct JFRT_Auth auth_ctx; + * jfrt_auth_init(&ctx); + * ``` + * + * Use your own input, but let the environment take over when variables are defined + * + * ```c + * struct JFRT_Auth auth_ctx; + * auth_ctx.user = strdup("myuser"); + * auth_ctx.password = strdup("mypassword"); + * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * jfrt_auth_init(&auth_ctx); + * ``` + * + * Use your own input without OMC's help. Purely an illustrative example. + * + * ```c + * struct JFRT_Auth auth_ctx; + * memset(auth_ctx, 0, sizeof(auth_ctx)); + * auth_ctx.user = strdup("myuser"); + * auth_ctx.password = strdup("mypassword"); + * auth_ctx.url = strdup("https://myserver.tld/artifactory"); + * ``` + * + * @param auth_ctx + * @return + */ +int jfrt_auth_init(struct JFRT_Auth *auth_ctx); + +/**   * Zero-out and apply likely defaults to a JFRT_Upload structure   * @param ctx JFRT_Upload structure   */ -void jfrt_upload_set_defaults(struct JFRT_Upload *ctx); +void jfrt_upload_init(struct JFRT_Upload *ctx);  #endif //OMC_ARTIFACTORY_H
\ No newline at end of file diff --git a/include/copy.h b/include/copy.h index 9e8bc11..5a53d7d 100644 --- a/include/copy.h +++ b/include/copy.h @@ -1,3 +1,4 @@ +//! @file copy.h  #ifndef OMC_COPY_H  #include <stdio.h> @@ -12,8 +13,49 @@  #define CT_OWNER 1 << 1  #define CT_PERM 1 << 2 +/** + * Recursively copy a directory, or a single file + * + * ```c + * if (copytree("/source/path", "/destination/path", CT_PERM | CT_OWNER)) { + *     fprintf(stderr, "Unable to copy files\n"); + *     exit(1); + * } + * ``` + * + * @param srcdir source file or directory path + * @param destdir destination directory + * @param op CT_OWNER (preserve ownership) + * @param op CT_PERM (preserve permission bits) + * @return 0 on success, -1 on error + */  int copytree(const char *srcdir, const char *destdir, unsigned op); + +/** + * Create all leafs in directory path + * @param _path directory path to create + * @param mode mode_t permissions + * @return + */  int mkdirs(const char *_path, mode_t mode); + +/** + * Copy a single file + * + * ```c + * if (copy2("/source/path/example.txt", "/destination/path/example.txt", CT_PERM | CT_OWNER)) { + *     fprintf(stderr, "Unable to copy file\n"); + *     exit(1); + * } + * ``` + * + * + * @param src source file path + * @param dest destination file path + * @param op CT_OWNER (preserve ownership) + * @param op CT_PERM (preserve permission bits) + * @return 0 on success, -1 on error + */  int copy2(const char *src, const char *dest, unsigned op);  #endif // OMC_COPY_H
\ No newline at end of file diff --git a/include/deliverable.h b/include/deliverable.h index 63adef1..1fbbd54 100644 --- a/include/deliverable.h +++ b/include/deliverable.h @@ -323,17 +323,18 @@ 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) + * + * - `%%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 diff --git a/include/docker.h b/include/docker.h index 03f5d0d..3c7dff6 100644 --- a/include/docker.h +++ b/include/docker.h @@ -1,20 +1,82 @@ +//! @file docker.h  #ifndef OMC_DOCKER_H  #define OMC_DOCKER_H +//! Flag to squelch output from docker_exec()  #define OMC_DOCKER_QUIET 1 << 1 +//! Flag for older style docker build  #define OMC_DOCKER_BUILD 1 << 1 +//! Flag for docker buildx  #define OMC_DOCKER_BUILD_X 1 << 2  struct DockerCapabilities { -    int podman; -    int build; -    int available; -    int usable; +    int podman;  //!< Is "docker" really podman? +    int build;  //!< Is a build plugin available? +    int available;  //!< Is a "docker" program available? +    int usable;  //!< Is docker in a usable state for the current user?  }; +/** + * Determine the state of docker on the system + * + * ```c + * struct DockerCapabilities docker_is; + * if (!docker_capable(&docker_is)) { + *     fprintf(stderr, "%s is %savailable, and %susable\n", + *         docker_is.podman ? "Podman" : "Docker", + *         docker_is.available ? "" : "not ", + *         docker_is.usable ? "" : "not "); + *     exit(1); + * } + * ``` + * + * @param result DockerCapabilities struct + * @return 1 on success, 0 on error + */  int docker_capable(struct DockerCapabilities *result); + +/** + * Execute a docker command + * + * Use the `OMC_DOCKER_QUIET` flag to suppress all output from stdout and stderr. + * + * ```c + * if (docker_exec("run --rm -t ubuntu:latest /bin/bash -c 'echo Hello world'", 0)) { + *     fprintf(stderr, "Docker hello world failed\n"); + *     exit(1); + * } + * ``` + * + * @param args arguments to pass to docker + * @param flags + * @return exit code from "docker" + */  int docker_exec(const char *args, unsigned flags); + +/** + * Build a docker image + * + * ```c + * struct DockerCapabilities docker_is; + * docker_capable(&docker_is); + * + * if (docker_is.usable) { + *     printf("Building docker image\n"); + *     if (docker_build("path/to/Dockerfile/dir")) { + *         fprintf("Docker build failed\n"); + *         exit(1); + *     } + * } else { + *     fprintf(stderr, "No usable docker installation available\n"); + * } + * ``` + * + * @param dirpath + * @param args + * @param engine + * @return + */  int docker_build(const char *dirpath, const char *args, int engine);  int docker_script(const char *image, char *data, unsigned flags);  int docker_save(const char *image, const char *destdir); diff --git a/include/download.h b/include/download.h index 440c749..568809e 100644 --- a/include/download.h +++ b/include/download.h @@ -1,7 +1,4 @@ -// -// Created by jhunk on 10/5/23. -// - +//! @file download.h  #ifndef OMC_DOWNLOAD_H  #define OMC_DOWNLOAD_H diff --git a/include/recipe.h b/include/recipe.h index f4c63c1..f1b4df1 100644 --- a/include/recipe.h +++ b/include/recipe.h @@ -1,20 +1,72 @@ -// -// Created by jhunk on 10/7/23. -// - +//! @file recipe.h  #ifndef OMC_RECIPE_H  #define OMC_RECIPE_H  #include "str.h"  #include "utils.h" -#define RECIPE_DIR "recipes" +//! Unable to determine recipe repo type  #define RECIPE_TYPE_UNKNOWN 0 +//! Recipe repo is from conda-forge  #define RECIPE_TYPE_CONDA_FORGE 1 +//! Recipe repo is from astroconda  #define RECIPE_TYPE_ASTROCONDA 2 +//! Recipe repo provides the required build configurations but doesn't match conda-forge or astroconda's signature  #define RECIPE_TYPE_GENERIC 3 +/** + * Download a Conda package recipe + * + * ```c + * char *recipe = NULL; + * + * if (recipe_clone("base/dir", "https://github.com/example/repo", "branch", &recipe)) { + *     fprintf(stderr, "Failed to clone conda recipe\n"); + *     exit(1); + * } else { + *     chdir(recipe); + * } + * ``` + * + * @param recipe_dir path to store repository + * @param url remote address of git repository + * @param gitref branch/tag/commit + * @param result absolute path to downloaded repository + * @return exit code from "git", -1 on error + */  int recipe_clone(char *recipe_dir, char *url, char *gitref, char **result); + +/** + * Determine the layout/type of repository path + * + * ```c + * if (recipe_clone("base/dir", "https://github.com/example/repo", "branch", &recipe)) { + *     fprintf(stderr, "Failed to clone conda recipe\n"); + *     exit(1); + * } + * + * int recipe_type; + * recipe_type = recipe_get_type(recipe); + * switch (recipe_type) { + *     case RECIPE_TYPE_CONDA_FORGE: + *         // do something specific for conda-forge directory structure + *         break; + *     case RECIPE_TYPE_ASTROCONDA: + *         // do something specific for astroconda directory structure + *         break; + *     case RECIPE_TYPE_GENERIC: + *         // do something specific for a directory containing a meta.yaml config + *         break; + *     case RECIPE_TYPE_UNKNOWN: + *     default: + *         // the structure is foreign or the path doesn't contain a conda recipe + *         break; + * } + * ``` + * + * @param repopath path to git repository containing conda recipe(s) + * @return One of RECIPE_TYPE_UNKNOWN, RECIPE_TYPE_CONDA_FORGE, RECIPE_TYPE_ASTROCONDA, RECIPE_TYPE_GENERIC + */  int recipe_get_type(char *repopath);  #endif //OMC_RECIPE_H diff --git a/include/template.h b/include/template.h index b12208f..116960f 100644 --- a/include/template.h +++ b/include/template.h @@ -1,7 +1,4 @@ -// -// Created by jhunk on 12/17/23. -// - +//! @file template.h  #ifndef OMC_TEMPLATE_H  #define OMC_TEMPLATE_H diff --git a/include/utils.h b/include/utils.h index 52f4f2d..71d3f8b 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,3 +1,4 @@ +//! @file utils.h  #ifndef OMC_UTILS_H  #define OMC_UTILS_H  #include <stdio.h> @@ -23,35 +24,204 @@  typedef int (ReaderFn)(size_t line, char **); +/** + * Change directory. Push path on directory stack. + * + * ```c + * pushd("/somepath"); + * + * FILE fp = fopen("somefile", "w"); // i.e. /somepath/somefile + * fprintf(fp, "Hello world.\n"); + * fclose(fp); + * + * popd(); + * ``` + * + * @param path of directory + * @return 0 on success, -1 on error + */  int pushd(const char *path); + +/** + * Return from directory. Pop last path from directory stack. + * + * @see pushd + * @return 0 on success, -1 if stack is empty + */  int popd(void); + +/** + * Expand "~" to the user's home directory + * + * ```c + * char *home = expandpath("~");             // == /home/username + * char *config = expandpath("~/.config");   // == /home/username/.config + * char *nope = expandpath("/tmp/test");     // == /tmp/test + * char *nada = expandpath("/~/broken");     // == /~/broken + * + * free(home); + * free(config); + * free(nope); + * free(nada); + * ``` + * + * @param _path (Must start with a `~`) + * @return success=expanded path or original path, failure=NULL + */  char *expandpath(const char *_path); + +/** + * Remove a directory tree recursively + * + * ```c + * mkdirs("a/b/c"); + * rmtree("a"); + * // a/b/c is removed + * ``` + * + * @param _path + * @return 0 on success, -1 on error + */  int rmtree(char *_path); + +  char **file_readlines(const char *filename, size_t start, size_t limit, ReaderFn *readerFn); + +/** + * Strip directory from file name + * Note: Caller is responsible for freeing memory + * + * @param _path + * @return success=file name, failure=NULL + */  char *path_basename(char *path); + +/** + * Return parent directory of file, or the parent of a directory + * + * @param path + * @return success=directory, failure=empty string + */ +char *path_dirname(char *path); + +/** + * Scan PATH directories for a named program + * @param name program name + * @return path to program, or NULL on error + */  char *find_program(const char *name); + +/** + * Create an empty file, or update modified timestamp on an existing file + * @param filename file to touch + * @return 0 on success, 1 on error + */  int touch(const char *filename); + +/** + * Clone a git repository + * + * ```c + * struct Process proc; + * memset(proc, 0, sizeof(proc)); + * + * if (git_clone(&proc, "https://github.com/myuser/myrepo", "./repos", "unstable_branch")) { + *     fprintf(stderr, "Failed to clone repository\n"); + *     exit(1); + * } + * + * if (pushd("./repos/myrepo")) { + *     fprintf(stderr, "Unable to enter repository directory\n"); + * } else { + *     // do something with repository + *     popd(); + * } + * ``` + * + * @see pushd + * + * @param proc Process struct + * @param url URL (or file system path) of repoistory to clone + * @param destdir destination directory + * @param gitref commit/branch/tag of checkout (NULL will use HEAD of default branch for repo) + * @return exit code from "git" + */  int git_clone(struct Process *proc, char *url, char *destdir, char *gitref); + +/** + * Git describe wrapper + * @param path to repository + * @return output from "git describe", or NULL on error + */  char *git_describe(const char *path); + +/** + * Git rev-parse wrapper + * @param path to repository + * @param args to pass to git rev-parse + * @return output from "git rev-parse", or NULL on error + */  char *git_rev_parse(const char *path, char *args); + +/** + * Helper function to initialize simple OMC internal path strings + * + * ```c + * char *mypath = NULL; + * + * if (path_store(&mypath, PATH_MAX, "/some", "path")) { + *     fprintf(stderr, "Unable to allocate memory for path elements\n"); + *     exit(1); + * } + * // mypath is allocated to size PATH_MAX and contains the string: /some/path + * // base+path will truncate at maxlen - 1 + * ``` + * + * @param destptr address of destination string pointer + * @param maxlen maximum length of the path + * @param base path + * @param path to append to base + * @return 0 on success, -1 on error + */  int path_store(char **destptr, size_t maxlen, const char *base, const char *path);  #define OMC_MSG_SUCCESS 0 +//! Suppress printing of the message text  #define OMC_MSG_NOP 1 << 0 +//! The message is an error  #define OMC_MSG_ERROR 1 << 1 +//! The message is a warning  #define OMC_MSG_WARN 1 << 2 +//! The message will be indented once  #define OMC_MSG_L1 1 << 3 +//! The message will be indented twice  #define OMC_MSG_L2 1 << 4 +//! The message will be indented thrice  #define OMC_MSG_L3 1 << 5 -#define OMC_MSG_RESTRICT 1 << 6     ///< Restrict to verbose mode +//! The message will only be printed in verbose mode +#define OMC_MSG_RESTRICT 1 << 6  void msg(unsigned type, char *fmt, ...); + +// Enter an interactive shell that ends the program on-exit  void debug_shell(); +  /**   * Creates a temporary file returning an open file pointer via @a fp, and the   * path to the file. The caller is responsible for closing @a fp and   * free()ing the returned file path. + * + * ```c + * FILE *fp = NULL; + * char *tempfile = xmkstemp(&fp, "r+"); + * if (!fp || !tempfile) { + *     fprintf(stderr, "Failed to generate temporary file for read/write\n"); + *     exit(1); + * } + * ``` + *   * @param fp pointer to FILE (to be initialized) + * @param mode fopen() style file mode string   * @return system path to the temporary file   * @return NULL on failure   */ diff --git a/src/artifactory.c b/src/artifactory.c index 55cb7dd..39a662d 100644 --- a/src/artifactory.c +++ b/src/artifactory.c @@ -115,7 +115,7 @@ void jfrt_register_opt_long(long jfrt_val, const char *opt_name, struct StrList      strlist_append(*opt_map, data);  } -void jfrt_upload_set_defaults(struct JFRT_Upload *ctx) { +void jfrt_upload_init(struct JFRT_Upload *ctx) {      memset(ctx, 0, sizeof(*ctx));      ctx->recursive = true;      ctx->threads = 3; @@ -135,6 +135,59 @@ static int auth_required(char *cmd) {      return 0;  } +int jfrt_auth_init(struct JFRT_Auth *auth_ctx) { +    char *url = getenv("OMC_JF_ARTIFACTORY_URL"); +    char *user = getenv("OMC_JF_USER"); +    char *access_token = getenv("OMC_JF_ACCESS_TOKEN"); +    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"); + +    if (!url) { +        fprintf(stderr, "Artifactory URL is not configured:\n"); +        fprintf(stderr, "please set OMC_JF_ARTIFACTORY_URL\n"); +        return -1; +    } +    auth_ctx->url = url; + +    if (access_token) { +        auth_ctx->user = NULL; +        auth_ctx->access_token = access_token; +        auth_ctx->password = NULL; +        auth_ctx->ssh_key_path = NULL; +    } else if (user && password) { +        auth_ctx->user = user; +        auth_ctx->password = password; +        auth_ctx->access_token = NULL; +        auth_ctx->ssh_key_path = NULL; +    } else if (ssh_key_path) { +        auth_ctx->user = NULL; +        auth_ctx->ssh_key_path = ssh_key_path; +        if (ssh_passphrase) { +            auth_ctx->ssh_passphrase = ssh_passphrase; +        } +        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, "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; +    } +    return 0; +} +  int jfrog_cli(struct JFRT_Auth *auth, char *args) {      struct Process proc;      char cmd[OMC_BUFSIZ]; diff --git a/src/deliverable.c b/src/deliverable.c index b935b64..f9c53ba 100644 --- a/src/deliverable.c +++ b/src/deliverable.c @@ -1433,59 +1433,6 @@ int delivery_init_artifactory(struct Delivery *ctx) {      return status;  } -int jfrt_auth_init(struct JFRT_Auth *auth_ctx) { -    char *url = getenv("OMC_JF_ARTIFACTORY_URL"); -    char *user = getenv("OMC_JF_USER"); -    char *access_token = getenv("OMC_JF_ACCESS_TOKEN"); -    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"); - -    if (!url) { -        fprintf(stderr, "Artifactory URL is not configured:\n"); -        fprintf(stderr, "please set OMC_JF_ARTIFACTORY_URL\n"); -        return -1; -    } -    auth_ctx->url = url; - -    if (access_token) { -        auth_ctx->user = NULL; -        auth_ctx->access_token = access_token; -        auth_ctx->password = NULL; -        auth_ctx->ssh_key_path = NULL; -    } else if (user && password) { -        auth_ctx->user = user; -        auth_ctx->password = password; -        auth_ctx->access_token = NULL; -        auth_ctx->ssh_key_path = NULL; -    } else if (ssh_key_path) { -        auth_ctx->user = NULL; -        auth_ctx->ssh_key_path = ssh_key_path; -        if (ssh_passphrase) { -            auth_ctx->ssh_passphrase = ssh_passphrase; -        } -        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, "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; -    } -    return 0; -} -  int delivery_artifact_upload(struct Delivery *ctx) {      int status = 0; @@ -1493,7 +1440,7 @@ int delivery_artifact_upload(struct Delivery *ctx) {          if (!ctx->deploy[i].files || !ctx->deploy[i].dest) {              break;          } -        jfrt_upload_set_defaults(&ctx->deploy[i].upload_ctx); +        jfrt_upload_init(&ctx->deploy[i].upload_ctx);          char *repo = getenv("OMC_JF_REPO");          if (repo) { diff --git a/src/utils.c b/src/utils.c index 2b4d275..b669e1c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -69,25 +69,6 @@ int rmtree(char *_path) {      return status;  } -/** - * Expand "~" to the user's home directory - * - * Example: - * ~~~{.c} - * char *home = expandpath("~");             // == /home/username - * char *config = expandpath("~/.config");   // == /home/username/.config - * char *nope = expandpath("/tmp/test");     // == /tmp/test - * char *nada = expandpath("/~/broken");     // == /~/broken - * - * free(home); - * free(config); - * free(nope); - * free(nada); - * ~~~ - * - * @param _path (Must start with a `~`) - * @return success=expanded path or original path, failure=NULL - */  char *expandpath(const char *_path) {      if (_path == NULL) {          return NULL; @@ -146,13 +127,6 @@ char *expandpath(const char *_path) {      return strdup(result);  } -/** - * Strip directory from file name - * Note: Caller is responsible for freeing memory - * - * @param _path - * @return success=file name, failure=NULL - */  char *path_basename(char *path) {      char *result = NULL;      char *last = NULL; @@ -168,12 +142,6 @@ char *path_basename(char *path) {      return result;  } -/** - * Return parent directory of file, or the parent of a directory - * - * @param path - * @return success=directory, failure=empty string - */  char *path_dirname(char *path) {      if (!path) {          return ""; @@ -374,11 +342,18 @@ int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) {  char *git_describe(const char *path) { -    pushd(path);      static char version[NAME_MAX];      FILE *pp; -    pp = popen("git describe --first-parent --always --tags", "r"); +      memset(version, 0, sizeof(version)); +    if (pushd(path)) { +        return NULL; +    } + +    pp = popen("git describe --first-parent --always --tags", "r"); +    if (!pp) { +        return NULL; +    }      fgets(version, sizeof(version) - 1, pp);      strip(version);      pclose(pp); @@ -387,18 +362,25 @@ char *git_describe(const char *path) {  }  char *git_rev_parse(const char *path, char *args) { -    pushd(path);      static char version[NAME_MAX];      char cmd[PATH_MAX];      FILE *pp; +    memset(version, 0, sizeof(version));      if (isempty(args)) { -        fprintf(stderr, "git_rev_parse args cannot be empty"); +        fprintf(stderr, "git_rev_parse args cannot be empty\n"); +        return NULL; +    } + +    if (pushd(path)) {          return NULL;      } +      sprintf(cmd, "git rev-parse %s", args);      pp = popen(cmd, "r"); -    memset(version, 0, sizeof(version)); +    if (!pp) { +        return NULL; +    }      fgets(version, sizeof(version) - 1, pp);      strip(version);      pclose(pp); | 
