diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2024-03-04 08:48:28 -0500 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2024-03-04 08:48:28 -0500 |
commit | 239330330a8fc49c6806cccdc17a910d23d22952 (patch) | |
tree | 0e2a3e2e852cafd718556bb31ab58ffc0968504c /include | |
parent | 39861d1872731119795f954acc0412af64cd539d (diff) | |
download | stasis-239330330a8fc49c6806cccdc17a910d23d22952.tar.gz |
Prototypes documentation
Includes minor changes:
* Rename jfrt_upload_set_defaults to jfrt_upload_init
* Move jfrt_auth_init to artifactory.c
* Adds missing error handling to git_describe and git_rev_parse
Diffstat (limited to 'include')
-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 |
8 files changed, 397 insertions, 37 deletions
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 */ |