Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added manual dd update #104

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ codexctl install latest
```
- Downloading rmpp version 3.15.4.2 to a folder named `out` and then installing it
```
codexctl download 3.15.4.2 -hw rmpp -o out
codexctl download 3.15.4.2 -d rmpp -o out
codexctl install ./out/remarkable-ct-prototype-image-3.15.4.2-ferrari-public.swu
```
- Backing up all documents to the cwd
Expand Down
88 changes: 64 additions & 24 deletions codexctl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,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 @@ -128,7 +128,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 @@ -165,18 +165,8 @@ def call_func(self, function: str, args: dict) -> None:
else:
rmWeb.upload(input_paths=args["paths"], remoteFolder=args["remote"])

### Transfer & Download functionalities
elif function in ("transfer", "download"):
remarkable = DeviceManager(
remote=remote,
address=args["address"],
logger=self.logger,
authentication=args["password"],
)


### Update & Version functionalities
elif function in ("install", "status", "restore"):
### Update, Transfer & Version functionalities
elif function in ("install", "status", "restore", "transfer"):
remote = False

if "remarkable" not in self.device:
Expand All @@ -189,13 +179,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!")
Comment on lines +182 to +184
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in exception message.

The exception message contains a typo: "alredy" should be corrected to "already".

Apply this diff to fix the typo:

-                        raise SystemError("You can't transfer files alredy on your device!")
+                        raise SystemError("You can't transfer files already on your device!")
📝 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
else:
if function == "transfer":
raise SystemError("You can't transfer files alredy on your device!")
else:
if function == "transfer":
raise SystemError("You can't transfer files already 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 @@ -205,7 +199,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 @@ -229,6 +227,7 @@ 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

version_lookup = lambda version: re.search(r'\b\d+\.\d+\.\d+\.\d+\b', version)
version_number = version_lookup(version)
Expand All @@ -240,18 +239,21 @@ def call_func(self, function: str, args: dict) -> None:

version_number = version_number.group()

update_file_requires_new_engine = UpdateManager.uses_new_update_engine(
version_number
)
device_version_uses_new_engine = UpdateManager.uses_new_update_engine(
remarkable.get_device_status()[2]
)
#update_file_requires_new_engine = UpdateManager.uses_new_update_engine(
# version_number
#)
#device_version_uses_new_engine = UpdateManager.uses_new_update_engine(
# remarkable.get_device_status()[2]
#)
device_version_uses_new_engine = True
update_file_requires_new_engine = False

#### PREVENT USERS FROM INSTALLING NON-COMPATIBLE IMAGES ####

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.")
#raise SystemError("Cannot downgrade to this version as it uses the old update engine, please manually downgrade.")
manual_dd_update = True
# TODO: Implement manual downgrading.
# `codexctl download --out . 3.11.2.5`
# `codexctl extract --out 3.11.2.5.img 3.11.2.5_reMarkable2-qLFGoqPtPL.signed`
Expand Down Expand Up @@ -303,7 +305,30 @@ def call_func(self, function: str, args: dict) -> 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 +314 to +316
Copy link

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 @@ -342,9 +367,24 @@ def main() -> None:
required=False,
help="Specify password or path to SSH key for remote access",
)
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 @@ -359,7 +399,7 @@ def main() -> None:
download.add_argument("version", help="Version to download")
download.add_argument("--out", "-o", help="Folder to download to", default=None)
download.add_argument(
"--hardware", "-hd", help="Hardware to download for", required=True
"--hardware", "-d", help="Hardware to download for", required=True
)

### 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