aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2026-06-18 12:29:13 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2026-06-18 12:29:13 -0400
commit9d2646362964d05f2c3346b5cd24ad010fdd2039 (patch)
tree78291d05dea3a52e941b1c0b21a3d813267a523f /src
parentb9cac8cfa9a0ef9138b860c2fa72c6fe51331adf (diff)
downloadstasis-9d2646362964d05f2c3346b5cd24ad010fdd2039.tar.gz
Add indexer arugment '--micromamba-download-url'
* Add is_file_compressed() function to utils * Call micromamba_install() from tests
Diffstat (limited to 'src')
-rw-r--r--src/cli/stasis_indexer/args.c2
-rw-r--r--src/cli/stasis_indexer/helpers.c1
-rw-r--r--src/cli/stasis_indexer/include/args.h2
-rw-r--r--src/cli/stasis_indexer/stasis_indexer_main.c14
-rw-r--r--src/lib/core/conda.c29
-rw-r--r--src/lib/core/include/utils.h18
-rw-r--r--src/lib/core/utils.c46
7 files changed, 104 insertions, 8 deletions
diff --git a/src/cli/stasis_indexer/args.c b/src/cli/stasis_indexer/args.c
index 0d0e9b9..ccc69e6 100644
--- a/src/cli/stasis_indexer/args.c
+++ b/src/cli/stasis_indexer/args.c
@@ -7,6 +7,7 @@ struct option long_options[] = {
{"verbose", no_argument, 0, 'v'},
{"unbuffered", no_argument, 0, 'U'},
{"web", no_argument, 0, 'w'},
+ {"micromamba-download-url", required_argument, 0, OPT_MICROMAMBA_DOWNLOAD_URL},
{0, 0, 0, 0},
};
@@ -16,6 +17,7 @@ const char *long_options_help[] = {
"Increase output verbosity",
"Disable line buffering",
"Generate HTML indexes (requires pandoc)",
+ "Set micromamba download URL",
NULL,
};
diff --git a/src/cli/stasis_indexer/helpers.c b/src/cli/stasis_indexer/helpers.c
index bdb538d..b89622b 100644
--- a/src/cli/stasis_indexer/helpers.c
+++ b/src/cli/stasis_indexer/helpers.c
@@ -157,6 +157,7 @@ int micromamba_configure(const struct Delivery *ctx, struct MicromambaInfo *m) {
m->conda_prefix = globals.conda_install_prefix;
m->micromamba_prefix = micromamba_prefix;
m->download_dir = ctx->storage.tmpdir;
+ m->download_url = globals.micromamba_download_url;
const size_t pathvar_len = strlen(getenv("PATH")) + strlen(m->micromamba_prefix) + strlen(m->conda_prefix) + 3 + 4 + 1;
// ^^^^^^^^^^^^^^^^^^
diff --git a/src/cli/stasis_indexer/include/args.h b/src/cli/stasis_indexer/include/args.h
index 543aa4b..080863c 100644
--- a/src/cli/stasis_indexer/include/args.h
+++ b/src/cli/stasis_indexer/include/args.h
@@ -3,6 +3,8 @@
#include <getopt.h>
+#define OPT_MICROMAMBA_DOWNLOAD_URL 1000
+
extern struct option long_options[];
void usage(char *name);
diff --git a/src/cli/stasis_indexer/stasis_indexer_main.c b/src/cli/stasis_indexer/stasis_indexer_main.c
index c13d175..5d86f29 100644
--- a/src/cli/stasis_indexer/stasis_indexer_main.c
+++ b/src/cli/stasis_indexer/stasis_indexer_main.c
@@ -197,6 +197,13 @@ int main(const int argc, char *argv[]) {
case 'w':
do_html = 1;
break;
+ case OPT_MICROMAMBA_DOWNLOAD_URL:
+ globals.micromamba_download_url = strdup(optarg);
+ if (!globals.micromamba_download_url) {
+ SYSERROR("unable to allocate memory for micromamba_download_url");
+ exit(1);
+ }
+ break;
case '?':
default:
exit(1);
@@ -326,6 +333,13 @@ int main(const int argc, char *argv[]) {
SYSWARN("Unable to open stasis configuration file: %s", cfg_path);
}
+ if (globals.micromamba_download_url && isempty(globals.micromamba_download_url)) {
+ // safeguard against supplying a zero-length URL
+ // this covers the case where the user supplied it as an argument and/or in the config file
+ SYSERROR("micromamba download URL cannot be empty");
+ exit(1);
+ }
+
indexer_init_dirs(&ctx, workdir);
msg(STASIS_MSG_L1, "%s delivery root %s\n",
diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c
index 444cfaa..5c7779f 100644
--- a/src/lib/core/conda.c
+++ b/src/lib/core/conda.c
@@ -36,9 +36,10 @@ int micromamba_install(const struct MicromambaInfo *info) {
// https://github.com/mamba-org/micromamba-releases/releases/download/${version}/micromamba-${arch}
// Micromamba hosts binaries on github and on their own website. Prefer github.
+ // The "latest" binary from micromamba's site is compressed with bzip2 (06/2026)
const char *url_fmts[] = {
info->download_url,
- "https://github.com/mamba-org/micromamba-releases/releases/latest/download/micromamba-%s-%s",
+ "https://github.com/mamba-org/micromamba-releases/releases/latest/download/micromamba-%s-%s.tar.bz2",
"https://micro.mamba.pm/api/micromamba/%s-%s/latest",
};
const size_t url_fmts_max = sizeof(url_fmts) / sizeof(url_fmts[0]);
@@ -84,14 +85,26 @@ int micromamba_install(const struct MicromambaInfo *info) {
snprintf(mmbin, sizeof(mmbin), "%s/micromamba", info->micromamba_prefix);
if (access(mmbin, F_OK)) {
- char untarcmd[PATH_MAX * 2];
mkdirs(info->micromamba_prefix, 0755);
- snprintf(untarcmd, sizeof(untarcmd),
- "tar -xvf %s -C %s --strip-components=1 bin/micromamba 1>/dev/null",
- installer_path, info->micromamba_prefix);
- int untarcmd_status = system(untarcmd);
- if (untarcmd_status) {
- return -1;
+
+ if (is_file_compressed(installer_path)) {
+ char untarcmd[PATH_MAX * 2] = {0};
+ snprintf(untarcmd, sizeof(untarcmd),
+ "tar -xvf %s -C %s --strip-components=1 bin/micromamba 1>/dev/null",
+ installer_path, info->micromamba_prefix);
+ int untarcmd_status = system(untarcmd);
+ if (untarcmd_status) {
+ return -1;
+ }
+ } else {
+ if (copy2(installer_path, mmbin, CT_PERM)) {
+ SYSERROR("unable to copy %s to %s", installer_path, mmbin);
+ return -1;
+ }
+ if (chmod(mmbin, 0755)) {
+ SYSERROR("unable to set permissions: %s (%s)", mmbin, strerror(errno));
+ return -1;
+ }
}
}
return 0;
diff --git a/src/lib/core/include/utils.h b/src/lib/core/include/utils.h
index 98b8ae8..3f0fe9f 100644
--- a/src/lib/core/include/utils.h
+++ b/src/lib/core/include/utils.h
@@ -492,4 +492,22 @@ int get_random_bytes(char *result, size_t maxlen);
int non_format_len(const char *s);
char *center_text(const char *s, size_t maxwidth);
+
+/**
+ * Check magic bytes against known compression formats
+ *
+ * ```c
+ * const char *filename = "/path/to/file.zip";
+ * if (is_file_compressed(filename)) {
+ * // file is compressed
+ * } else {
+ * // file is not compressed
+ * }
+ * ```
+ *
+ * @param filename path to maybe-compressed file
+ * @return 0 if not compressed
+ * @return 1 if compressed
+ */
+int is_file_compressed(const char *filename);
#endif //STASIS_UTILS_H
diff --git a/src/lib/core/utils.c b/src/lib/core/utils.c
index b4a520d..152c5c5 100644
--- a/src/lib/core/utils.c
+++ b/src/lib/core/utils.c
@@ -1273,3 +1273,49 @@ char *center_text(const char *s, const size_t maxwidth) {
return result;
}
+int is_file_compressed(const char *filename) {
+ FILE *fp = fopen(filename, "rb");
+ if (!fp) {
+ SYSERROR("Unable to open for reading: %s", filename);
+ return -1;
+ }
+
+ // Container for magic bytes
+ struct Magic {
+ const unsigned char *byte;
+ const size_t size;
+ };
+
+ // Array of magic bytes for different compression types
+ const struct Magic magic[] = {
+ {(unsigned char *) "BZh", 3}, // bzip2
+ {(unsigned char *) "\x1f\x8b", 2}, // gzip
+ {(unsigned char *) "\xfd\x37\x7a\x58\x5a\x00", 6}, // xz
+ {(unsigned char *) "PK\03\04", 3}, // zip
+ {(unsigned char *) "PK\05\06", 3}, // zip (empty)
+ {(unsigned char *) "PK\07\08", 3}, // zip (spanned)
+ {(unsigned char *) "\xfd\x2f\xb5\x28", 4} // zstd
+ };
+ unsigned char buf[8] = {0}; // unsigned long
+ size_t bytes_read = 0;
+ bytes_read = fread(buf, 1, sizeof(buf), fp);
+ if (bytes_read < sizeof(buf)) {
+ SYSWARN("consumed fewer than %zu bytes (%zu) from %s", sizeof(buf), bytes_read, filename);
+ fclose(fp);
+ // well, the file isn't compressed.
+ return 0;
+ }
+ fclose(fp);
+
+ // Compare known magic bytes to consumed data
+ for (size_t i = 0; i < sizeof(magic) / sizeof(magic[0]); i++) {
+ if (memcmp(buf, magic[i].byte, magic[i].size) == 0) {
+ // match
+ return 1;
+ }
+ }
+
+ // no match
+ return 0;
+}
+