aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@users.noreply.github.com>2024-08-20 10:45:09 -0400
committerGitHub <noreply@github.com>2024-08-20 10:45:09 -0400
commit0eda05963f3c70c3969ddd2aa72926b871ef4b07 (patch)
treef38b6fd60a36b82e1247ff60ef28e694cd9517ff
parenta7568dc03c1c6851ff6c690e8e35ade9a3199c4a (diff)
downloadstasis-0eda05963f3c70c3969ddd2aa72926b871ef4b07.tar.gz
Pypi existence check (#30)
* Add python_package_exists() function * Poll pypi.org or compatible index to see if a package exists * Returns non-zero on success * Implements python_package_exists() in delivery_defer_packages() * Implements python_package_exists() in delivery_defer_packages() * Bugfix: Avoid incorrect package selection * With large package lists that contain multiple packages starting with the same strstr() would pick the first match * This adds a temporary name variable that strcmp() can check against. * Message correction: * Change "release" to "testing" in testing environment failure message * Amend message to fit the flow of the output * Disable outdated conda notifications * The latest version isn't always the greatest. Don't give the end-user any ideas. Just use whatever the installer provides... quietly * Rename python_package_exists to pip_index_provides * Document the function prototype * Add missing comments in micromamba structure * Ensure the temporary output file does not linger
-rw-r--r--include/conda.h15
-rw-r--r--src/conda.c80
-rw-r--r--src/delivery.c15
-rw-r--r--src/stasis_main.c2
4 files changed, 101 insertions, 11 deletions
diff --git a/include/conda.h b/include/conda.h
index d439371..c546672 100644
--- a/include/conda.h
+++ b/include/conda.h
@@ -7,10 +7,11 @@
#include "core.h"
#define CONDA_INSTALL_PREFIX "conda"
+#define PYPI_INDEX_DEFAULT "https://pypi.org/simple"
struct MicromambaInfo {
- char *micromamba_prefix;
- char *conda_prefix;
+ char *micromamba_prefix; //!< Path to write micromamba binary
+ char *conda_prefix; //!< Path to install conda base tree
};
/**
@@ -181,4 +182,14 @@ int conda_env_export(char *name, char *output_dir, char *output_filename);
* @return exit code from "conda"
*/
int conda_index(const char *path);
+
+/**
+ * Determine whether a simple index contains a package
+ * @param index_url a file system path or url pointing to a simple index
+ * @param name package name (required)
+ * @param version package version (may be NULL)
+ * @return not found = 0, found = 1, error = -1
+ */
+int pip_index_provides(const char *index_url, const char *name, const char *version);
+
#endif //STASIS_CONDA_H
diff --git a/src/conda.c b/src/conda.c
index 6ed96c7..ff55f14 100644
--- a/src/conda.c
+++ b/src/conda.c
@@ -79,6 +79,73 @@ int pip_exec(const char *args) {
return system(command);
}
+int pip_index_provides(const char *index_url, const char *name, const char *version) {
+ char cmd[PATH_MAX] = {0};
+ char name_local[255];
+ char version_local[255] = {0};
+ char spec[255] = {0};
+
+ if (isempty((char *) name) < 0) {
+ // no package name means nothing to do.
+ return -1;
+ }
+
+ // Fix up the package name
+ strncpy(name_local, name, sizeof(name_local) - 1);
+ tolower_s(name_local);
+ lstrip(name_local);
+ strip(name_local);
+
+ if (version) {
+ // Fix up the package version
+ strncpy(version_local, version, sizeof(version_local) - 1);
+ tolower_s(version_local);
+ lstrip(version_local);
+ strip(version_local);
+ sprintf(spec, "==%s", version);
+ }
+
+ char logfile[] = "/tmp/STASIS-package_exists.XXXXXX";
+ int logfd = mkstemp(logfile);
+ if (logfd < 0) {
+ perror(logfile);
+ remove(logfile); // fail harmlessly if not present
+ return -1;
+ }
+
+
+ int status = 0;
+ struct Process proc;
+ memset(&proc, 0, sizeof(proc));
+ proc.redirect_stderr = 1;
+ strcpy(proc.f_stdout, logfile);
+
+ // Do an installation in dry-run mode to see if the package exists in the given index.
+ snprintf(cmd, sizeof(cmd) - 1, "python -m pip install --dry-run --no-deps --index-url=%s %s%s", index_url, name_local, spec);
+ status = shell(&proc, cmd);
+
+ // Print errors only when shell() itself throws one
+ // If some day we want to see the errors thrown by pip too, use this condition instead: (status != 0)
+ if (status < 0) {
+ FILE *fp = fdopen(logfd, "r");
+ if (!fp) {
+ remove(logfile);
+ return -1;
+ } else {
+ char line[BUFSIZ] = {0};
+ fflush(stdout);
+ fflush(stderr);
+ while (fgets(line, sizeof(line) - 1, fp) != NULL) {
+ fprintf(stderr, "%s", line);
+ }
+ fflush(stderr);
+ fclose(fp);
+ }
+ }
+ remove(logfile);
+ return proc.returncode == 0;
+}
+
int conda_exec(const char *args) {
char command[PATH_MAX];
const char *mamba_commands[] = {
@@ -275,12 +342,13 @@ int conda_setup_headless() {
}
// Configure conda for headless CI
- conda_exec("config --system --set auto_update_conda false"); // never update conda automatically
- conda_exec("config --system --set always_yes true"); // never prompt for input
- conda_exec("config --system --set safety_checks disabled"); // speedup
- conda_exec("config --system --set rollback_enabled false"); // speedup
- conda_exec("config --system --set report_errors false"); // disable data sharing
- conda_exec("config --system --set solver libmamba"); // use a real solver
+ conda_exec("config --system --set auto_update_conda false"); // never update conda automatically
+ conda_exec("config --system --set notify_outdated_conda false"); // never notify about outdated conda version
+ conda_exec("config --system --set always_yes true"); // never prompt for input
+ conda_exec("config --system --set safety_checks disabled"); // speedup
+ conda_exec("config --system --set rollback_enabled false"); // speedup
+ conda_exec("config --system --set report_errors false"); // disable data sharing
+ conda_exec("config --system --set solver libmamba"); // use a real solver
char cmd[PATH_MAX];
size_t total = 0;
diff --git a/src/delivery.c b/src/delivery.c
index e69ce2f..524dd0a 100644
--- a/src/delivery.c
+++ b/src/delivery.c
@@ -1466,8 +1466,14 @@ void delivery_defer_packages(struct Delivery *ctx, int type) {
struct Test *test = &ctx->tests[x];
version = NULL;
+ char nametmp[1024] = {0};
+ if (spec_end != NULL && spec_begin != NULL) {
+ strncpy(nametmp, name, spec_begin - name);
+ } else {
+ strcpy(nametmp, name);
+ }
// Is the [test:NAME] in the package name?
- if (strstr(name, test->name)) {
+ if (!strcmp(nametmp, test->name)) {
// Override test->version when a version is provided by the (pip|conda)_package list item
guard_free(test->version);
if (spec_begin && spec_end) {
@@ -1498,7 +1504,12 @@ void delivery_defer_packages(struct Delivery *ctx, int type) {
}
}
- ignore_pkg = 1;
+ if (DEFER_PIP == type && pip_index_provides(PYPI_INDEX_DEFAULT, name, version)) {
+ fprintf(stderr, "(%s present on index %s): ", version, PYPI_INDEX_DEFAULT);
+ ignore_pkg = 0;
+ } else {
+ ignore_pkg = 1;
+ }
break;
}
}
diff --git a/src/stasis_main.c b/src/stasis_main.c
index cf07b3a..7ea465c 100644
--- a/src/stasis_main.c
+++ b/src/stasis_main.c
@@ -482,7 +482,7 @@ int main(int argc, char *argv[]) {
exit(1);
}
if (conda_env_create(env_name_testing, ctx.meta.python, NULL)) {
- msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "failed to create release environment\n");
+ msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "failed to create testing environment\n");
exit(1);
}
}