diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01697619..52ad600b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,9 @@ jobs: libgl1 \ libegl1 \ libdbus-1-3 \ + ruby-dev\ + build-essential\ + rpm\ libxcb-cursor0 - name: Prepare Scripts run: | @@ -109,4 +112,48 @@ jobs: files: | Lufus-x86_64-${{ github.ref_name }}.tar.gz Lufus-x86_64-${{ github.ref_name }}.tar.gz.sha256 - append_body: true \ No newline at end of file + append_body: true + + # for fpm packages + build-fpm: + needs: build-tarball + runs-on: ubuntu-latest + permissions: + contents: write + strategy: + matrix: + pkg_type: [deb, rpm] + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: actions/setup-ruby@v1 + with: + ruby-version: '3.2' + + - name: Install FPM + run: gem install fpm + + - name: Download tarball from release + run: | + wget https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/Lufus-x86_64-${{ github.ref_name }}.tar.gz + + - name: Build ${{ matrix.pkg_type }} Package + run: | + mkdir tmp_source && tar -xzf Lufus-x86_64-${{ github.ref_name }}.tar.gz -C tmp_source + fpm -s dir -t ${{ matrix.pkg_type }} \ + -n "Lufus" \ + -v "${{ github.ref_name }}" \ + --iteration 1 \ + --prefix /opt/lufus \ + --description "Physical drive imaging and formatting utility" \ + -C tmp_source/Lufus-x86_64-${{ github.ref_name }} . + + - name: Upload to Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + files: | + *.deb + *.rpm + append_body: true diff --git a/src/lufus/drives/formatting.py b/src/lufus/drives/formatting.py index 2077a101..8f6dd017 100644 --- a/src/lufus/drives/formatting.py +++ b/src/lufus/drives/formatting.py @@ -407,6 +407,7 @@ def _status(msg: str) -> None: else: _status(f"ERROR: Unknown fs_type={fs_type}") + unexpected() return False # Apply volume label after successful format diff --git a/tests/test_flash_windows_and_install_ventoy_fixes.py b/tests/test_flash_windows_and_install_ventoy_fixes.py index db19e268..9fbbcec8 100644 --- a/tests/test_flash_windows_and_install_ventoy_fixes.py +++ b/tests/test_flash_windows_and_install_ventoy_fixes.py @@ -47,11 +47,11 @@ class TestFlashWindowsOsErrorOnMissingIso: def test_returns_false_when_iso_does_not_exist(self, tmp_path): missing_iso = str(tmp_path / "nonexistent.iso") - result = fw_module.flash_windows("/dev/sdb", missing_iso) + result = fw_module.flash_windows("/dev/sdb", missing_iso, fw_module.PartitionScheme.SIMPLE_FAT32) assert result is False def test_returns_false_when_iso_is_a_directory(self, tmp_path): - result = fw_module.flash_windows("/dev/sdb", str(tmp_path)) + result = fw_module.flash_windows("/dev/sdb", str(tmp_path), fw_module.PartitionScheme.SIMPLE_FAT32) assert result is False class TestGetWimSizeCaseInsensitive: diff --git a/tests/test_formatting.py b/tests/test_formatting.py index 734da7ed..6a2e873d 100644 --- a/tests/test_formatting.py +++ b/tests/test_formatting.py @@ -70,9 +70,9 @@ def fake_run(cmd, check=True, **kwargs): formatting.dskformat() # Find the mkfs call (partition scheme parted calls come first) - mkfs_calls = [c for c in calls if c and c[0].startswith("mkfs")] + mkfs_calls = [c for c in calls if c and Path(c[0]).name.startswith("mkfs")] assert len(mkfs_calls) == 1, f"Expected 1 mkfs call, got: {calls}" - assert mkfs_calls[0][0] == expected_tool + assert Path(mkfs_calls[0][0]).name == expected_tool def test_dskformat_calls_unexpected_for_unknown_fs(monkeypatch) -> None: @@ -352,7 +352,7 @@ def test_get_mount_and_drive_falls_back_to_find_dn(monkeypatch) -> None: # unmount / remount # --------------------------------------------------------------------------- -def test_unmount_skips_subprocess_when_no_drive(monkeypatch, capsys) -> None: +def test_unmount_skips_subprocess_when_no_drive(monkeypatch, caplog) -> None: monkeypatch.setattr(formatting, "_get_mount_and_drive", lambda: (None, None, {})) def bad_run(*a, **kw): @@ -360,11 +360,10 @@ def bad_run(*a, **kw): monkeypatch.setattr(formatting.subprocess, "run", bad_run) formatting.unmount() - out = capsys.readouterr() - assert out.out or out.err + assert "No drive node found" in caplog.text -def test_remount_skips_subprocess_when_no_drive(monkeypatch, capsys) -> None: +def test_remount_skips_subprocess_when_no_drive(monkeypatch, caplog) -> None: monkeypatch.setattr(formatting, "_get_mount_and_drive", lambda: (None, None, {})) def bad_run(*a, **kw): @@ -372,18 +371,21 @@ def bad_run(*a, **kw): monkeypatch.setattr(formatting.subprocess, "run", bad_run) formatting.remount() - out = capsys.readouterr() - assert out.out or out.err + assert "No drive node found" in caplog.text def test_unmount_issues_umount_command(monkeypatch) -> None: mount = "/media/testuser/USB" drive = "/dev/sdb1" monkeypatch.setattr(formatting, "_get_mount_and_drive", lambda: (mount, drive, {})) + monkeypatch.setattr(formatting.glob, "glob", lambda path: [drive]) calls = [] monkeypatch.setattr(formatting.subprocess, "run", lambda cmd, *a, **kw: calls.append(cmd)) + monkeypatch.setattr(formatting.time, "sleep", lambda x: None) formatting.unmount() - assert calls and calls[0][0] == "umount" and drive in calls[0] + assert any("umount" in cmd for cmd in calls), f"umount not found in {calls}" + assert any(drive in cmd for cmd in calls) + assert calls[-1] == ["udevadm", "settle"] def test_remount_issues_mount_command(monkeypatch) -> None: @@ -396,121 +398,122 @@ def test_remount_issues_mount_command(monkeypatch) -> None: assert calls and calls[0][0] == "mount" and drive in calls[0] and mount in calls[0] +# I think these are Redundant so i commented them out for now -@pytest.mark.parametrize( - ("fs_type", "expected_tool"), - [ - (0, "mkfs.ntfs"), - (1, "mkfs.vfat"), - (2, "mkfs.exfat"), - (3, "mkfs.ext4"), - ], -) -def test_dskformat_runs_expected_mkfs_command(monkeypatch, fs_type: int, expected_tool: str) -> None: - _setup_common_monkeypatch(monkeypatch) - monkeypatch.setattr(formatting.states, "currentFS", fs_type) - monkeypatch.setattr(formatting.states, "cluster_size", 0) - monkeypatch.setattr(formatting.states, "partition_scheme", 0) +# @pytest.mark.parametrize( +# ("fs_type", "expected_tool"), +# [ +# (0, "mkfs.ntfs"), +# (1, "mkfs.vfat"), +# (2, "mkfs.exfat"), +# (3, "mkfs.ext4"), +# ], +# ) +# def test_dskformat_runs_expected_mkfs_command(monkeypatch, fs_type: int, expected_tool: str) -> None: +# _setup_common_monkeypatch(monkeypatch) +# monkeypatch.setattr(formatting.states, "currentFS", fs_type) +# monkeypatch.setattr(formatting.states, "cluster_size", 0) +# monkeypatch.setattr(formatting.states, "partition_scheme", 0) - calls = [] +# calls = [] - def fake_run(cmd, check=True, **kwargs): - calls.append(cmd) +# def fake_run(cmd, check=True, **kwargs): +# calls.append(cmd) - monkeypatch.setattr(formatting.subprocess, "run", fake_run) +# monkeypatch.setattr(formatting.subprocess, "run", fake_run) - formatting.dskformat() +# formatting.dskformat() - # Find the mkfs call (partition scheme parted calls come first) - mkfs_calls = [c for c in calls if c and c[0].startswith("mkfs")] - assert len(mkfs_calls) == 1, f"Expected 1 mkfs call, got: {calls}" - assert mkfs_calls[0][0] == expected_tool +# # Find the mkfs call (partition scheme parted calls come first) +# mkfs_calls = [c for c in calls if c and Path(c[0]).name.startswith("mkfs")] +# assert len(mkfs_calls) == 1, f"Expected 1 mkfs call, got: {calls}" +# assert expected_tool in mkfs_calls[0][0] -def test_dskformat_calls_unexpected_for_unknown_fs(monkeypatch) -> None: - _setup_common_monkeypatch(monkeypatch) - monkeypatch.setattr(formatting.states, "currentFS", 99) - monkeypatch.setattr(formatting.states, "cluster_size", 0) - monkeypatch.setattr(formatting.states, "partition_scheme", 0) +# def test_dskformat_calls_unexpected_for_unknown_fs(monkeypatch) -> None: +# _setup_common_monkeypatch(monkeypatch) +# monkeypatch.setattr(formatting.states, "currentFS", 99) +# monkeypatch.setattr(formatting.states, "cluster_size", 0) +# monkeypatch.setattr(formatting.states, "partition_scheme", 0) - called = {"unexpected": False} +# called = {"unexpected": False} - def fake_unexpected(): - called["unexpected"] = True +# def fake_unexpected(): +# called["unexpected"] = True - monkeypatch.setattr(formatting, "unexpected", fake_unexpected) - monkeypatch.setattr(formatting.subprocess, "run", lambda *args, **kwargs: None) +# monkeypatch.setattr("lufus.drives.formatting.unexpected", fake_unexpected) +# monkeypatch.setattr(formatting.subprocess, "run", lambda *args, **kwargs: None) - formatting.dskformat() +# formatting.dskformat() - assert called["unexpected"] is True +# assert called["unexpected"] is True -def test_cluster_returns_tuple_even_without_usb(monkeypatch) -> None: - """cluster() must never crash — it must always return a valid 3-tuple.""" - monkeypatch.setattr(formatting.fu, "find_usb", lambda: {}) - monkeypatch.setattr(formatting.fu, "find_DN", lambda: None) - monkeypatch.setattr(formatting.states, "DN", "") +# def test_cluster_returns_tuple_even_without_usb(monkeypatch) -> None: +# """cluster() must never crash — it must always return a valid 3-tuple.""" +# monkeypatch.setattr(formatting.fu, "find_usb", lambda: {}) +# monkeypatch.setattr(formatting.fu, "find_DN", lambda: None) +# monkeypatch.setattr(formatting.states, "DN", "") - result = formatting.cluster() - assert isinstance(result, tuple) - assert len(result) == 3 - cluster1, cluster2, sector = result - assert cluster1 > 0 - assert cluster2 > 0 - assert sector == cluster1 // cluster2 +# result = formatting.cluster() +# assert isinstance(result, tuple) +# assert len(result) == 3 +# cluster1, cluster2, sector = result +# assert cluster1 > 0 +# assert cluster2 > 0 +# assert sector == cluster1 // cluster2 -def test_cluster_respects_cluster_size_state(monkeypatch) -> None: - monkeypatch.setattr(formatting.fu, "find_usb", lambda: {"/media/testuser/USB": "USB"}) - monkeypatch.setattr(formatting.fu, "find_DN", lambda: "/dev/sdb1") - monkeypatch.setattr(formatting.states, "DN", "/dev/sdb1") +# def test_cluster_respects_cluster_size_state(monkeypatch) -> None: +# monkeypatch.setattr(formatting.fu, "find_usb", lambda: {"/media/testuser/USB": "USB"}) +# monkeypatch.setattr(formatting.fu, "find_DN", lambda: "/dev/sdb1") +# monkeypatch.setattr(formatting.states, "DN", "/dev/sdb1") - monkeypatch.setattr(formatting.states, "cluster_size", 0) - c1, _, _ = formatting.cluster() - assert c1 == 4096 +# monkeypatch.setattr(formatting.states, "cluster_size", 0) +# c1, _, _ = formatting.cluster() +# assert c1 == 4096 - monkeypatch.setattr(formatting.states, "cluster_size", 1) - c1, _, _ = formatting.cluster() - assert c1 == 8192 +# monkeypatch.setattr(formatting.states, "cluster_size", 1) +# c1, _, _ = formatting.cluster() +# assert c1 == 8192 -def test_apply_partition_scheme_gpt(monkeypatch) -> None: - calls = [] - monkeypatch.setattr(formatting.subprocess, "run", lambda cmd, check=True, **kw: calls.append(cmd)) - monkeypatch.setattr(formatting.states, "partition_scheme", 0) +# def test_apply_partition_scheme_gpt(monkeypatch) -> None: +# calls = [] +# monkeypatch.setattr(formatting.subprocess, "run", lambda cmd, check=True, **kw: calls.append(cmd)) +# monkeypatch.setattr(formatting.states, "partition_scheme", 0) - formatting._apply_partition_scheme("/dev/sdb1") +# formatting._apply_partition_scheme("/dev/sdb1") - assert any("gpt" in c for c in calls) +# assert any("gpt" in c for c in calls) -def test_apply_partition_scheme_mbr(monkeypatch) -> None: - calls = [] - monkeypatch.setattr(formatting.subprocess, "run", lambda cmd, check=True, **kw: calls.append(cmd)) - monkeypatch.setattr(formatting.states, "partition_scheme", 1) +# def test_apply_partition_scheme_mbr(monkeypatch) -> None: +# calls = [] +# monkeypatch.setattr(formatting.subprocess, "run", lambda cmd, check=True, **kw: calls.append(cmd)) +# monkeypatch.setattr(formatting.states, "partition_scheme", 1) - formatting._apply_partition_scheme("/dev/sdb1") +# formatting._apply_partition_scheme("/dev/sdb1") - assert any("msdos" in c for c in calls) +# assert any("msdos" in c for c in calls) -def test_checkdevicebadblock_returns_false_when_no_drive(monkeypatch) -> None: - monkeypatch.setattr(formatting.fu, "find_usb", lambda: {}) - monkeypatch.setattr(formatting.fu, "find_DN", lambda: None) - monkeypatch.setattr(formatting.states, "DN", "") +# def test_checkdevicebadblock_returns_false_when_no_drive(monkeypatch) -> None: +# monkeypatch.setattr(formatting.fu, "find_usb", lambda: {}) +# monkeypatch.setattr(formatting.fu, "find_DN", lambda: None) +# monkeypatch.setattr(formatting.states, "DN", "") - result = formatting.checkdevicebadblock() - assert result is False +# result = formatting.checkdevicebadblock() +# assert result is False -def test_volumecustomlabel_no_drive_does_not_crash(monkeypatch) -> None: - """volumecustomlabel() should gracefully handle missing drive node.""" - monkeypatch.setattr(formatting.fu, "find_usb", lambda: {}) - monkeypatch.setattr(formatting.fu, "find_DN", lambda: None) - monkeypatch.setattr(formatting.states, "DN", "") - monkeypatch.setattr(formatting.states, "currentFS", 0) - monkeypatch.setattr(formatting.states, "new_label", "TESTLABEL") +# def test_volumecustomlabel_no_drive_does_not_crash(monkeypatch) -> None: +# """volumecustomlabel() should gracefully handle missing drive node.""" +# monkeypatch.setattr(formatting.fu, "find_usb", lambda: {}) +# monkeypatch.setattr(formatting.fu, "find_DN", lambda: None) +# monkeypatch.setattr(formatting.states, "DN", "") +# monkeypatch.setattr(formatting.states, "currentFS", 0) +# monkeypatch.setattr(formatting.states, "new_label", "TESTLABEL") - # Should not raise - formatting.volumecustomlabel() +# # Should not raise +# formatting.volumecustomlabel()