From bd558e5c2921efb1c29de8b381fcfbd99a73dfec Mon Sep 17 00:00:00 2001 From: JGZYES Date: Wed, 2 Sep 2026 18:10:22 +0800 Subject: [PATCH] feat: pmm cache clean; install --no-cache; --git repo@tag / -b branch (specific release) --- src/install.c | 3 +++ src/install.h | 3 +++ src/main.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/repo.c | 34 ++++++++++++++++++++++++-------- src/repo.h | 1 + 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/src/install.c b/src/install.c index 0a923dd..650a783 100644 --- a/src/install.c +++ b/src/install.c @@ -152,10 +152,13 @@ static int native_binary_ok(const char *path, PmmOS os) { static int install_path(const char *path, const char *name); +int pmm_no_cache = 0; /* --no-cache: drop cached file before download */ + int install_file(const char *url, const char *name) { char cache[1024], path[1200]; pmm_cache_dir(cache, sizeof(cache)); snprintf(path, sizeof(path), "%s/%s", cache, name); + if (pmm_no_cache) remove(path); /* force a fresh download */ /* apt-style fallback: mirrors (by priority) first, then the origin URL */ MirrorList *ml = mirrors_load(); diff --git a/src/install.h b/src/install.h index 5a006c7..a223349 100644 --- a/src/install.h +++ b/src/install.h @@ -10,6 +10,9 @@ int install_file(const char *url, const char *name); * `pmm install -dpkg foo.deb` (Linux) or `pmm install -msi foo.msi` (Windows). */ int install_local_file(const char *path); +/* Set by `pmm install --no-cache` to force a fresh download (drop cache file). */ +extern int pmm_no_cache; + /* Install from a remote registry (apt-style multi-mirror fallback): * looks up package `name` in the configured registry mirrors by priority. * If `version` is non-NULL, fetches {registry}/{name}/{version}.json, otherwise diff --git a/src/main.c b/src/main.c index cbd2b99..3af21e5 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,11 @@ #include #ifdef _WIN32 #include +#else +#include +#include +#include +#include #endif #include #include @@ -261,9 +266,50 @@ static int cmd_verify(int argc, char **argv) { return 0; } +/* Recursively delete a directory (for the download cache). */ +static void rm_tree(const char *dir) { +#ifdef _WIN32 + char pat[1200]; snprintf(pat, sizeof(pat), "%s\\*", dir); + WIN32_FIND_DATAA fd; HANDLE h = FindFirstFileA(pat, &fd); + if (h != INVALID_HANDLE_VALUE) { + do { + if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; + char full[1400]; snprintf(full, sizeof(full), "%s\\%s", dir, fd.cFileName); + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) rm_tree(full); + else DeleteFileA(full); + } while (FindNextFileA(h, &fd)); + FindClose(h); + } + RemoveDirectoryA(dir); +#else + DIR *d = opendir(dir); + if (!d) return; + struct dirent *e; + while ((e = readdir(d))) { + if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; + char full[1400]; snprintf(full, sizeof(full), "%s/%s", dir, e->d_name); + struct stat st; + if (stat(full, &st) == 0 && S_ISDIR(st.st_mode)) rm_tree(full); + else remove(full); + } + closedir(d); + rmdir(dir); +#endif +} + +/* pmm cache clean — wipe the download cache. */ +static int cmd_cache_clean(void) { + char dir[1024]; + pmm_cache_dir(dir, sizeof(dir)); + printf("pmm: cleaning cache %s\n", dir); + rm_tree(dir); + return 0; +} + /* [--host ] */ static int cmd_install_git(int argc, char **argv, const char *flag) { const char *repo = NULL; + const char *branch = NULL; PmmHost host = HOST_AUTO; PmmConfig cfg; MirrorSel mirror; load_config(&cfg); @@ -281,6 +327,8 @@ static int cmd_install_git(int argc, char **argv, const char *flag) { fprintf(stderr, "pmm: unknown host '%s'\n", argv[i]); return 1; } + } else if ((strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--branch") == 0) && i + 1 < argc) { + branch = argv[++i]; } else if (!repo) { repo = argv[i]; } @@ -299,6 +347,7 @@ static int cmd_install_git(int argc, char **argv, const char *flag) { RepoContext *ctx = repo_open(host, repo, mirror.api_base); if (!ctx) return 1; + if (branch && *branch) { free(ctx->ref); ctx->ref = strdup(branch); } printf("pmm: host=%s repo=%s%s%s\n", host_name(host), repo, mirror.name ? " mirror=" : "", mirror.name ? mirror.name : ""); @@ -539,6 +588,7 @@ int main(int argc, char **argv) { const char *items[64]; int ni = 0; for (int i = 2; i < argc; i++) { const char *a = argv[i]; + if (strcmp(a, "--no-cache") == 0) { pmm_no_cache = 1; continue; } if (strcmp(a, "-dpkg") == 0) { forced = 1; if (i + 1 < argc) items[ni++] = argv[++i]; continue; } if (strcmp(a, "-msi") == 0) { forced = 2; if (i + 1 < argc) items[ni++] = argv[++i]; continue; } if (strncmp(a, "-v", 2) == 0 && a[2]) { version = a + 2; continue; } @@ -613,6 +663,10 @@ int main(int argc, char **argv) { if (strcmp(argv[1], "search") == 0) return cmd_search(argc - 2, argv + 2); + if (strcmp(argv[1], "cache") == 0) { + if (argc >= 3 && strcmp(argv[2], "clean") == 0) return cmd_cache_clean(); + fprintf(stderr, "pmm: usage: pmm cache clean\n"); return 1; + } if (strcmp(argv[1], "info") == 0) return cmd_info(argc - 2, argv + 2); if (strcmp(argv[1], "verify") == 0) diff --git a/src/repo.c b/src/repo.c index 58ca0ae..864d750 100644 --- a/src/repo.c +++ b/src/repo.c @@ -93,12 +93,24 @@ RepoContext *repo_open(PmmHost host, const char *repo, const char *mirror_api_ba RepoContext *ctx = calloc(1, sizeof(RepoContext)); if (!ctx) return NULL; ctx->host = host; - char *norm = normalize_repo(repo); + /* support "repo@ref" (e.g. owner/repo@v1.2.3) → specific release tag */ + const char *lastslash = strrchr(repo, '/'); + const char *at = strrchr(repo, '@'); + char repo_buf[1024]; + const char *repo_src = repo; + if (at && lastslash && at > lastslash) { + snprintf(repo_buf, sizeof(repo_buf), "%.*s", (int)(at - repo), repo); + repo_src = repo_buf; + ctx->ref = strdup(at + 1); + } else { + ctx->ref = strdup(""); + } + char *norm = normalize_repo(repo_src); char api[1024]; ctx->repo_norm = strdup(norm); char origin[512]; - extract_origin(repo, origin, sizeof(origin)); + extract_origin(repo_src, origin, sizeof(origin)); ctx->origin = strdup(origin); const char *base = NULL; @@ -155,6 +167,7 @@ void repo_close(RepoContext *ctx) { free(ctx->page_url); free(ctx->origin); free(ctx->repo_norm); + free(ctx->ref); free(ctx); } @@ -333,10 +346,12 @@ ReleaseAsset *repo_latest_asset(RepoContext *ctx, PmmOS os) { switch (ctx->host) { case HOST_GITLAB: - snprintf(url, sizeof(url), "%s/releases/permalink/latest", ctx->api_url); + if (ctx->ref && *ctx->ref) { char *e = url_encode(ctx->ref); snprintf(url, sizeof(url), "%s/releases/%s", ctx->api_url, e); free(e); } + else snprintf(url, sizeof(url), "%s/releases/permalink/latest", ctx->api_url); break; default: - snprintf(url, sizeof(url), "%s/releases/latest", ctx->api_url); + if (ctx->ref && *ctx->ref) { char *e = url_encode(ctx->ref); snprintf(url, sizeof(url), "%s/releases/tags/%s", ctx->api_url, e); free(e); } + else snprintf(url, sizeof(url), "%s/releases/latest", ctx->api_url); break; } return fetch_latest(url, ctx->host, os, NULL); @@ -349,10 +364,13 @@ ReleaseAsset *repo_latest_asset(RepoContext *ctx, PmmOS os) { ReleaseAsset *repo_asset_matching(RepoContext *ctx, PmmOS os, const char *name_sub) { if (!ctx || !ctx->api_url || !name_sub) return NULL; char url[2048]; - if (ctx->host == HOST_GITLAB) - snprintf(url, sizeof(url), "%s/releases/permalink/latest", ctx->api_url); - else - snprintf(url, sizeof(url), "%s/releases/latest", ctx->api_url); + if (ctx->host == HOST_GITLAB) { + if (ctx->ref && *ctx->ref) { char *e = url_encode(ctx->ref); snprintf(url, sizeof(url), "%s/releases/%s", ctx->api_url, e); free(e); } + else snprintf(url, sizeof(url), "%s/releases/permalink/latest", ctx->api_url); + } else { + if (ctx->ref && *ctx->ref) { char *e = url_encode(ctx->ref); snprintf(url, sizeof(url), "%s/releases/tags/%s", ctx->api_url, e); free(e); } + else snprintf(url, sizeof(url), "%s/releases/latest", ctx->api_url); + } return fetch_latest(url, ctx->host, os, name_sub); } diff --git a/src/repo.h b/src/repo.h index be0da5f..6d13d1b 100644 --- a/src/repo.h +++ b/src/repo.h @@ -16,6 +16,7 @@ typedef struct { char *page_url; /* resolved raw/home endpoint */ char *origin; /* scheme://host for full URLs (HOST_AUTO probing) */ char *repo_norm; /* normalized "owner/repo" */ + char *ref; /* optional specific release tag/branch (repo@ref); "" = latest */ } RepoContext; /* Detect host from a repo slug like "owner/repo" or a full URL. */