Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
da47b5d
Try patching tauri config
fetchfern Sep 15, 2025
7ddb2c0
Select branches
fetchfern Sep 15, 2025
7a758c1
Fix json
fetchfern Sep 15, 2025
d880952
Correct paths
fetchfern Sep 15, 2025
719e001
Fix MacOS builds
fetchfern Sep 15, 2025
b1a2a84
Fix theseus build
fetchfern Sep 17, 2025
e1208ef
Don't hardcode ModrinthApp as app identifier
fetchfern Sep 17, 2025
210862c
Fix identifier use
fetchfern Sep 17, 2025
ba41129
Fix initial setup
fetchfern Sep 17, 2025
cc4023a
Fix `updater` buidls
fetchfern Sep 17, 2025
85a0378
Force bash
fetchfern Sep 17, 2025
a2e9eb0
Tweak build
fetchfern Sep 18, 2025
9cac99a
Merge branch 'main' into fetch/alternative-app-installation-locations
fetchfern Sep 19, 2025
c22dc20
fix(app): properly show all versions and notify loaders (#4395)
teaSummer Sep 20, 2025
7eff66f
fix: hide versions checkbox depending on what game versions are avail…
IMB11 Sep 21, 2025
ac1e088
feat: temporary tax compliance impl (#4393)
IMB11 Sep 21, 2025
cdabdeb
Changelog
Prospector Sep 21, 2025
2174fc8
"Create a server" tooltip i18n fixes (#4402)
Jerozgen Sep 22, 2025
438254f
New translations from Crowdin (main) (#4401)
modrinth-bot Sep 22, 2025
b980681
feat: email template for subscription price changes due to tax (#4386)
IMB11 Sep 22, 2025
7df06d7
more loggging (#4404)
fetchfern Sep 22, 2025
0bcffac
Allow users to manage their own affiliate codes (#4392)
aecsocket Sep 22, 2025
a2460b5
Fix track1099 (#4405)
fetchfern Sep 22, 2025
516de6f
Less emails per transactinos (#4406)
fetchfern Sep 22, 2025
0f467f1
Fix version upload for popular projects (#4410)
fetchfern Sep 22, 2025
93b992d
Add IntelliJ project icon (#4412)
Jerozgen Sep 23, 2025
d08c391
Fill variables for subject line (#4415)
fetchfern Sep 24, 2025
98c7703
"Submit for review" button translation (#4381)
Jerozgen Sep 25, 2025
e853e8b
Tax compliance adjustments (#4414)
fetchfern Sep 25, 2025
9eaa39f
Charge tax on products (#4361)
fetchfern Sep 25, 2025
0ac4b11
Org disabled frontend (#4424)
Prospector Sep 26, 2025
9b81e26
update changelog
Prospector Sep 26, 2025
4a6380a
Billing fixes (#4422)
fetchfern Sep 26, 2025
290c9fc
feat(labrinth): hide orgs without a purpose, re-enable organization c…
AlexTMjugador Sep 26, 2025
e9a837d
More billing fixes (#4431)
fetchfern Sep 27, 2025
435b2d2
Hard caps on creating projects/orgs/collections (#4430)
aecsocket Sep 28, 2025
723ad3e
feat: create modal limit alerting (#4429)
IMB11 Sep 28, 2025
d221a03
Tax fixes (#4435)
fetchfern Sep 28, 2025
91d16e8
fix withdraw button looking disabled (#4440)
Prospector Sep 29, 2025
7a9caf3
"Create" modal i18n capitalization (#4441)
Jerozgen Sep 29, 2025
f729bd4
Reworked app update flow (#3960)
Gaming32 Sep 29, 2025
b034161
Update changelog
Prospector Sep 29, 2025
c768ca4
update time
Prospector Sep 29, 2025
456a5f3
Fix user deletion with new notification_deliveries table (#4437)
triphora Sep 29, 2025
5e3abf6
Readd MODRINTH_EXTERNAL_UPDATE_PROVIDER (#4444)
Gaming32 Sep 29, 2025
a1aaa57
chore: cleanup unintended `.sqlx` folder at root of repository (#4445)
AlexTMjugador Sep 29, 2025
db738ff
changelog
Prospector Sep 29, 2025
0e14ff9
Tweaks and fixes to background tasks (#4447)
fetchfern Sep 30, 2025
6f7618d
fix(labrinth): hide hidden orgs from user profiles (#4452)
AlexTMjugador Sep 30, 2025
ec30583
Skip synchronizing transactions to Anrok if missing payment intent ID…
fetchfern Sep 30, 2025
8a05101
App update fixes (#4450)
Jerozgen Sep 30, 2025
a8606cd
Update MOTD Parser package (#4455)
SnowFireWolf Sep 30, 2025
b8f1db3
prospector/russia-blogpost (#4459)
Prospector Oct 1, 2025
c0c8a31
Only skip attaching payment method when using ctoken (#4460)
fetchfern Oct 1, 2025
0c11565
Update artifact globs, enable dev linux builds
fetchfern Oct 1, 2025
0967e45
Update .github/workflows/theseus-build.yml
fetchfern Oct 1, 2025
07320d4
Merge branch 'main' into fetch/alternative-app-installation-locations
fetchfern Oct 1, 2025
b883819
Merge branch 'main' into fetch/alternative-app-installation-locations
fetchfern Oct 3, 2025
200c1b6
Merge branch 'main' into fetch/alternative-app-installation-locations
fetchfern Oct 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions .github/workflows/theseus-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ jobs:
node-version-file: .nvmrc
cache: pnpm

- name: 📄 Generate tauri-dev.conf.json
shell: bash
run: |
GIT_HASH=$(git rev-parse --short HEAD)
cat > apps/app/tauri-dev.conf.json <<EOF
{
"productName": "Modrinth App (dev-${GIT_HASH})",
"mainBinaryName": "Modrinth App (dev-${GIT_HASH})",
"identifier": "ModrinthApp-dev-${GIT_HASH}",
"bundle": {
"fileAssociations": []
}
}
EOF

- name: 🧰 Install Linux build dependencies
if: startsWith(matrix.platform, 'ubuntu')
run: |
Expand Down Expand Up @@ -103,7 +118,7 @@ jobs:
fi

- name: 🔨 Build macOS app
run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json
run: ${{ github.ref == 'refs/heads/main' && 'pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-dev.conf.json' }}
if: startsWith(matrix.platform, 'macos')
env:
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
Expand All @@ -118,6 +133,7 @@ jobs:

- name: 🔨 Build Linux app
run: pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json
run: ${{ github.ref == 'refs/heads/main' && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json' }}
if: startsWith(matrix.platform, 'ubuntu')
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
Expand All @@ -128,7 +144,7 @@ jobs:
[System.Convert]::FromBase64String("$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64") | Set-Content -Path signer-client-cert.p12 -AsByteStream
$env:DIGICERT_ONE_SIGNER_CREDENTIALS = "$env:DIGICERT_ONE_SIGNER_API_KEY|$PWD\signer-client-cert.p12|$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD"
$env:JAVA_HOME = "$env:JAVA_HOME_11_X64"
pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles 'nsis,updater'
${{ github.ref == 'refs/heads/main' && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles "nsis,updater"' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json --verbose --bundles "nsis,updater"' }}
Remove-Item -Path signer-client-cert.p12 -ErrorAction SilentlyContinue
if: startsWith(matrix.platform, 'windows')
env:
Expand All @@ -146,7 +162,12 @@ jobs:
target/release/bundle/appimage/Modrinth App_*.AppImage*
target/release/bundle/deb/Modrinth App_*.deb*
target/release/bundle/rpm/Modrinth App-*.rpm*
target/release/bundle/appimage/Modrinth App (dev-*)_*.AppImage*
target/release/bundle/deb/Modrinth App (dev-*)_*.deb*
target/release/bundle/rpm/Modrinth App (dev-*)-*.rpm*
target/universal-apple-darwin/release/bundle/macos/Modrinth App.app.tar.gz*
target/universal-apple-darwin/release/bundle/macos/Modrinth App (dev-*)*.app.tar.gz
target/universal-apple-darwin/release/bundle/dmg/Modrinth App (dev-*)*.dmg
target/universal-apple-darwin/release/bundle/dmg/Modrinth App_*.dmg*
target/release/bundle/nsis/Modrinth App_*-setup.exe*
target/release/bundle/nsis/Modrinth App_*-setup.nsis.zip*
4 changes: 2 additions & 2 deletions apps/app-playground/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ pub async fn authenticate_run() -> theseus::Result<Credentials> {
async fn main() -> theseus::Result<()> {
println!("Starting.");

let _log_guard = theseus::start_logger();
let _log_guard = theseus::start_logger("ModrinthApp");

// Initialize state
State::init().await?;
State::init("ModrinthApp".to_owned()).await?;

let worlds = get_recent_worlds(4, EnumSet::all()).await?;
for world in worlds {
Expand Down
7 changes: 5 additions & 2 deletions apps/app/src/api/settings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::api::Result;
use tauri::Runtime;
use theseus::prelude::*;

pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
Expand Down Expand Up @@ -28,7 +29,9 @@ pub async fn settings_set(settings: Settings) -> Result<()> {
}

#[tauri::command]
pub async fn cancel_directory_change() -> Result<()> {
settings::cancel_directory_change().await?;
pub async fn cancel_directory_change<R: Runtime>(
handle: tauri::AppHandle<R>,
) -> Result<()> {
settings::cancel_directory_change(&handle.config().identifier).await?;
Ok(())
}
10 changes: 6 additions & 4 deletions apps/app/src/api/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,12 @@ pub fn open_path<R: Runtime>(app: tauri::AppHandle<R>, path: PathBuf) {

#[tauri::command]
pub fn show_launcher_logs_folder<R: Runtime>(app: tauri::AppHandle<R>) {
let path = DirectoryInfo::launcher_logs_dir().unwrap_or_default();
// failure to get folder just opens filesystem
// (ie: if in debug mode only and launcher_logs never created)
open_path(app, path);
if let Some(d) = DirectoryInfo::global_handle_if_ready() {
let path = d.launcher_logs_dir().unwrap_or_default();
// failure to get folder just opens filesystem
// (ie: if in debug mode only and launcher_logs never created)
open_path(app, path)
}
}

// Get opening command
Expand Down
79 changes: 75 additions & 4 deletions apps/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,77 @@
tracing::info!("Initializing app event state...");
theseus::EventState::init(app.clone()).await?;

tracing::info!("Initializing app state...");
State::init().await?;
let app_identifier = app.config().identifier.clone();

#[cfg(feature = "updater")]
'updater: {
if env::var("MODRINTH_EXTERNAL_UPDATE_PROVIDER").is_ok() {
State::init(app_identifier).await?;
break 'updater;
}

use tauri_plugin_updater::UpdaterExt;

let updater = app.updater_builder().build()?;

let update_fut = updater.check();

tracing::info!("Initializing app state...");
State::init(app_identifier).await?;

let check_bar = theseus::init_loading(
theseus::LoadingBarType::CheckingForUpdates,

Check failure on line 48 in apps/app/src/main.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

no variant or associated item named `CheckingForUpdates` found for enum `theseus::LoadingBarType` in the current scope
1.0,
"Checking for updates...",
)
.await?;

tracing::info!("Checking for updates...");
let update = update_fut.await;

drop(check_bar);

if let Some(update) = update.ok().flatten() {
tracing::info!("Update found: {:?}", update.download_url);
let loader_bar_id = theseus::init_loading(
theseus::LoadingBarType::LauncherUpdate {
version: update.version.clone(),
current_version: update.current_version.clone(),
},
1.0,
"Updating Modrinth App...",
)
.await?;

// 100 MiB
const DEFAULT_CONTENT_LENGTH: u64 = 1024 * 1024 * 100;

update
.download_and_install(
|chunk_length, content_length| {
let _ = theseus::emit_loading(
&loader_bar_id,
(chunk_length as f64)
/ (content_length
.unwrap_or(DEFAULT_CONTENT_LENGTH)
as f64),
None,
);
},
|| {},
)
.await?;

app.restart();
}
}

#[cfg(not(feature = "updater"))]
{
State::init(app_identifier).await?;
}

tracing::info!("Finished checking for updates!");
let state = State::get().await?;
app.asset_protocol_scope()
.allow_directory(state.directories.caches_dir(), true)?;
Expand Down Expand Up @@ -109,7 +177,10 @@
RUST_LOG="theseus=trace" {run command}

*/
let _log_guard = theseus::start_logger();

let tauri_context = tauri::generate_context!();

let _log_guard = theseus::start_logger(&tauri_context.config().identifier);

tracing::info!("Initialized tracing subscriber. Loading Modrinth App!");

Expand Down Expand Up @@ -241,7 +312,7 @@
]);

tracing::info!("Initializing app...");
let app = builder.build(tauri::generate_context!());
let app = builder.build(tauri_context);

match app {
Ok(app) => {
Expand Down
6 changes: 4 additions & 2 deletions packages/app-lib/src/api/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ pub async fn set(settings: Settings) -> crate::Result<()> {
}

#[tracing::instrument]
pub async fn cancel_directory_change() -> crate::Result<()> {
pub async fn cancel_directory_change(
app_identifier: &str,
) -> crate::Result<()> {
// This is called to handle state initialization errors due to folder migrations
// failing, so fetching a DB connection pool from `State::get` is not reliable here
let pool = crate::state::db::connect().await?;
let pool = crate::state::db::connect(app_identifier).await?;
let mut settings = Settings::get(&pool).await?;

if let Some(prev_custom_dir) = settings.prev_custom_dir {
Expand Down
8 changes: 5 additions & 3 deletions packages/app-lib/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// Handling for the live development logging
// This will log to the console, and will not log to a file
#[cfg(debug_assertions)]
pub fn start_logger() -> Option<()> {
pub fn start_logger(_app_identifier: &str) -> Option<()> {
use tracing_subscriber::prelude::*;

let filter = tracing_subscriber::EnvFilter::try_from_default_env()
Expand All @@ -36,15 +36,17 @@ pub fn start_logger() -> Option<()> {
// Handling for the live production logging
// This will log to a file in the logs directory, and will not show any logs in the console
#[cfg(not(debug_assertions))]
pub fn start_logger() -> Option<()> {
pub fn start_logger(app_identifier: &str) -> Option<()> {
use crate::prelude::DirectoryInfo;
use chrono::Local;
use std::fs::OpenOptions;
use tracing_subscriber::fmt::time::ChronoLocal;
use tracing_subscriber::prelude::*;

// Initialize and get logs directory path
let logs_dir = if let Some(d) = DirectoryInfo::launcher_logs_dir() {
let logs_dir = if let Some(d) =
DirectoryInfo::launcher_logs_dir_path(app_identifier)
{
d
} else {
eprintln!("Could not start logger");
Expand Down
11 changes: 6 additions & 5 deletions packages/app-lib/src/state/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use sqlx::{Pool, Sqlite};
use std::str::FromStr;
use std::time::Duration;

pub(crate) async fn connect() -> crate::Result<Pool<Sqlite>> {
let settings_dir = DirectoryInfo::get_initial_settings_dir().ok_or(
crate::ErrorKind::FSError(
pub(crate) async fn connect(
app_identifier: &str,
) -> crate::Result<Pool<Sqlite>> {
let settings_dir = DirectoryInfo::initial_settings_dir_path(app_identifier)
.ok_or(crate::ErrorKind::FSError(
"Could not find valid config dir".to_string(),
),
)?;
))?;

if !settings_dir.exists() {
crate::util::io::create_dir_all(&settings_dir).await?;
Expand Down
45 changes: 32 additions & 13 deletions packages/app-lib/src/state/dirs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Theseus directory information
use crate::LoadingBarType;
use crate::event::emit::{emit_loading, init_loading};
use crate::state::LAUNCHER_STATE;
use crate::state::{JavaVersion, Profile, Settings};
use crate::util::fetch::IoSemaphore;
use dashmap::DashSet;
Expand All @@ -17,24 +18,35 @@ pub const METADATA_FOLDER_NAME: &str = "meta";
pub struct DirectoryInfo {
pub settings_dir: PathBuf, // Base settings directory- app database
pub config_dir: PathBuf, // Base config directory- instances, minecraft downloads, etc. Changeable as a setting.
pub app_identifier: String,
}

impl DirectoryInfo {
pub fn global_handle_if_ready() -> Option<&'static Self> {
LAUNCHER_STATE.get().map(|x| &x.directories)
}

pub fn get_initial_settings_dir(&self) -> Option<PathBuf> {
Self::initial_settings_dir_path(&self.app_identifier)
}

// Get the settings directory
// init() is not needed for this function
pub fn get_initial_settings_dir() -> Option<PathBuf> {
pub fn initial_settings_dir_path(app_identifier: &str) -> Option<PathBuf> {
Self::env_path("THESEUS_CONFIG_DIR")
.or_else(|| Some(dirs::data_dir()?.join("ModrinthApp")))
.or_else(|| Some(dirs::data_dir()?.join(app_identifier)))
}

/// Get all paths needed for Theseus to operate properly
#[tracing::instrument]
pub async fn init(config_dir: Option<String>) -> crate::Result<Self> {
let settings_dir = Self::get_initial_settings_dir().ok_or(
crate::ErrorKind::FSError(
pub async fn init(
config_dir: Option<String>,
app_identifier: &str,
) -> crate::Result<Self> {
let settings_dir = Self::initial_settings_dir_path(app_identifier)
.ok_or(crate::ErrorKind::FSError(
"Could not find valid settings dir".to_string(),
),
)?;
))?;

fs::create_dir_all(&settings_dir).await.map_err(|err| {
crate::ErrorKind::FSError(format!(
Expand All @@ -48,6 +60,7 @@ impl DirectoryInfo {
Ok(Self {
settings_dir,
config_dir,
app_identifier: app_identifier.to_owned(),
})
}

Expand Down Expand Up @@ -154,8 +167,14 @@ impl DirectoryInfo {
}

#[inline]
pub fn launcher_logs_dir() -> Option<PathBuf> {
Self::get_initial_settings_dir()
pub fn launcher_logs_dir(&self) -> Option<PathBuf> {
self.get_initial_settings_dir()
.map(|d| d.join(LAUNCHER_LOGS_FOLDER_NAME))
}

#[inline]
pub fn launcher_logs_dir_path(app_identifier: &str) -> Option<PathBuf> {
Self::initial_settings_dir_path(app_identifier)
.map(|d| d.join(LAUNCHER_LOGS_FOLDER_NAME))
}

Expand All @@ -176,15 +195,15 @@ impl DirectoryInfo {
settings: &mut Settings,
exec: E,
io_semaphore: &IoSemaphore,
app_identifier: &str,
) -> crate::Result<()>
where
E: sqlx::Executor<'a, Database = sqlx::Sqlite> + Copy,
{
let app_dir = DirectoryInfo::get_initial_settings_dir().ok_or(
crate::ErrorKind::FSError(
let app_dir = DirectoryInfo::initial_settings_dir_path(app_identifier)
.ok_or(crate::ErrorKind::FSError(
"Could not find valid config dir".to_string(),
),
)?;
))?;

if let Some(ref prev_custom_dir) = settings.prev_custom_dir {
let prev_dir = PathBuf::from(prev_custom_dir);
Expand Down
Loading
Loading