Skip to content
Open
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
70 changes: 54 additions & 16 deletions codexctl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def call_func(self, function: str, args: dict) -> None:
args["out"] = os.getcwd() + "/extracted"

logger.debug(f"Extracting {args['file']} to {args['out']}")
image, volume = get_update_image(args["file"])
image, volume = get_update_image(args["file"], logger=logger)
image.seek(0)

with open(args["out"], "wb") as f:
Expand Down Expand Up @@ -131,7 +131,7 @@ def call_func(self, function: str, args: dict) -> None:
)

try:
image, volume = get_update_image(args["file"])
image, volume = get_update_image(args["file"], logger=logger)
inode = volume.inode_at(args["target_path"])

except FileNotFoundError:
Expand Down Expand Up @@ -192,13 +192,17 @@ def call_func(self, function: str, args: dict) -> None:
"Psutil is required for SSH access. Please install it."
)
remote = True
else:
if function == "transfer":
raise SystemError("You can't transfer files alredy on your device!")

from .device import DeviceManager
from .server import get_available_version

remarkable = DeviceManager(
remote=remote,
address=args["address"],
port=args["port"],
logger=self.logger,
authentication=args["password"],
)
Expand All @@ -208,7 +212,11 @@ def call_func(self, function: str, args: dict) -> None:
elif version == "toltec":
version = self.updater.get_toltec_version(remarkable.hardware)

if function == "status":
if function == "transfer":
remarkable.transfer_file_to_remote(args["file"], args["destination"])
print("Done!")

elif function == "status":
beta, prev, current, version_id = remarkable.get_device_status()
print(
f"\nCurrent version: {current}\nOld update engine: {prev}\nBeta active: {beta}\nVersion id: {version_id}"
Expand All @@ -232,10 +240,11 @@ def call_func(self, function: str, args: dict) -> None:
# Do we have a specific update file to serve?

update_file = version if os.path.isfile(version) else None
manual_dd_update = False

def version_lookup(version: str | None) -> re.Match[str] | None:
return re.search(r"\b\d+\.\d+\.\d+\.\d+\b", cast(str, version))

version_number = version_lookup(version)

if not version_number:
Expand All @@ -260,22 +269,13 @@ def version_lookup(version: str | None) -> re.Match[str] | None:

if device_version_uses_new_engine:
if not update_file_requires_new_engine:
raise SystemError(
"Cannot downgrade to this version as it uses the old update engine, please manually downgrade."
)
# TODO: Implement manual downgrading.
manual_dd_update = True
# `codexctl download --out . 3.11.2.5`
# `codexctl extract --out 3.11.2.5.img 3.11.2.5_reMarkable2-qLFGoqPtPL.signed`
# `codexctl transfer 3.11.2.5.img ~/root`
# `dd if=/home/root/3.11.2.5.img of=/dev/mmcblk2p2` (depending on fallback partition)
# `codexctl restore`

else:
if update_file_requires_new_engine:
raise SystemError(
"This version requires the new update engine, please upgrade your device to version 3.11.2.5 first."
)

#############################################################

if not update_file_requires_new_engine:
Expand Down Expand Up @@ -318,7 +318,30 @@ def version_lookup(version: str | None) -> re.Match[str] | None:
)

if device_version_uses_new_engine:
remarkable.install_sw_update(update_file)
if not manual_dd_update:
remarkable.install_sw_update(update_file)
else:
try:
from .analysis import get_update_image
except ImportError:
raise ImportError(
"remarkable_update_image is required for this update. Please install it!"
)
Comment on lines +327 to +329
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use exception chaining with raise ... from None.

When re-raising exceptions within an except block, use from None to suppress the context of the original exception. This provides clearer error messages to the user.

Apply this diff to include from None:

-                                raise ImportError(
-                                    "remarkable_update_image is required for this update. Please install it!"
-                                )
+                                raise ImportError(
+                                    "remarkable_update_image is required for this update. Please install it!"
+                                ) from None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
raise ImportError(
"remarkable_update_image is required for this update. Please install it!"
)
raise ImportError(
"remarkable_update_image is required for this update. Please install it!"
) from None
🧰 Tools
🪛 Ruff (0.8.0)

314-316: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


out_image_file = f"{version_number}.img"

logger.debug(f"Extracting {update_file} to ./{out_image_file}")
image, volume = get_update_image(update_file, logger=logger)
image.seek(0)

with open(out_image_file, "wb") as f:
f.write(image.read())

print("Now installing from .img")

remarkable.install_manual_update(out_image_file)

os.remove(out_image_file)
else:
remarkable.install_ohma_update(update_file)

Expand Down Expand Up @@ -360,9 +383,24 @@ def main() -> None:
help="Specify password or path to SSH key for remote access",
dest="password",
)
parser.add_argument(
"--port",
required=False,
type=int,
default=22,
help="Specify specific SSH port, shouldn't be needed unless you've changed it."
)
subparsers = parser.add_subparsers(dest="command")
subparsers.required = True # This fixes a bug with older versions of python

### Transfer subcommand
transfer = subparsers.add_parser(
"transfer",
help="Transfer a file from your host to the device",
)
transfer.add_argument("file", help="Location of file to transfer")
transfer.add_argument("destination", help="Where the file should be put on the device")

### Install subcommand
install = subparsers.add_parser(
"install",
Expand All @@ -382,7 +420,7 @@ def main() -> None:
"-d",
help="Hardware to download for",
required=True,
dest="hardware",
dest="hardware"
)

### Backup subcommand
Expand Down
10 changes: 4 additions & 6 deletions codexctl/analysis.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import ext4
import warnings
import errno

from remarkable_update_image import UpdateImage
from remarkable_update_image import UpdateImageSignatureException


def get_update_image(file: str):
def get_update_image(file: str, logger):
"""Extracts files from an update image (<3.11 currently)"""

image = UpdateImage(file)
Expand All @@ -20,14 +18,14 @@ def get_update_image(file: str):
image.verify(inode.open().read())

except UpdateImageSignatureException:
warnings.warn("Signature doesn't match contents", RuntimeWarning)
logger.warning("Signature doesn't match contents", RuntimeWarning)

except FileNotFoundError:
warnings.warn("Public key missing", RuntimeWarning)
logger.warning("Public key missing", RuntimeWarning)

except OSError as e:
if e.errno != errno.ENOTDIR:
raise
warnings.warn("Unable to open public key", RuntimeWarning)
logger.warning("Unable to open public key", RuntimeWarning)

return image, volume
Loading
Loading