mirror of
https://github.com/Nostalgica-Reverie/Content-Monorepo.git
synced 2026-05-09 00:24:15 +00:00
chore(ci): improve auto updater
This commit is contained in:
@@ -11,56 +11,68 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.FORGEJO_TOKEN }}
|
token: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
filter: blob:none
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
modpacks
|
modpacks
|
||||||
src/actions
|
src/actions/updater
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Cache Updater Binary
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: 'stable'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Cache Packwiz Binaries
|
|
||||||
id: cache-tooling
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: $HOME/go/bin
|
|
||||||
key: tooling-${{ runner.os }}-packwiz
|
|
||||||
|
|
||||||
- name: Install Tooling
|
|
||||||
if: steps.cache-tooling.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
mkdir -p $HOME/go/bin
|
|
||||||
go install github.com/packwiz/packwiz@latest
|
|
||||||
go install github.com/Merith-TK/packwiz-wrapper/cmd/pw@main
|
|
||||||
|
|
||||||
- name: Add Path
|
|
||||||
run: echo "$HOME/go/bin" >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Cache Updater
|
|
||||||
id: cache-updater
|
id: cache-updater
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./updater-bin
|
path: ./updater-bin
|
||||||
key: updater-v3-${{ runner.os }}-${{ hashFiles('src/actions/updater/**') }}
|
key: updater-v4-${{ runner.os }}-${{ hashFiles('src/actions/updater/**/*.rs', 'src/actions/updater/Cargo.toml', 'src/actions/updater/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Rust Cache
|
- name: Install Rust
|
||||||
if: steps.cache-updater.outputs.cache-hit != 'true'
|
if: steps.cache-updater.outputs.cache-hit != 'true'
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: https://github.com/dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Rust Cache (Compiler Internals)
|
||||||
|
if: steps.cache-updater.outputs.cache-hit != 'true'
|
||||||
|
uses: https://github.com/Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: "src/actions/updater -> target"
|
workspaces: "src/actions/updater -> target"
|
||||||
|
|
||||||
- name: Build Updater
|
- name: Build Updater
|
||||||
if: steps.cache-updater.outputs.cache-hit != 'true'
|
if: steps.cache-updater.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
cargo build --release --manifest-path src/actions/updater/Cargo.toml
|
cargo build --release --manifest-path src/actions/updater/Cargo.toml --bin updater
|
||||||
mkdir -p ./updater-bin
|
mkdir -p ./updater-bin
|
||||||
cp src/actions/updater/target/release/updater ./updater-bin/updater
|
cp src/actions/updater/target/release/updater ./updater-bin/updater
|
||||||
|
|
||||||
|
- name: Cache Packwiz Binary
|
||||||
|
id: cache-go
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/go/bin
|
||||||
|
key: go-bin-packwiz-v1-${{ runner.os }}
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
if: steps.cache-go.outputs.cache-hit != 'true'
|
||||||
|
uses: https://github.com/actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install Packwiz
|
||||||
|
if: steps.cache-go.outputs.cache-hit != 'true'
|
||||||
|
run: go install github.com/packwiz/packwiz@latest
|
||||||
|
|
||||||
|
- name: Add Go bin to PATH
|
||||||
|
run: echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
|
||||||
|
- name: Cache Packwiz Downloads
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/packwiz
|
||||||
|
key: packwiz-cache-${{ runner.os }}-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
packwiz-cache-${{ runner.os }}-
|
||||||
|
|
||||||
- name: Run Updater
|
- name: Run Updater
|
||||||
id: rust-update
|
id: rust-update
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@@ -68,10 +80,10 @@ jobs:
|
|||||||
chmod +x ./updater-bin/updater
|
chmod +x ./updater-bin/updater
|
||||||
./updater-bin/updater
|
./updater-bin/updater
|
||||||
|
|
||||||
- name: Run Shell Updater
|
- name: Run Shell Fallback
|
||||||
if: steps.rust-update.outcome == 'failure'
|
if: steps.rust-update.outcome == 'failure'
|
||||||
run: |
|
run: |
|
||||||
echo "Rust Updater failed. Falling back to Shell..."
|
echo "Rust updater failed, falling back to shell..."
|
||||||
chmod +x ./modpacks/update-refresh.sh
|
chmod +x ./modpacks/update-refresh.sh
|
||||||
./modpacks/update-refresh.sh
|
./modpacks/update-refresh.sh
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,41 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo Updating
|
set -eu
|
||||||
(cd ./modpacks/simply && pw batch update -a -y && pw batch refresh -y) &
|
|
||||||
(cd ./modpacks/rc-plus && pw batch update -a -y && pw batch refresh -y) &
|
PACKS=("simply" "rc-plus" "2k" "rekindled")
|
||||||
(cd ./modpacks/2k && pw batch update -a -y && pw batch refresh -y) &
|
|
||||||
(cd ./modpacks/rekindled && pw batch update -a -y && pw batch refresh -y) &
|
echo "Updating..."
|
||||||
wait
|
|
||||||
echo Done
|
pids=()
|
||||||
|
for pack in "${PACKS[@]}"; do
|
||||||
|
pack_dir="modpacks/$pack"
|
||||||
|
if [ ! -d "$pack_dir" ]; then
|
||||||
|
echo "warning: $pack_dir missing, skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
for subdir in "$pack_dir"/*-mr "$pack_dir"/*-cf; do
|
||||||
|
[ -d "$subdir" ] || continue
|
||||||
|
(
|
||||||
|
echo "[$subdir] updating"
|
||||||
|
if (cd "$subdir" && packwiz update -a -y); then
|
||||||
|
echo "[$subdir] ok"
|
||||||
|
else
|
||||||
|
echo "[$subdir] FAIL" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
) &
|
||||||
|
pids+=($!)
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
for pid in "${pids[@]}"; do
|
||||||
|
wait "$pid" || fail=$((fail + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$fail" -gt 0 ]; then
|
||||||
|
echo "$fail subdir(s) failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Done"
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ name = "updater"
|
|||||||
version = "26.4.0"
|
version = "26.4.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "updater"
|
name = "updater"
|
||||||
path = "updater.rs"
|
path = "updater.rs"
|
||||||
|
|||||||
@@ -1,87 +1,121 @@
|
|||||||
use std::process::Command;
|
use anyhow::{bail, Context, Result};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::{
|
||||||
use std::thread;
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
process::Command,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
const PACKS: &[&str] = &["simply", "rc-plus", "2k", "rekindled"];
|
||||||
let modpacks = vec!["simply", "rc-plus", "2k", "rekindled"];
|
const MAX_CONCURRENT: usize = 8;
|
||||||
let max_concurrent = 4;
|
|
||||||
|
|
||||||
let modpacks_queue = Arc::new(Mutex::new(modpacks.into_iter()));
|
|
||||||
let errors = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
let mut workers = vec![];
|
|
||||||
|
|
||||||
println!("starting throttled parallel updates ({} at a time)...", max_concurrent);
|
fn main() -> Result<()> {
|
||||||
|
let mut jobs: Vec<PathBuf> = Vec::new();
|
||||||
|
for pack in PACKS {
|
||||||
|
let pack_dir = PathBuf::from("modpacks").join(pack);
|
||||||
|
if !pack_dir.exists() {
|
||||||
|
eprintln!("warning: pack directory missing: {}", pack_dir.display());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for i in 0..max_concurrent {
|
let entries = fs::read_dir(&pack_dir)
|
||||||
let queue_clone = Arc::clone(&modpacks_queue);
|
.with_context(|| format!("failed to read {}", pack_dir.display()))?;
|
||||||
let err_clone = Arc::clone(&errors);
|
|
||||||
|
|
||||||
let handle = thread::spawn(move || {
|
for entry in entries {
|
||||||
|
let entry = entry.context("failed to read directory entry")?;
|
||||||
|
let path = entry.path();
|
||||||
|
if !path.is_dir() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(name) = path.file_name().and_then(|s| s.to_str()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if name.ends_with("-mr") || name.ends_with("-cf") {
|
||||||
|
jobs.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if jobs.is_empty() {
|
||||||
|
println!("no packs to update.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"queued {} subdir(s) across {} pack(s), running up to {} in parallel",
|
||||||
|
jobs.len(),
|
||||||
|
PACKS.len(),
|
||||||
|
MAX_CONCURRENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
let jobs = Arc::new(Mutex::new(jobs.into_iter()));
|
||||||
|
let failures: Arc<Mutex<Vec<(PathBuf, String)>>> = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
|
||||||
|
let mut handles = Vec::new();
|
||||||
|
for worker_id in 0..MAX_CONCURRENT {
|
||||||
|
let jobs = Arc::clone(&jobs);
|
||||||
|
let failures = Arc::clone(&failures);
|
||||||
|
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
let pack = {
|
let job = { jobs.lock().unwrap().next() };
|
||||||
let mut queue = queue_clone.lock().unwrap();
|
let Some(path) = job else { break };
|
||||||
queue.next()
|
|
||||||
};
|
|
||||||
|
|
||||||
let pack = match pack {
|
let label = path.display().to_string();
|
||||||
Some(p) => p,
|
println!("[W{worker_id}] updating {label}");
|
||||||
None => break,
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = format!("modpacks/{}", pack);
|
let output = Command::new("packwiz")
|
||||||
println!("[Worker {}] starting: {}", i, pack);
|
.args(["update", "-a", "-y"])
|
||||||
|
|
||||||
if !std::path::Path::new(&path).exists() {
|
|
||||||
let mut e = err_clone.lock().unwrap();
|
|
||||||
e.push(format!("directory missing: {}", path));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let refresh = Command::new("pw")
|
|
||||||
.args(["batch", "refresh", "-y"])
|
|
||||||
.current_dir(&path)
|
.current_dir(&path)
|
||||||
.status();
|
.output();
|
||||||
|
|
||||||
let update = Command::new("pw")
|
match output {
|
||||||
.args(["batch", "update", "-a", "-y"])
|
Ok(o) if o.status.success() => {
|
||||||
.current_dir(&path)
|
println!("[W{worker_id}] ok: {label}");
|
||||||
.status();
|
}
|
||||||
|
Ok(o) => {
|
||||||
let failed = match (refresh, update) {
|
let stderr = String::from_utf8_lossy(&o.stderr).into_owned();
|
||||||
(Ok(s1), Ok(s2)) => !s1.success() || !s2.success(),
|
let stdout = String::from_utf8_lossy(&o.stdout).into_owned();
|
||||||
(Err(e), _) => {
|
eprintln!("[W{worker_id}] FAIL {label} (exit {})", o.status);
|
||||||
println!("[Worker {}] refresh failed for {}: {}", i, pack, e);
|
if !stdout.is_empty() {
|
||||||
true
|
eprintln!(" stdout:\n{}", indent(&stdout, " "));
|
||||||
},
|
}
|
||||||
(_, Err(e)) => {
|
if !stderr.is_empty() {
|
||||||
println!("[Worker {}] update failed for {}: {}", i, pack, e);
|
eprintln!(" stderr:\n{}", indent(&stderr, " "));
|
||||||
true
|
}
|
||||||
|
let reason = if !stderr.is_empty() { stderr } else { stdout };
|
||||||
|
failures.lock().unwrap().push((path, reason));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("[W{worker_id}] FAIL {label}: could not launch packwiz: {e}");
|
||||||
|
failures.lock().unwrap().push((path, e.to_string()));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if failed {
|
|
||||||
let mut e = err_clone.lock().unwrap();
|
|
||||||
e.push(format!("failed: {}", pack));
|
|
||||||
} else {
|
|
||||||
println!("[Worker {}] done: {}", i, pack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
workers.push(handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for handle in workers {
|
for h in handles {
|
||||||
handle.join().unwrap();
|
h.join().expect("worker thread panicked");
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_errors = errors.lock().unwrap();
|
let failures = failures.lock().unwrap();
|
||||||
if !final_errors.is_empty() {
|
if failures.is_empty() {
|
||||||
eprintln!("\nsummary of failures");
|
|
||||||
for err in final_errors.iter() {
|
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
std::process::exit(1);
|
|
||||||
} else {
|
|
||||||
println!("\nall updates finished successfully.");
|
println!("\nall updates finished successfully.");
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
eprintln!("\n{} subdir(s) failed:", failures.len());
|
||||||
|
for (path, _reason) in failures.iter() {
|
||||||
|
eprintln!(" - {}", path.display());
|
||||||
|
}
|
||||||
|
bail!("{} update(s) failed", failures.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn indent(s: &str, prefix: &str) -> String {
|
||||||
|
s.lines()
|
||||||
|
.map(|l| format!("{prefix}{l}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user