From 05701d9d85493961d3bc08fa355205ea630c6b18 Mon Sep 17 00:00:00 2001 From: Asger Gitz-Johansen Date: Sun, 25 Aug 2024 15:52:43 +0200 Subject: [PATCH] wip: custom environment variable passing You should be able to tell your sci deployment which env vars should be passed to the pipelines with -e ENV1 -e ENV2 and so on --- Makefile | 5 +- TODO.md | 2 + include/cli.h | 1 + include/optional.h | 2 + include/strlist.h | 51 +++++++++++++++++ include/threadlist.h | 5 +- scripts/wget-and-sci.sh | 1 + src/cli.c | 38 ++++++++++--- src/sci.1 | 1 - src/strlist.c | 62 +++++++++++++++++++++ src/util.c | 120 ++++++++++++++++++++-------------------- 11 files changed, 215 insertions(+), 73 deletions(-) create mode 100644 include/strlist.h create mode 100644 src/strlist.c diff --git a/Makefile b/Makefile index 4bf0d8d..a1eb224 100644 --- a/Makefile +++ b/Makefile @@ -29,14 +29,15 @@ all: out/bin/sci out/obj/%.o: src/%.c | $(OBJDIR) $(CC) -c $? $(CFLAGS) -o $@ -OBJ += out/obj/main.o OBJ += out/obj/cli.o OBJ += out/obj/log.o +OBJ += out/obj/main.o OBJ += out/obj/notify.o -OBJ += out/obj/util.o OBJ += out/obj/pipeline.o +OBJ += out/obj/strlist.o OBJ += out/obj/threadlist.o OBJ += out/obj/threadpool.o +OBJ += out/obj/util.o out/bin/sci: $(OBJ) | $(BINDIR) $(CC) -o $@ $^ $(CFLAGS) diff --git a/TODO.md b/TODO.md index 5414299..d4a5a66 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,8 @@ - [-] ~~docker~~ later. - [ ] Eight things eight, try it out! - maybe even write the python webhook extension. - [ ] Port this document to gitea issue tracking + - [x] enable PATH-able programs and argv in the command section + - [ ] custom environment variable passing. Something like `-e MY_TOKEN` ala docker-style - [ ] Ninth things ninth, fix bugs, see below - [ ] Tenth things tenth, write manpages, choose license - [ ] Eleventh things Eleventh, polish diff --git a/include/cli.h b/include/cli.h index 95cf127..f3f511c 100644 --- a/include/cli.h +++ b/include/cli.h @@ -29,6 +29,7 @@ typedef struct { bool use_colors; optional_str log_file; optional_str pipeline_log_dir; + optional_strlist environment_vars; } cli_options; // Construct a new cli_options struct instance. diff --git a/include/optional.h b/include/optional.h index 77ebfc1..b1b2e1e 100644 --- a/include/optional.h +++ b/include/optional.h @@ -17,6 +17,7 @@ */ #ifndef SCI_OPTIONAL_H #define SCI_OPTIONAL_H +#include "strlist.h" #include #define optional_type(type) struct { bool has_value; type value; } @@ -24,5 +25,6 @@ typedef optional_type(int) optional_int; typedef optional_type(float) optional_float; typedef optional_type(char*) optional_str; typedef optional_type(const char*) optional_cstr; +typedef optional_type(strlist_node*) optional_strlist; #endif diff --git a/include/strlist.h b/include/strlist.h new file mode 100644 index 0000000..2298ecd --- /dev/null +++ b/include/strlist.h @@ -0,0 +1,51 @@ +/** + * sci - a simple ci system + Copyright (C) 2024 Asger Gitz-Johansen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +#ifndef SCI_STRLIST_H +#define SCI_STRLIST_H + +// doubly linked list +typedef struct strlist_node { + char* str; + struct strlist_node* previous; + struct strlist_node* next; +} strlist_node; + +// Create a new root node. +// This function is not threadsafe. +strlist_node* create_strlist_node(char* str); + +// Add a new string to the string list. +// This function is not threadsafe. +strlist_node* add_str(char* str, strlist_node* root); + +// add a new string list node to the list. +// This function is not threadsafe. +strlist_node* add_str_node(strlist_node* str_node, strlist_node* root); + +// Remove a string list node from the list. +// This will free the str and stitch the "previous" and "next" ptrs. +// This function is not threadsafe. +void remove_strlist_node(strlist_node* node); + +// Completely clear the list. +// The list is completely invalid after this call and should be discarded. +// root itself will not be free'd by this function, but all content will be. +// This function is not threadsafe. +void clear_strlist(strlist_node* root); + +#endif diff --git a/include/threadlist.h b/include/threadlist.h index d7fb249..ce62459 100644 --- a/include/threadlist.h +++ b/include/threadlist.h @@ -42,9 +42,8 @@ void remove_thread_node(pthread_list_node* node); // Completely clear the thread list. // This will call pthread_join on all nodes. // The list is completely invalid after this call and should be discarded. -// Note: -// - `root` has already been free'd. -// - this function is not thread-safe. +// Even root itself will be free'd by this function so it should be discarded as well. +// This function is not thread-safe. void clear_thread_list(pthread_list_node* root); #endif diff --git a/scripts/wget-and-sci.sh b/scripts/wget-and-sci.sh index 4ee671f..1b79fc3 100755 --- a/scripts/wget-and-sci.sh +++ b/scripts/wget-and-sci.sh @@ -2,6 +2,7 @@ # NOTE: This script assumes that the url is a .tar.gz file. # TODO: check if $# is >= 1 and give a warning that the extract dir should be provided. set -ex # print all that we're doing (no need for echo's) +env tmpdir=$(mktemp -d) wget "$SCI_PIPELINE_URL" -P "$tmpdir" cd "$tmpdir" diff --git a/src/cli.c b/src/cli.c index c8b8aa3..ade9c6b 100644 --- a/src/cli.c +++ b/src/cli.c @@ -15,11 +15,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include "cli.h" +#include "log.h" +#include #include #include #include -#include -#include "cli.h" cli_options new_options() { cli_options result; @@ -50,6 +51,19 @@ cli_options new_options() { char* pipeline_log_dir = getenv("SCI_PIPELINE_LOG_DIR"); result.pipeline_log_dir.has_value = pipeline_log_dir != NULL; result.pipeline_log_dir.value = pipeline_log_dir; + + char* environment_vars = getenv("SCI_PIPELINE_ENV_VARS"); + if(environment_vars == NULL) { + result.environment_vars.has_value = false; + result.environment_vars.value = NULL; + } else { + char* tok = strtok(environment_vars, ";"); + result.environment_vars.has_value = true; + result.environment_vars.value = create_strlist_node(tok); + tok = strtok(NULL, ";"); + while(tok != NULL) + add_str(tok, result.environment_vars.value); + } return result; } @@ -60,24 +74,27 @@ void destroy_options(cli_options v) { free(v.log_file.value); if(v.pipeline_log_dir.has_value) free(v.pipeline_log_dir.value); + if(v.environment_vars.has_value) + clear_strlist(v.environment_vars.value); } // . -\" TODO: decide on license .SH COPYRIGHT Copyright (C) 2024 Asger Gitz-Johansen diff --git a/src/strlist.c b/src/strlist.c new file mode 100644 index 0000000..6fe70e3 --- /dev/null +++ b/src/strlist.c @@ -0,0 +1,62 @@ +#include "strlist.h" +#include +#include + +#define MAX_STRLEN 512 + +strlist_node* create_strlist_node(char* str) { + strlist_node* new = malloc(sizeof(strlist_node)); + new->previous = NULL; + new->next = NULL; + if(str) + new->str = strndup(str, MAX_STRLEN); + else + new->str = NULL; + return new; +} + +strlist_node* add_str(char* str, strlist_node* root) { + strlist_node* cursor = root; + while(cursor->next != NULL) + cursor = cursor->next; + strlist_node* new = malloc(sizeof(strlist_node)); + new->previous = cursor; + new->next = NULL; + new->str = strndup(str, MAX_STRLEN); + cursor->next = new; + return new; +} + +strlist_node* add_str_node(strlist_node* root, strlist_node* node) { + strlist_node* cursor = root; + while(cursor->next != NULL) + cursor = cursor->next; + node->previous = cursor; + node->next = NULL; + cursor->next = node; + return node; +} + +void remove_strlist_node(strlist_node* node) { + strlist_node* prev = node->previous; + strlist_node* next = node->next; + if(node->str) + free(node->str); + node->str = NULL; + free(node); + if(prev != NULL) + prev->next = next; + if(next != NULL) + next->previous = prev; +} + +void clear_strlist(strlist_node* root) { + strlist_node* cursor = root; + while(cursor != NULL) { + cursor = cursor->next; + if(cursor->str) + free(cursor->str); + cursor->str = NULL; + free(cursor->previous); + } +} diff --git a/src/util.c b/src/util.c index bc08701..1f8973a 100644 --- a/src/util.c +++ b/src/util.c @@ -22,45 +22,45 @@ #include char* trim(const char* const str) { - char* begin = strdup(str); - char* end; - while(isspace((unsigned char)*begin)) - begin++; - if(*begin == 0) - return begin; - end = begin + strlen(begin) - 1; - while(end > begin && isspace((unsigned char)*end)) - end--; - *(end + 1) = '\0'; - return begin; + char* begin = strdup(str); + char* end; + while(isspace((unsigned char)*begin)) + begin++; + if(*begin == 0) + return begin; + end = begin + strlen(begin) - 1; + while(end > begin && isspace((unsigned char)*end)) + end--; + *(end + 1) = '\0'; + return begin; } void per_line(const char* file, line_handler handler) { - FILE* stream; - char* line = NULL; - size_t len = 0; - ssize_t nread; - log_trace("reading file %s", file); - stream = fopen(file, "r"); - if(stream == NULL) { - perror("fopen"); - return; - } - while((nread = getline(&line, &len, stream)) != -1) { - char* line_trimmed = trim(line); - handler(line_trimmed); - free(line_trimmed); - } - free(line); - fclose(stream); + FILE* stream; + char* line = NULL; + size_t len = 0; + ssize_t nread; + log_trace("reading file %s", file); + stream = fopen(file, "r"); + if(stream == NULL) { + perror("fopen"); + return; + } + while((nread = getline(&line, &len, stream)) != -1) { + char* line_trimmed = trim(line); + handler(line_trimmed); + free(line_trimmed); + } + free(line); + fclose(stream); } char* join(const char* a, const char* b) { - size_t alen = strlen(a); - size_t blen = strlen(b); - char* result = malloc(alen + blen + 1); - sprintf(result, "%s%s", a, b); - return result; + size_t alen = strlen(a); + size_t blen = strlen(b); + char* result = malloc(alen + blen + 1); + sprintf(result, "%s%s", a, b); + return result; } const char* skip_arg(const char* cp) { @@ -122,31 +122,31 @@ char** argv_split(const char* str, int* argc_out) { } int which(const char* program_name, char* out_full_program, int max_path) { - assert(out_full_program); - assert(max_path > 0); - // sanity check - maybe program_name is actually a full-path to begin with - if(access(program_name, X_OK) == 0) { - snprintf(out_full_program, max_path, "%s", program_name); - return 0; - } - char* path = getenv("PATH"); - if (path == NULL) { - log_error("PATH environment variable not found."); - return -1; - } - char* path_cpy = strdup(path); - char* dir = strtok(path_cpy, ":"); - char full_path[PATH_MAX]; - while(dir != NULL) { - snprintf(full_path, sizeof(full_path), "%s/%s", dir, program_name); - if(access(full_path, X_OK) == 0) { - snprintf(out_full_program, max_path, "%s", full_path); - free(path_cpy); - return 0; - } - dir = strtok(NULL, ":"); - } - log_error("'%s' not found in PATH", program_name); - free(path_cpy); - return -1; + assert(out_full_program); + assert(max_path > 0); + // sanity check - maybe program_name is actually a full-path to begin with + if(access(program_name, X_OK) == 0) { + snprintf(out_full_program, max_path, "%s", program_name); + return 0; + } + char* path = getenv("PATH"); + if (path == NULL) { + log_error("PATH environment variable not found."); + return -1; + } + char* path_cpy = strdup(path); + char* dir = strtok(path_cpy, ":"); + char full_path[PATH_MAX]; + while(dir != NULL) { + snprintf(full_path, sizeof(full_path), "%s/%s", dir, program_name); + if(access(full_path, X_OK) == 0) { + snprintf(out_full_program, max_path, "%s", full_path); + free(path_cpy); + return 0; + } + dir = strtok(NULL, ":"); + } + log_error("'%s' not found in PATH", program_name); + free(path_cpy); + return -1; }