dwipe 3.2.0__tar.gz → 3.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dwipe-3.2.0 → dwipe-3.2.2}/PKG-INFO +9 -3
- {dwipe-3.2.0 → dwipe-3.2.2}/README.md +5 -1
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/DeviceInfo.py +12 -10
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/DiskWipe.py +130 -11
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/README-DEV.md +30 -15
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/Utils.py +8 -1
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/__init__.py +1 -1
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/main.py +11 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/pyproject.toml +6 -2
- {dwipe-3.2.0 → dwipe-3.2.2}/LICENSE +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/DeviceChangeMonitor.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/DeviceWorker.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/DrivePreChecker.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/FirmwareWipeTask.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/NvmeTool.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/PersistentState.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/Prereqs.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/SataTool.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/StructuredLogger.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/Tunables.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/VerifyTask.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/WipeJob.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/WipeTask.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/WriteTask.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/device_io.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/markers.py +0 -0
- {dwipe-3.2.0 → dwipe-3.2.2}/dwipe/verification.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dwipe
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: A tool to wipe disks and partitions for Linux
|
|
5
5
|
Keywords: disk,partition,wipe,clean,scrub
|
|
6
6
|
Author-email: Joe Defen <joedef@google.com>
|
|
@@ -15,15 +15,17 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Classifier: Operating System :: POSIX :: Linux
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: console-window == 2.
|
|
18
|
+
Requires-Dist: console-window == 2.2.2
|
|
19
19
|
Requires-Dist: pytest >= 7.0 ; extra == "dev"
|
|
20
20
|
Requires-Dist: pytest-cov >= 4.0 ; extra == "dev"
|
|
21
21
|
Requires-Dist: ruff >= 0.9 ; extra == "dev"
|
|
22
22
|
Requires-Dist: build >= 1.2 ; extra == "dev"
|
|
23
23
|
Requires-Dist: twine >= 7 ; extra == "dev"
|
|
24
24
|
Requires-Dist: packaging >= 26 ; extra == "dev"
|
|
25
|
+
Requires-Dist: tomli >= 2.0 ; extra == "dev" and ( python_version < "3.11")
|
|
25
26
|
Requires-Dist: pytest >= 7.0 ; extra == "test"
|
|
26
27
|
Requires-Dist: pytest-cov >= 4.0 ; extra == "test"
|
|
28
|
+
Requires-Dist: tomli >= 2.0 ; extra == "test" and ( python_version < "3.11")
|
|
27
29
|
Project-URL: Bug Tracker, https://github.com/joedefen/dwipe/issues
|
|
28
30
|
Project-URL: Homepage, https://github.com/joedefen/dwipe
|
|
29
31
|
Provides-Extra: dev
|
|
@@ -33,7 +35,7 @@ Import-Name: dwipe
|
|
|
33
35
|
# dwipe
|
|
34
36
|
`dwipe` is a tool to wipe disks and partitions for Linux to help secure your data. `dwipe` aims to reduce mistakes by providing ample information about your devices during selection.
|
|
35
37
|
|
|
36
|
-

|
|
37
39
|
### Quick Comparison
|
|
38
40
|
|
|
39
41
|
| Feature | dwipe | nwipe | shred | dd |
|
|
@@ -137,6 +139,8 @@ Simply run `dwipe` from the command line without arguments: `dwipe`
|
|
|
137
139
|
- Percentage of disk to verify after wipe (1% = quick spot-check, 100% = full verify)
|
|
138
140
|
|
|
139
141
|
#### Display and Performance
|
|
142
|
+
- `--theme {default,dark-mono,light-mono,solarized-dark,solarized-light,gruvbox,nord}` - Color palette
|
|
143
|
+
- `--no-color` - Draw semantic roles with attributes (bold/reverse) instead of color; also honored when the `NO_COLOR` environment variable is set
|
|
140
144
|
- `--dense {True,False}` - Compact view mode (default: your last preference)
|
|
141
145
|
- `True` - Compact spacing (fewer blank lines between disks)
|
|
142
146
|
- `False` - Spaced view (blank lines between disks for readability)
|
|
@@ -190,11 +194,13 @@ Simply run `dwipe` from the command line without arguments: `dwipe`
|
|
|
190
194
|
- `solarized-light` - Solarized Light palette (for light terminal backgrounds)
|
|
191
195
|
- `gruvbox` - Gruvbox Dark palette
|
|
192
196
|
- `nord` - Nord palette
|
|
197
|
+
- `mono` - Draw without color at all, using bold/reverse attributes instead (also available as `--no-color` and through the `NO_COLOR` environment variable)
|
|
193
198
|
|
|
194
199
|
**Changing themes:**
|
|
195
200
|
- Press **t** from the main screen to open the theme preview screen
|
|
196
201
|
- The theme screen shows color examples for each color purpose (DANGER, SUCCESS, WARNING, etc.)
|
|
197
202
|
- Press **t** while on the theme screen to cycle through available themes and preview them live
|
|
203
|
+
- The list ends with **mono**, which turns color off and leaves the selected palette underneath
|
|
198
204
|
- Press **ESC** or **ENTER** to return to the main screen
|
|
199
205
|
- Selected theme is saved and persists across sessions
|
|
200
206
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# dwipe
|
|
2
2
|
`dwipe` is a tool to wipe disks and partitions for Linux to help secure your data. `dwipe` aims to reduce mistakes by providing ample information about your devices during selection.
|
|
3
3
|
|
|
4
|
-

|
|
5
5
|
### Quick Comparison
|
|
6
6
|
|
|
7
7
|
| Feature | dwipe | nwipe | shred | dd |
|
|
@@ -105,6 +105,8 @@ Simply run `dwipe` from the command line without arguments: `dwipe`
|
|
|
105
105
|
- Percentage of disk to verify after wipe (1% = quick spot-check, 100% = full verify)
|
|
106
106
|
|
|
107
107
|
#### Display and Performance
|
|
108
|
+
- `--theme {default,dark-mono,light-mono,solarized-dark,solarized-light,gruvbox,nord}` - Color palette
|
|
109
|
+
- `--no-color` - Draw semantic roles with attributes (bold/reverse) instead of color; also honored when the `NO_COLOR` environment variable is set
|
|
108
110
|
- `--dense {True,False}` - Compact view mode (default: your last preference)
|
|
109
111
|
- `True` - Compact spacing (fewer blank lines between disks)
|
|
110
112
|
- `False` - Spaced view (blank lines between disks for readability)
|
|
@@ -158,11 +160,13 @@ Simply run `dwipe` from the command line without arguments: `dwipe`
|
|
|
158
160
|
- `solarized-light` - Solarized Light palette (for light terminal backgrounds)
|
|
159
161
|
- `gruvbox` - Gruvbox Dark palette
|
|
160
162
|
- `nord` - Nord palette
|
|
163
|
+
- `mono` - Draw without color at all, using bold/reverse attributes instead (also available as `--no-color` and through the `NO_COLOR` environment variable)
|
|
161
164
|
|
|
162
165
|
**Changing themes:**
|
|
163
166
|
- Press **t** from the main screen to open the theme preview screen
|
|
164
167
|
- The theme screen shows color examples for each color purpose (DANGER, SUCCESS, WARNING, etc.)
|
|
165
168
|
- Press **t** while on the theme screen to cycle through available themes and preview them live
|
|
169
|
+
- The list ends with **mono**, which turns color off and leaves the selected palette underneath
|
|
166
170
|
- Press **ESC** or **ENTER** to return to the main screen
|
|
167
171
|
- Selected theme is saved and persists across sessions
|
|
168
172
|
|
|
@@ -13,7 +13,7 @@ import traceback
|
|
|
13
13
|
from fnmatch import fnmatch
|
|
14
14
|
from types import SimpleNamespace
|
|
15
15
|
|
|
16
|
-
from console_window import Theme
|
|
16
|
+
from console_window import Theme, appearance
|
|
17
17
|
|
|
18
18
|
from .DeviceWorker import DeviceWorkerManager, ProbeState
|
|
19
19
|
from .DrivePreChecker import DrivePreChecker
|
|
@@ -1045,38 +1045,40 @@ class DeviceInfo:
|
|
|
1045
1045
|
else:
|
|
1046
1046
|
emit += f'{sep}{ns.marker}'
|
|
1047
1047
|
|
|
1048
|
-
# Determine color attribute based on state
|
|
1048
|
+
# Determine color attribute based on state. Semantic attributes, not
|
|
1049
|
+
# pairs: the same states must degrade to the monochrome hierarchy when
|
|
1050
|
+
# color is off (--no-color or NO_COLOR).
|
|
1049
1051
|
attr = None
|
|
1050
1052
|
# Check for newly inserted flag first (hot-swapped devices should always show orange)
|
|
1051
1053
|
if getattr(ns, 'newly_inserted', False):
|
|
1052
1054
|
# Newly inserted device - orange/bright
|
|
1053
1055
|
if ns.state in ('Mnt', 'iMnt', 'Blk', 'iBlk'):
|
|
1054
1056
|
# Dim the orange for mounted/blocked devices
|
|
1055
|
-
attr =
|
|
1057
|
+
attr = appearance.semantic_attr(Theme.HOTSWAP) | curses.A_DIM
|
|
1056
1058
|
else:
|
|
1057
|
-
attr =
|
|
1059
|
+
attr = appearance.semantic_attr(Theme.HOTSWAP) | curses.A_BOLD
|
|
1058
1060
|
elif ns.state == 's':
|
|
1059
1061
|
# Yellow/warning color for stopped/partial wipes (with bold for visibility)
|
|
1060
|
-
attr =
|
|
1062
|
+
attr = appearance.semantic_attr(Theme.WARNING) | curses.A_BOLD
|
|
1061
1063
|
elif ns.state == 'W' and getattr(ns, 'wiped_this_session', False):
|
|
1062
1064
|
# Green/success color for completed wipes (done in THIS session only) - bold and bright
|
|
1063
|
-
attr =
|
|
1065
|
+
attr = appearance.semantic_attr(Theme.SUCCESS) | curses.A_BOLD
|
|
1064
1066
|
elif ns.state == 'W':
|
|
1065
1067
|
# Green/success color for completed wipes before this session
|
|
1066
|
-
attr =
|
|
1068
|
+
attr = appearance.semantic_attr(Theme.OLD_SUCCESS) | curses.A_BOLD
|
|
1067
1069
|
elif ns.state.endswith('%') and ns.state not in ('100%',):
|
|
1068
1070
|
# Active wipe in progress - bright cyan/blue with bold
|
|
1069
|
-
attr =
|
|
1071
|
+
attr = appearance.semantic_attr(Theme.INFO) | curses.A_BOLD
|
|
1070
1072
|
elif ns.state == '^':
|
|
1071
1073
|
# Newly inserted device (hot-swapped) - orange/bright
|
|
1072
|
-
attr =
|
|
1074
|
+
attr = appearance.semantic_attr(Theme.HOTSWAP) | curses.A_BOLD
|
|
1073
1075
|
elif ns.state in ('Mnt', 'iMnt', 'Blk', 'iBlk'):
|
|
1074
1076
|
# Dim mounted or blocked devices
|
|
1075
1077
|
attr = curses.A_DIM
|
|
1076
1078
|
|
|
1077
1079
|
# Override with red/danger color if verify failed
|
|
1078
1080
|
if hasattr(ns, 'verify_failed_msg') and ns.verify_failed_msg:
|
|
1079
|
-
attr =
|
|
1081
|
+
attr = appearance.semantic_attr(Theme.DANGER) | curses.A_BOLD
|
|
1080
1082
|
|
|
1081
1083
|
# Make disk lines bold
|
|
1082
1084
|
if ns.parent is None:
|
|
@@ -29,6 +29,7 @@ from console_window import (
|
|
|
29
29
|
Screen,
|
|
30
30
|
ScreenStack,
|
|
31
31
|
Theme,
|
|
32
|
+
appearance,
|
|
32
33
|
)
|
|
33
34
|
|
|
34
35
|
from .DeviceChangeMonitor import DeviceChangeMonitor
|
|
@@ -61,6 +62,52 @@ LOG_ST = 2
|
|
|
61
62
|
THEME_ST = 3
|
|
62
63
|
SCREEN_NAMES = ('MAIN', 'HELP', 'HISTORY', 'THEMES')
|
|
63
64
|
|
|
65
|
+
#: dwipe's own pseudo-theme. ``mono`` is not a palette: choosing it turns
|
|
66
|
+
#: semantic color off for the whole process, exactly like ``--no-color``. The
|
|
67
|
+
#: library leaves pseudo-themes to the application (``console_window.appearance``
|
|
68
|
+
#: names ``mono`` as its standing example), so dwipe owns the name and the
|
|
69
|
+
#: switch behind it.
|
|
70
|
+
MONO_THEME = 'mono'
|
|
71
|
+
#: The entries the theme picker cycles through: every library palette, then the
|
|
72
|
+
#: mono pseudo-theme, so ``t`` wraps from the last palette into mono.
|
|
73
|
+
THEME_CHOICES = (*Theme.list_all(), MONO_THEME)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def resolve_theme_selection(cli_theme=None, no_color=False,
|
|
77
|
+
saved_theme='', environ=None):
|
|
78
|
+
"""Resolve startup appearance into ``(selected, theme, colors)``.
|
|
79
|
+
|
|
80
|
+
``selected`` is the value the theme option should hold: a palette name or
|
|
81
|
+
:data:`MONO_THEME`. ``theme`` is the palette to install (``''`` keeps the
|
|
82
|
+
library default) and ``colors`` is the process-wide switch.
|
|
83
|
+
|
|
84
|
+
Precedence is the library's: ``--no-color``, an explicit ``--theme``, the
|
|
85
|
+
``NO_COLOR`` convention, then the remembered choice. A remembered ``mono``
|
|
86
|
+
is normalized to "no palette plus color off" before resolving.
|
|
87
|
+
|
|
88
|
+
A per-run ``--no-color``/``NO_COLOR`` keeps the remembered palette in
|
|
89
|
+
``selected`` rather than replacing it with ``mono``. The switch is not a
|
|
90
|
+
saved preference, so the next ordinary start has to come back to the same
|
|
91
|
+
palette; only choosing ``mono`` in the picker saves it.
|
|
92
|
+
"""
|
|
93
|
+
saved_theme = saved_theme or ''
|
|
94
|
+
saved_colors = saved_theme != MONO_THEME
|
|
95
|
+
saved_palette = '' if saved_theme == MONO_THEME else saved_theme
|
|
96
|
+
theme, colors = appearance.resolve_appearance(
|
|
97
|
+
cli_theme=cli_theme,
|
|
98
|
+
no_color=no_color,
|
|
99
|
+
saved_theme=saved_palette,
|
|
100
|
+
saved_colors=saved_colors,
|
|
101
|
+
environ=environ,
|
|
102
|
+
)
|
|
103
|
+
if cli_theme:
|
|
104
|
+
selected = Theme.resolve(cli_theme)
|
|
105
|
+
elif not colors and saved_theme == MONO_THEME:
|
|
106
|
+
selected = MONO_THEME
|
|
107
|
+
else:
|
|
108
|
+
selected = Theme.resolve(theme or Theme.get_current())
|
|
109
|
+
return selected, theme, colors
|
|
110
|
+
|
|
64
111
|
|
|
65
112
|
class DiskWipe:
|
|
66
113
|
"""Main application controller and UI manager"""
|
|
@@ -118,6 +165,47 @@ class DiskWipe:
|
|
|
118
165
|
on_cancel=self._on_filter_cancel
|
|
119
166
|
)
|
|
120
167
|
|
|
168
|
+
def _settle_appearance(self, environ=None):
|
|
169
|
+
"""Apply the startup color decision to the process and the theme option.
|
|
170
|
+
|
|
171
|
+
The remembered theme, ``--theme``, ``--no-color`` and the ``NO_COLOR``
|
|
172
|
+
convention all meet here; see :func:`resolve_theme_selection` for the
|
|
173
|
+
precedence and for why a per-run ``--no-color`` does not overwrite the
|
|
174
|
+
remembered palette.
|
|
175
|
+
"""
|
|
176
|
+
saved_theme = self.persistent_state.state.get('theme', '') or ''
|
|
177
|
+
selected, theme, colors = resolve_theme_selection(
|
|
178
|
+
cli_theme=getattr(self.opts, 'theme', None),
|
|
179
|
+
no_color=bool(getattr(self.opts, 'no_color', False)),
|
|
180
|
+
saved_theme=saved_theme,
|
|
181
|
+
environ=environ,
|
|
182
|
+
)
|
|
183
|
+
appearance.set_colors_enabled(colors)
|
|
184
|
+
if theme:
|
|
185
|
+
Theme.set(theme)
|
|
186
|
+
self.opts.theme = selected
|
|
187
|
+
return selected, colors
|
|
188
|
+
|
|
189
|
+
def apply_theme_choice(self, choice):
|
|
190
|
+
"""Make a theme-picker *choice* live and remember it.
|
|
191
|
+
|
|
192
|
+
``mono`` is the pseudo-theme: a color mode rather than a palette, so it
|
|
193
|
+
flips the process-wide switch and leaves the installed palette where it
|
|
194
|
+
was (the same way ``--no-color`` keeps ``--theme``). Choosing a real
|
|
195
|
+
palette is a request for color, so it lifts the switch again.
|
|
196
|
+
"""
|
|
197
|
+
self.opts.theme = choice
|
|
198
|
+
if choice == MONO_THEME:
|
|
199
|
+
appearance.set_colors_enabled(False)
|
|
200
|
+
else:
|
|
201
|
+
appearance.set_colors_enabled(True)
|
|
202
|
+
self.win.set_theme(choice)
|
|
203
|
+
# A picker choice is a preference, so write it now: quitting from the
|
|
204
|
+
# same frame must not lose it.
|
|
205
|
+
self.persistent_state.state['theme'] = choice
|
|
206
|
+
self.persistent_state.dirty = True
|
|
207
|
+
self.persistent_state.save()
|
|
208
|
+
|
|
121
209
|
def _start_wipe(self):
|
|
122
210
|
"""Start the wipe job after confirmation"""
|
|
123
211
|
if self.confirmation.identity and self.confirmation.identity in self.partitions:
|
|
@@ -513,8 +601,8 @@ class DiskWipe:
|
|
|
513
601
|
def main_loop(self):
|
|
514
602
|
"""Main event loop"""
|
|
515
603
|
|
|
516
|
-
# Create screen instances
|
|
517
|
-
|
|
604
|
+
# Create screen instances. ThemeScreen is defined below with the other
|
|
605
|
+
# screens; it adds dwipe's ``mono`` entry to the library's palette list.
|
|
518
606
|
self.screens = {
|
|
519
607
|
MAIN_ST: MainScreen(self),
|
|
520
608
|
HELP_ST: HelpScreen(self),
|
|
@@ -553,6 +641,10 @@ class DiskWipe:
|
|
|
553
641
|
exit(1)
|
|
554
642
|
|
|
555
643
|
self.win = ConsoleWindow(opts=win_opts)
|
|
644
|
+
# Color is one decision with two parts: which palette and whether to use
|
|
645
|
+
# it. Settle both before the first draw so NO_COLOR works as well as
|
|
646
|
+
# the switches.
|
|
647
|
+
self._settle_appearance()
|
|
556
648
|
# Initialize screen stack
|
|
557
649
|
self.stack = ScreenStack(self.win, None, SCREEN_NAMES, self.screens)
|
|
558
650
|
|
|
@@ -585,9 +677,8 @@ class DiskWipe:
|
|
|
585
677
|
spin.add_key('expand', 'e - expand history entry', genre='action', scope=LOG_ST)
|
|
586
678
|
spin.add_key('copy', 'c - copy entry to clipboard', genre='action', scope=LOG_ST)
|
|
587
679
|
spin.add_key('show_keys', 'K - show keys (demo mode)', genre='action')
|
|
588
|
-
#
|
|
589
|
-
|
|
590
|
-
Theme.set(self.opts.theme)
|
|
680
|
+
# The appearance (and therefore opts.theme) was settled when the window
|
|
681
|
+
# was created, so the picker and the saved preference already agree.
|
|
591
682
|
self.win.set_handled_keys(self.spin.keys)
|
|
592
683
|
|
|
593
684
|
# Background device change monitor started above
|
|
@@ -687,6 +778,28 @@ class DiskWipeScreen(Screen):
|
|
|
687
778
|
""" Show last key for demo"""
|
|
688
779
|
self.app.win.set_demo_mode(enabled=None) # toggle it
|
|
689
780
|
|
|
781
|
+
|
|
782
|
+
class ThemeScreen(Theme.create_picker_screen(DiskWipeScreen, cycle_key='t')):
|
|
783
|
+
"""The library's theme picker plus dwipe's ``mono`` pseudo-theme.
|
|
784
|
+
|
|
785
|
+
``mono`` is a color mode, not a palette, so the picker's swatches follow the
|
|
786
|
+
shared switch and show the monochrome role hierarchy while it is selected.
|
|
787
|
+
Overriding ``theme_label`` names that choice in the header instead of naming
|
|
788
|
+
the palette left installed underneath it.
|
|
789
|
+
"""
|
|
790
|
+
|
|
791
|
+
def theme_label(self, theme_name):
|
|
792
|
+
if not appearance.colors_enabled():
|
|
793
|
+
return MONO_THEME
|
|
794
|
+
return super().theme_label(theme_name)
|
|
795
|
+
|
|
796
|
+
def spin_theme_ACTION(self):
|
|
797
|
+
app = self.app
|
|
798
|
+
current = getattr(app.opts, 'theme', None)
|
|
799
|
+
index = THEME_CHOICES.index(current) if current in THEME_CHOICES else -1
|
|
800
|
+
app.apply_theme_choice(THEME_CHOICES[(index + 1) % len(THEME_CHOICES)])
|
|
801
|
+
|
|
802
|
+
|
|
690
803
|
class MainScreen(DiskWipeScreen):
|
|
691
804
|
"""Main device list screen"""
|
|
692
805
|
|
|
@@ -1064,14 +1177,15 @@ class MainScreen(DiskWipeScreen):
|
|
|
1064
1177
|
msg = ' ' * 5 + msg
|
|
1065
1178
|
|
|
1066
1179
|
# Add confirmation message base as DECOR (non-pickable)
|
|
1067
|
-
app.win.add_body(msg,
|
|
1180
|
+
app.win.add_body(msg,
|
|
1181
|
+
attr=appearance.semantic_attr(Theme.DANGER) | cs.A_BOLD,
|
|
1068
1182
|
context=Context(genre='DECOR'))
|
|
1069
1183
|
|
|
1070
1184
|
# Add input or hint on same line
|
|
1071
1185
|
if app.confirmation.input_buffer:
|
|
1072
1186
|
# Show current input with cursor
|
|
1073
1187
|
app.win.add_body(app.confirmation.input_buffer + '_',
|
|
1074
|
-
attr=
|
|
1188
|
+
attr=appearance.semantic_attr(Theme.DANGER) | cs.A_BOLD,
|
|
1075
1189
|
resume=True)
|
|
1076
1190
|
else:
|
|
1077
1191
|
# Show hint in dimmed italic
|
|
@@ -1402,9 +1516,13 @@ class HelpScreen(DiskWipeScreen):
|
|
|
1402
1516
|
f'--slowdown-stop: {opts.slowdown_stop:<{wid}} Stop if disk slows',
|
|
1403
1517
|
f'--stall-timeout: {opts.stall_timeout:<{wid}} Stall timeout in sec',
|
|
1404
1518
|
f'--auto-block: . {"on" if getattr(opts, "auto_block_containers", True) else "off":<{wid}} Auto-block containers',
|
|
1519
|
+
f'--theme: . . . . {(getattr(opts, "theme", None) or "-"):<{wid}} Color palette (or mono)',
|
|
1520
|
+
f'--no-color: . . . {"on" if getattr(opts, "no_color", False) else "off":<{wid}} Draw without color',
|
|
1405
1521
|
]
|
|
1406
1522
|
for opt in cli_options:
|
|
1407
1523
|
app.win.add_body(opt, attr=cs.A_DIM)
|
|
1524
|
+
app.win.add_body('Theme "mono" (last in the picker) turns color off; '
|
|
1525
|
+
'NO_COLOR does the same at startup.', attr=cs.A_DIM)
|
|
1408
1526
|
|
|
1409
1527
|
|
|
1410
1528
|
class HistoryScreen(DiskWipeScreen):
|
|
@@ -1547,13 +1665,14 @@ class HistoryScreen(DiskWipeScreen):
|
|
|
1547
1665
|
# Add deep match indicator if this entry matched only in JSON
|
|
1548
1666
|
deep_indicator = " *" if timestamp in self.search_matches else ""
|
|
1549
1667
|
|
|
1550
|
-
# Choose color based on log level
|
|
1668
|
+
# Choose color based on log level. Semantic attributes, not pairs:
|
|
1669
|
+
# the same rows must degrade to bold/reverse when color is off.
|
|
1551
1670
|
if level == 'ERR':
|
|
1552
|
-
level_attr =
|
|
1671
|
+
level_attr = appearance.semantic_attr(Theme.ERROR) | cs.A_BOLD
|
|
1553
1672
|
elif level in ('WIPE_STOPPED', 'VERIFY_STOPPED'):
|
|
1554
|
-
level_attr =
|
|
1673
|
+
level_attr = appearance.semantic_attr(Theme.WARNING) | cs.A_BOLD
|
|
1555
1674
|
elif level in ('WIPE_COMPLETE', 'VERIFY_COMPLETE'):
|
|
1556
|
-
level_attr =
|
|
1675
|
+
level_attr = appearance.semantic_attr(Theme.SUCCESS) | cs.A_BOLD
|
|
1557
1676
|
else:
|
|
1558
1677
|
level_attr = cs.A_BOLD
|
|
1559
1678
|
|
|
@@ -14,7 +14,8 @@ to the modules it describes; every path below is therefore relative to
|
|
|
14
14
|
| --- | --- |
|
|
15
15
|
| [`dwipe/__init__.py`](../dwipe/__init__.py) | `__version__`, and nothing else. The build backend reads it, `dwipe --version` prints it, `bless` reads the line without importing the package. |
|
|
16
16
|
| [`pyproject.toml`](../pyproject.toml) | Name, description, keywords, `requires-python`, runtime dependencies, the SPDX `license` and `license-files`, the `test`/`dev` extras, and the pytest, coverage and ruff configuration. |
|
|
17
|
-
| [`bless`](../bless) | The release gate.
|
|
17
|
+
| [`bless`](../bless) | The release gate. Emitted from `~/Projects/blessd/bless` by that repository's `build.py` and never edited here; it holds no copy of this project's identity. |
|
|
18
|
+
| `~/Projects/blessd/docs/release-strategy.md` | The shared rationale behind the gate: why the upload is local and irreversible, and how the branch model follows from that. |
|
|
18
19
|
| [`tests/inspect_dist.py`](../tests/inspect_dist.py) | The distribution inspector. `bless` refuses to upload a wheel and sdist it has not inspected with this file. |
|
|
19
20
|
| [`runner`](../runner) | Runs the tree from source (`python -m dwipe.main`) for debugging. |
|
|
20
21
|
| [`linter`](../linter) | The advisory pylint run for one file, with the project root on `sys.path`. |
|
|
@@ -82,7 +83,7 @@ again. Enter, `quit` or Ctrl-C stops with nothing tagged, uploaded or pushed.
|
|
|
82
83
|
**Stages, in order**
|
|
83
84
|
|
|
84
85
|
1. **preflight** -- clean tree, the `vX.Y.Z` tag absent, `twine>=7` and
|
|
85
|
-
`packaging>=26` present, and `
|
|
86
|
+
`packaging>=26` present, and `python -m build` answering.
|
|
86
87
|
2. **index check** -- refuses if `__version__` is already on PyPI, printing
|
|
87
88
|
every published version, before anything is built or uploaded.
|
|
88
89
|
3. **gates** -- the three commands above.
|
|
@@ -170,17 +171,31 @@ the tagged commit. Everything else about the release is one of the stages above.
|
|
|
170
171
|
|
|
171
172
|
## Notes on this adoption
|
|
172
173
|
|
|
173
|
-
* [`bless`](../bless)
|
|
174
|
-
`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
174
|
+
* [`bless`](../bless) is emitted from `~/Projects/blessd/bless` by that
|
|
175
|
+
repository's `build.py`; it is never edited here. The policy the gate
|
|
176
|
+
enforces -- the branch plan, the prompt, the index check, the build, the
|
|
177
|
+
emitter -- and the tests of that policy live in `~/Projects/blessd`, once.
|
|
178
|
+
This tree carries the build with SHA-256
|
|
179
|
+
`3ef60e9e806ca58a3d900dd6622bb8be0052210da1ef26f9918bf0a1deb1e3b2`
|
|
180
|
+
(55222 bytes), which
|
|
181
|
+
|
|
182
|
+
python3 ~/Projects/blessd/build.py --into . --force
|
|
183
|
+
|
|
184
|
+
reproduces byte for byte, so `cmp ~/Projects/blessd/bless bless` is silent.
|
|
185
|
+
A later gate is adopted the same way, as a new emission rather than an edit.
|
|
186
|
+
* `dwipe` had to provide what the gate reads instead of being told it: one
|
|
187
|
+
version location (`dynamic = ["version"]`, the line in
|
|
188
|
+
[`dwipe/__init__.py`](../dwipe/__init__.py)), an inspector
|
|
189
|
+
([`tests/inspect_dist.py`](../tests/inspect_dist.py)), a console script that
|
|
190
|
+
answers `--version` (`dwipe`), the `dev` extra with `build`, `twine>=7` and
|
|
191
|
+
`packaging>=26`, and the `main`/`dev` branch pair -- the gate's default
|
|
192
|
+
model, so no `[tool.bless]` table is needed. The metadata was moved to the
|
|
193
|
+
PEP 639 `license` expression and `license-files` at the same time, which is
|
|
194
|
+
why the build backend floor is `flit_core>=3.12`.
|
|
183
195
|
* The ruff rule set was added with the gate, so the whole tree was brought to a
|
|
184
|
-
clean `ruff check .` once. [`tests/test_bless.py`](../tests/test_bless.py)
|
|
185
|
-
|
|
186
|
-
|
|
196
|
+
clean `ruff check .` once. [`tests/test_bless.py`](../tests/test_bless.py) now
|
|
197
|
+
covers only this tree's identity -- the name, module, console script and
|
|
198
|
+
branch model `bless` derives from `pyproject.toml`, the version reported by
|
|
199
|
+
`dwipe --version` -- and the inspector; the gate's policy tests are part of
|
|
200
|
+
the shared repository. [`tests/test_repo_hygiene.py`](../tests/test_repo_hygiene.py)
|
|
201
|
+
keeps the gate, the dev extra and the single version location from drifting.
|
|
@@ -71,7 +71,14 @@ class Utils:
|
|
|
71
71
|
"""Rerun using the module name"""
|
|
72
72
|
if os.geteuid() != 0: # Re-run the script with sudo
|
|
73
73
|
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
74
|
-
|
|
74
|
+
args = sys.argv[1:]
|
|
75
|
+
# sudo resets the environment by default, so an exported NO_COLOR
|
|
76
|
+
# would not reach the process that actually draws the UI. Carry it
|
|
77
|
+
# across as the equivalent switch; --no-color already wins over
|
|
78
|
+
# --theme, matching the no-color.org convention.
|
|
79
|
+
if os.environ.get('NO_COLOR') and '--no-color' not in args:
|
|
80
|
+
args = ['--no-color', *args]
|
|
81
|
+
vp = ['sudo', sys.executable, '-m', module_name] + args
|
|
75
82
|
os.execvp('sudo', vp)
|
|
76
83
|
|
|
77
84
|
@staticmethod
|
|
@@ -12,6 +12,8 @@ import os
|
|
|
12
12
|
import sys
|
|
13
13
|
import traceback
|
|
14
14
|
|
|
15
|
+
from console_window import cli
|
|
16
|
+
|
|
15
17
|
from . import __version__
|
|
16
18
|
from .DiskWipe import DiskWipe
|
|
17
19
|
from .PersistentState import PersistentState
|
|
@@ -55,6 +57,11 @@ def main():
|
|
|
55
57
|
parser.add_argument('--auto-block-containers', choices=['on', 'off'],
|
|
56
58
|
default='on' if persist.state['auto_block_containers'] else 'off',
|
|
57
59
|
help='auto-block LUKS/LVM/RAID/swap containers on first sight (on/off)')
|
|
60
|
+
# --theme / --no-color come from the library, so the appearance switch and
|
|
61
|
+
# palette live in its one process-wide implementation rather than a copy
|
|
62
|
+
# here. --no-color is a per-run switch; the remembered choice is settled
|
|
63
|
+
# later by DiskWipe, alongside NO_COLOR.
|
|
64
|
+
cli.add_display_arguments(parser, prog='dwipe')
|
|
58
65
|
opts = parser.parse_args()
|
|
59
66
|
|
|
60
67
|
# Convert string booleans to actual booleans
|
|
@@ -67,6 +74,10 @@ def main():
|
|
|
67
74
|
# Re-run the script with sudo needed and opted
|
|
68
75
|
Utils.rerun_module_as_root('dwipe.main')
|
|
69
76
|
|
|
77
|
+
# Apply --ascii/--glyphs/--theme/--no-color once, in the process that
|
|
78
|
+
# will draw the UI: a sudo re-run starts over from the same argv.
|
|
79
|
+
cli.apply_display_arguments(opts, prog='dwipe')
|
|
80
|
+
|
|
70
81
|
prereqs = Prereqs(verbose=True)
|
|
71
82
|
# blkid for filesystem detection; hdparm and nvme for firmware wipes
|
|
72
83
|
prereqs.check_all(['blkid', 'hdparm', 'nvme'])
|
|
@@ -79,15 +79,18 @@ classifiers = [
|
|
|
79
79
|
"Operating System :: POSIX :: Linux"
|
|
80
80
|
]
|
|
81
81
|
dependencies = [
|
|
82
|
-
'console-window == 2.
|
|
82
|
+
'console-window == 2.2.2',
|
|
83
83
|
]
|
|
84
84
|
|
|
85
85
|
[project.optional-dependencies]
|
|
86
86
|
# The minimum a contributor needs to run the suite (what CI installs on every
|
|
87
|
-
# supported Python).
|
|
87
|
+
# supported Python). `tomli` only appears below 3.11, where the stdlib has no
|
|
88
|
+
# `tomllib`, yet tests and `bless` read pyproject.toml on every interpreter
|
|
89
|
+
# `requires-python` allows.
|
|
88
90
|
test = [
|
|
89
91
|
'pytest >= 7.0',
|
|
90
92
|
'pytest-cov >= 4.0',
|
|
93
|
+
'tomli >= 2.0; python_version < "3.11"',
|
|
91
94
|
]
|
|
92
95
|
# The release toolchain. `./bless` refuses to start unless `twine` and
|
|
93
96
|
# `packaging` meet its own floors (`twine>=7`, `packaging>=26`), so declaring
|
|
@@ -100,6 +103,7 @@ dev = [
|
|
|
100
103
|
'build >= 1.2',
|
|
101
104
|
'twine >= 7',
|
|
102
105
|
'packaging >= 26',
|
|
106
|
+
'tomli >= 2.0; python_version < "3.11"',
|
|
103
107
|
]
|
|
104
108
|
|
|
105
109
|
[project.urls]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|