Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 38 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,23 @@ env:
jobs:
lint-test:
name: Lint & Unit Tests
runs-on:
group: hf-mount-ci-pub
# GitHub-hosted so the job also runs on forks without access to the
# huggingface runner groups; the heavy integration jobs below stay on
# internal runners and skip on forks.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# This job runs on fork PRs; don't leave the workflow token in the
# local git config where later build/test steps could read it.
persist-credentials: false

Comment thread
ArturLauche marked this conversation as resolved.
- name: Configure internal registries
# Head-repo check: on fork PRs github.repository is the *base* repo,
# which must not grant fork code the internal registry setup.
if: >-
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'huggingface/hf-mount') ||
(github.event_name != 'pull_request' && github.repository == 'huggingface/hf-mount')
run: curl -sSL https://registries.huggingface.tech/setup.sh | bash

# Pin nightly so rustfmt rules don't drift between CI and contributor machines.
Expand Down Expand Up @@ -61,11 +72,24 @@ jobs:
- name: Clippy (all)
run: cargo clippy --features fuse,nfs -- -D warnings

# The GUI only ships for Windows/macOS, but compile-checking it here
# catches breakage on every PR without waiting for the platform builders.
- name: Clippy (NFS + GUI)
run: cargo clippy --no-default-features --features nfs,gui --bins --tests -- -D warnings

- name: Unit tests
run: cargo test --lib --features fuse,nfs

- name: GUI unit tests
run: cargo test --no-default-features --features nfs,gui --bin hf-mount-gui

smoke-test:
name: Smoke Tests (FUSE + NFS)
# Internal runner group + HF_TOKEN: only for code from the upstream repo
# itself. The head-repo check keeps fork-PR code off internal runners.
if: >-
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'huggingface/hf-mount') ||
(github.event_name != 'pull_request' && github.repository == 'huggingface/hf-mount')
runs-on:
group: hf-mount-ci-pub
needs: lint-test
Expand Down Expand Up @@ -99,6 +123,9 @@ jobs:

fsx:
name: fsx (data integrity)
if: >-
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'huggingface/hf-mount') ||
(github.event_name != 'pull_request' && github.repository == 'huggingface/hf-mount')
runs-on:
group: hf-mount-ci-pub
needs: lint-test
Expand All @@ -123,6 +150,9 @@ jobs:

xfstests:
name: xfstests (filesystem exerciser)
if: >-
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'huggingface/hf-mount') ||
(github.event_name != 'pull_request' && github.repository == 'huggingface/hf-mount')
runs-on:
group: hf-mount-ci-pub-m5dn-24xlarge
needs: lint-test
Expand All @@ -147,6 +177,9 @@ jobs:

pjdfstest:
name: POSIX Compliance (pjdfstest)
if: >-
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'huggingface/hf-mount') ||
(github.event_name != 'pull_request' && github.repository == 'huggingface/hf-mount')
runs-on:
group: hf-mount-ci-pub
needs: lint-test
Expand Down Expand Up @@ -216,6 +249,9 @@ jobs:

bench:
name: Benchmarks
if: >-
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'huggingface/hf-mount') ||
(github.event_name != 'pull_request' && github.repository == 'huggingface/hf-mount')
runs-on:
group: hf-mount-ci-pub
needs: lint-test
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ required-features = ["nfs"]

[[bin]]
name = "hf-mount-gui"
path = "src/bin/hf-mount-gui.rs"
path = "src/bin/hf-mount-gui/main.rs"
required-features = ["nfs", "gui"]

[[bin]]
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ Download the GUI binary from [GitHub Releases](https://github.com/huggingface/hf
- macOS Apple Silicon app bundle: `hf-mount-gui-arm64-apple-darwin.app.zip`
- macOS Apple Silicon raw binary: `hf-mount-gui-arm64-apple-darwin`

Windows users must enable Client for NFS and run the GUI from an Administrator session. The GUI includes a **Check setup** action that validates elevation, the Windows NFS client tools, port `111`, and the mount target before starting. If it was launched without elevation, use **Restart as admin** in the GUI and approve the Windows UAC prompt. Fill in the repo or bucket ID, mount point, optional token, then press **Start mount**. Press **Stop mount** to unmount.
The window has three tabs: **Mount** (the source/mount form and Start/Stop actions), **Activity** (the session log, with copy-to-clipboard), and **Setup** (environment checks with fix actions). A status bar at the bottom always shows the current mount state and, while mounted, the elapsed time.

Windows users must enable Client for NFS and run the GUI from an Administrator session. The **Setup** tab validates elevation, the Windows NFS client tools, port `111`, and the mount target; each failing check comes with a fix action (**Enable NFS** launches the feature install, **Restart as admin** relaunches with a UAC prompt) or a copyable command. On Windows the mount-point field has a **Free** drive-letter picker that lists currently unassigned letters. Fill in the repo or bucket ID, mount point, optional token, then press **Start mount**. Press **Stop** to unmount — this also works while the mount is still starting. Recently mounted sources are offered in a **Recent** dropdown for one-click refill.

Enable **Background** before starting if the mount should keep running after the GUI window is closed. Enable **Start at login** to register the saved GUI mount profile for autostart. On Windows this creates a user logon Scheduled Task with highest privileges; on macOS it writes a user LaunchAgent; on Linux desktops it writes an XDG autostart entry. The GUI saves the mount profile under the user config directory and writes background status/log files there as well. Inline HF tokens are not saved in the profile; background and autostart mounts use `HF_TOKEN` from the worker environment or a saved token-file path.

Expand Down
42 changes: 25 additions & 17 deletions src/bin/hf-mount-fuse-sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,13 @@ fn main() {
// One tokio runtime shared across all volumes. Each `build_with_runtime`
// call below borrows its handle, avoiding N full multi-threaded runtimes
// (~4 worker threads each) for N volumes. See #96.
let runtime = build_runtime();
let runtime = match build_runtime() {
Ok(runtime) => runtime,
Err(e) => {
error!("Failed to create tokio runtime: {e}");
std::process::exit(1);
}
};

let pending = wait_for_configs(&args.tmp_dir, args.poll_secs, args.timeout_secs, args.expected_mounts);
if pending.is_empty() {
Expand Down Expand Up @@ -163,8 +169,20 @@ fn main() {
error_paths.push(error_path.clone());
let vfs_registry = Arc::clone(&vfs_registry);
let rt_handle = runtime.handle().clone();
let panic_error_path = error_path.clone();
let panic_label = label.clone();
handles.push(std::thread::spawn(move || {
run_mount(fuse_fds, mount.mount_args, error_path, vfs_registry, rt_handle);
// The readiness wait loop below has no timeout, so a panic that
// unwound this thread before `run_mount` wrote its ready or error
// marker would hang pod readiness forever. Convert a last-resort
// panic (e.g. an `expect` deep in client construction) into an error
// marker so the wait loop sees the failure and reports it.
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
run_mount(fuse_fds, mount.mount_args, error_path, vfs_registry, rt_handle);
}));
if outcome.is_err() {
write_error(&panic_error_path, &format!("Mount thread panicked for {}", panic_label));
}
}));
}

Expand Down Expand Up @@ -388,22 +406,12 @@ fn run_mount(
) {
let label = mount_args.source.label();

// build_with_runtime() panics on auth/config errors (e.g. invalid token,
// CAS 401). Catch the panic so we can write the error to the error file
// for the CSI driver to report as FailedMount.
let setup = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
build_with_runtime(mount_args.source, mount_args.options, false, runtime)
})) {
// Auth/config errors (e.g. invalid token, CAS 401) are written to the
// error file for the CSI driver to report as FailedMount.
let setup = match build_with_runtime(mount_args.source, mount_args.options, false, runtime) {
Ok(s) => s,
Err(panic) => {
let msg = match panic.downcast_ref::<String>() {
Some(s) => s.clone(),
None => match panic.downcast_ref::<&str>() {
Some(s) => s.to_string(),
None => "unknown panic".to_string(),
},
};
write_error(&error_path, &format!("Setup failed for {}: {}", label, msg));
Err(e) => {
write_error(&error_path, &format!("Setup failed for {}: {}", label, e));
return;
Comment thread
ArturLauche marked this conversation as resolved.
}
};
Expand Down
Loading
Loading