Skip to content

Commit 8debdfc

Browse files
committed
Add some new stuff yeah
1 parent 833cad4 commit 8debdfc

26 files changed

Lines changed: 1072 additions & 70 deletions

.github/workflows/build-check.yml

Lines changed: 0 additions & 24 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, dev]
6+
pull_request:
7+
branches: [main, dev]
8+
9+
jobs:
10+
# Native unit tests on Ubuntu — pure C++ logic, no Wii U toolchain needed.
11+
tests:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Configure
17+
run: |
18+
mkdir -p tests/build
19+
cd tests/build
20+
cmake .. -DCMAKE_BUILD_TYPE=Release
21+
22+
- name: Build & run tests
23+
run: |
24+
cd tests/build
25+
make -j$(nproc)
26+
./cupstore_tests
27+
28+
# Cross-build for both target modes. A regression in either
29+
# (e.g. a bug behind #if BUILD_HW) is caught here before merge.
30+
build:
31+
runs-on: ubuntu-latest
32+
container:
33+
image: devkitpro/devkitppc:latest
34+
strategy:
35+
fail-fast: false
36+
matrix:
37+
mode: [cemu, hw]
38+
steps:
39+
- uses: actions/checkout@v4
40+
41+
- name: Build (${{ matrix.mode }})
42+
run: |
43+
mkdir -p build
44+
cd build
45+
cmake .. \
46+
-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/wut/share/wut.toolchain.cmake \
47+
-DBUILD_MODE=${{ matrix.mode }} \
48+
-DAPP_VERSION=0.0.0-ci
49+
make -j$(nproc)
50+
51+
- name: Verify wuhb produced
52+
run: |
53+
test -s build/wiiu_mod_store.wuhb
54+
ls -la build/wiiu_mod_store.wuhb
55+
56+
- name: Upload artifact
57+
if: matrix.mode == 'hw' && github.event_name == 'push' && github.ref == 'refs/heads/main'
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: wiiu_mod_store-${{ matrix.mode }}-${{ github.sha }}
61+
path: build/wiiu_mod_store.wuhb
62+
retention-days: 14

.github/workflows/test.yml

Lines changed: 0 additions & 25 deletions
This file was deleted.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
</p>
99

1010
<p align="center">
11-
<a href="https://github.com/timkicker/coffeeshop/actions">
12-
<img alt="CI" src="https://img.shields.io/github/actions/workflow/status/timkicker/coffeeshop/release.yml?label=build">
11+
<a href="https://github.com/timkicker/coffeeshop/actions/workflows/ci.yml">
12+
<img alt="CI" src="https://img.shields.io/github/actions/workflow/status/timkicker/coffeeshop/ci.yml?branch=main&label=ci">
1313
</a>
1414
<a href="https://github.com/timkicker/coffeeshop/releases/latest">
1515
<img alt="Release" src="https://img.shields.io/github/v/release/timkicker/coffeeshop?display_name=tag&sort=semver">

src/app/App.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ bool App::init() {
112112
pushScreen(std::make_unique<MainLayout>(this));
113113
elog("after pushScreen");
114114
CacheManager::cleanupStaleZips();
115+
CacheManager::cleanupStalePartials();
116+
CacheManager::cleanupStaleStaging();
115117
CacheManager::cleanupCorruptMods();
116118
DownloadQueue::get().start();
117119
ImageCache::get().start();

src/app/CacheManager.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <sys/stat.h>
88
#include <unistd.h>
99
#include <cstring>
10+
#include <ctime>
1011
#include <vector>
1112
#include <string>
1213

@@ -32,6 +33,82 @@ void CacheManager::cleanupStaleZips() {
3233
LOG_INFO("CacheManager: cleaned up %d stale zip(s)", removed);
3334
}
3435

36+
void CacheManager::cleanupStalePartials() {
37+
std::string dir = Paths::cacheDir();
38+
DIR* d = opendir(dir.c_str());
39+
if (!d) return;
40+
41+
int removed = 0;
42+
time_t now = time(nullptr);
43+
constexpr time_t kMaxAgeSec = 24 * 60 * 60; // 24h
44+
45+
struct dirent* e;
46+
while ((e = readdir(d)) != nullptr) {
47+
std::string name = e->d_name;
48+
if (name.size() < 8) continue;
49+
if (name.substr(name.size() - 8) != ".partial") continue;
50+
std::string path = dir + "/" + name;
51+
struct stat st;
52+
if (stat(path.c_str(), &st) != 0) continue;
53+
if (now - st.st_mtime > kMaxAgeSec) {
54+
if (remove(path.c_str()) == 0) {
55+
LOG_INFO("CacheManager: removed stale .partial: %s", name.c_str());
56+
removed++;
57+
}
58+
}
59+
}
60+
closedir(d);
61+
if (removed > 0)
62+
LOG_INFO("CacheManager: cleaned up %d stale partial(s)", removed);
63+
}
64+
65+
void CacheManager::cleanupStaleStaging() {
66+
// 1) Delete every subdirectory of cache/staging.
67+
std::string staging = Paths::stagingDir();
68+
DIR* d = opendir(staging.c_str());
69+
if (d) {
70+
int removed = 0;
71+
struct dirent* e;
72+
while ((e = readdir(d)) != nullptr) {
73+
std::string name = e->d_name;
74+
if (name == "." || name == "..") continue;
75+
if (DownloadManager::rmrf(staging + "/" + name)) removed++;
76+
}
77+
closedir(d);
78+
if (removed > 0)
79+
LOG_INFO("CacheManager: cleared %d stale staging entr%s",
80+
removed, removed == 1 ? "y" : "ies");
81+
}
82+
83+
// 2) Delete any "<modid>.old" folders in active sdcafiine paths
84+
// (leftover from a crashed atomic-rename swap).
85+
std::vector<std::string> bases = { Paths::sdcafiineBase(), Paths::disabledBase() };
86+
for (auto& base : bases) {
87+
DIR* td = opendir(base.c_str());
88+
if (!td) continue;
89+
struct dirent* te;
90+
while ((te = readdir(td)) != nullptr) {
91+
std::string titleId = te->d_name;
92+
if (titleId == "." || titleId == "..") continue;
93+
std::string titlePath = base + "/" + titleId;
94+
DIR* md = opendir(titlePath.c_str());
95+
if (!md) continue;
96+
struct dirent* me;
97+
while ((me = readdir(md)) != nullptr) {
98+
std::string entry = me->d_name;
99+
if (entry == "." || entry == "..") continue;
100+
if (entry.size() > 4 && entry.substr(entry.size() - 4) == ".old") {
101+
std::string oldPath = titlePath + "/" + entry;
102+
if (DownloadManager::rmrf(oldPath))
103+
LOG_INFO("CacheManager: removed stale .old: %s", oldPath.c_str());
104+
}
105+
}
106+
closedir(md);
107+
}
108+
closedir(td);
109+
}
110+
}
111+
35112
void CacheManager::cleanupCorruptMods() {
36113
// Check both active and disabled base dirs
37114
std::vector<std::string> bases = {

src/app/CacheManager.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ class CacheManager {
77
// Call once at app startup
88
static void cleanupStaleZips();
99

10+
// Delete *.zip.partial files older than 24h. Recent partials are kept so
11+
// resume-after-restart can pick them up.
12+
static void cleanupStalePartials();
13+
14+
// Delete any subdirectories under Paths::stagingDir() (leftover from a
15+
// crash mid-extract) and any "<modid>.old" siblings under sdcafiineBase()
16+
// (leftover from a crashed atomic-rename swap).
17+
static void cleanupStaleStaging();
18+
1019
// Delete mod folders in sdcafiineBase() and disabledBase() that have no modinfo.json
1120
// These are left over from a crash during extraction
1221
static void cleanupCorruptMods();

src/app/Paths.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ namespace Paths {
2525
return modstoreBase() + "/cache";
2626
}
2727

28+
// Mods are extracted here first, then atomically renamed into the active
29+
// sdcafiine path. Failed extracts never leave half-installed mods in the
30+
// active path.
31+
inline std::string stagingDir() {
32+
return cacheDir() + "/staging";
33+
}
34+
2835
inline std::string configFile() {
2936
#if BUILD_HW
3037
return modstoreBase() + "/config.json"; // SD card

0 commit comments

Comments
 (0)