Compare commits

...

569 Commits

Author SHA1 Message Date
elseif
2daa51ba67 modified: README.md 2026-07-18 17:02:34 +08:00
elseif
cebf02d5f6 modified: README.md 2026-07-18 17:00:15 +08:00
elseif
85582304cb modified: README.md 2026-07-18 16:56:24 +08:00
elseif
d4287e92a3 modified: README.md 2026-07-18 16:53:58 +08:00
elseif
4de5fd88c0 modified: README.md 2026-07-18 16:51:51 +08:00
elseif
d970a10e08 modified: README.md 2026-07-18 16:51:07 +08:00
elseif
88b7f61b42 modified: index.html 2026-07-18 16:23:42 +08:00
elseif
630e9be8e6 modified: README.md 2026-07-17 09:13:17 +08:00
elseif
b3398d2341 Merge branch 'main' of github.com:elseif/MikroTikPatch 2026-07-17 09:10:23 +08:00
elseif
7aabc6ffcd modified: README.md 2026-07-17 09:08:16 +08:00
elseif
17b6db1acc modified: README.md
modified:   README.md

Changes not staged for commit:
	modified:   README.md
2026-07-17 09:08:09 +08:00
elseif
a6e8bb3c87 modified: README.md 2026-07-17 09:06:37 +08:00
elseif
50b4c1036e modified: README.md 2026-07-17 09:04:42 +08:00
elseif
5e4e7eead0 deleted: README_EN.md
deleted:    README_PT.md
	deleted:    image/arm.png
	deleted:    image/install.png
	deleted:    image/mips.png
	deleted:    image/renew.png
	deleted:    image/renew_v6.png
	deleted:    image/routeros.png
2026-07-17 09:03:58 +08:00
elseif
37ba5451aa modified: README.md 2026-07-17 09:01:41 +08:00
elseif
e07ec52d34 modified: .github/workflows/build_docker.yml 2026-07-16 14:45:52 +08:00
elseif
bf73ab201b modified: .github/workflows/build_docker.yml 2026-07-16 14:42:38 +08:00
elseif
ae7943de83 modified: .github/workflows/build_docker.yml 2026-07-16 14:26:58 +08:00
elseif
3b42f1c5fb modified: .github/workflows/build_docker.yml 2026-07-16 14:22:52 +08:00
elseif
7b273f0464 modified: .github/workflows/build_docker.yml 2026-07-16 14:01:06 +08:00
elseif
5033270988 modified: .github/workflows/build_docker.yml 2026-07-16 13:54:42 +08:00
elseif
063cd7785f modified: .github/workflows/build_docker.yml 2026-07-16 13:28:07 +08:00
elseif
b628eaadd2 modified: .github/workflows/build_docker.yml 2026-07-16 13:25:36 +08:00
elseif
4d32f80f8b modified: .github/workflows/build_docker.yml 2026-07-16 13:19:44 +08:00
elseif
e576f75398 modified: .github/workflows/build_docker.yml 2026-07-16 13:15:39 +08:00
elseif
c7eba4be0c modified: .github/workflows/build_docker.yml 2026-07-16 13:14:00 +08:00
elseif
78a2720c82 modified: .github/workflows/build_docker.yml 2026-07-16 13:10:22 +08:00
elseif
9cc32db4d2 modified: .github/workflows/build_docker.yml 2026-07-16 12:47:25 +08:00
elseif
4f3f8b5e40 modified: .github/workflows/build_docker.yml 2026-07-16 12:43:14 +08:00
elseif
68e9f1f5c2 modified: .github/workflows/build_docker.yml 2026-07-16 12:14:25 +08:00
elseif
a46286d886 modified: .github/workflows/build_docker.yml 2026-07-16 10:03:43 +08:00
elseif
e44ea1504b modified: .github/workflows/build_docker.yml 2026-07-16 09:59:39 +08:00
elseif
57f2724d69 modified: .github/workflows/build_docker.yml 2026-07-16 09:57:55 +08:00
elseif
672c225645 modified: .github/workflows/build_docker.yml 2026-07-16 09:47:00 +08:00
elseif
ad036099ce modified: .github/workflows/build_docker.yml 2026-07-16 09:42:18 +08:00
elseif
723969a497 modified: .github/workflows/build_docker.yml 2026-07-16 09:31:54 +08:00
elseif
ae84c4ecde modified: .github/workflows/build_docker.yml 2026-07-16 09:12:32 +08:00
elseif
54c562f054
Simplify multi arch manifest creation
Removed unnecessary output of JSON file contents before creating the multi-architecture manifest.

Signed-off-by: elseif <elseif@live.cn>
2026-07-16 07:00:23 +08:00
elseif
ddb8fe368e
Update Docker build workflow to use QEMU setup
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 06:57:58 +08:00
elseif
e4138453e1
Add IMAGE variable for Docker build process
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 06:55:37 +08:00
elseif
db0273d518
Output digests before creating Docker manifest
Add commands to output digests from JSON files before creating a multi-architecture Docker manifest.

Signed-off-by: elseif <elseif@live.cn>
2026-07-16 06:45:05 +08:00
elseif
c4cada441a
Add permissions for package writing in workflow
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 06:33:59 +08:00
elseif
89ea52c7df
Refactor Docker build workflow and update inputs
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 06:26:37 +08:00
elseif
0670bf0818
Add Patch_Development_V7 workflow for version v7
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 03:20:46 +08:00
elseif
ea433daaf8
Add 'development' option to channel input
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 03:20:05 +08:00
elseif
18fd42ae68
Rename jobs for V7 in GitHub Actions workflow
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 01:16:56 +08:00
elseif
2e1198189c
Fix duplicate routeros*.npk entry in patch workflow
Removed duplicate entry for routeros*.npk in file upload section.

Signed-off-by: elseif <elseif@live.cn>
2026-07-16 00:48:44 +08:00
elseif
b8f382bc34
Update RouterOS v6 package details in index.html
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 00:47:17 +08:00
elseif
e7a8f2bffb
Add support for routeros NPK files in workflow
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 00:41:06 +08:00
elseif
79a24e44eb
Add support for routeros NPK files in workflow
Signed-off-by: elseif <elseif@live.cn>
2026-07-16 00:38:47 +08:00
elseif
d546633361 modified: .github/workflows/patch_v7.yml 2026-07-15 18:16:55 +08:00
elseif
d5701f354c modified: .github/workflows/patch_v7.yml 2026-07-15 18:14:41 +08:00
elseif
d90a121e42 modified: .github/workflows/patch_v7.yml 2026-07-15 18:10:15 +08:00
elseif
a7af632dc5 modified: .github/workflows/patch_v7.yml 2026-07-15 18:07:26 +08:00
elseif
21147e34f2 modified: .github/workflows/main.yml 2026-07-15 18:02:53 +08:00
elseif
63f495d811 modified: .github/workflows/patch_v7.yml 2026-07-15 17:59:49 +08:00
elseif
0b3d6862ad modified: .github/workflows/main.yml 2026-07-15 11:00:32 +08:00
elseif
399cc879ed modified: .github/workflows/main.yml 2026-07-15 10:58:51 +08:00
elseif
a5601a7320 modified: .github/workflows/main.yml 2026-07-15 10:55:37 +08:00
elseif
067327e43e modified: .github/workflows/main.yml 2026-07-15 10:54:26 +08:00
elseif
2b6ed9e615 modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-15 10:48:02 +08:00
elseif
2750b4502c modified: README.md
modified:   README_EN.md
	modified:   README_PT.md
2026-07-15 09:43:32 +08:00
elseif
30c069ac9e modified: index.html
modified:   README.md
	modified:   README_EN.md
	modified:   README_PT.md
2026-07-15 09:43:20 +08:00
elseif
22fefaf3f4 modified: patch.py 2026-07-15 01:57:33 +08:00
elseif
dc5e2e0ac8 modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-15 01:46:19 +08:00
elseif
1f552fcd84 modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-15 01:34:24 +08:00
elseif
73c149696f modified: .github/workflows/main.yml
modified:   .github/workflows/patch_v6.yml
	modified:   .github/workflows/patch_v7.yml
2026-07-15 01:25:35 +08:00
elseif
8955a366fb modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-15 01:22:31 +08:00
elseif
eb5926ac6f modified: .github/workflows/patch_v6.yml 2026-07-15 01:19:59 +08:00
elseif
5bfb3ce1d0 modified: .github/workflows/patch_v6.yml 2026-07-15 00:56:49 +08:00
elseif
99573e837d modified: .github/workflows/patch_v6.yml 2026-07-15 00:11:04 +08:00
elseif
b797695ea3 modified: .github/workflows/patch_v6.yml 2026-07-15 00:04:01 +08:00
elseif
01aa897fda modified: .github/workflows/patch_v6.yml 2026-07-14 23:51:31 +08:00
elseif
c80e3ddcb4 modified: .github/workflows/patch_v6.yml 2026-07-14 23:18:12 +08:00
elseif
312a340f1f modified: patch.py 2026-07-14 23:04:43 +08:00
elseif
93429aa0d9 modified: .github/workflows/patch_v6.yml 2026-07-14 23:02:45 +08:00
elseif
0d4e77dc05 modified: .github/workflows/patch_v6.yml
modified:   npk.py
2026-07-14 22:56:32 +08:00
elseif
ef8acaa55b modified: patch.py 2026-07-14 22:29:34 +08:00
elseif
20c4031ae6 modified: .github/workflows/patch_v6.yml 2026-07-14 22:12:59 +08:00
elseif
fd52dcf9ec modified: .github/workflows/patch_v6.yml 2026-07-14 21:51:31 +08:00
elseif
8eaa652e3c modified: .github/workflows/patch_v6.yml 2026-07-14 21:15:28 +08:00
elseif
1f76f9ea4c modified: .github/workflows/patch_v6.yml 2026-07-14 21:08:29 +08:00
elseif
b782e4bd65 modified: .github/workflows/patch_v6.yml 2026-07-14 21:01:17 +08:00
elseif
2e6862590e modified: .github/workflows/patch_v7.yml 2026-07-14 20:56:28 +08:00
elseif
7b6f03adc5 modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-14 20:42:28 +08:00
elseif
a9b8746917 modified: .github/workflows/patch_v7.yml 2026-07-14 20:40:04 +08:00
elseif
3a6289c70a modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-14 20:33:28 +08:00
elseif
68a45aeae2 modified: patch.py 2026-07-14 20:28:10 +08:00
elseif
ecf7d9b5ea modified: .github/workflows/patch_v6.yml
modified:   .github/workflows/patch_v7.yml
2026-07-14 19:55:47 +08:00
elseif
74a89f9af6 deleted: .github/workflows/mikrotik_patch_6.yml
deleted:    .github/workflows/mikrotik_patch_7.yml
	modified:   README.md
	modified:   README_EN.md
	modified:   README_PT.md
2026-07-14 19:49:29 +08:00
elseif
d95c1523f1 modified: .github/workflows/main.yml
renamed:    .github/workflows/build_v6.yml -> .github/workflows/patch_v6.yml
	renamed:    .github/workflows/build_v7.yml -> .github/workflows/patch_v7.yml
2026-07-14 18:26:29 +08:00
elseif
b7ef8c8304 new file: .github/workflows/build_v6.yml
modified:   .github/workflows/main.yml
	modified:   .gitignore
2026-07-14 18:19:22 +08:00
elseif
c8ca24f00f modified: .github/workflows/build_v7.yml 2026-07-14 16:13:12 +08:00
elseif
6014d957f4 modified: .github/workflows/build_v7.yml
modified:   toyecc/ShortWeierstrassCurve.py
2026-07-14 16:05:39 +08:00
elseif
f9d2554725 modified: .github/workflows/build_v7.yml
deleted:    package.py
	modified:   patch.py
2026-07-14 16:02:44 +08:00
elseif
fbdba025ba modified: patch.py 2026-07-14 15:55:04 +08:00
elseif
e01697e0bf modified: .github/workflows/build_v7.yml
modified:   patch.py
2026-07-14 15:53:35 +08:00
elseif
1bc1d82048 modified: .github/workflows/build_v7.yml 2026-07-14 15:37:36 +08:00
elseif
cdc8f8df65 renamed: keygen/keygen_aarch64 -> keygen/keygen_arm64 2026-07-14 15:30:18 +08:00
elseif
dcfa022145 modified: .github/workflows/build_v7.yml 2026-07-14 15:28:34 +08:00
elseif
ab2e4463b6 modified: .github/workflows/build_v7.yml 2026-07-14 14:58:32 +08:00
elseif
43a30fddb0 new file: .github/workflows/build_v7.yml
new file:   .github/workflows/main.yml
	modified:   .github/workflows/mikrotik_patch_7.yml
	renamed:    busybox/busybox_aarch64 -> busybox/busybox_arm64
	modified:   npk.py
	modified:   patch.py
2026-07-14 14:52:29 +08:00
elseif
d0dc0620f9 modified: patch.py 2026-07-14 13:59:01 +08:00
elseif
55783f7975 modified: chr.sh 2026-07-10 11:39:29 +08:00
elseif
81e4c63ceb modified: .github/workflows/mikrotik_patch_7.yml 2026-07-09 16:38:13 +08:00
elseif
c756852094 modified: chr.sh 2026-07-09 12:37:54 +08:00
elseif
b7ee8aaa1a modified: chr.sh 2026-07-09 12:33:14 +08:00
elseif
1a5647bd98 modified: chr.sh 2026-07-09 12:31:45 +08:00
elseif
60709c4fd6 modified: README.md
modified:   README_EN.md
	modified:   README_PT.md
	deleted:    image/chr.png
	deleted:    image/x86.png
2026-07-09 12:28:10 +08:00
elseif
91f4eed086 modified: chr.sh 2026-07-09 11:59:57 +08:00
elseif
497ed531f3 modified: chr.sh 2026-07-09 11:45:01 +08:00
elseif
3bde30196c modified: chr.sh 2026-07-09 11:39:52 +08:00
elseif
8991f41f0b modified: chr.sh 2026-07-09 11:37:47 +08:00
elseif
dc20f46a94 modified: chr.sh 2026-07-09 11:33:26 +08:00
elseif
447f7990ae modified: chr.sh 2026-07-09 11:19:43 +08:00
elseif
969c4fb369 modified: README.md
modified:   README_EN.md
2026-07-01 12:52:02 +08:00
elseif
bbf8ed23e3 modified: .github/workflows/mikrotik_patch_7.yml 2026-06-28 05:22:27 +08:00
elseif
7039d76825 update busybox to v1.38.0 2026-06-27 10:52:21 +08:00
elseif
5e9340c741 modified: .github/workflows/mikrotik_patch_7.yml 2026-06-27 10:27:23 +08:00
elseif
ac2cfdeafd modified: .github/workflows/mikrotik_patch_7.yml 2026-06-27 01:17:47 +08:00
elseif
e728aec84c modified: .github/workflows/mikrotik_patch_7.yml 2026-06-27 01:15:51 +08:00
elseif
1d9971b387 modified: .github/workflows/mikrotik_patch_7.yml 2026-06-27 01:13:22 +08:00
elseif
1cfb6dba3d modified: .github/workflows/mikrotik_patch_7.yml 2026-06-27 01:10:16 +08:00
elseif
ba0ffe3cd7 modified: README.md 2026-05-26 06:55:31 +08:00
elseif
7cf469dc0c modified: .github/workflows/mikrotik_patch_7.yml 2026-05-25 09:34:19 +08:00
elseif
30d6ac5fe6 modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2026-05-19 23:52:12 +08:00
elseif
7fd1b6c563 modified: .github/workflows/mikrotik_patch_7.yml 2026-05-19 23:17:13 +08:00
elseif
ed3dcd0ab1 modified: patch.py 2026-05-19 23:06:47 +08:00
elseif
0a270a1108
Upgrade actions/cache and action-gh-release versions
Signed-off-by: elseif <elseif@live.cn>
2026-04-20 16:00:24 +08:00
elseif
7a6fdb6323
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-04-18 01:09:01 +08:00
elseif
44bc4346c6
Merge pull request #185 from LucasStone98/main
Fix sourceforge link for building images
2026-04-18 01:07:38 +08:00
Daniel Samuels
98d6af79f7
Update mikrotik_patch_7.yml
Fix sourceforge link for refind

Signed-off-by: Daniel Samuels <47313460+LucasStone98@users.noreply.github.com>
2026-04-17 12:41:21 +02:00
elseif
11eec19e7d
Upgrade GitHub Actions to checkout@v6 and setup-python@v6
Signed-off-by: elseif <elseif@live.cn>
2026-04-02 10:28:05 +08:00
elseif
9e1d7a5239
Upgrade GitHub Actions to checkout@v6 and setup-python@v6
Signed-off-by: elseif <elseif@live.cn>
2026-04-02 10:27:38 +08:00
elseif
569e0b5749
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 08:29:36 +08:00
elseif
5d9cc6d745
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 08:09:57 +08:00
elseif
0f62f5de4e
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 07:55:25 +08:00
elseif
7f2120dca8
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 04:06:04 +08:00
elseif
9be7cdc6ec
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 04:01:33 +08:00
elseif
b7eccac7c5
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 03:58:29 +08:00
elseif
e0afc29cca
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 03:53:34 +08:00
elseif
98dfe256e6
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-03-30 03:50:34 +08:00
elseif
f179b74d3e
Update wget command to include no-check-certificate option
Signed-off-by: elseif <elseif@live.cn>
2026-03-25 13:06:51 +08:00
elseif
c71a8ff9c1
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-01-10 12:42:37 +08:00
elseif
b35ce475ab
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-01-10 12:29:19 +08:00
elseif
976e562e83
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2026-01-10 11:41:38 +08:00
elseif
86e11b647d
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-10-22 14:12:58 +08:00
elseif
07c6617805
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-10-22 14:09:03 +08:00
elseif
bf2865921a
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-10-21 18:14:25 +08:00
elseif
604631539c
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-10-21 17:53:20 +08:00
elseif
fc7bb4f206
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-10-21 17:51:22 +08:00
elseif
410c19d2ce
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-10-09 11:25:48 +08:00
elseif
d5eea3df4b
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-29 11:22:03 +08:00
elseif
73559ce979
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-28 00:21:06 +08:00
elseif
c54b46c760
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-26 13:42:03 +08:00
elseif
8603498d39
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-26 13:29:16 +08:00
elseif
d3fa483c4b
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-26 13:26:27 +08:00
elseif
e338ddb7b2
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-26 13:15:28 +08:00
elseif
826072d302
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-26 13:10:28 +08:00
elseif
5eee7b4dbb
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 19:15:43 +08:00
elseif
1691d01550
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 19:13:01 +08:00
elseif
4543b7b3b2
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 19:07:44 +08:00
elseif
3cddd9e6fb
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 18:47:24 +08:00
elseif
3fb99a94ac
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 18:44:44 +08:00
elseif
356575af79
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 16:56:06 +08:00
elseif
58970bd829
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 16:47:18 +08:00
elseif
6e8d5cf26c
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 16:45:03 +08:00
elseif
502136464a
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 10:10:52 +08:00
elseif
6b7d1a849f
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 10:04:34 +08:00
elseif
77f07bbf27
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 09:45:43 +08:00
elseif
06a9d80798
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-25 09:14:16 +08:00
elseif
3c2e1197ce
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 20:59:05 +08:00
elseif
befaa60509
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 20:56:13 +08:00
elseif
1a2f73d363
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 19:36:08 +08:00
elseif
e7063fdcbd
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 19:19:08 +08:00
elseif
b26437cb47
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 18:30:41 +08:00
elseif
b6c24eb657
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 18:21:53 +08:00
elseif
f1d3c640f8
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 18:17:55 +08:00
elseif
2f627420a2
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-24 14:16:40 +08:00
zyb
482041cac3 modified: index.html 2025-09-24 13:50:40 +08:00
zyb
c139e9012a modified: chr.sh 2025-09-24 13:31:41 +08:00
zyb
89e9e0a92e modified: chr.sh 2025-09-24 13:14:08 +08:00
zyb
9ad1b924a1 modified: chr.sh 2025-09-24 13:09:15 +08:00
elseif
68c53e4a19
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 12:25:11 +08:00
elseif
e70a8aff16
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 12:24:06 +08:00
elseif
1b857342ba
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 12:23:53 +08:00
elseif
4b4b257b4a
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 12:23:29 +08:00
elseif
597cbf30d8
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 02:23:17 +08:00
elseif
6d7f3c0fd7
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 02:22:46 +08:00
elseif
fc6fc897e1
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 00:37:36 +08:00
elseif
0f45d580ba
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-22 00:34:33 +08:00
elseif
930a496ce4
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:26:54 +08:00
elseif
b7f321f748
Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:26:38 +08:00
elseif
f9662f6d23
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:26:25 +08:00
elseif
db5c0c3c8f
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:26:09 +08:00
elseif
93455ac7e7
Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:26:01 +08:00
elseif
29e25d9e8a
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:23:40 +08:00
elseif
30a45fc829
Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:23:23 +08:00
elseif
9d91a8348d
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:23:07 +08:00
elseif
66877c2071
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:22:41 +08:00
elseif
671ad79933
Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-09-21 22:22:30 +08:00
elseif
61ba016da0
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 23:28:40 +08:00
elseif
96d23defd3
Merge pull request #148 from FakeErrorX/main
Enhance UI with changelog modal and improved comments
2025-09-19 13:44:05 +08:00
ErrorX
5e80b52278
Merge branch 'main' into main
Signed-off-by: ErrorX <99178547+FakeErrorX@users.noreply.github.com>
2025-09-19 11:39:35 +06:00
ErrorX
54252e8645 Enhance UI with changelog modal and improved comments
Added a popup modal for viewing changelogs directly within the page, including loading and error states. Improved code readability with detailed comments, enhanced footer with banner-style social links, and clarified section headers and UI element purposes throughout the HTML and JavaScript. Also updated download links to use the changelog modal and improved GitHub proxy handling.
2025-09-19 11:32:05 +06:00
elseif
f03d0300d2
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 13:07:29 +08:00
elseif
8281f0193f
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 13:02:02 +08:00
elseif
500c7b5748
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 12:27:07 +08:00
elseif
e5b8288b35
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 12:23:54 +08:00
elseif
fe493a8f0f
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 12:07:24 +08:00
elseif
e8bd998953
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 12:05:41 +08:00
elseif
9ad4028007
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 10:59:11 +08:00
elseif
f55fba6699
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 01:03:56 +08:00
elseif
e2debac366
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 00:59:40 +08:00
elseif
7fe82f7498
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-19 00:32:10 +08:00
elseif
e7a08895f5
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-18 23:35:16 +08:00
elseif
b436141665
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-18 23:29:20 +08:00
elseif
86e2727c56
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-18 23:15:40 +08:00
elseif
96670cbe60
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-09-18 23:04:30 +08:00
elseif
f91f8e15d0
Merge pull request #145 from FakeErrorX/main
Redesign index.html
2025-09-18 16:49:01 +08:00
elseif
c4dd46205b
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-18 16:35:11 +08:00
elseif
4f9d2133fd
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-09-18 16:33:57 +08:00
ErrorX
b6721ffd7d Redesign index.html
Replaces legacy CSS and table-based layout with modernizes the UI with responsive cards, details/summary accordions, and improved theme/language switching. Adds dynamic loading, new download tables, and enhanced accessibility. Removes old inline styles and scripts, streamlining the code for maintainability and better user experience.
2025-09-16 15:08:43 +06:00
elseif
6cbdbffbcb
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-09-13 03:43:55 +08:00
elseif
6d27075389
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-13 03:39:43 +08:00
elseif
9b92fc3fa6
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 13:21:58 +08:00
elseif
0c92a04b58
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 13:21:30 +08:00
elseif
296db5b558
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 13:20:35 +08:00
elseif
49bbcb1983
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 10:04:24 +08:00
elseif
f750442687
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:56 +08:00
elseif
06bd69f331
Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:42 +08:00
elseif
e35cf5f74e
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:27 +08:00
elseif
e12e1aa67b
Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:06 +08:00
elseif
6a8aaa89d8
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:44:53 +08:00
elseif
495927d0de
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 08:57:23 +08:00
elseif
c73c6bacc8
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 08:56:31 +08:00
elseif
8e7134e0a6
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 17:27:26 +08:00
elseif
f4cc728789
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 17:26:58 +08:00
elseif
8c3278f477
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 17:07:20 +08:00
elseif
04a831b463
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 16:30:01 +08:00
elseif
c75dd48261
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:36:11 +08:00
elseif
6519ab2e12
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:26:20 +08:00
elseif
ec1c0f1719
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:16:03 +08:00
elseif
3de34ef556
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:09:47 +08:00
elseif
8499e4591a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:23:18 +08:00
elseif
f520ff799c
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:12:45 +08:00
elseif
ccb4220a61
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:10:02 +08:00
elseif
8159018e3c
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:09:07 +08:00
elseif
dc30d89afb
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 07:47:39 +08:00
elseif
4472649719
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 10:23:13 +08:00
elseif
fc469087be
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 10:22:15 +08:00
elseif
477fd66fe0
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 10:04:22 +08:00
elseif
cd32348c52
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 08:37:29 +08:00
elseif
37f92c3def
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 01:39:38 +08:00
elseif
2bb452b300
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:16:44 +08:00
elseif
710921ef23
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:13:21 +08:00
elseif
8bfcdfe5a8
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:12:51 +08:00
elseif
2baf264aec
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:11:15 +08:00
elseif
fd6f36d484
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:08:06 +08:00
elseif
2ab01e5328
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:06:52 +08:00
elseif
16854dadf9
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-31 23:56:16 +08:00
elseif
d981a4b137
Delete keygen/tmp
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:53:58 +08:00
elseif
378c9009b8
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:53:34 +08:00
elseif
73f9ba0506
Create tmp
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:52:28 +08:00
elseif
646f479290
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:49:53 +08:00
elseif
e3b46ed7c3
Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:49:41 +08:00
elseif
1a66685cc4
Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:49:29 +08:00
elseif
cbd6ffa50c
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 19:38:57 +08:00
elseif
73d90b27e0
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 19:31:24 +08:00
elseif
60728d6fe2
Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 19:27:34 +08:00
elseif
556cc81474
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 18:35:01 +08:00
elseif
2286063b65
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 17:29:36 +08:00
elseif
907b335d3a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 17:29:10 +08:00
elseif
567819bec4
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 17:19:41 +08:00
elseif
bbc6b854cd
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:53:43 +08:00
elseif
6f4c9b43fc
Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:52:57 +08:00
elseif
659de9d659
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:34:23 +08:00
elseif
78707050a0
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:31:09 +08:00
elseif
cdb378b45d
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:25:09 +08:00
elseif
9b40d04e38
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:17:43 +08:00
elseif
110cda779c
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:12:13 +08:00
elseif
36be15859e
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 15:56:02 +08:00
elseif
c40954115b
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 17:22:44 +08:00
elseif
2babd89f27
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 17:19:46 +08:00
elseif
7a6e538834
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 14:10:29 +08:00
elseif
a2f9af37ad
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 14:05:54 +08:00
elseif
6e630b852a
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:49:44 +08:00
elseif
4987501168
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:35:49 +08:00
elseif
c2ec6d439c
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:27:00 +08:00
elseif
c5712825ba
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:22:40 +08:00
elseif
072417bf1b
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:17:09 +08:00
elseif
f2d18e32f6
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:10:04 +08:00
elseif
7758330905
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:25:45 +08:00
elseif
0ff47ed53e
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:24:17 +08:00
elseif
90dfb10d32
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:21:53 +08:00
elseif
a217ae8df0
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:18:07 +08:00
elseif
fa9c53bef1
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:07:27 +08:00
elseif
18b6449701
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:05:07 +08:00
elseif
d7517cb9f4
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 21:55:55 +08:00
elseif
3a17176568
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 21:54:18 +08:00
elseif
da3653fcb7
更新 mikrotik_patch_7.yml 2025-08-25 15:21:45 +08:00
elseif
48bd5e9601
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 14:26:08 +08:00
elseif
c0f53a242a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 13:05:25 +08:00
elseif
8d18551f5b
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 12:54:44 +08:00
elseif
f29d91d1a4
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 12:39:16 +08:00
elseif
89c13ae6fa
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 12:34:02 +08:00
elseif
e028fdd880
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 05:14:47 +08:00
elseif
88ff1f7130
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 05:07:35 +08:00
elseif
92f26c270e
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:59:06 +08:00
elseif
c1163894f3
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:45:03 +08:00
elseif
5196f20043
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:43:38 +08:00
elseif
bba676a2d6
Rename chr_busybox.sh to chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:40:23 +08:00
elseif
259051906c
Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:40:09 +08:00
elseif
2162e76e64
Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:19:30 +08:00
elseif
7c22877423
Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:14:22 +08:00
elseif
6a486adfc6
Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:11:51 +08:00
elseif
49608606f2
Update and rename chr.sh to chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:08:09 +08:00
elseif
85a2526bcd
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 03:58:06 +08:00
elseif
b5049c0ebb
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 03:56:41 +08:00
elseif
15273578e8
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 03:31:43 +08:00
elseif
a85cbf1548
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-22 00:26:03 +08:00
elseif
7195dae015
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 21:47:12 +08:00
elseif
5ef845c148
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 21:35:32 +08:00
elseif
00a510fc1e
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 21:01:44 +08:00
elseif
3d491261f3
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:58:57 +08:00
elseif
4ba3cc3f11
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:57:56 +08:00
elseif
b5904ff5ae
Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:57:46 +08:00
elseif
5a8e9d19b3
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:56:41 +08:00
elseif
3c5ed64380
Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:52:04 +08:00
elseif
808b637ebd
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 17:35:33 +08:00
elseif
ced9e23040
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:40:31 +08:00
elseif
4623e18824
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:39:59 +08:00
elseif
87e7644916
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:39:34 +08:00
elseif
40676b67af
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:10:20 +08:00
elseif
e674ef263a
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:09:39 +08:00
elseif
9cd8640e0a
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 15:55:17 +08:00
elseif
fcf7fddd2e
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 15:54:36 +08:00
elseif
bdb3de0777
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 07:36:05 +08:00
elseif
923225902b
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 07:35:48 +08:00
elseif
3b4391d582
Delete keygen/rc.local
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 19:04:13 +08:00
elseif
6c6899386b
Update rc.local
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 19:03:54 +08:00
elseif
5220cb9d8e
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 19:02:20 +08:00
elseif
cfe551fdda
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 18:59:43 +08:00
elseif
241c81356b
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 17:13:20 +08:00
elseif
9cc2f9063d
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 17:08:29 +08:00
elseif
399c670504
Create rc.local
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:40:09 +08:00
elseif
ee6d299e9f
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:38:31 +08:00
elseif
0234cf6f75
Delete busybox/busybox_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:38:09 +08:00
elseif
9df0184656
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:37:46 +08:00
elseif
5623d2215c
Delete keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:37:17 +08:00
elseif
7239cd4cdf
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:36:27 +08:00
elseif
b60cab8b45
Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:34:19 +08:00
elseif
e8b9bef9aa
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:31:09 +08:00
elseif
dc959f9b16
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 21:19:16 +08:00
elseif
2ea2d3fbdf
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:46:46 +08:00
elseif
3d3f3fee33
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:46:03 +08:00
elseif
8d411a43c6
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:44:59 +08:00
elseif
d483639d46
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:44:47 +08:00
elseif
4d09aa8aab
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:41:00 +08:00
elseif
8f59b90921
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:40:34 +08:00
elseif
5d08e5308d
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:31:22 +08:00
elseif
18e256ff41
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:30:34 +08:00
elseif
855fbc85d1
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:30:00 +08:00
elseif
9715f13b6a
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:14:57 +08:00
elseif
b2893b91b7
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:57:48 +08:00
elseif
988ded50c3
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:56:30 +08:00
elseif
950505e0ab
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:50:20 +08:00
elseif
96c0fd251e
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:49:17 +08:00
elseif
d4dcf6dfeb
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:48:04 +08:00
elseif
65b7c57616
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 14:15:12 +08:00
elseif
edb40667ef
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 14:04:49 +08:00
elseif
f98f599cb7
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 07:08:18 +08:00
elseif
f28acb89ae
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 07:05:08 +08:00
elseif
259fb4bf2a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 06:54:12 +08:00
elseif
8f15749897
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-29 11:52:44 +08:00
elseif
9712d7d464
Update CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:59:12 +08:00
elseif
7bebf605a0
Update LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:58:30 +08:00
elseif
f018e0de99
Update LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:58:05 +08:00
elseif
7ef69a1e31
Update README_PT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:56:07 +08:00
elseif
1ba7057fc7
Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:55:53 +08:00
elseif
8a52ef8df4
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:55:38 +08:00
elseif
7292ad7a80
Update README_PT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:47:06 +08:00
elseif
88de7162b7
Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:46:51 +08:00
elseif
f3497a9a4b
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:46:34 +08:00
elseif
702efdb828
Update CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:43:37 +08:00
elseif
a2038ece24
Update CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:40:04 +08:00
elseif
0b3fd16ba7
Update LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:35:14 +08:00
elseif
8367df6ef0
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-17 22:07:56 +08:00
zyb
2b9db1436c modified: patch.py 2025-07-13 14:27:34 +08:00
zyb
6dccdda7bb modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 21:10:15 +08:00
zyb
1d0b4f9563 modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 20:48:53 +08:00
zyb
ce13b4b959 modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 20:42:06 +08:00
zyb
3f98072f2d modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 20:38:55 +08:00
zyb
ceb85aaf72 modified: .github/workflows/mikrotik_patch_7.yml
modified:   busybox/busybox_aarch64
	new file:   busybox/busybox_arm
	modified:   busybox/busybox_x86
	modified:   keygen/keygen_aarch64
	new file:   keygen/keygen_arm
	modified:   patch.py
2025-07-11 20:36:23 +08:00
elseif
ad132d5a8d
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-10 18:49:16 +08:00
elseif
0e6364e06a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-10 18:46:53 +08:00
elseif
c7f31476e7
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-07-08 00:58:38 +08:00
elseif
674370280d
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-07-06 01:30:56 +08:00
elseif
9a7ea04b45
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-05 14:40:35 +08:00
elseif
4965f266db
Create CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-05 06:07:33 +08:00
elseif
6c33b4c2c9
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-04 00:26:48 +08:00
zyb
5729c4746a modified: README.md
modified:   README_EN.md
	modified:   README_PT.md
2025-06-29 01:36:00 +08:00
elseif
0e89b026b8
Create LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-06-23 12:41:14 +08:00
zyb
88925b0057 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-23 12:37:27 +08:00
zyb
18f7b4cf36 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 14:49:16 +08:00
zyb
77b0644c95 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 14:32:03 +08:00
zyb
e72436306b modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 10:37:46 +08:00
zyb
d32177f1e7 modified: .github/workflows/mikrotik_patch_6.yml 2025-06-21 09:49:51 +08:00
zyb
2ad3d69a4d modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 09:47:49 +08:00
zyb
70819de5a4 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 14:31:23 +08:00
zyb
f22db63a10 modified: npk.py 2025-06-19 13:48:23 +08:00
zyb
178ffcbadb modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2025-06-19 13:45:31 +08:00
zyb
70bca68232 modified: npk.py
modified:   patch.py
2025-06-19 13:38:45 +08:00
zyb
4cdd6d5619 modified: patch.py 2025-06-19 13:31:48 +08:00
zyb
af97ad2a07 modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2025-06-19 13:28:29 +08:00
zyb
fd50885bf4 modified: patch.py 2025-06-19 13:23:52 +08:00
zyb
2551551828 modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2025-06-19 13:21:08 +08:00
zyb
d8f092eb80 modified: patch.py 2025-06-19 13:16:46 +08:00
zyb
fe68e753c6 modified: patch.py 2025-06-19 13:15:26 +08:00
zyb
9ca203dcad modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 13:11:50 +08:00
zyb
939e01725c modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 13:07:24 +08:00
zyb
1935f35864 modified: .github/workflows/mikrotik_patch_7.yml
modified:   npk.py
2025-06-19 12:48:56 +08:00
zyb
676f0e0f82 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:43:23 +08:00
zyb
b50ba21932 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:34:03 +08:00
zyb
78f63469c9 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:30:13 +08:00
zyb
511392906b modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:22:59 +08:00
elseif
2d2ca3e493
Merge pull request #118 from PauloNRocha/main
Add Portuguese translation of README
2025-06-19 12:12:44 +08:00
zyb
3f988f27ed modified: .github/workflows/mikrotik_patch_7.yml
deleted:    .github/workflows/self_hosted_patch.yml
	modified:   .gitignore
2025-06-19 12:09:51 +08:00
elseif
8a2b8f629c
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:26:00 +08:00
elseif
2ce0f85f8a
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:17:52 +08:00
elseif
a07fe281f1
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:07:34 +08:00
elseif
70321348ad
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:04:58 +08:00
Paulo Rocha
6b1d9a4a62 minor spelling corrections 2025-06-18 23:04:09 -03:00
elseif
9f5f72bb03
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:00:30 +08:00
Paulo Rocha
173dfd69e3 Add Portuguese translation of README
Added README_PT.md with full Brazilian Portuguese translation of the original README. Helpful for Portuguese-speaking users.
2025-06-18 22:57:15 -03:00
elseif
f0698d8814
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:50:58 +08:00
elseif
db75ad1711
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:47:03 +08:00
elseif
f54f9f15b6
Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:13:42 +08:00
elseif
17b37b7124
Delete mbr.bin
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:09:13 +08:00
elseif
48ce77cf80
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:32:27 +08:00
elseif
202dab0201
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:23:34 +08:00
elseif
ef13a0cf68
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:22:07 +08:00
elseif
cc05886c0a
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:16:56 +08:00
elseif
018f060462
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:52:44 +08:00
elseif
fcd6926272
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:37:08 +08:00
elseif
6cd71c9efe
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:33:59 +08:00
elseif
a350e2badd
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:24:26 +08:00
elseif
9bad9f06f1
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:15:06 +08:00
elseif
a5db54eca5
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:11:41 +08:00
elseif
dedfe06698
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:55:56 +08:00
elseif
cdae26e673
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:46:50 +08:00
elseif
33cd5313f0
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:41:27 +08:00
elseif
2b0548ba76
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:33:59 +08:00
elseif
4a9d5672c1
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:22:06 +08:00
elseif
23dd4c7f43
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:10:48 +08:00
elseif
80bda12745
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:01:56 +08:00
elseif
954fcfdbe3
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:53:19 +08:00
elseif
78516e3695
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:29:47 +08:00
elseif
e43686eca2
Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:25:01 +08:00
elseif
3f3bd3978b
self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:04:42 +08:00
elseif
d6c9e7c528
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-17 15:54:00 +08:00
elseif
476691ac29
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-06-17 15:20:57 +08:00
elseif
8e3ff90b2d
Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-06-17 15:20:15 +08:00
elseif
50a2f3f03b
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-14 20:19:25 +08:00
elseif
317904dfbf
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-14 19:48:15 +08:00
elseif
6e0c8a52bd
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:29:26 +08:00
elseif
2fe1832095
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:17:39 +08:00
elseif
7a15204f3a
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:15:02 +08:00
elseif
6e0c64cd73
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:10:46 +08:00
elseif
f4a4187e23
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-03-04 11:23:03 +08:00
elseif
66323bbfea
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 13:14:44 +08:00
elseif
7f2ec84067
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 13:13:36 +08:00
elseif
5589babdcc
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 13:09:00 +08:00
elseif
18bae23bf3
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 12:47:17 +08:00
elseif
3d52d65eb5
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 12:39:41 +08:00
elseif
a951a224b0
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 12:37:48 +08:00
elseif
343fe653ee
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-24 14:37:08 +08:00
elseif
bfb4e91be7
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-24 14:19:57 +08:00
elseif
898d1c1789
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:34:42 +08:00
elseif
e4167afbec
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:33:07 +08:00
elseif
aeaf99729f
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:27:06 +08:00
elseif
d125a063ce
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:22:40 +08:00
elseif
ce926f7cc9
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:45:20 +08:00
elseif
aea2da2a36
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:31:26 +08:00
elseif
c869dca3c1
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:23:14 +08:00
elseif
cbe8254d84
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:18:39 +08:00
elseif
331df17ee7
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:12:01 +08:00
elseif
35852182b2
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:30:46 +08:00
elseif
e0d1eb1a98
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:26:17 +08:00
elseif
0717a39c02
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:24:30 +08:00
elseif
4993be82a6
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:22:42 +08:00
elseif
0dbed4777f
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:17:23 +08:00
elseif
3b529862d1
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:43:29 +08:00
elseif
6da98653fc
Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:43:02 +08:00
elseif
27e023fb28
Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:42:28 +08:00
elseif
df6b25a30e
Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:42:06 +08:00
elseif
60294067c7
Merge pull request #80 from emam06/patch-3
Update
2025-02-19 15:41:19 +08:00
elseif
d309253b06
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 11:12:54 +08:00
elseif
b3579cb363
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 11:09:18 +08:00
elseif
0f2c5e6081
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:50:25 +08:00
elseif
8f59465bf0
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:43:39 +08:00
elseif
736af6817b
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:33:03 +08:00
elseif
10a56eb673
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:31:07 +08:00
elseif
6518dda504
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:42:28 +08:00
elseif
74e812fa2b
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:40:17 +08:00
elseif
810909828a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:16:22 +08:00
elseif
49f4ad0a59
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:02:01 +08:00
elseif
ece607c8f7
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:01:10 +08:00
elseif
1426fc061a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-17 12:44:05 +08:00
elseif
7579dadaea
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-17 12:29:48 +08:00
elseif
6b6669fb23
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-17 12:27:07 +08:00
elseif
5f603dcebc
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-11 17:47:22 +08:00
elseif
e1994f69d6
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 13:43:33 +08:00
elseif
39af3326bd
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 03:39:14 +08:00
elseif
c657f2e736
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 03:08:29 +08:00
elseif
a8dd672a66
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 00:51:42 +08:00
elseif
cf01bbda4d
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 00:43:57 +08:00
elseif
1c9207d498
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 00:40:47 +08:00
elseif
f1ed27cb52
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:32:42 +08:00
elseif
8d38a80dd8
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:30:14 +08:00
elseif
f4af3671be
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:19:28 +08:00
elseif
2f0f0b1abb
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:01:27 +08:00
elseif
ea10a1a4c7
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 22:47:38 +08:00
emam06
bd03ecb963
Update
Signed-off-by: emam06 <egr7yco@gmail.com>
2025-01-27 15:17:41 +02:00
elseif
5b909283a0
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 21:35:52 +08:00
elseif
289ea3a34f
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 03:15:17 +08:00
elseif
aaf997c20d
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 03:06:18 +08:00
elseif
ab61c4937f
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 03:04:50 +08:00
elseif
8790f8f322
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:51:46 +08:00
elseif
5137e190ac
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:46:33 +08:00
elseif
97974bd9c6
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:39:29 +08:00
elseif
a99490184d
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:22:55 +08:00
elseif
da97108900
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:06:07 +08:00
elseif
ba72caaead
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 23:28:49 +08:00
elseif
8e8f42831f
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 23:16:16 +08:00
elseif
aea034bcf6
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 22:55:09 +08:00
elseif
a7ddc582b1
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:57:12 +08:00
elseif
cb0310bbcd
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:50:09 +08:00
elseif
38b73158f4
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:42:02 +08:00
elseif
ac9c61924d
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:32:29 +08:00
elseif
073e8811d8
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:24:07 +08:00
elseif
3207b0ccd3
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 11:13:51 +08:00
elseif
65dff5204a
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 11:13:31 +08:00
elseif
10349947e6
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 11:11:16 +08:00
elseif
2e7b31abf5
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 10:55:50 +08:00
elseif
32ef5f3aeb
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 10:53:36 +08:00
elseif
3ba9e027e3
Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 10:46:54 +08:00
elseif
f6fea00c16
Merge pull request #74 from vnxme/system-random
Use SystemRandom to ensure crossplatform compatibility
2024-12-30 11:21:29 +08:00
vnxme
391d019a42 Use random.SystemRandom().randint
instead of toyecc.Random.secure_rand_int_between
2024-12-28 10:57:32 +03:00
elseif
4c6ab80b09
Merge pull request #66 from vnxme/env-consistency-fixes
Env consistency fixes
2024-12-14 12:40:16 +08:00
elseif
098f967cfd
Merge pull request #69 from loskiq/patch-1
Update mikro.py
2024-12-14 12:40:05 +08:00
Amir
e61c981f84
Update mikro.py
Signed-off-by: Amir <loskiq@gmail.com>
2024-12-13 17:23:54 +03:00
vnxme
29663ed175 Fix *_LICENSE_URL spelling 2024-12-12 11:45:40 +03:00
vnxme
fd2baf25a0 Fix MIKRO_NPK_SIGN_PUBLIC_KEY spelling 2024-12-12 11:45:40 +03:00
elseif
f65a65e830
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2024-12-06 03:00:00 +08:00
elseif
dc8118e5ab
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-29 11:30:52 +08:00
elseif
9b4d525aa2
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-29 11:30:14 +08:00
elseif
eef9552fda
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-29 11:03:27 +08:00
elseif
2126382b54
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-29 11:03:24 +08:00
elseif
a9eef1d574
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-29 10:51:22 +08:00
elseif
bd097583eb
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-29 10:50:39 +08:00
elseif
23a853d585
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-19 10:06:44 +08:00
elseif
9b6b8c7e2f
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-15 07:11:44 +08:00
elseif
4c52806821
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-15 06:54:21 +08:00
elseif
136c1ddcd4
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-15 06:50:21 +08:00
elseif
e5fb3ff784
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-15 06:45:54 +08:00
elseif
9dd745578b
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-11-15 06:27:16 +08:00
elseif
9d06111ac5
Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2024-10-20 18:58:03 +08:00
elseif
7fdd5724bb
Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2024-10-20 18:56:38 +08:00
33 changed files with 3471 additions and 997 deletions

246
.github/workflows/build_docker.yml vendored Normal file
View File

@ -0,0 +1,246 @@
name: Build CHR Docker
on:
workflow_dispatch:
inputs:
version:
description: "Specify version (e.g., 7.19.2), empty for latest"
required: false
default: ''
type: string
permissions:
packages: write
jobs:
Prepare:
runs-on: ubuntu-24.04
outputs:
MATRIX: ${{ steps.get_versions.outputs.MATRIX }}
steps:
- name: Get versions and generate matrix
id: get_versions
run: |
VERSION="${{ inputs.version }}"
if [ -n "$VERSION" ]; then
MATRIX=$(jq -c -n '
[
{ "version": $VERSION, "channel": ""}
]
' --arg VERSION "$VERSION")
else
TIMESTAMP=$(date +%s)
LONG_TERM=$(wget -nv -O - "https://upgrade.mikrotik.ltd/routeros/NEWESTa7.long-term?t=$TIMESTAMP" | cut -d ' ' -f1)
STABLE=$(wget -nv -O - "https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable?t=$TIMESTAMP" | cut -d ' ' -f1)
echo "Long-term version: $LONG_TERM"
echo "Stable version: $STABLE"
if [ -z "$LONG_TERM" ]; then
echo "Failed to get long-term version"
exit 1
fi
if [ -z "$STABLE" ]; then
echo "Failed to get stable version"
exit 1
fi
MATRIX=$(jq -c -n '
[
{version: $LONG_TERM, channel: "long-term"},
{version: $STABLE, channel: "stable"}
]
' --arg LONG_TERM "$LONG_TERM" --arg STABLE "$STABLE")
fi
echo "Generated matrix: $MATRIX"
echo "MATRIX=$MATRIX" >> $GITHUB_OUTPUT
Build:
runs-on: ubuntu-24.04
needs: Prepare
strategy:
max-parallel: 1
matrix:
include: ${{ fromJson(needs.Prepare.outputs.MATRIX) }}
env:
TZ: 'Asia/Shanghai'
VERSION: ${{ matrix.version }}
CHANNEL: ${{ matrix.channel }}
IMAGE: "ghcr.io/${{ github.repository_owner }}/chr"
DOCKER_BUILD_SUMMARY: 'false'
DOCKER_BUILD_RECORD_UPLOAD: 'false'
steps:
- name: Show build info
run: |
echo "Version: ${{ env.VERSION }}"
echo "Channel: ${{ env.CHANNEL }}"
- name: Download CHR image
run: |
# x86 架构
mkdir -p "amd64"
wget -nv -O "amd64/chr-$VERSION.img.zip" \
"https://github.com/elseif/MikroTikPatch/releases/download/$VERSION/chr-$VERSION.img.zip"
unzip "amd64/chr-$VERSION.img.zip" -d "amd64/"
rm "amd64/chr-$VERSION.img.zip"
# arm64 架构
mkdir -p "arm64"
wget -nv -O "arm64/chr-$VERSION-arm64.img.zip" \
"https://github.com/elseif/MikroTikPatch/releases/download/$VERSION-arm64/chr-$VERSION-arm64.img.zip"
unzip "arm64/chr-$VERSION-arm64.img.zip" -d "arm64/"
rm "arm64/chr-$VERSION-arm64.img.zip"
ls -la amd64/ arm64/
- name: Create start.sh
run: |
cat <<'EOF' > start.sh
#!/bin/sh
set -e
MEM="${MEM:=512m}"
DISK="${DISK:=128M}"
CPUS="${CPUS:=1}"
bridge_index=0
get_mac_address() {
ifname="$1"
ip link show "$ifname" 2>/dev/null | awk '/link\/ether/ {print $2}'
}
increment_mac() {
mac=$1
mac_no_colons=$(echo "$mac" | tr -d ':' | tr '[:lower:]' '[:upper:]')
mac_dec=$((16#${mac_no_colons}))
mac_dec=$((mac_dec + 1))
mac_hex=$(printf "%012X" "$mac_dec")
echo "$mac_hex" | sed 's/.\{2\}/&:/g;s/:$//'
}
echo "" > /etc/qemu/bridge.conf
qemu="qemu-system-x86_64"
if [[ `uname -m` == "aarch64" ]]; then
qemu="qemu-system-aarch64 -pflash /usr/share/qemu/edk2-aarch64-code.fd -M virt"
fi
if [ -e /dev/kvm ]; then
qemu="$qemu -enable-kvm -cpu host"
else
if [[ `uname -m` == "aarch64" ]]; then
qemu="$qemu -cpu cortex-a57"
fi
fi
img=`ls /diskimage/*.img`
qemu="$qemu -nographic -m $MEM -smp cpus=$CPUS -drive file=$img,format=raw"
truncate -s $DISK $img
while read -r line; do
iface=$(echo "$line" | sed "s/ *\([a-z0-9]*\):.*/\1/")
if [[ "$iface" =~ " " ]]; then
continue
fi
if [[ "$iface" =~ ^br ]]; then
continue
fi
if [[ "$iface" == "lo" ]]; then
continue
fi
if [[ "$iface" == "" ]]; then
continue
fi
echo "iface: $iface"
bridge="br$bridge_index"
ip link add name "$bridge" type bridge
ip link set dev "$bridge" up
ip link set "$iface" master "$bridge"
echo "allow $bridge" >> /etc/qemu/bridge.conf
veth_mac=$(get_mac_address "$iface")
tap_mac=$(increment_mac "$veth_mac")
bridge_index=$((bridge_index + 1))
qemu="$qemu -netdev bridge,id=$bridge,br=$bridge -device virtio-net,netdev=$bridge,mac=$tap_mac "
done < /proc/net/dev
echo "qemu: $qemu"
exec $qemu
EOF
chmod +x start.sh
- name: Create Dockerfile
run: |
cat > Dockerfile << 'EOF'
FROM alpine:latest
ARG TARGETARCH
ARG VERSION
RUN case "$TARGETARCH" in \
amd64) apk add --no-cache qemu-system-x86_64 ;; \
arm64) apk add --no-cache qemu-system-aarch64 ;; \
esac \
&& rm -rf /var/cache/apk/*
RUN mkdir -p /diskimage
COPY ${TARGETARCH}/*.img /diskimage/
COPY start.sh /start.sh
ENTRYPOINT ["/start.sh"]
EOF
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and Push
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.IMAGE }}:${{ env.VERSION }}
build-args: VERSION=${{ env.VERSION }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: false
sbom: false
outputs: >
type=image,
annotation-index.org.opencontainers.image.title=MikroTik CHR RouterOS,
annotation-index.org.opencontainers.image.description=MikroTik CHR (Cloud Hosted Router) running on QEMU with Docker,
annotation-index.org.opencontainers.image.version=${{ env.VERSION }},
annotation-index.org.opencontainers.image.url=https://mikrotik.ltd
- name: Update channel tags
if: env.CHANNEL != ''
run: |
TAGS="-t ${{ env.IMAGE }}:${{ env.CHANNEL }}"
if [ "${{ env.CHANNEL }}" = "stable" ]; then
TAGS="$TAGS -t ${{ env.IMAGE }}:latest"
fi
echo "Creating tags: $TAGS"
docker buildx imagetools create $TAGS ${{ env.IMAGE }}:${{ env.VERSION }}
- name: Save Docker Image as tar
run: |
mkdir -p artifacts
echo "Exporting amd64 tar..."
docker pull --platform linux/amd64 ${{ env.IMAGE }}:${{ env.VERSION }}
docker save ${{ env.IMAGE }}:${{ env.VERSION }} -o artifacts/chr-${{ env.VERSION }}-amd64.tar
echo "Exporting arm64 tar..."
docker pull --platform linux/arm64 ${{ env.IMAGE }}:${{ env.VERSION }}
docker save ${{ env.IMAGE }}:${{ env.VERSION }} -o artifacts/chr-${{ env.VERSION }}-arm64.tar
ls -lh artifacts/
- name: Upload tar artifact
uses: actions/upload-artifact@v7
with:
name: chr-${{ env.VERSION }}-docker-images
path: artifacts/
retention-days: 30

76
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,76 @@
name: Patch Mikrotik RouterOS
on:
schedule:
- cron: "00 10 * * *" # At UTC 10:00 (Beijing 18:00) on every day
workflow_dispatch:
inputs:
version:
description: 'Select Version (v6/v7/all)'
required: true
default: 'all'
type: choice
options:
- all
- v6
- v7
jobs:
Patch_Stable_V7:
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.version == 'all' || github.event.inputs.version == 'v7'))
uses: ./.github/workflows/patch_v7.yml
with:
channel: stable
release: true
secrets: inherit
Patch_LongTerm_V7:
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.version == 'all' || github.event.inputs.version == 'v7'))
uses: ./.github/workflows/patch_v7.yml
with:
channel: long-term
release: true
secrets: inherit
Patch_Testing_V7:
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.version == 'all' || github.event.inputs.version == 'v7'))
uses: ./.github/workflows/patch_v7.yml
with:
channel: testing
release: true
secrets: inherit
Patch_Development_V7:
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.version == 'all' || github.event.inputs.version == 'v7'))
uses: ./.github/workflows/patch_v7.yml
with:
channel: development
release: true
secrets: inherit
Patch_Stable_V6:
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.version == 'all' || github.event.inputs.version == 'v6'))
uses: ./.github/workflows/patch_v6.yml
with:
channel: stable
release: true
secrets: inherit
Patch_LongTerm_V6:
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.version == 'all' || github.event.inputs.version == 'v6'))
uses: ./.github/workflows/patch_v6.yml
with:
channel: long-term
release: true
secrets: inherit

View File

@ -1,273 +0,0 @@
name: Patch Mikrotik RouterOS 6.x
on:
# push:
# branches: [ "main" ]
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: write
env:
CUSTOM_LICENSE_PRIVATE_KEY: ${{ secrets.CUSTOM_LICENSE_PRIVATE_KEY }}
CUSTOM_LICENSE_PUBLIC_KEY: ${{ secrets.CUSTOM_LICENSE_PUBLIC_KEY }}
CUSTOM_NPK_SIGN_PRIVATE_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PRIVATE_KEY }}
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }}
CUSTOM_CLOUD_PUBLIC_KEY: ${{ secrets.CUSTOM_CLOUD_PUBLIC_KEY }}
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }}
MIKRO_NPK_SIGN_PUBLIC_LKEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_LKEY }}
MIKRO_CLOUD_PUBLIC_KEY: ${{ secrets.MIKRO_CLOUD_PUBLIC_KEY }}
MIKRO_LICENCE_URL: ${{ secrets.MIKRO_LICENCE_URL }}
CUSTOM_LICENCE_URL: ${{ secrets.CUSTOM_LICENCE_URL }}
MIKRO_UPGRADE_URL: ${{ secrets.MIKRO_UPGRADE_URL }}
CUSTOM_UPGRADE_URL: ${{ secrets.CUSTOM_UPGRADE_URL }}
MIKRO_RENEW_URL: ${{ secrets.MIKRO_RENEW_URL }}
CUSTOM_RENEW_URL: ${{ secrets.CUSTOM_RENEW_URL }}
MIKRO_CLOUD_URL: ${{ secrets.MIKRO_CLOUD_URL }}
CUSTOM_CLOUD_URL: ${{ secrets.CUSTOM_CLOUD_URL }}
jobs:
Set_BuildTime:
runs-on: ubuntu-22.04
outputs:
BUILD_TIME: ${{ steps.set_buildtime.outputs.BUILD_TIME }}
steps:
- name: Set build time
id: set_buildtime
run: echo "BUILD_TIME=$(date +'%s')" >> $GITHUB_OUTPUT
Patch_RouterOS:
needs: Set_BuildTime
runs-on: ubuntu-22.04
strategy:
matrix:
channel: [long-term,stable]
env:
TZ: 'Asia/Shanghai'
LATEST_VERSION: ""
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Get latest routeros version
id: get_latest
run: |
echo $(uname -a)
LATEST_VERSION=$(wget -nv -O - https://upgrade.mikrotik.com/routeros/NEWEST6.${{ matrix.channel }} | cut -d ' ' -f1)
echo Latest Version:$LATEST_VERSION
if [ "${{ github.event_name }}" == "schedule" ]; then
_LATEST_VERSION=$(wget -nv -O - https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/NEWEST6.${{ matrix.channel }} | cut -d ' ' -f1)
if [ "$_LATEST_VERSION" == "$LATEST_VERSION" ]; then
echo "No new version found"
echo "has_new_version=false" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "has_new_version=true" >> $GITHUB_OUTPUT
BUILD_TIME=${{ needs.Set_BuildTime.outputs.BUILD_TIME }}
echo Build Time:$BUILD_TIME
wget -nv -O CHANGELOG https://${{ env.MIKRO_UPGRADE_URL }}/routeros/$LATEST_VERSION/CHANGELOG
cat CHANGELOG
echo "LATEST_VERSION=${LATEST_VERSION}" >> $GITHUB_ENV
echo "BUILD_TIME=${BUILD_TIME}" >> $GITHUB_ENV
- name: Cache mikrotik-${{ env.LATEST_VERSION }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
id: cache-mikrotik
uses: actions/cache@v4
with:
path: |
mikrotik.iso
key: mikrotik-${{ env.LATEST_VERSION }}
- name: Get mikrotik-${{ env.LATEST_VERSION }}.iso
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-mikrotik.outputs.cache-hit != 'true'
run: |
sudo wget -nv -O mikrotik.iso https://download.mikrotik.com/routeros/$LATEST_VERSION/mikrotik-$LATEST_VERSION.iso
- name: Patch mikrotik-${{ env.LATEST_VERSION }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo apt-get install -y mkisofs xorriso > /dev/null
sudo mkdir ./iso
sudo mount -o loop,ro mikrotik.iso ./iso
sudo mkdir ./new_iso
sudo cp -r ./iso/* ./new_iso/
sudo rsync -a ./iso/ ./new_iso/
sudo umount ./iso
sudo rm -rf ./iso
sudo mv ./new_iso/system-$LATEST_VERSION.npk ./
sudo -E python3 patch.py npk system-$LATEST_VERSION.npk
sudo -E python3 patch.py kernel ./new_iso/isolinux/initrd.rgz
NPK_FILES=$(find ./new_iso/*.npk)
for file in $NPK_FILES; do
sudo -E python3 npk.py sign $file $file
done
sudo cp system-$LATEST_VERSION.npk ./new_iso/
sudo mkisofs -o mikrotik-$LATEST_VERSION.iso \
-V "MikroTik $LATEST_VERSION" \
-sysid "" -preparer "MiKroTiK" \
-publisher "" -A "MiKroTiK RouterOS" \
-input-charset utf-8 \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
-R -J \
./new_iso
sudo mkdir ./all_packages
sudo cp ./new_iso/*.npk ./all_packages/
sudo rm -rf ./new_iso
cd ./all_packages
sudo zip ../all_packages-x86-$LATEST_VERSION.zip *.npk
cd ..
- name: Create install-image-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo modprobe nbd
sudo apt-get update
sudo apt-get install -y qemu-utils extlinux > /dev/null
truncate --size 64M install-image-$LATEST_VERSION.img
sudo qemu-nbd -c /dev/nbd0 -f raw install-image-$LATEST_VERSION.img
sudo mkfs.vfat -n "Install" /dev/nbd0
sudo mkdir ./install
sudo mount /dev/nbd0 ./install
sudo extlinux --install -H 64 -S 32 ./install/
echo -e 'default system\nLABEL system\n\tKERNEL linux\n\tAPPEND load_ramdisk=1 initrd=initrd.rgz -hdd-install' \
> syslinux.cfg
sudo cp syslinux.cfg ./install/
sudo rm syslinux.cfg
NPK_FILES=($(find ./all_packages/*.npk))
for ((i=1; i<=${#NPK_FILES[@]}; i++))
do
echo "${NPK_FILES[$i-1]}=>$i.npk"
sudo cp ${NPK_FILES[$i-1]} ./install/$i.npk
done
sudo touch ./install/CHOOSE
sudo touch ./install/autorun.scr
sudo umount /dev/nbd0
sudo qemu-nbd -d /dev/nbd0
sudo rm -rf ./install
sudo zip install-image-$LATEST_VERSION.zip ./install-image-$LATEST_VERSION.img
sudo rm ./install-image-$LATEST_VERSION.img
- name: Cache chr-${{ env.LATEST_VERSION }}.img.zip
if: steps.get_latest.outputs.has_new_version == 'true'
id: cache-chr-img
uses: actions/cache@v4
with:
path: |
chr.img
key: chr-${{ env.LATEST_VERSION }}.img
- name: Get chr-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-chr-img.outputs.cache-hit != 'true'
run: |
sudo wget -nv -O chr.img.zip https://download.mikrotik.com/routeros/$LATEST_VERSION/chr-$LATEST_VERSION.img.zip
sudo unzip chr.img.zip
sudo rm chr.img.zip
sudo mv chr-$LATEST_VERSION.img chr.img
- name: Create chr-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo mkdir ./chr
sudo cp chr.img chr-$LATEST_VERSION.img
sudo qemu-nbd -c /dev/nbd0 -f raw chr-$LATEST_VERSION.img
sudo -E python3 patch.py block /dev/nbd0p1 boot/initrd.rgz
sudo mount /dev/nbd0p1 ./chr
sudo cp ./all_packages/dude-$LATEST_VERSION.npk ./chr/var/pdb/dude/image
sudo -E python3 patch.py npk ./chr/var/pdb/routeros-x86/image
sudo cp ./chr/var/pdb/routeros-x86/image ./all_packages/routeros-x86-$LATEST_VERSION.npk
sudo cp ./all_packages/routeros-x86-$LATEST_VERSION.npk routeros-x86-$LATEST_VERSION.npk
sudo umount ./chr
sudo qemu-nbd -d /dev/nbd0
sudo rm -rf ./chr
sudo qemu-img convert -f raw -O qcow2 chr-$LATEST_VERSION.img chr-$LATEST_VERSION.qcow2
sudo qemu-img convert -f raw -O vmdk chr-$LATEST_VERSION.img chr-$LATEST_VERSION.vmdk
sudo qemu-img convert -f raw -O vpc chr-$LATEST_VERSION.img chr-$LATEST_VERSION.vhd
sudo qemu-img convert -f raw -O vhdx chr-$LATEST_VERSION.img chr-$LATEST_VERSION.vhdx
sudo qemu-img convert -f raw -O vdi chr-$LATEST_VERSION.img chr-$LATEST_VERSION.vdi
sudo zip chr-$LATEST_VERSION.qcow2.zip chr-$LATEST_VERSION.qcow2
sudo zip chr-$LATEST_VERSION.vmdk.zip chr-$LATEST_VERSION.vmdk
sudo zip chr-$LATEST_VERSION.vhd.zip chr-$LATEST_VERSION.vhd
sudo zip chr-$LATEST_VERSION.vhdx.zip chr-$LATEST_VERSION.vhdx
sudo zip chr-$LATEST_VERSION.vdi.zip chr-$LATEST_VERSION.vdi
sudo zip chr-$LATEST_VERSION.img.zip chr-$LATEST_VERSION.img
sudo rm chr-$LATEST_VERSION$ARCH.qcow2
sudo rm chr-$LATEST_VERSION$ARCH.vmdk
sudo rm chr-$LATEST_VERSION$ARCH.vhd
sudo rm chr-$LATEST_VERSION$ARCH.vhdx
sudo rm chr-$LATEST_VERSION$ARCH.vdi
sudo rm chr-$LATEST_VERSION$ARCH.img
- name: Upload Files
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
mkdir -p ./publish/$LATEST_VERSION
echo $LATEST_VERSION $BUILD_TIME > ./publish/NEWEST6.${{ matrix.channel }}
cp CHANGELOG ./publish/$LATEST_VERSION/
cp ./all_packages/*.npk ./publish/$LATEST_VERSION/
sudo apt-get install -y lftp > /dev/null 2>&1
sudo -E lftp -u ${{ secrets.SSH_USERNAME }},'${{ secrets.SSH_PASSWORD }}' sftp://${{ secrets.SSH_SERVER }}:${{ secrets.SSH_PORT }} <<EOF
set sftp:auto-confirm yes
mirror --reverse --verbose --only-newer ./publish ${{ secrets.SSH_DIRECTORY }}
bye
EOF
- name: Clear Cloudflare cache
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
curl --request POST --url "https://api.cloudflare.com/client/v4/zones/fe6831ed6dc9e6235e69ef2a31f2e7fe/purge_cache" \
--header "Authorization: Bearer 9GDQkzU51QXaqzp1qMjyFKpyeJyOdnNoG9GZQaGP" \
--header "Content-Type:application/json" \
--data '{"purge_everything": true}'
- name: Delete Release tag ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
RELEASE_INFO=$(curl -s -H $HEADER https://api.github.com/repos/${{ github.repository }}/releases/tags/$LATEST_VERSION$ARCH)
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id')
echo "Release ID: $RELEASE_ID"
if [ "$RELEASE_ID" != "null" ]; then
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$LATEST_VERSION$ARCH
echo "Tag $LATEST_VERSION$ARCH deleted successfully."
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID
echo "Release with tag $LATEST_VERSION$ARCH deleted successfully."
else
echo "Release not found for tag: $LATEST_VERSION)"
fi
- name: Create Release tag ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true'
uses: softprops/action-gh-release@v2
with:
name: "RouterOS ${{ env.LATEST_VERSION }}"
body_path: "CHANGELOG"
tag_name: ${{ env.LATEST_VERSION }}
make_latest: false
prerelease: ${{ matrix.channel == 'testing' }}
files: |
mikrotik-${{ env.LATEST_VERSION }}.iso
chr-${{ env.LATEST_VERSION }}*.zip
install-image-${{ env.LATEST_VERSION }}.zip
routeros-x86-${{ env.LATEST_VERSION }}.npk
all_packages-x86-${{ env.LATEST_VERSION }}.zip

View File

@ -1,433 +0,0 @@
name: Patch Mikrotik RouterOS 7.x
on:
# push:
# branches: [ "main" ]
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: write
env:
CUSTOM_LICENSE_PRIVATE_KEY: ${{ secrets.CUSTOM_LICENSE_PRIVATE_KEY }}
CUSTOM_LICENSE_PUBLIC_KEY: ${{ secrets.CUSTOM_LICENSE_PUBLIC_KEY }}
CUSTOM_NPK_SIGN_PRIVATE_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PRIVATE_KEY }}
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }}
CUSTOM_CLOUD_PUBLIC_KEY: ${{ secrets.CUSTOM_CLOUD_PUBLIC_KEY }}
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }}
MIKRO_NPK_SIGN_PUBLIC_LKEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_LKEY }}
MIKRO_CLOUD_PUBLIC_KEY: ${{ secrets.MIKRO_CLOUD_PUBLIC_KEY }}
MIKRO_LICENCE_URL: ${{ secrets.MIKRO_LICENCE_URL }}
CUSTOM_LICENCE_URL: ${{ secrets.CUSTOM_LICENCE_URL }}
MIKRO_UPGRADE_URL: ${{ secrets.MIKRO_UPGRADE_URL }}
CUSTOM_UPGRADE_URL: ${{ secrets.CUSTOM_UPGRADE_URL }}
MIKRO_RENEW_URL: ${{ secrets.MIKRO_RENEW_URL }}
CUSTOM_RENEW_URL: ${{ secrets.CUSTOM_RENEW_URL }}
MIKRO_CLOUD_URL: ${{ secrets.MIKRO_CLOUD_URL }}
CUSTOM_CLOUD_URL: ${{ secrets.CUSTOM_CLOUD_URL }}
jobs:
Set_BuildTime:
runs-on: ubuntu-22.04
outputs:
BUILD_TIME: ${{ steps.set_buildtime.outputs.BUILD_TIME }}
steps:
- name: Set build time
id: set_buildtime
run: echo "BUILD_TIME=$(date +'%s')" >> $GITHUB_OUTPUT
Patch_RouterOS:
needs: Set_BuildTime
runs-on: ubuntu-22.04
strategy:
matrix:
arch: [x86,arm64]
channel: [stable, testing]
env:
TZ: 'Asia/Shanghai'
LATEST_VERSION: ""
ARCH: ""
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Get latest routeros version
id: get_latest
run: |
echo $(uname -a)
LATEST_VERSION=$(wget -nv -O - https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWESTa7.${{ matrix.channel }} | cut -d ' ' -f1)
echo Latest Version:$LATEST_VERSION
if [ "${{ github.event_name }}" == "schedule" ]; then
_LATEST_VERSION=$(wget -nv -O - https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/NEWESTa7.${{ matrix.channel }} | cut -d ' ' -f1)
if [ "$_LATEST_VERSION" == "$LATEST_VERSION" ]; then
echo "No new version found"
echo "has_new_version=false" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "has_new_version=true" >> $GITHUB_OUTPUT
BUILD_TIME=${{ needs.Set_BuildTime.outputs.BUILD_TIME }}
echo Build Time:$BUILD_TIME
wget -nv -O CHANGELOG https://${{ env.MIKRO_UPGRADE_URL }}/routeros/$LATEST_VERSION/CHANGELOG
cat CHANGELOG
echo "LATEST_VERSION=${LATEST_VERSION}" >> $GITHUB_ENV
echo "BUILD_TIME=${BUILD_TIME}" >> $GITHUB_ENV
if [ "${{ matrix.arch }}" == "x86" ]; then
ARCH=''
echo "ARCH=$ARCH" >> $GITHUB_ENV
elif [ "${{ matrix.arch }}" == "arm64" ]; then
ARCH='-arm64'
echo "ARCH=$ARCH" >> $GITHUB_ENV
fi
- name: Cache Squashfs
if: steps.get_latest.outputs.has_new_version == 'true'
id: cache-squashfs
uses: actions/cache@v4
with:
path: |
python3.sfs
option.sfs
key: busybox-python3-squashfs-${{ matrix.arch }}
- name: Create Squashfs for option and python3
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-squashfs.outputs.cache-hit != 'true'
run: |
sudo mkdir -p ./option-root/bin/
if [ "${{ matrix.arch }}" == "x86" ]; then
sudo cp busybox/busybox_x86 ./option-root/bin/busybox
sudo chmod +x ./option-root/bin/busybox
sudo cp keygen/keygen_x86 ./option-root/bin/keygen
sudo chmod +x ./option-root/bin/keygen
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo cp busybox/busybox_aarch64 ./option-root/bin/busybox
sudo chmod +x ./option-root/bin/busybox
sudo cp keygen/keygen_aarch64 ./option-root/bin/keygen
sudo chmod +x ./option-root/bin/keygen
fi
sudo chmod +x ./busybox/busybox_x86
COMMANDS=$(./busybox/busybox_x86 --list)
for cmd in $COMMANDS; do
sudo ln -sf /pckg/option/bin/busybox ./option-root/bin/$cmd
done
sudo mksquashfs option-root option.sfs -quiet -comp xz -no-xattrs -b 256k
sudo rm -rf option-root
if [ "${{ matrix.arch }}" == "x86" ]; then
sudo wget -O cpython-3.11.9.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.11.9+20240713-x86_64-unknown-linux-gnu-install_only.tar.gz
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo wget -O cpython-3.11.9.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.11.9+20240713-aarch64-unknown-linux-gnu-install_only.tar.gz
fi
sudo tar -xf cpython-3.11.9.tar.gz
sudo rm cpython-3.11.9.tar.gz
sudo rm -rf ./python/include
sudo rm -rf ./python/share
sudo mksquashfs python python3.sfs -quiet -comp xz -no-xattrs -b 256k
sudo rm -rf ./python
- name: Cache NetInstall ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
id: cache-netinstall
uses: actions/cache@v4
with:
path: |
netinstall.zip
netinstall.tar.gz
key: netinstall-${{ env.LATEST_VERSION }}
- name: Get netinstall ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && steps.cache-netinstall.outputs.cache-hit != 'true'
run: |
sudo wget -nv -O netinstall.zip https://download.mikrotik.com/routeros/$LATEST_VERSION/netinstall-$LATEST_VERSION.zip
sudo wget -nv -O netinstall.tar.gz https://download.mikrotik.com/routeros/$LATEST_VERSION/netinstall-$LATEST_VERSION.tar.gz
- name: Patch netinstall ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
run: |
sudo unzip netinstall.zip
sudo -E python3 patch.py netinstall netinstall.exe
sudo zip netinstall-$LATEST_VERSION.zip ./netinstall.exe
sudo tar -xvf netinstall.tar.gz
sudo -E python3 patch.py netinstall netinstall-cli
sudo tar -czvf netinstall-$LATEST_VERSION.tar.gz ./netinstall-cli
- name: Cache mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
id: cache-mikrotik
uses: actions/cache@v4
with:
path: |
mikrotik.iso
key: mikrotik-${{ env.LATEST_VERSION }}-${{ matrix.arch }}
- name: Get mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-mikrotik.outputs.cache-hit != 'true'
run: |
sudo wget -nv -O mikrotik.iso https://download.mikrotik.com/routeros/$LATEST_VERSION/mikrotik-$LATEST_VERSION$ARCH.iso
- name: Patch mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo apt-get install -y mkisofs xorriso > /dev/null
sudo mkdir ./iso
sudo mount -o loop,ro mikrotik.iso ./iso
sudo mkdir ./new_iso
sudo cp -r ./iso/* ./new_iso/
sudo rsync -a ./iso/ ./new_iso/
sudo umount ./iso
sudo rm -rf ./iso
sudo mv ./new_iso/routeros-$LATEST_VERSION$ARCH.npk ./
sudo -E python3 patch.py npk routeros-$LATEST_VERSION$ARCH.npk
NPK_FILES=$(find ./new_iso/*.npk)
for file in $NPK_FILES; do
sudo -E python3 npk.py sign $file $file
done
sudo cp routeros-$LATEST_VERSION$ARCH.npk ./new_iso/
sudo -E python3 npk.py create ./new_iso/gps-$LATEST_VERSION$ARCH.npk ./option-$LATEST_VERSION$ARCH.npk option ./option.sfs -desc="busybox"
sudo cp option-$LATEST_VERSION$ARCH.npk ./new_iso/
sudo -E python3 npk.py create ./new_iso/gps-$LATEST_VERSION$ARCH.npk ./python3-$LATEST_VERSION$ARCH.npk python3 ./python3.sfs -desc="python 3.11.9"
sudo cp python3-$LATEST_VERSION$ARCH.npk ./new_iso/
sudo mkdir ./efiboot
sudo mount -o loop ./new_iso/efiboot.img ./efiboot
if [ "${{ matrix.arch }}" == "x86" ]; then
sudo -E python3 patch.py kernel ./efiboot/linux.x86_64
sudo cp ./efiboot/linux.x86_64 ./BOOTX64.EFI
sudo cp ./BOOTX64.EFI ./new_iso/isolinux/linux
sudo umount ./efiboot
sudo mkisofs -o mikrotik-$LATEST_VERSION$ARCH.iso \
-V "MikroTik $LATEST_VERSION ${{ matrix.arch }}" \
-sysid "" -preparer "MiKroTiK" \
-publisher "" -A "MiKroTiK RouterOS" \
-input-charset utf-8 \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
-eltorito-alt-boot \
-e efiboot.img \
-no-emul-boot \
-R -J \
./new_iso
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo -E python3 patch.py kernel ./efiboot/EFI/BOOT/BOOTAA64.EFI
sudo umount ./efiboot
sudo xorriso -as mkisofs -o mikrotik-$LATEST_VERSION$ARCH.iso \
-V "MikroTik $LATEST_VERSION ${{ matrix.arch }}" \
-sysid "" -preparer "MiKroTiK" \
-publisher "" -A "MiKroTiK RouterOS" \
-input-charset utf-8 \
-b efiboot.img \
-no-emul-boot \
-R -J \
./new_iso
fi
sudo rm -rf ./efiboot
sudo mkdir ./all_packages
sudo cp ./new_iso/*.npk ./all_packages/
sudo rm -rf ./new_iso
cd ./all_packages
sudo zip ../all_packages-${{ matrix.arch }}-$LATEST_VERSION.zip *.npk
cd ..
- name: Cache refind
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
id: cache-refind
uses: actions/cache@v4
with:
path: refind-bin-0.14.2.zip
key: refind
- name: Get refind
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && steps.cache-refind.outputs.cache-hit != 'true'
run: sudo wget -nv -O refind-bin-0.14.2.zip https://nchc.dl.sourceforge.net/project/refind/0.14.2/refind-bin-0.14.2.zip
- name: Create install-image-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
run: |
sudo modprobe nbd
sudo apt-get install -y qemu-utils extlinux > /dev/null
truncate --size 128M install-image-$LATEST_VERSION.img
sudo qemu-nbd -c /dev/nbd0 -f raw install-image-$LATEST_VERSION.img
sudo mkfs.vfat -n "Install" /dev/nbd0
sudo mkdir ./install
sudo mount /dev/nbd0 ./install
sudo mkdir -p ./install/EFI/BOOT
sudo unzip refind-bin-0.14.2.zip refind-bin-0.14.2/refind/refind_x64.efi
sudo cp refind-bin-0.14.2/refind/refind_x64.efi ./install/EFI/BOOT/BOOTX64.EFI
sudo rm -rf refind-bin-0.14.2
echo -e 'timeout 0\ntextonly\ntextmode 0\nshowtools shutdown, reboot, exit\nmenuentry "Install RouterOS" {\n\tloader /linux\n\toptions "load_ramdisk=1 root=/dev/ram0 -install -hdd"\n}\ndefault_selection /EFI/BOOT/BOOTX64.EFI' \
> refind.conf
sudo cp refind.conf ./install/EFI/BOOT/
sudo rm refind.conf
sudo extlinux --install -H 64 -S 32 ./install/
echo -e 'default system\nLABEL system\n\tKERNEL linux\n\tAPPEND load_ramdisk=1 -install -hdd' \
> syslinux.cfg
sudo cp syslinux.cfg ./install/
sudo rm syslinux.cfg
sudo cp ./BOOTX64.EFI ./install/linux
NPK_FILES=($(find ./all_packages/*.npk))
for ((i=1; i<=${#NPK_FILES[@]}; i++))
do
echo "${NPK_FILES[$i-1]}=>$i.npk"
sudo cp ${NPK_FILES[$i-1]} ./install/$i.npk
done
sudo touch ./install/CHOOSE
sudo touch ./install/autorun.scr
sudo umount /dev/nbd0
sudo qemu-nbd -d /dev/nbd0
sudo rm -rf ./install
sudo zip install-image-$LATEST_VERSION.zip ./install-image-$LATEST_VERSION.img
sudo rm ./install-image-$LATEST_VERSION.img
- name: Cache chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img.zip
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'arm64'
id: cache-chr-img
uses: actions/cache@v4
with:
path: |
chr.img
key: chr-${{ env.LATEST_VERSION }}-${{ matrix.arch }}.img
- name: Get chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-chr-img.outputs.cache-hit != 'true' && matrix.arch == 'arm64'
run: |
sudo wget -nv -O chr.img.zip https://download.mikrotik.com/routeros/$LATEST_VERSION/chr-$LATEST_VERSION$ARCH.img.zip
sudo unzip chr.img.zip
sudo rm chr.img.zip
sudo mv chr-$LATEST_VERSION$ARCH.img chr.img
- name: Create chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo modprobe nbd
sudo apt-get install -y qemu-utils extlinux > /dev/null
truncate --size 128M chr-$LATEST_VERSION$ARCH.img
sgdisk --clear --set-alignment=2 \
--new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
chr-$LATEST_VERSION$ARCH.img
dd if=chr-$LATEST_VERSION$ARCH.img of=pt.bin bs=1 count=66 skip=446
echo -e "\x80" | dd of=pt.bin bs=1 count=1 conv=notrunc
sgdisk --mbrtogpt --clear --set-alignment=2 \
--new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
chr-$LATEST_VERSION$ARCH.img
dd if=mbr.bin of=chr-$LATEST_VERSION$ARCH.img bs=1 count=446 conv=notrunc
dd if=pt.bin of=chr-$LATEST_VERSION$ARCH.img bs=1 count=66 seek=446 conv=notrunc
sudo qemu-nbd -c /dev/nbd0 -f raw chr-$LATEST_VERSION$ARCH.img
sudo mkfs.vfat -n "Boot" /dev/nbd0p1
sudo mkfs.ext4 -F -L "RouterOS" -m 0 /dev/nbd0p2
sudo mkdir -p ./img/{boot,routeros}
sudo mount /dev/nbd0p1 ./img/boot/
if [ "${{ matrix.arch }}" == "x86" ]; then
sudo mkdir -p ./img/boot/{BOOT,EFI/BOOT}
sudo cp BOOTX64.EFI ./img/boot/EFI/BOOT/BOOTX64.EFI
sudo extlinux --install -H 64 -S 32 ./img/boot/BOOT
echo -e "default system\nlabel system\n\tkernel /EFI/BOOT/BOOTX64.EFI\n\tappend load_ramdisk=1 root=/dev/ram0 quiet" > syslinux.cfg
sudo cp syslinux.cfg ./img/boot/BOOT/
sudo rm syslinux.cfg
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo qemu-nbd -c /dev/nbd1 -f raw chr.img
sudo mkdir -p ./chr/boot
sudo mount /dev/nbd1p1 ./chr/boot/
sudo -E python3 patch.py kernel ./chr/boot/EFI/BOOT/BOOTAA64.EFI -O ./BOOTAA64.EFI
sudo mkdir -p ./img/boot/EFI/BOOT
sudo cp ./BOOTAA64.EFI ./img/boot/EFI/BOOT/BOOTAA64.EFI
sudo umount /dev/nbd1p1
sudo rm -rf ./chr
sudo qemu-nbd -d /dev/nbd1
fi
sudo umount /dev/nbd0p1
sudo mount /dev/nbd0p2 ./img/routeros/
sudo mkdir -p ./img/routeros/{var/pdb/{system,option},boot,rw}
sudo cp ./all_packages/option-$LATEST_VERSION$ARCH.npk ./img/routeros/var/pdb/option/image
sudo cp ./all_packages/routeros-$LATEST_VERSION$ARCH.npk ./img/routeros/var/pdb/system/image
sudo umount /dev/nbd0p2
sudo rm -rf ./img
sudo qemu-nbd -d /dev/nbd0
sudo qemu-img convert -f raw -O qcow2 chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.qcow2
sudo qemu-img convert -f raw -O vmdk chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vmdk
sudo qemu-img convert -f raw -O vpc chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vhd
sudo qemu-img convert -f raw -O vhdx chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vhdx
sudo qemu-img convert -f raw -O vdi chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vdi
sudo zip chr-$LATEST_VERSION$ARCH.qcow2.zip chr-$LATEST_VERSION$ARCH.qcow2
sudo zip chr-$LATEST_VERSION$ARCH.vmdk.zip chr-$LATEST_VERSION$ARCH.vmdk
sudo zip chr-$LATEST_VERSION$ARCH.vhd.zip chr-$LATEST_VERSION$ARCH.vhd
sudo zip chr-$LATEST_VERSION$ARCH.vhdx.zip chr-$LATEST_VERSION$ARCH.vhdx
sudo zip chr-$LATEST_VERSION$ARCH.vdi.zip chr-$LATEST_VERSION$ARCH.vdi
sudo zip chr-$LATEST_VERSION$ARCH.img.zip chr-$LATEST_VERSION$ARCH.img
sudo rm chr-$LATEST_VERSION$ARCH.qcow2
sudo rm chr-$LATEST_VERSION$ARCH.vmdk
sudo rm chr-$LATEST_VERSION$ARCH.vhd
sudo rm chr-$LATEST_VERSION$ARCH.vhdx
sudo rm chr-$LATEST_VERSION$ARCH.vdi
sudo rm chr-$LATEST_VERSION$ARCH.img
- name: Upload Files
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
mkdir -p ./publish/$LATEST_VERSION
echo $LATEST_VERSION $BUILD_TIME > ./publish/NEWESTa7.${{ matrix.channel }}
cp CHANGELOG ./publish/$LATEST_VERSION/
cp ./all_packages/*.npk ./publish/$LATEST_VERSION/
sudo apt-get install -y lftp > /dev/null 2>&1
sudo -E lftp -u ${{ secrets.SSH_USERNAME }},'${{ secrets.SSH_PASSWORD }}' sftp://${{ secrets.SSH_SERVER }}:${{ secrets.SSH_PORT }} <<EOF
set sftp:auto-confirm yes
mirror --reverse --verbose --only-newer ./publish ${{ secrets.SSH_DIRECTORY }}
bye
EOF
- name: Clear Cloudflare cache
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
curl --request POST --url "https://api.cloudflare.com/client/v4/zones/fe6831ed6dc9e6235e69ef2a31f2e7fe/purge_cache" \
--header "Authorization: Bearer 9GDQkzU51QXaqzp1qMjyFKpyeJyOdnNoG9GZQaGP" \
--header "Content-Type:application/json" \
--data '{"purge_everything": true}'
- name: Delete Release tag ${{ env.LATEST_VERSION }} ${{ matrix.arch }}
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
RELEASE_INFO=$(curl -s -H $HEADER https://api.github.com/repos/${{ github.repository }}/releases/tags/$LATEST_VERSION$ARCH)
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id')
echo "Release ID: $RELEASE_ID"
if [ "$RELEASE_ID" != "null" ]; then
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$LATEST_VERSION$ARCH
echo "Tag $LATEST_VERSION$ARCH deleted successfully."
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID
echo "Release with tag $LATEST_VERSION$ARCH deleted successfully."
else
echo "Release not found for tag: $LATEST_VERSION)"
fi
- name: Create Release tag ${{ env.LATEST_VERSION }} ${{ matrix.arch }}
if: steps.get_latest.outputs.has_new_version == 'true'
uses: softprops/action-gh-release@v2
with:
name: "RouterOS ${{ env.LATEST_VERSION }} ${{ matrix.arch }}"
body_path: "CHANGELOG"
tag_name: ${{ env.LATEST_VERSION }}${{ env.ARCH }}
make_latest: ${{ matrix.channel == 'stable'}} && ${{ matrix.arch == 'x86'}}
prerelease: ${{ matrix.channel == 'testing' }}
files: |
mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
chr-${{ env.LATEST_VERSION }}*.zip
netinstall-${{ env.LATEST_VERSION }}.*
install-image-${{ env.LATEST_VERSION }}.zip
routeros-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
option-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
all_packages-*-${{ env.LATEST_VERSION }}.zip
*.EFI

570
.github/workflows/patch_v6.yml vendored Normal file
View File

@ -0,0 +1,570 @@
name: Patch RouterOS v6
on:
workflow_dispatch:
inputs:
channel:
description: 'Channel (stable, long-term)'
required: true
default: 'stable'
type: choice
options:
- stable
- long-term
version:
description: "Specify version (e.g., 6.49.19), empty for latest"
required: false
default: ''
type: string
buildtime:
description: "Custom build time, leave empty for original"
required: false
default: ''
type: string
release:
description: "Release to Upload "
required: false
default: false
type: boolean
workflow_call:
inputs:
channel:
required: true
type: string
version:
required: false
type: string
default: ''
buildtime:
required: false
type: string
default: ''
release:
required: false
type: boolean
default: false
env:
CUSTOM_LICENSE_PRIVATE_KEY: ${{ secrets.CUSTOM_LICENSE_PRIVATE_KEY }}
CUSTOM_LICENSE_PUBLIC_KEY: ${{ secrets.CUSTOM_LICENSE_PUBLIC_KEY }}
CUSTOM_NPK_SIGN_PRIVATE_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PRIVATE_KEY }}
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }}
CUSTOM_CLOUD_PUBLIC_KEY: ${{ secrets.CUSTOM_CLOUD_PUBLIC_KEY }}
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }}
MIKRO_NPK_SIGN_PUBLIC_KEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_KEY }}
MIKRO_CLOUD_PUBLIC_KEY: ${{ secrets.MIKRO_CLOUD_PUBLIC_KEY }}
MIKRO_LICENCE_URL: ${{ secrets.MIKRO_LICENCE_URL }}
CUSTOM_LICENCE_URL: ${{ secrets.CUSTOM_LICENCE_URL }}
MIKRO_UPGRADE_URL: ${{ secrets.MIKRO_UPGRADE_URL }}
CUSTOM_UPGRADE_URL: ${{ secrets.CUSTOM_UPGRADE_URL }}
MIKRO_RENEW_URL: ${{ secrets.MIKRO_RENEW_URL }}
CUSTOM_RENEW_URL: ${{ secrets.CUSTOM_RENEW_URL }}
MIKRO_CLOUD_URL: ${{ secrets.MIKRO_CLOUD_URL }}
CUSTOM_CLOUD_URL: ${{ secrets.CUSTOM_CLOUD_URL }}
jobs:
Prepare_Patch:
runs-on: ubuntu-24.04
outputs:
VERSION: ${{ steps.prepare.outputs.VERSION }}
BUILD_TIME: ${{ steps.prepare.outputs.BUILD_TIME }}
ARCHS: ${{ steps.prepare.outputs.ARCHS }}
RELEASE: ${{ steps.prepare.outputs.RELEASE }}
SKIP_BUILD: ${{ steps.prepare.outputs.SKIP_BUILD }}
steps:
- name: Prepare Patch for ${{ inputs.channel }}
id: prepare
run: |
CHANNEL="${{ inputs.channel }}"
VERSION="${{ inputs.version }}"
RELEASE="${{ inputs.release }}"
BUILD_TIME="${{ inputs.buildtime }}"
ARCHS='["x86"]'
SKIP_BUILD=false
if [ -z "$VERSION" ]; then
echo "Checking latest version from $CHANNEL channel..."
TIMESTAMP=$(date +%s)
NEWEST=$(wget -nv -O - "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWEST6.$CHANNEL?t=$TIMESTAMP")
VERSION=$(echo "$NEWEST" | cut -d ' ' -f1)
echo "Official version: $VERSION"
if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then
PATCH_NEWEST=$(wget -nv -O - "https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/NEWEST6.$CHANNEL?t=$TIMESTAMP" 2>/dev/null || echo "")
PATCH_VERSION=$(echo "$PATCH_NEWEST" | cut -d ' ' -f1)
echo "Patch version: ${PATCH_VERSION:-none}"
if [ "$PATCH_VERSION" == "$VERSION" ]; then
echo "No new version for $CHANNEL, skipping..."
SKIP_BUILD=true
else
echo "New version $VERSION available, building..."
fi
fi
fi
echo "SKIP_BUILD=$SKIP_BUILD" >> $GITHUB_OUTPUT
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "BUILD_TIME=$BUILD_TIME" >> $GITHUB_OUTPUT
echo "ARCHS=$ARCHS" >> $GITHUB_OUTPUT
echo "RELEASE=$RELEASE" >> $GITHUB_OUTPUT
echo "Version: $VERSION, Archs: $ARCHS"
Patch_RouterOS:
needs: Prepare_Patch
if: needs.Prepare_Patch.outputs.SKIP_BUILD == 'false'
runs-on: ubuntu-24.04
strategy:
matrix:
arch: ${{ fromJson(needs.Prepare_Patch.outputs.ARCHS) }}
fail-fast: false
env:
TZ: 'Asia/Shanghai'
CHANNEL: ${{ inputs.channel }}
ARCH: ${{ matrix.arch }}
VERSION: ${{ needs.Prepare_Patch.outputs.VERSION }}
BUILD_TIME: ${{ needs.Prepare_Patch.outputs.BUILD_TIME }}
BUILD_DIR: "/tmp/build/${{ needs.Prepare_Patch.outputs.VERSION }}"
PUBLISH_DIR: "${{ github.workspace }}/publish/${{ needs.Prepare_Patch.outputs.VERSION }}"
ARCH_EXT: ""
PACKAGES: ""
steps:
- uses: actions/checkout@v6
- name: Setup environment
run: |
sudo apt-get update > /dev/null
sudo apt-get install -y zip unzip squashfs-tools jq > /dev/null
sudo apt-get install -y python3-crcmod python3-pefile python3-pyelftools > /dev/null
if [ "$ARCH" == "x86" ]; then
ARCH_EXT=""
else
ARCH_EXT="-$ARCH"
fi
echo "ARCH_EXT=$ARCH_EXT" >> $GITHUB_ENV
echo "ARCH_EXT: $ARCH_EXT"
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
echo "BUILD_DIR: $BUILD_DIR"
rm -rf "$PUBLISH_DIR"
mkdir -p "$PUBLISH_DIR"
echo "PUBLISH_DIR: $PUBLISH_DIR"
wget -nv -O $PUBLISH_DIR/CHANGELOG "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/$VERSION/CHANGELOG"
cat $PUBLISH_DIR/CHANGELOG
- name: Create Squashfs for Option Package
run: |
echo "Creating Option Package Squashfs for channel: $CHANNEL, arch: $ARCH"
mkdir -p $BUILD_DIR/option-root/bin/
cp ./busybox/busybox_$ARCH $BUILD_DIR/option-root/bin/busybox
chmod +x $BUILD_DIR/option-root/bin/busybox
cp ./keygen/keygen_$ARCH $BUILD_DIR/option-root/bin/keygen
chmod +x $BUILD_DIR/option-root/bin/keygen
chmod +x ./busybox/busybox_x86
COMMANDS=$(./busybox/busybox_x86 --list)
for cmd in $COMMANDS; do
ln -sf /pckg/option/bin/busybox $BUILD_DIR/option-root/bin/$cmd
done
mkdir -p $BUILD_DIR/option-root/home/web/webfig/
BUSYBOX=$(./busybox/busybox_x86 | head -1 | cut -d' ' -f2)
tee $BUILD_DIR/option.jg > /dev/null << EOF
[
{
name:'Patch About',
title:'Patch About',
icon: 30,
prio: 2000,
c:[
{
title:'Patch About',
type:'item',
path:[24, 2],
autorefresh: 1000,
ro: 1,
c: [
{type:'gridcell'},
{name:'Patch',type:'tab',inline: 1},
{name:'Author',type:'string',def:'elseif@live.cn'},
{name:'GitHub',type:'string',def:'https://github.com/elseif/MikroTikPatch', url:1},
{name:'Telegram',type:'string',def:'https://telegram.me/mikrotikpatch', url:1},
{name:'Homepage',type:'string',def:'https://mikrotik.ltd', url:1},
{name:'Busybox',type:'string',def:'$BUSYBOX'},
{name:'System',type:'tab',inline: 1},
{name:'Uptime',type:'interval',id:'u1'},
{name:'Architecture Name',type:'string',id:'s1a'},
{name:'Board Name',type:'string',id:'s2c'},
{name:'Version',type:'version',id:'s16'},
{name:'Build Time',type:'string',id:'s27'},
{type:'separator'},
{type:'toolbar',
c: [
{name:'License',type:'contextmenu',autostart: 1,group:'System',link: [],open:'License',tab:'License'},
{name:'Check For Updates',type:'contextmenu',autostart: 1,group:'System',link: [],open:'Packages',tab:'Check For Updates'}
]
}
]
}
]
}
]
EOF
HASH=$(python3 -E <<EOF
import hashlib, crcmod, sys
with open('$BUILD_DIR/option.jg','rb') as f:
data = f.read()
sha1 = hashlib.sha1(data).hexdigest()[:12]
crc = crcmod.mkCrcFun(0x104C11DB7, initCrc=0xFFFFFFFF, rev=False, xorOut=0xFFFFFFFF)(data)
print(f"{sha1} {crc}")
EOF
)
SHA1_PREFIX=$(echo "$HASH" | awk '{print $1}')
CRC=$(echo "$HASH" | awk '{print $2}')
mv $BUILD_DIR/option.jg $BUILD_DIR/option-$SHA1_PREFIX.jg
python3 -E << EOF
import gzip
gzip.open('$BUILD_DIR/option-$SHA1_PREFIX.jg.gz', 'wb', compresslevel=0).write(open('$BUILD_DIR/option-$SHA1_PREFIX.jg', 'rb').read())
EOF
SIZE=$(stat -c %s $BUILD_DIR/option-$SHA1_PREFIX.jg.gz)
mv $BUILD_DIR/option-$SHA1_PREFIX.jg.gz $BUILD_DIR/option-root/home/web/webfig/
tee $BUILD_DIR/option-root/home/web/webfig/option.info > /dev/null << EOF
{ crc: $CRC, size: $SIZE, name: "option.jg", unique: "option-$SHA1_PREFIX.jg",version: "$VERSION" },
EOF
ln -sf option-$SHA1_PREFIX.jg.gz $BUILD_DIR/option-root/home/web/webfig/option.jg.gz
mksquashfs $BUILD_DIR/option-root $BUILD_DIR/option.sfs -no-recovery -noappend -exit-on-error -quiet -comp xz -no-xattrs -b 256k -all-root
rm -rf $BUILD_DIR/option-root
echo "✅ Option Package Squashfs created: $BUILD_DIR/option.sfs"
ls -la "$BUILD_DIR/option.sfs"
- name: Cache mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
id: cache-mikrotik-iso
uses: actions/cache@v5
with:
path: ${{ env.BUILD_DIR }}/mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
key: mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
- name: Get mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
if: steps.cache-mikrotik-iso.outputs.cache-hit != 'true'
run: |
wget -nv -O $BUILD_DIR/mikrotik-$VERSION$ARCH_EXT.iso \
https://download.mikrotik.com/routeros/$VERSION/mikrotik-$VERSION$ARCH_EXT.iso
- name: Unpack ISO
run: |
mkdir -p $BUILD_DIR/all_packages
mkdir -p $BUILD_DIR/iso
mkdir -p $BUILD_DIR/new_iso
sudo mount -o loop,ro $BUILD_DIR/mikrotik-$VERSION$ARCH_EXT.iso $BUILD_DIR/iso
cp -r $BUILD_DIR/iso/* $BUILD_DIR/new_iso/
chmod -R +w $BUILD_DIR/new_iso/
mv $BUILD_DIR/new_iso/*.npk $BUILD_DIR/all_packages/
mv $BUILD_DIR/all_packages/system-$VERSION$ARCH_EXT.npk $BUILD_DIR/
sudo umount $BUILD_DIR/iso
rm -rf $BUILD_DIR/iso
- name: Cache routeros-${{ env.ARCH }}-${{ env.VERSION }}.npk
id: cache-routeros-npk
uses: actions/cache@v5
with:
path: ${{ env.BUILD_DIR }}/routeros-${{ env.ARCH }}-${{ env.VERSION }}.npk
key: routeros-${{ env.ARCH }}-${{ env.VERSION }}.npk
- name: Get routeros-${{ env.ARCH }}-${{ env.VERSION }}.npk
if: steps.cache-routeros-npk.outputs.cache-hit != 'true'
run: |
wget -nv -O $BUILD_DIR/routeros-$ARCH-$VERSION.npk \
https://download.mikrotik.com/routeros/$VERSION/routeros-$ARCH-$VERSION.npk
- name: Get loader patch files
run: |
wget -nv -O loader.7z https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/loader.7z
sudo apt-get install -y p7zip-full > /dev/null
7z x -p"${{ secrets.LOADER_7Z_PASSWORD }}" loader.7z >> /dev/null
rm loader.7z
- name: Patch NPK files
run: |
sudo apt-get install build-essential -y > /dev/null
chmod +w $BUILD_DIR/all_packages/*.npk
NPK_FILES=$(find $BUILD_DIR/all_packages/*.npk)
for file in $NPK_FILES; do
python3 npk.py sign $file $file
done
OPTION_FROM_NPK=gps-$VERSION$ARCH_EXT.npk
OPTION_NPK=option-$VERSION$ARCH_EXT.npk
python3 npk.py create $BUILD_DIR/all_packages/$OPTION_FROM_NPK $BUILD_DIR/all_packages/$OPTION_NPK option $BUILD_DIR/option.sfs -desc="busybox"
rm -rf $BUILD_DIR/option.sfs
python3 patch.py npk $BUILD_DIR/system-$VERSION$ARCH_EXT.npk -O $BUILD_DIR/all_packages/system-$VERSION$ARCH_EXT.npk
(cd $BUILD_DIR/all_packages && zip -r $PUBLISH_DIR/all_packages-$ARCH-$VERSION.zip .)
mv $BUILD_DIR/all_packages/*.npk $PUBLISH_DIR/
python3 patch.py npk $BUILD_DIR/routeros-$ARCH-$VERSION.npk -O $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk
echo "✅ all package npk files are signed and patched: "
ls -la $PUBLISH_DIR/*.npk
- name: Create ISO & INSTALL image files
run: |
set -e
echo "Creating ISO..."
sudo modprobe nbd
sudo apt-get install syslinux syslinux-common xorriso extlinux qemu-utils -y > /dev/null
unzip $PUBLISH_DIR/all_packages-$ARCH-$VERSION.zip -d $BUILD_DIR/new_iso/
mv $BUILD_DIR/new_iso/isolinux/initrd.rgz $BUILD_DIR/initrd.rgz
python3 patch.py kernel $BUILD_DIR/initrd.rgz -O $BUILD_DIR/new_iso/isolinux/initrd.rgz
rm -f $BUILD_DIR/initrd.rgz
xorriso -as mkisofs -o $PUBLISH_DIR/mikrotik-$VERSION$ARCH_EXT.iso \
-V "MikroTik $VERSION" \
-sysid "" -preparer "MiKroTiK" \
-publisher "" -A "MiKroTiK RouterOS" \
-input-charset utf-8 \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
-R -J \
$BUILD_DIR/new_iso
echo "✅ ISO created: "
ls -la $PUBLISH_DIR/mikrotik-$VERSION$ARCH_EXT.iso
echo "Creating install image..."
truncate --size 128M $BUILD_DIR/install-image-$VERSION.img
mkfs.vfat -I -F 32 -n "Install" $BUILD_DIR/install-image-$VERSION.img
LOOP=$(sudo losetup -f --show $BUILD_DIR/install-image-$VERSION.img)
sudo syslinux --install $LOOP
mkdir -p $BUILD_DIR/install
sudo mount -o loop,uid=$(id -u),gid=$(id -g),umask=000 $LOOP $BUILD_DIR/install
cp -vf $BUILD_DIR/new_iso/isolinux/linux $BUILD_DIR/install/
cp -vf /usr/lib/syslinux/modules/bios/ldlinux.c32 $BUILD_DIR/install/
cp -vf $BUILD_DIR/new_iso/isolinux/initrd.rgz $BUILD_DIR/install/initrd.rgz
tee $BUILD_DIR/install/syslinux.cfg > /dev/null << 'EOF'
default system
LABEL system
KERNEL linux
APPEND load_ramdisk=1 initrd=initrd.rgz -hdd-install
EOF
NPK_FILES=($(find $BUILD_DIR/new_iso/*.npk))
for ((i=1; i<=${#NPK_FILES[@]}; i++))
do
echo "${NPK_FILES[$i-1]}=>$i.npk"
cp ${NPK_FILES[$i-1]} $BUILD_DIR/install/$i.npk
done
touch $BUILD_DIR/install/CHOOSE
sync
sudo umount $BUILD_DIR/install
sudo losetup -d $LOOP
rm -rf $BUILD_DIR/install
echo "✅ install imgage created: "
ls -la $BUILD_DIR/install-image-$VERSION.img
(cd $BUILD_DIR && zip $PUBLISH_DIR/install-image-$VERSION.zip install-image-$VERSION.img)
rm $BUILD_DIR/install-image-$VERSION.img
rm -rf $BUILD_DIR/new_iso
- name: Create CHR image files
run: |
set -e
echo "Creating CHR image..."
mkdir -p $BUILD_DIR/chr
truncate --size 128M $BUILD_DIR/chr-$VERSION$ARCH_EXT.img
sfdisk $BUILD_DIR/chr-$VERSION$ARCH_EXT.img <<EOF
label: dos
unit: sectors
start=1, size=, type=83, bootable
EOF
dd if=$BUILD_DIR/chr-$VERSION$ARCH_EXT.img of=$BUILD_DIR/mbr.bin bs=1 count=512 conv=notrunc
echo "FA31C08ED0BC007C89E65007501FFBFCBF0006B90001F2A5EA1D060000BEBE07B304803C807423803C00750983C610FECB75EFCD18BE9B06AC3C00740B56BB0700B40ECD105EEBF0EBFE8B148B4C0289F5BF0500BB007CB8010257CD135F730C31C0CD134F75EDBE7C06EBCCBFFE7D813D55AA75C289EEEA007C00004572726F72206C6F6164696E67206F7065726174696E672073797374656D004D697373696E67206F7065726174696E672073797374656D0000000000" | xxd -r -p | sudo dd of=$BUILD_DIR/mbr.bin bs=1 count=440 conv=notrunc
printf '\x01' | sudo dd of=$BUILD_DIR/mbr.bin bs=1 count=1 seek=336 conv=notrunc
dd if=$BUILD_DIR/mbr.bin of=$BUILD_DIR/chr-$VERSION$ARCH_EXT.img bs=1 count=512 conv=notrunc
sudo qemu-nbd -d /dev/nbd0
sudo qemu-nbd -c /dev/nbd0 -f raw $BUILD_DIR/chr-$VERSION$ARCH_EXT.img
sudo partprobe /dev/nbd0
sudo mkfs.ext3 -F -L "system" -m 0 /dev/nbd0p1
sudo mount /dev/nbd0p1 $BUILD_DIR/chr
sudo mkdir -p $BUILD_DIR/chr/{boot,bin,rw/disk,var/pdb/{routeros-x86,option,dude}}
mkdir -p $BUILD_DIR/{boot,bin}
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk bin/milo $BUILD_DIR/bin/milo
chmod +x $BUILD_DIR/bin/milo
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk boot/vmlinuz-64 $BUILD_DIR/boot/vmlinuz-64
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk boot/initrd.rgz $BUILD_DIR/boot/initrd.rgz
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk boot/map $BUILD_DIR/boot/map
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk boot/milo.conf $BUILD_DIR/boot/milo.conf
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk boot/vmlinuz $BUILD_DIR/boot/vmlinuz
python3 npk.py extract $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk boot/vmlinuz-smp $BUILD_DIR/boot/vmlinuz-smp
sudo mv $BUILD_DIR/boot $BUILD_DIR/chr/
sudo mv $BUILD_DIR/bin $BUILD_DIR/chr/
sudo cp $PUBLISH_DIR/routeros-$ARCH-$VERSION.npk $BUILD_DIR/chr/var/pdb/routeros-x86/image
sudo cp $PUBLISH_DIR/option-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/var/pdb/option/image
sudo cp $PUBLISH_DIR/dude-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/var/pdb/dude/image
echo -e '#!/bin/sh\n# This script will be executed *before* RouterOS *loader* start.\n# You can put your own initialization stuff in here\n' |sudo tee $BUILD_DIR/chr/rw/disk/rc.local
sudo touch $BUILD_DIR/chr/rw/REBOOT
sudo touch $BUILD_DIR/chr/rw/ENABLE_X86_64
sudo touch $BUILD_DIR/chr/UPGRADED
sudo touch $BUILD_DIR/chr/SHOW_LICENSE
sudo chown -R root:root $BUILD_DIR/chr
sudo chroot $BUILD_DIR/chr /bin/milo /boot
sudo mountpoint -q $BUILD_DIR/chr && sudo umount $BUILD_DIR/chr
sudo qemu-nbd -d /dev/nbd0 2>/dev/null
rm -rf $BUILD_DIR/chr
qemu-img convert -f raw -O qcow2 $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.qcow2
qemu-img convert -f raw -O vmdk $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vmdk
qemu-img convert -f raw -O vpc $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vhd
qemu-img convert -f raw -O vhdx $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vhdx
qemu-img convert -f raw -O vdi $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vdi
echo "✅ CHR created: "
ls -la $BUILD_DIR/chr-$VERSION$ARCH_EXT.*
(cd $BUILD_DIR && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.qcow2.zip chr-$VERSION$ARCH_EXT.qcow2 && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vmdk.zip chr-$VERSION$ARCH_EXT.vmdk && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vhd.zip chr-$VERSION$ARCH_EXT.vhd && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vhdx.zip chr-$VERSION$ARCH_EXT.vhdx && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vdi.zip chr-$VERSION$ARCH_EXT.vdi &&
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.img.zip chr-$VERSION$ARCH_EXT.img )
rm $BUILD_DIR/chr-$VERSION$ARCH_EXT.*
- name: Upload Files as Artifact (No Release)
if: needs.Prepare_Patch.outputs.RELEASE == 'false'
uses: actions/upload-artifact@v7
with:
name: mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}
path: |
${{ env.PUBLISH_DIR }}/*.zip
${{ env.PUBLISH_DIR }}/*.iso
- name: Generate SHA256 checksum files
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
(
cd $PUBLISH_DIR
for file in *.zip *.iso *.npk; do
if [ -f "$file" ]; then
sha256sum "$file" > "$file.sha256"
cat "$file.sha256"
fi
done
)
- name: Delete Release tag ${{ env.VERSION }}${{ env.ARCH_EXT }}
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
sed -i "1i Build Time:$BUILD_TIME" $PUBLISH_DIR/CHANGELOG
HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
RELEASE_INFO=$(curl -s -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/tags/$VERSION$ARCH_EXT)
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id')
echo "Release ID: $RELEASE_ID"
if [ "$RELEASE_ID" != "null" ]; then
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$VERSION$ARCH_EXT
echo "Tag $VERSION$ARCH_EXT deleted successfully."
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID
echo "Release with tag $VERSION$ARCH_EXT deleted successfully."
# Wait for a moment to ensure the deletion is complete
sleep 3
else
echo "Release not found for tag: $VERSION$ARCH_EXT)"
fi
- name: Create Release tag ${{ env.VERSION }}${{ env.ARCH_EXT }}
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
uses: softprops/action-gh-release@v3
with:
name: "RouterOS ${{ env.VERSION }}${{ env.ARCH_EXT }}"
repository: ${{ github.repository }}
token: ${{ secrets.GITHUB_TOKEN }}
body_path: ${{env.PUBLISH_DIR}}/CHANGELOG
tag_name: ${{ env.VERSION }}${{ env.ARCH_EXT }}
make_latest: false
prerelease: false
files: |
${{env.PUBLISH_DIR}}/*.zip
${{env.PUBLISH_DIR}}/*.iso
${{env.PUBLISH_DIR}}/routeros*.npk
${{env.PUBLISH_DIR}}/*.zip.sha256
${{env.PUBLISH_DIR}}/*.iso.sha256
${{env.PUBLISH_DIR}}/routeros*.npk.sha256
- name: Upload Files
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
sudo apt-get install -y knockd lftp > /dev/null
knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }}
echo "Knocking on server..."
for i in 1 2 3; do
if knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }} ; then
echo "Knock successful"
break
else
echo "Knock attempt $i failed, retrying..."
sleep 2
fi
done
echo "Waiting for firewall to open SSH port..."
sleep 3
rm -f $PUBLISH_DIR/*.zip
rm -f $PUBLISH_DIR/*.iso
rm -f $PUBLISH_DIR/*.zip.sha256
rm -f $PUBLISH_DIR/*.iso.sha256
sed -i '1d' $PUBLISH_DIR/CHANGELOG
echo $VERSION $BUILD_TIME | tee $PUBLISH_DIR/info
touch $PUBLISH_DIR/$CHANNEL
REMOTE_PATH=${{ secrets.SSH_DIRECTORY }}/$VERSION
SERVER=${{ secrets.SSH_SERVER }}
USER=${{ secrets.SSH_USERNAME }}
PASS=${{ secrets.SSH_PASSWORD }}
PORT=${{ secrets.SSH_PORT }}
lftp -u "$USER","$PASS" sftp://$SERVER:$PORT <<EOF
set sftp:auto-confirm yes
mirror --reverse --verbose --only-newer $PUBLISH_DIR "$REMOTE_PATH"
bye
EOF
- name: Clear Cloudflare cache
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
curl --request POST --url "https://api.cloudflare.com/client/v4/zones/fe6831ed6dc9e6235e69ef2a31f2e7fe/purge_cache" \
--header "Authorization: Bearer 9GDQkzU51QXaqzp1qMjyFKpyeJyOdnNoG9GZQaGP" \
--header "Content-Type:application/json" \
--data '{"purge_everything": true}'
Update_Release:
needs: [Prepare_Patch,Patch_RouterOS]
if: |
needs.Prepare_Patch.outputs.SKIP_BUILD == 'false' &&
needs.Patch_RouterOS.result == 'success' &&
needs.Prepare_Patch.outputs.RELEASE == 'true'
runs-on: ubuntu-24.04
env:
CHANNEL: ${{ inputs.channel }}
VERSION: ${{ needs.Prepare_Patch.outputs.VERSION }}
steps:
- name: Update Release
run: |
sudo apt-get install -y knockd lftp > /dev/null
knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }}
echo "Knocking on server..."
for i in 1 2 3; do
if knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }} ; then
echo "Knock successful"
break
else
echo "Knock attempt $i failed, retrying..."
sleep 2
fi
done
echo "Waiting for firewall to open SSH port..."
sleep 3
REMOTE_PATH=${{ secrets.SSH_DIRECTORY }}
SERVER=${{ secrets.SSH_SERVER }}
USER=${{ secrets.SSH_USERNAME }}
PASS=${{ secrets.SSH_PASSWORD }}
PORT=${{ secrets.SSH_PORT }}
sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no -p $PORT $USER@$SERVER \
"ln -sf /root/$REMOTE_PATH/$VERSION/info /rw/disk/$REMOTE_PATH/NEWEST6.$CHANNEL; \
ln -sf /root/$REMOTE_PATH/$VERSION/info /rw/disk/$REMOTE_PATH/NEWESTa6.$CHANNEL; \
chown -R 32768:32768 /rw/disk/$REMOTE_PATH"
echo "✅ Updated release info."

993
.github/workflows/patch_v7.yml vendored Normal file
View File

@ -0,0 +1,993 @@
name: Patch RouterOS v7
on:
workflow_dispatch:
inputs:
channel:
description: 'Channel (stable, long-term, testing, development)'
required: true
default: 'stable'
type: choice
options:
- stable
- long-term
- testing
- development
arch:
description: 'Architecture (x86, arm64)'
required: true
default: 'all'
type: choice
options:
- all
- x86
- arm64
version:
description: "Specify version (e.g., 7.19.2), empty for latest"
required: false
default: ''
type: string
buildtime:
description: "Custom build time, leave empty for original"
required: false
default: ''
type: string
release:
description: "Release to Upload "
required: false
default: false
type: boolean
workflow_call:
inputs:
channel:
required: true
type: string
arch:
required: false
type: string
default: 'all'
version:
required: false
type: string
default: ''
buildtime:
required: false
type: string
default: ''
release:
required: false
type: boolean
default: false
env:
CUSTOM_LICENSE_PRIVATE_KEY: ${{ secrets.CUSTOM_LICENSE_PRIVATE_KEY }}
CUSTOM_LICENSE_PUBLIC_KEY: ${{ secrets.CUSTOM_LICENSE_PUBLIC_KEY }}
CUSTOM_NPK_SIGN_PRIVATE_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PRIVATE_KEY }}
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }}
CUSTOM_CLOUD_PUBLIC_KEY: ${{ secrets.CUSTOM_CLOUD_PUBLIC_KEY }}
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }}
MIKRO_NPK_SIGN_PUBLIC_KEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_KEY }}
MIKRO_CLOUD_PUBLIC_KEY: ${{ secrets.MIKRO_CLOUD_PUBLIC_KEY }}
MIKRO_LICENCE_URL: ${{ secrets.MIKRO_LICENCE_URL }}
CUSTOM_LICENCE_URL: ${{ secrets.CUSTOM_LICENCE_URL }}
MIKRO_UPGRADE_URL: ${{ secrets.MIKRO_UPGRADE_URL }}
CUSTOM_UPGRADE_URL: ${{ secrets.CUSTOM_UPGRADE_URL }}
MIKRO_RENEW_URL: ${{ secrets.MIKRO_RENEW_URL }}
CUSTOM_RENEW_URL: ${{ secrets.CUSTOM_RENEW_URL }}
MIKRO_CLOUD_URL: ${{ secrets.MIKRO_CLOUD_URL }}
CUSTOM_CLOUD_URL: ${{ secrets.CUSTOM_CLOUD_URL }}
jobs:
Prepare_Patch:
runs-on: ubuntu-24.04
outputs:
VERSION: ${{ steps.prepare.outputs.VERSION }}
BUILD_TIME: ${{ steps.prepare.outputs.BUILD_TIME }}
ARCHS: ${{ steps.prepare.outputs.ARCHS }}
RELEASE: ${{ steps.prepare.outputs.RELEASE }}
SKIP_BUILD: ${{ steps.prepare.outputs.SKIP_BUILD }}
steps:
- name: Prepare Patch for ${{ inputs.channel }}
id: prepare
run: |
CHANNEL="${{ inputs.channel }}"
ARCH_INPUT="${{ inputs.arch }}"
VERSION="${{ inputs.version }}"
RELEASE="${{ inputs.release }}"
BUILD_TIME="${{ inputs.buildtime }}"
if [ "$ARCH_INPUT" == "all" ]; then
ARCHS='["x86", "arm64"]'
else
ARCHS=$(jq -c -n --arg arch "$ARCH_INPUT" '[$arch]')
fi
SKIP_BUILD=false
if [ -z "$VERSION" ]; then
echo "Checking latest version from $CHANNEL channel..."
TIMESTAMP=$(date +%s)
NEWEST=$(wget -nv -O - "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWESTa7.$CHANNEL?t=$TIMESTAMP")
VERSION=$(echo "$NEWEST" | cut -d ' ' -f1)
echo "Official version: $VERSION"
if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then
PATCH_NEWEST=$(wget -nv -O - "https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/NEWESTa7.$CHANNEL?t=$TIMESTAMP" 2>/dev/null || echo "")
PATCH_VERSION=$(echo "$PATCH_NEWEST" | cut -d ' ' -f1)
echo "Patch version: ${PATCH_VERSION:-none}"
if [ "$PATCH_VERSION" == "$VERSION" ]; then
echo "No new version for $CHANNEL, skipping..."
SKIP_BUILD=true
else
echo "New version $VERSION available, building..."
fi
fi
fi
echo "SKIP_BUILD=$SKIP_BUILD" >> $GITHUB_OUTPUT
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "BUILD_TIME=$BUILD_TIME" >> $GITHUB_OUTPUT
echo "ARCHS=$ARCHS" >> $GITHUB_OUTPUT
echo "RELEASE=$RELEASE" >> $GITHUB_OUTPUT
echo "Version: $VERSION, Archs: $ARCHS"
Patch_RouterOS:
needs: Prepare_Patch
if: needs.Prepare_Patch.outputs.SKIP_BUILD == 'false'
runs-on: ubuntu-24.04
strategy:
matrix:
arch: ${{ fromJson(needs.Prepare_Patch.outputs.ARCHS) }}
fail-fast: false
env:
TZ: 'Asia/Shanghai'
CHANNEL: ${{ inputs.channel }}
ARCH: ${{ matrix.arch }}
VERSION: ${{ needs.Prepare_Patch.outputs.VERSION }}
BUILD_TIME: ${{ needs.Prepare_Patch.outputs.BUILD_TIME }}
BUILD_DIR: "/tmp/build/${{ needs.Prepare_Patch.outputs.VERSION }}"
PUBLISH_DIR: "${{ github.workspace }}/publish/${{ needs.Prepare_Patch.outputs.VERSION }}"
ARCH_EXT: ""
PACKAGES: ""
steps:
- uses: actions/checkout@v6
- name: Setup environment
run: |
sudo apt-get update > /dev/null
sudo apt-get install -y zip unzip squashfs-tools jq > /dev/null
sudo apt-get install -y python3-crcmod python3-pefile python3-pyelftools > /dev/null
if [ "$ARCH" == "x86" ]; then
ARCH_EXT=""
else
ARCH_EXT="-$ARCH"
fi
echo "ARCH_EXT=$ARCH_EXT" >> $GITHUB_ENV
echo "ARCH_EXT: $ARCH_EXT"
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
echo "BUILD_DIR: $BUILD_DIR"
rm -rf "$PUBLISH_DIR"
mkdir -p "$PUBLISH_DIR"
echo "PUBLISH_DIR: $PUBLISH_DIR"
wget -nv -O $PUBLISH_DIR/CHANGELOG "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/$VERSION/CHANGELOG"
cat $PUBLISH_DIR/CHANGELOG
wget -nv -O $BUILD_DIR/packages.csv "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/$VERSION/packages.csv"
PACKAGES=$(grep "^$ARCH," $BUILD_DIR/packages.csv | cut -d',' -f2 | tr '\n' ' ')
rm $BUILD_DIR/packages.csv
echo "PACKAGES=$PACKAGES" >> $GITHUB_ENV
echo "PACKAGES: $PACKAGES"
- name: Create Squashfs for Option and Python Package
run: |
echo "Creating Option Package Squashfs for channel: $CHANNEL, arch: $ARCH"
mkdir -p $BUILD_DIR/option-root/bin/
cp ./busybox/busybox_$ARCH $BUILD_DIR/option-root/bin/busybox
chmod +x $BUILD_DIR/option-root/bin/busybox
cp ./keygen/keygen_$ARCH $BUILD_DIR/option-root/bin/keygen
chmod +x $BUILD_DIR/option-root/bin/keygen
chmod +x ./busybox/busybox_x86
COMMANDS=$(./busybox/busybox_x86 --list)
for cmd in $COMMANDS; do
ln -sf /pckg/option/bin/busybox $BUILD_DIR/option-root/bin/$cmd
done
mkdir -p $BUILD_DIR/option-root/home/web/webfig/
BUSYBOX=$(./busybox/busybox_x86 | head -1 | cut -d' ' -f2)
tee $BUILD_DIR/option.jg > /dev/null << EOF
[
{
name:'Patch About',
title:'Patch About',
icon: 30,
prio: 2000,
c:[
{
title:'Patch About',
type:'item',
path:[24, 2],
autorefresh: 1000,
ro: 1,
c: [
{type:'gridcell'},
{name:'Patch',type:'tab',inline: 1},
{name:'Author',type:'string',def:'elseif@live.cn'},
{name:'GitHub',type:'string',def:'https://github.com/elseif/MikroTikPatch', url:1},
{name:'Telegram',type:'string',def:'https://telegram.me/mikrotikpatch', url:1},
{name:'Homepage',type:'string',def:'https://mikrotik.ltd', url:1},
{name:'Busybox',type:'string',def:'$BUSYBOX'},
{name:'System',type:'tab',inline: 1},
{name:'Uptime',type:'interval',id:'u1'},
{name:'Architecture Name',type:'string',id:'s1a'},
{name:'Board Name',type:'string',id:'s2c'},
{name:'Version',type:'version',id:'s16'},
{name:'Build Time',type:'string',id:'s27'},
{type:'separator'},
{type:'toolbar',
c: [
{name:'License',type:'contextmenu',autostart: 1,group:'System',link: [],open:'License',tab:'License'},
{name:'Check For Updates',type:'contextmenu',autostart: 1,group:'System',link: [],open:'Packages',tab:'Check For Updates'}
]
}
]
}
]
}
]
EOF
HASH=$(python3 -E <<EOF
import hashlib, crcmod, sys
with open('$BUILD_DIR/option.jg','rb') as f:
data = f.read()
sha1 = hashlib.sha1(data).hexdigest()[:12]
crc = crcmod.mkCrcFun(0x104C11DB7, initCrc=0xFFFFFFFF, rev=False, xorOut=0xFFFFFFFF)(data)
print(f"{sha1} {crc}")
EOF
)
SHA1_PREFIX=$(echo "$HASH" | awk '{print $1}')
CRC=$(echo "$HASH" | awk '{print $2}')
mv $BUILD_DIR/option.jg $BUILD_DIR/option-$SHA1_PREFIX.jg
python3 -E << EOF
import gzip
gzip.open('$BUILD_DIR/option-$SHA1_PREFIX.jg.gz', 'wb', compresslevel=0).write(open('$BUILD_DIR/option-$SHA1_PREFIX.jg', 'rb').read())
EOF
SIZE=$(stat -c %s $BUILD_DIR/option-$SHA1_PREFIX.jg.gz)
mv $BUILD_DIR/option-$SHA1_PREFIX.jg.gz $BUILD_DIR/option-root/home/web/webfig/
tee $BUILD_DIR/option-root/home/web/webfig/option.info > /dev/null << EOF
{ crc: $CRC, size: $SIZE, name: "option.jg", unique: "option-$SHA1_PREFIX.jg",version: "$VERSION" },
EOF
ln -sf option-$SHA1_PREFIX.jg.gz $BUILD_DIR/option-root/home/web/webfig/option.jg.gz
mksquashfs $BUILD_DIR/option-root $BUILD_DIR/option.sfs -no-recovery -noappend -exit-on-error -quiet -comp xz -no-xattrs -b 256k -all-root
rm -rf $BUILD_DIR/option-root
echo "✅ Option Package Squashfs created: $BUILD_DIR/option.sfs"
ls -la "$BUILD_DIR/option.sfs"
if [ "$ARCH" == "x86" ]; then
wget -nv -O $BUILD_DIR/cpython.tar.gz -nv https://github.com/astral-sh/python-build-standalone/releases/download/20241206/cpython-3.11.11+20241206-x86_64-unknown-linux-musl-install_only_stripped.tar.gz
elif [ "$ARCH" == "arm64" ]; then
wget -nv -O $BUILD_DIR/cpython.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20241206/cpython-3.11.11+20241206-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz
fi
tar -xzf $BUILD_DIR/cpython.tar.gz -C $BUILD_DIR
rm $BUILD_DIR/cpython.tar.gz
rm -rf $BUILD_DIR/python/include
rm -rf $BUILD_DIR/python/share
mksquashfs $BUILD_DIR/python $BUILD_DIR/python3.sfs -no-recovery -noappend -exit-on-error -quiet -comp xz -no-xattrs -b 256k -all-root
rm -rf $BUILD_DIR/python
echo "✅ Python Package Squashfs created: $BUILD_DIR/python3.sfs"
ls -la "$BUILD_DIR/python3.sfs"
- name: Cache mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
id: cache-mikrotik-iso
uses: actions/cache@v5
with:
path: ${{ env.BUILD_DIR }}/mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
key: mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
- name: Get mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}.iso
if: steps.cache-mikrotik-iso.outputs.cache-hit != 'true'
run: |
wget -nv -O $BUILD_DIR/mikrotik-$VERSION$ARCH_EXT.iso \
https://download.mikrotik.com/routeros/$VERSION/mikrotik-$VERSION$ARCH_EXT.iso
- name: Unpack ISO
run: |
mkdir -p $BUILD_DIR/all_packages
mkdir -p $BUILD_DIR/iso
mkdir -p $BUILD_DIR/new_iso
sudo mount -o loop,ro $BUILD_DIR/mikrotik-$VERSION$ARCH_EXT.iso $BUILD_DIR/iso
cp -v $BUILD_DIR/iso/*.npk $BUILD_DIR/all_packages/
mv $BUILD_DIR/all_packages/routeros-$VERSION$ARCH_EXT.npk $BUILD_DIR/
cp -v $BUILD_DIR/iso/efiboot.img $BUILD_DIR/new_iso/
cp -v $BUILD_DIR/iso/defpacks $BUILD_DIR/new_iso/
sudo umount $BUILD_DIR/iso
rm -rf $BUILD_DIR/iso
- name: Check and download missing packages
run: |
for pkg in $PACKAGES; do
PKG_FILE="${pkg}-$VERSION$ARCH_EXT.npk"
PKG_PATH="$BUILD_DIR/all_packages/$PKG_FILE"
if [ ! -f "$PKG_PATH" ]; then
wget -nv -O "$PKG_PATH" "https://download.mikrotik.com/routeros/$VERSION/$PKG_FILE"
echo "✅ Package downloaded: $PKG_FILE"
fi
done
- name: Get loader patch files
run: |
wget -nv -O loader.7z https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/loader.7z
sudo apt-get install -y p7zip-full > /dev/null
7z x -p"${{ secrets.LOADER_7Z_PASSWORD }}" loader.7z >> /dev/null
rm loader.7z
- name: Patch NPK files
run: |
sudo apt-get install build-essential binutils-arm-linux-gnueabi -y > /dev/null
chmod +w $BUILD_DIR/all_packages/*.npk
NPK_FILES=$(find $BUILD_DIR/all_packages/*.npk)
for file in $NPK_FILES; do
python3 npk.py sign $file $file
done
python3 npk.py create $BUILD_DIR/all_packages/gps-$VERSION$ARCH_EXT.npk $BUILD_DIR/all_packages/option-$VERSION$ARCH_EXT.npk option $BUILD_DIR/option.sfs -desc="busybox"
rm -rf $BUILD_DIR/option.sfs
python3 npk.py create $BUILD_DIR/all_packages/gps-$VERSION$ARCH_EXT.npk $BUILD_DIR/all_packages/python3-$VERSION$ARCH_EXT.npk python3 $BUILD_DIR/python3.sfs -desc="python 3.11.13"
rm -rf $BUILD_DIR/python3.sfs
echo "✅ all package npk files already signed: "
ls -la $BUILD_DIR/all_packages/*.npk
(cd $BUILD_DIR/all_packages && zip -r $PUBLISH_DIR/all_packages-$ARCH-$VERSION.zip .)
mv $BUILD_DIR/all_packages/*.npk $PUBLISH_DIR/
python3 patch.py npk $BUILD_DIR/routeros-$VERSION$ARCH_EXT.npk -O $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk
echo "✅ Main package npk file already patched: "
ls -la $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk
rm -rf $BUILD_DIR/all_packages
- name: Cache NetInstall ${{ env.VERSION }}
if: matrix.arch == 'x86' && needs.Prepare_Patch.outputs.RELEASE == 'true'
id: cache-netinstall
uses: actions/cache@v5
with:
path: ${{ env.BUILD_DIR }}/netinstall-${{ env.VERSION }}.zip
key: netinstall-${{ env.VERSION }}.zip
- name: Get netinstall ${{ env.VERSION }}
if: |
steps.cache-netinstall.outputs.cache-hit != 'true' &&
matrix.arch == 'x86' && needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
wget -nv -O $BUILD_DIR/netinstall-$VERSION.zip https://download.mikrotik.com/routeros/$VERSION/netinstall-$VERSION.zip
- name: Patch netinstall ${{ env.VERSION }}
if: matrix.arch == 'x86' && needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
unzip $BUILD_DIR/netinstall-$VERSION.zip -d $BUILD_DIR/
python3 patch.py netinstall $BUILD_DIR/netinstall.exe
(cd $BUILD_DIR && zip -r $PUBLISH_DIR/netinstall-$VERSION.zip netinstall.exe)
rm $BUILD_DIR/netinstall.exe
- name: Cache refind
if: matrix.arch == 'x86'
id: cache-refind
uses: actions/cache@v5
with:
path: ${{ env.BUILD_DIR }}/refind-bin-0.14.2.zip
key: refind-bin-0.14.2.zip
- name: Get refind
if: matrix.arch == 'x86' && steps.cache-refind.outputs.cache-hit != 'true'
run: wget -nv -O $BUILD_DIR/refind-bin-0.14.2.zip https://downloads.sourceforge.net/project/refind/0.14.2/refind-bin-0.14.2.zip
- name: Create ISO & INSTALL image files
run: |
set -e
sudo modprobe nbd
sudo apt-get install isolinux syslinux syslinux-common xorriso dosfstools qemu-utils -y > /dev/null
cp -v $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk $BUILD_DIR/new_iso/
unzip $PUBLISH_DIR/all_packages-$ARCH-$VERSION.zip -d $BUILD_DIR/new_iso/
mkdir $BUILD_DIR/efiboot
if [ "$ARCH" == "x86" ]; then
echo "Creating x86 ISO..."
mkdir -p $BUILD_DIR/new_iso/isolinux
cp /usr/lib/ISOLINUX/isolinux.bin $BUILD_DIR/new_iso/isolinux/
cp /usr/lib/syslinux/modules/bios/ldlinux.c32 $BUILD_DIR/new_iso/isolinux/
echo -e 'default system\n\nlabel system\n\tkernel linux\n\tappend load_ramdisk=1 root=/dev/ram0 -install -cdrom' | tee $BUILD_DIR/new_iso/isolinux/isolinux.cfg
sudo mount -o loop $BUILD_DIR/new_iso/efiboot.img $BUILD_DIR/efiboot
python3 patch.py kernel $BUILD_DIR/efiboot/linux.x86_64 -O $BUILD_DIR/new_iso/isolinux/linux
sudo umount $BUILD_DIR/efiboot
#修改为16M,减小镜像体积
rm -f $BUILD_DIR/new_iso/efiboot.img
truncate --size 16M $BUILD_DIR/new_iso/efiboot.img
mkfs.vfat -n "Boot" $BUILD_DIR/new_iso/efiboot.img
sudo mount -o loop,uid=$(id -u),gid=$(id -g) $BUILD_DIR/new_iso/efiboot.img $BUILD_DIR/efiboot
mkdir -p $BUILD_DIR/efiboot/EFI/BOOT
unzip -j $BUILD_DIR/refind-bin-0.14.2.zip "refind-bin-0.14.2/refind/refind_x64.efi" -d $BUILD_DIR/efiboot/EFI/BOOT/
mv $BUILD_DIR/efiboot/EFI/BOOT/refind_x64.efi $BUILD_DIR/efiboot/EFI/BOOT/BOOTX64.EFI
tee $BUILD_DIR/efiboot/EFI/BOOT/refind.conf > /dev/null << 'EOF'
timeout 0
textonly
textmode 0
showtools shutdown, reboot, exit
menuentry "Install RouterOS" {
loader /linux.x86_64
options "load_ramdisk=1 root=/dev/ram0 -install -cdrom debug"
}
default_selection /EFI/BOOT/BOOTX64.EFI
EOF
cp -v $BUILD_DIR/new_iso/isolinux/linux $BUILD_DIR/efiboot/linux.x86_64
sync
sudo umount $BUILD_DIR/efiboot
xorriso -as mkisofs -o $PUBLISH_DIR/mikrotik-$VERSION$ARCH_EXT.iso \
-V 'MikroTik' \
-publisher "WWW.MIKROTIK.COM" \
-preparer "MIKROTIK" \
-A "MIKROTIK_ROUTER" \
-sysid "" \
-b 'isolinux/isolinux.bin' \
-c 'isolinux/boot.cat' \
-no-emul-boot \
-boot-load-size 4 \
-eltorito-alt-boot \
-e efiboot.img \
-no-emul-boot \
-boot-load-size 2048 \
-R -J \
-input-charset utf-8 \
-iso-level 3 \
$BUILD_DIR/new_iso
echo "✅ ISO created: "
ls -la $PUBLISH_DIR/mikrotik-$VERSION$ARCH_EXT.iso
echo "Creating x86 install..."
truncate --size 128M $BUILD_DIR/install-image-$VERSION.img
mkfs.vfat -I -F 32 -n "Install" $BUILD_DIR/install-image-$VERSION.img
LOOP=$(sudo losetup -f --show $BUILD_DIR/install-image-$VERSION.img)
sudo syslinux --install $LOOP
mkdir -p $BUILD_DIR/install
sudo mount -o loop,uid=$(id -u),gid=$(id -g),umask=000 $LOOP $BUILD_DIR/install
cp -vf $BUILD_DIR/new_iso/isolinux/linux $BUILD_DIR/install/
cp -vf /usr/lib/syslinux/modules/bios/ldlinux.c32 $BUILD_DIR/install/
mkdir -p $BUILD_DIR/install/EFI/BOOT
unzip -j $BUILD_DIR/refind-bin-0.14.2.zip "refind-bin-0.14.2/refind/refind_x64.efi" -d $BUILD_DIR/install/EFI/BOOT
mv $BUILD_DIR/install/EFI/BOOT/refind_x64.efi $BUILD_DIR/install/EFI/BOOT/BOOTX64.EFI
tee $BUILD_DIR/install/EFI/BOOT/refind.conf > /dev/null << 'EOF'
timeout 0
textonly
textmode 0
showtools shutdown, reboot, exit
menuentry "Install RouterOS" {
loader /linux
options "load_ramdisk=1 root=/dev/ram0 -install -hdd"
}
default_selection /EFI/BOOT/BOOTX64.EFI
EOF
tee $BUILD_DIR/install/syslinux.cfg > /dev/null << 'EOF'
default system
LABEL system
KERNEL linux
APPEND load_ramdisk=1 -install -hdd
EOF
NPK_FILES=($(find $BUILD_DIR/new_iso/*.npk))
for ((i=1; i<=${#NPK_FILES[@]}; i++))
do
echo "${NPK_FILES[$i-1]}=>$i.npk"
cp ${NPK_FILES[$i-1]} $BUILD_DIR/install/$i.npk
done
touch $BUILD_DIR/install/CHOOSE
touch $BUILD_DIR/install/autorun.scr
sync
sudo umount $BUILD_DIR/install
sudo losetup -d $LOOP
rm -rf $BUILD_DIR/install
echo "✅ install imgage created: "
ls -la $BUILD_DIR/install-image-$VERSION.img
(cd $BUILD_DIR && zip $PUBLISH_DIR/install-image-$VERSION.zip install-image-$VERSION.img)
rm $BUILD_DIR/install-image-$VERSION.img
elif [ "$ARCH" == "arm64" ]; then
echo "Creating arm64 ISO..."
sudo mount -o loop $BUILD_DIR/new_iso/efiboot.img $BUILD_DIR/efiboot
python3 patch.py kernel $BUILD_DIR/efiboot/EFI/BOOT/BOOTAA64.EFI -O $BUILD_DIR/BOOTAA64.EFI
sudo umount $BUILD_DIR/efiboot
#修改为16M,减小镜像体积
rm -f $BUILD_DIR/new_iso/efiboot.img
truncate --size 16M $BUILD_DIR/new_iso/efiboot.img
mkfs.vfat -n "Boot" $BUILD_DIR/new_iso/efiboot.img
sudo mount -o loop,uid=$(id -u),gid=$(id -g) $BUILD_DIR/new_iso/efiboot.img $BUILD_DIR/efiboot
mkdir -p $BUILD_DIR/efiboot/EFI/BOOT
mv $BUILD_DIR/BOOTAA64.EFI $BUILD_DIR/efiboot/EFI/BOOT/BOOTAA64.EFI
sync
sudo umount $BUILD_DIR/efiboot
xorriso -as mkisofs -o $PUBLISH_DIR/mikrotik-$VERSION$ARCH_EXT.iso \
-V 'MikroTik' \
-publisher "WWW.MIKROTIK.COM" \
-preparer "MIKROTIK" \
-A "MIKROTIK_ROUTER" \
-sysid "" \
-e '/efiboot.img' \
-no-emul-boot \
-boot-load-size 2048 \
-R -J \
-input-charset utf-8 \
-iso-level 3 \
$BUILD_DIR/new_iso
echo "✅ ISO created: "
ls -la $PUBLISH_DIR/mikrotik-$VERSION$ARCH_EXT.iso
fi
rm -rf $BUILD_DIR/new_iso
rm -rf $BUILD_DIR/efiboot
- name: Create CHR image files
run: |
set -e
OVF_TEMPLATE='<?xml version="1.0" encoding="UTF-8"?>
<Envelope vmw:buildId="build-18663434" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<References>
<File ovf:href="{VMDK_FILE}" ovf:id="file1" ovf:size="{FILE_SIZE}"/>
</References>
<DiskSection>
<Info>Virtual disk information</Info>
<Disk ovf:capacity="128" ovf:capacityAllocationUnits="byte * 2^20" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"/>
</DiskSection>
<NetworkSection>
<Info>The list of logical networks</Info>
<Network ovf:name="VM Network">
<Description>The VM Network network</Description>
</Network>
</NetworkSection>
<VirtualSystem ovf:id="vm">
<Info>A virtual machine</Info>
<Name>MikroTik_RouterOS_CHR</Name>
<OperatingSystemSection ovf:id="100" vmw:osType="other3xLinux64Guest">
<Info>The kind of installed guest operating system</Info>
</OperatingSystemSection>
<VirtualHardwareSection>
<Info>Virtual hardware requirements</Info>
<System>
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
<vssd:InstanceID>0</vssd:InstanceID>
<vssd:VirtualSystemIdentifier>MikroTik_RouterOS_CHR</vssd:VirtualSystemIdentifier>
<vssd:VirtualSystemType>vmx-10</vssd:VirtualSystemType>
</System>
<Item>
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
<rasd:Description>Number of Virtual CPUs</rasd:Description>
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
<rasd:Description>Memory Size</rasd:Description>
<rasd:ElementName>1024MB of memory</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>1024</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>ideController0</rasd:ElementName>
<rasd:InstanceID>3</rasd:InstanceID>
<rasd:ResourceType>5</rasd:ResourceType>
</Item>
<Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:ElementName>disk0</rasd:ElementName>
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
<rasd:InstanceID>4</rasd:InstanceID>
<rasd:Parent>3</rasd:Parent>
<rasd:ResourceType>17</rasd:ResourceType>
</Item>
<Item>
<rasd:AddressOnParent>1</rasd:AddressOnParent>
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
<rasd:Connection>VM Network</rasd:Connection>
<rasd:Description>VmxNet3 ethernet adapter on &quot;VM Network&quot;</rasd:Description>
<rasd:ElementName>ethernet0</rasd:ElementName>
<rasd:InstanceID>5</rasd:InstanceID>
<rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
<rasd:ResourceType>10</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="160"/>
<vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="true"/>
</Item>
<Item ovf:required="false">
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>video</rasd:ElementName>
<rasd:InstanceID>6</rasd:InstanceID>
<rasd:ResourceType>24</rasd:ResourceType>
</Item>
<Item ovf:required="false">
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>vmci</rasd:ElementName>
<rasd:InstanceID>7</rasd:InstanceID>
<rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
<rasd:ResourceType>1</rasd:ResourceType>
</Item>
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="{FIRMWARE_TYPE}"/>
</VirtualHardwareSection>
<AnnotationSection ovf:required="false">
<Info>A human-readable annotation</Info>
<Annotation>MikroTik RouterOS CHR</Annotation>
</AnnotationSection>
</VirtualSystem>
</Envelope>'
sudo apt-get install -y parted > /dev/null
mkdir -p $BUILD_DIR/chr/{boot,routeros}
if [ "$ARCH" == "x86" ]; then
echo "Creating x86 uefi CHR..."
python3 npk.py extract $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk boot/EFI/BOOT/BOOTX64.EFI $BUILD_DIR/chr/BOOTX64.EFI
python3 npk.py extract $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk bin/milo $BUILD_DIR/chr/milo
chmod +x $BUILD_DIR/chr/milo
#要求MBR分区表第一个分区类型必须为0x8300,否则ERROR: could not find disk!
truncate --size 128M $BUILD_DIR/chr-$VERSION$ARCH_EXT.img
sgdisk \
--zap-all \
--set-alignment=1 \
--new=1:34:+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" \
--new=2:0:0 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
$BUILD_DIR/chr-$VERSION$ARCH_EXT.img
dd if=$BUILD_DIR/chr-$VERSION$ARCH_EXT.img of=$BUILD_DIR/mbr.bin bs=1 count=512 conv=notrunc
printf '\x01' | dd of=$BUILD_DIR/mbr.bin bs=1 count=1 seek=336 conv=notrunc
sgdisk \
--zap-all \
--set-alignment=1 \
--new=1:34:+32M --typecode=1:EF00 --change-name=1:"RouterOS Boot" \
--new=2:0:0 --typecode=2:8300 --change-name=2:"RouterOS" \
$BUILD_DIR/chr-$VERSION$ARCH_EXT.img
dd if=$BUILD_DIR/mbr.bin of=$BUILD_DIR/chr-$VERSION$ARCH_EXT.img bs=1 count=512 conv=notrunc
rm $BUILD_DIR/mbr.bin
sudo qemu-nbd -d /dev/nbd0
sudo qemu-nbd -c /dev/nbd0 -f raw $BUILD_DIR/chr-$VERSION$ARCH_EXT.img
sudo partprobe /dev/nbd0
sudo mkfs.vfat -n "Boot" /dev/nbd0p1
sudo mkfs.ext4 -F -L "RouterOS" -m 0 /dev/nbd0p2
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/nbd0p1 $BUILD_DIR/chr/boot
sudo mkdir -p $BUILD_DIR/chr/boot/EFI/BOOT
sudo cp -v $BUILD_DIR/chr/BOOTX64.EFI $BUILD_DIR/chr/boot/EFI/BOOT/BOOTX64.EFI
sudo umount /dev/nbd0p1
sudo mount /dev/nbd0p2 $BUILD_DIR/chr/routeros
sudo mkdir -p $BUILD_DIR/chr/routeros/{var/pdb/{system,option},boot,bin,rw/disk}
# cmdStartVerify bin/milo
sudo cp -v $BUILD_DIR/chr/milo $BUILD_DIR/chr/routeros/bin/milo
sudo cp -v $PUBLISH_DIR/option-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/routeros/var/pdb/option/image
sudo cp -v $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/routeros/var/pdb/system/image
echo -e '#!/bin/sh\n# This script will be executed *before* RouterOS *loader* start.\n# You can put your own initialization stuff in here\n' |sudo tee $BUILD_DIR/chr/routeros/rw/disk/rc.local
sudo umount /dev/nbd0p2
sudo qemu-nbd -d /dev/nbd0
qemu-img convert -f raw -O qcow2 $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.qcow2
qemu-img convert -f raw -O vmdk $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vmdk
qemu-img convert -f raw -O vpc $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vhd
qemu-img convert -f raw -O vhdx $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vhdx
qemu-img convert -f raw -O vdi $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vdi
VMDK_FILE=chr-$VERSION$ARCH_EXT-disk1.vmdk
qemu-img convert -f raw -O vmdk -o subformat=streamOptimized \
$BUILD_DIR/chr-$VERSION$ARCH_EXT.img \
$BUILD_DIR/$VMDK_FILE
FILE_SIZE=$(stat -c %s "$BUILD_DIR/$VMDK_FILE")
OVF_FILE=chr-$VERSION$ARCH_EXT.ovf
OVA_FILE=chr-$VERSION$ARCH_EXT.ova
echo "$OVF_TEMPLATE" | sed -e "s|{VMDK_FILE}|$VMDK_FILE|g" \
-e "s|{FILE_SIZE}|$FILE_SIZE|g" \
-e "s|{FIRMWARE_TYPE}|efi|g" | tee $BUILD_DIR/$OVF_FILE
(cd $BUILD_DIR && tar -cvf "$OVA_FILE" "$OVF_FILE" "$VMDK_FILE")
rm $BUILD_DIR/$VMDK_FILE
rm $BUILD_DIR/$OVF_FILE
echo "✅ UEFI CHR created: "
ls -la $BUILD_DIR/chr-$VERSION$ARCH_EXT.*
(cd $BUILD_DIR && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.ova.zip chr-$VERSION$ARCH_EXT.ova && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.qcow2.zip chr-$VERSION$ARCH_EXT.qcow2 && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vmdk.zip chr-$VERSION$ARCH_EXT.vmdk && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vhd.zip chr-$VERSION$ARCH_EXT.vhd && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vhdx.zip chr-$VERSION$ARCH_EXT.vhdx && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vdi.zip chr-$VERSION$ARCH_EXT.vdi &&
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.img.zip chr-$VERSION$ARCH_EXT.img )
rm $BUILD_DIR/chr-$VERSION$ARCH_EXT.*
FIRMWARE_TYPE="bios"
echo "Creating x86 legacy bios CHR..."
truncate --size 128M $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img
sgdisk \
--zap-all \
--set-alignment=1 \
--new=1:34:+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2:0:-4096 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
$BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img
dd if=$BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img of=$BUILD_DIR/mbr.bin bs=1 count=512 conv=notrunc
echo "FA31C08ED0BC007C89E65007501FFBFCBF0006B90001F2A5EA1D060000BEBE07B304803C807423803C00750983C610FECB75EFCD18BE9B06AC3C00740B56BB0700B40ECD105EEBF0EBFE8B148B4C0289F5BF0500BB007CB8010257CD135F730C31C0CD134F75EDBE7C06EBCCBFFE7D813D55AA75C289EEEA007C00004572726F72206C6F6164696E67206F7065726174696E672073797374656D004D697373696E67206F7065726174696E672073797374656D0000000000" | xxd -r -p | sudo dd of=$BUILD_DIR/mbr.bin bs=1 count=440 conv=notrunc
printf '\x01' | sudo dd of=$BUILD_DIR/mbr.bin bs=1 count=1 seek=336 conv=notrunc
printf '\x80' | sudo dd of=$BUILD_DIR/mbr.bin bs=1 count=1 seek=446 conv=notrunc
sgdisk \
--zap-all \
--set-alignment=1 \
--new=1:34:+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2:0:-4096 --typecode=2:8300 --change-name=2:"RouterOS" \
$BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img
dd if=$BUILD_DIR/mbr.bin of=$BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img bs=1 count=512 conv=notrunc
sudo rm $BUILD_DIR/mbr.bin
sudo qemu-nbd -d /dev/nbd0
sudo qemu-nbd -c /dev/nbd0 -f raw $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img
sudo partprobe /dev/nbd0
sudo mkfs.ext2 -F -m 0 -L "RouterOS Boot" /dev/nbd0p1
sudo mkfs.ext4 -F -m 0 -L "RouterOS" /dev/nbd0p2
sudo mount /dev/nbd0p1 $BUILD_DIR/chr/boot
sudo mkdir -p $BUILD_DIR/chr/boot/EFI/BOOT
sudo cp -vf $BUILD_DIR/chr/BOOTX64.EFI $BUILD_DIR/chr/boot/EFI/BOOT/BOOTX64.EFI
sudo $BUILD_DIR/chr/milo $BUILD_DIR/chr/boot
sudo umount /dev/nbd0p1
sudo mount /dev/nbd0p2 $BUILD_DIR/chr/routeros
sudo mkdir -p $BUILD_DIR/chr/routeros/{var/pdb/{system,option},boot,bin,rw/disk}
sudo cp -v $BUILD_DIR/chr/milo $BUILD_DIR/chr/routeros/bin/milo
sudo cp -v $PUBLISH_DIR/option-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/routeros/var/pdb/option/image
sudo cp -v $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/routeros/var/pdb/system/image
echo -e '#!/bin/sh\n# This script will be executed *before* RouterOS *loader* start.\n# You can put your own initialization stuff in here\n' | sudo tee $BUILD_DIR/chr/routeros/rw/disk/rc.local
sudo umount /dev/nbd0p2
sudo qemu-nbd -d /dev/nbd0
qemu-img convert -f raw -O qcow2 $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.qcow2
qemu-img convert -f raw -O vmdk $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vmdk
qemu-img convert -f raw -O vpc $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vhd
qemu-img convert -f raw -O vhdx $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vhdx
qemu-img convert -f raw -O vdi $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vdi
VMDK_FILE=chr-$VERSION$ARCH_EXT-disk1.vmdk
qemu-img convert -f raw -O vmdk -o subformat=streamOptimized \
$BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img \
$BUILD_DIR/$VMDK_FILE
FILE_SIZE=$(stat -c %s "$BUILD_DIR/$VMDK_FILE")
OVF_FILE=chr-$VERSION$ARCH_EXT-legacy-bios.ovf
OVA_FILE=chr-$VERSION$ARCH_EXT-legacy-bios.ova
echo "$OVF_TEMPLATE" | sed -e "s|{VMDK_FILE}|$VMDK_FILE|g" \
-e "s|{FILE_SIZE}|$FILE_SIZE|g" \
-e "s|{FIRMWARE_TYPE}|bios|g" | tee $BUILD_DIR/$OVF_FILE
(cd $BUILD_DIR && tar -cvf "$OVA_FILE" "$OVF_FILE" "$VMDK_FILE")
rm $BUILD_DIR/$VMDK_FILE
rm $BUILD_DIR/$OVF_FILE
echo "✅ legacy BIOS CHR created: "
ls -la $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.*
(cd $BUILD_DIR && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.ova.zip chr-$VERSION$ARCH_EXT-legacy-bios.ova && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.qcow2.zip chr-$VERSION$ARCH_EXT-legacy-bios.qcow2 && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vmdk.zip chr-$VERSION$ARCH_EXT-legacy-bios.vmdk && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vhd.zip chr-$VERSION$ARCH_EXT-legacy-bios.vhd && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vhdx.zip chr-$VERSION$ARCH_EXT-legacy-bios.vhdx && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.vdi.zip chr-$VERSION$ARCH_EXT-legacy-bios.vdi && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.img.zip chr-$VERSION$ARCH_EXT-legacy-bios.img )
rm $BUILD_DIR/chr-$VERSION$ARCH_EXT-legacy-bios.*
rm $BUILD_DIR/chr/BOOTX64.EFI
rm $BUILD_DIR/chr/milo
elif [ "$ARCH" == "arm64" ]; then
echo "Creating arm64 CHR..."
python3 npk.py extract $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk boot/kernel $BUILD_DIR/chr/kernel
python3 patch.py buildefi $BUILD_DIR/chr/kernel $BUILD_DIR/chr/BOOTAA64.EFI
rm -r $BUILD_DIR/chr/kernel
#要求MBR分区表第一个分区类型必须为0x8300,否则ERROR: could not find disk!
truncate --size 128M $BUILD_DIR/chr-$VERSION$ARCH_EXT.img
sgdisk \
--zap-all \
--set-alignment=1 \
--new=1:34:+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" \
--new=2:0:0 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
$BUILD_DIR/chr-$VERSION$ARCH_EXT.img
dd if=$BUILD_DIR/chr-$VERSION$ARCH_EXT.img of=$BUILD_DIR/mbr.bin bs=1 count=512 conv=notrunc
printf '\x01' | dd of=$BUILD_DIR/mbr.bin bs=1 count=1 seek=336 conv=notrunc
sgdisk \
--zap-all \
--set-alignment=1 \
--new=1:34:+32M --typecode=1:EF00 --change-name=1:"RouterOS Boot" \
--new=2:0:0 --typecode=2:8300 --change-name=2:"RouterOS" \
$BUILD_DIR/chr-$VERSION$ARCH_EXT.img
dd if=$BUILD_DIR/mbr.bin of=$BUILD_DIR/chr-$VERSION$ARCH_EXT.img bs=1 count=512 conv=notrunc
rm $BUILD_DIR/mbr.bin
sudo qemu-nbd -d /dev/nbd0
sudo qemu-nbd -c /dev/nbd0 -f raw $BUILD_DIR/chr-$VERSION$ARCH_EXT.img
sudo partprobe /dev/nbd0
sudo mkfs.vfat -n "Boot" "/dev/nbd0p1"
sudo mkfs.ext4 -F -m 0 -L "RouterOS" "/dev/nbd0p2"
sudo mount -o uid=$(id -u),gid=$(id -g) /dev/nbd0p1 $BUILD_DIR/chr/boot
sudo mkdir -p $BUILD_DIR/chr/boot/EFI/BOOT
sudo mv -v $BUILD_DIR/chr/BOOTAA64.EFI $BUILD_DIR/chr/boot/EFI/BOOT/BOOTAA64.EFI
sudo umount /dev/nbd0p1
sudo mount /dev/nbd0p2 $BUILD_DIR/chr/routeros
sudo mkdir -p $BUILD_DIR/chr/routeros/{var/pdb/{system,option},boot,rw/disk}
sudo cp -v $PUBLISH_DIR/option-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/routeros/var/pdb/option/image
sudo cp -v $PUBLISH_DIR/routeros-$VERSION$ARCH_EXT.npk $BUILD_DIR/chr/routeros/var/pdb/system/image
echo -e '#!/bin/sh\n# This script will be executed *before* RouterOS *loader* start.\n# You can put your own initialization stuff in here\n' | sudo tee $BUILD_DIR/chr/routeros/rw/disk/rc.local
sudo umount /dev/nbd0p2
sudo qemu-nbd -d /dev/nbd0
qemu-img convert -f raw -O qcow2 $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.qcow2
qemu-img convert -f raw -O vmdk $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vmdk
qemu-img convert -f raw -O vpc $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vhd
qemu-img convert -f raw -O vhdx $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vhdx
qemu-img convert -f raw -O vdi $BUILD_DIR/chr-$VERSION$ARCH_EXT.img $BUILD_DIR/chr-$VERSION$ARCH_EXT.vdi
VMDK_FILE=chr-$VERSION$ARCH_EXT-disk1.vmdk
qemu-img convert -f raw -O vmdk -o subformat=streamOptimized \
$BUILD_DIR/chr-$VERSION$ARCH_EXT.img \
$BUILD_DIR/$VMDK_FILE
FILE_SIZE=$(stat -c %s "$BUILD_DIR/$VMDK_FILE")
OVF_FILE=chr-$VERSION$ARCH_EXT.ovf
OVA_FILE=chr-$VERSION$ARCH_EXT.ova
echo "$OVF_TEMPLATE" | sed -e "s|{VMDK_FILE}|$VMDK_FILE|g" \
-e "s|{FILE_SIZE}|$FILE_SIZE|g" \
-e "s|{FIRMWARE_TYPE}|efi|g" | tee $BUILD_DIR/$OVF_FILE
(cd $BUILD_DIR && tar -cvf "$OVA_FILE" "$OVF_FILE" "$VMDK_FILE")
rm $BUILD_DIR/$VMDK_FILE
rm $BUILD_DIR/$OVF_FILE
echo "✅ CHR created: "
ls -la $BUILD_DIR/chr-$VERSION$ARCH_EXT.*
(cd $BUILD_DIR && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.ova.zip chr-$VERSION$ARCH_EXT.ova && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.qcow2.zip chr-$VERSION$ARCH_EXT.qcow2 && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vmdk.zip chr-$VERSION$ARCH_EXT.vmdk && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vhd.zip chr-$VERSION$ARCH_EXT.vhd && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vhdx.zip chr-$VERSION$ARCH_EXT.vhdx && \
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.vdi.zip chr-$VERSION$ARCH_EXT.vdi &&
zip $PUBLISH_DIR/chr-$VERSION$ARCH_EXT.img.zip chr-$VERSION$ARCH_EXT.img )
rm $BUILD_DIR/chr-$VERSION$ARCH_EXT.*
fi
rm -rf $BUILD_DIR/chr
- name: Upload Files as Artifact (No Release)
if: needs.Prepare_Patch.outputs.RELEASE == 'false'
uses: actions/upload-artifact@v7
with:
name: mikrotik-${{ env.VERSION }}${{ env.ARCH_EXT }}
path: |
${{ env.PUBLISH_DIR }}/*.zip
${{ env.PUBLISH_DIR }}/*.iso
- name: Generate SHA256 checksum files
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
(
cd $PUBLISH_DIR
for file in *.zip *.iso *.npk; do
if [ -f "$file" ]; then
sha256sum "$file" > "$file.sha256"
cat "$file.sha256"
fi
done
)
- name: Delete Release tag ${{ env.VERSION }}${{ env.ARCH_EXT }}
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
sed -i "1i Build Time:$BUILD_TIME" $PUBLISH_DIR/CHANGELOG
HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
RELEASE_INFO=$(curl -s -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/tags/$VERSION$ARCH_EXT)
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id')
echo "Release ID: $RELEASE_ID"
if [ "$RELEASE_ID" != "null" ]; then
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$VERSION$ARCH_EXT
echo "Tag $VERSION$ARCH_EXT deleted successfully."
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID
echo "Release with tag $VERSION$ARCH_EXT deleted successfully."
# Wait for a moment to ensure the deletion is complete
sleep 3
else
echo "Release not found for tag: $VERSION$ARCH_EXT)"
fi
- name: Create Release tag ${{ env.VERSION }}${{ env.ARCH_EXT }}
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
uses: softprops/action-gh-release@v3
with:
name: "RouterOS ${{ env.VERSION }}${{ env.ARCH_EXT }}"
repository: ${{ github.repository }}
token: ${{ secrets.GITHUB_TOKEN }}
body_path: ${{env.PUBLISH_DIR}}/CHANGELOG
tag_name: ${{ env.VERSION }}${{ env.ARCH_EXT }}
make_latest: ${{ env.CHANNEL == 'stable' && matrix.arch == 'x86'}}
prerelease: ${{ env.CHANNEL == 'testing'}}
files: |
${{env.PUBLISH_DIR}}/*.zip
${{env.PUBLISH_DIR}}/*.iso
${{env.PUBLISH_DIR}}/routeros*.npk
${{env.PUBLISH_DIR}}/*.zip.sha256
${{env.PUBLISH_DIR}}/*.iso.sha256
${{env.PUBLISH_DIR}}/routeros*.npk.sha256
- name: Upload Files
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
sudo apt-get install -y knockd lftp > /dev/null
knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }}
echo "Knocking on server..."
for i in 1 2 3; do
if knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }} ; then
echo "Knock successful"
break
else
echo "Knock attempt $i failed, retrying..."
sleep 2
fi
done
echo "Waiting for firewall to open SSH port..."
sleep 3
rm -f $PUBLISH_DIR/*.zip
rm -f $PUBLISH_DIR/*.iso
rm -f $PUBLISH_DIR/*.zip.sha256
rm -f $PUBLISH_DIR/*.iso.sha256
sed -i '1d' $PUBLISH_DIR/CHANGELOG
echo $VERSION $BUILD_TIME | tee $PUBLISH_DIR/info
touch $PUBLISH_DIR/$CHANNEL
REMOTE_PATH=${{ secrets.SSH_DIRECTORY }}/$VERSION
SERVER=${{ secrets.SSH_SERVER }}
USER=${{ secrets.SSH_USERNAME }}
PASS=${{ secrets.SSH_PASSWORD }}
PORT=${{ secrets.SSH_PORT }}
lftp -u "$USER","$PASS" sftp://$SERVER:$PORT <<EOF
set sftp:auto-confirm yes
mirror --reverse --verbose --only-newer $PUBLISH_DIR "$REMOTE_PATH"
bye
EOF
- name: Clear Cloudflare cache
if: needs.Prepare_Patch.outputs.RELEASE == 'true'
run: |
curl --request POST --url "https://api.cloudflare.com/client/v4/zones/fe6831ed6dc9e6235e69ef2a31f2e7fe/purge_cache" \
--header "Authorization: Bearer 9GDQkzU51QXaqzp1qMjyFKpyeJyOdnNoG9GZQaGP" \
--header "Content-Type:application/json" \
--data '{"purge_everything": true}'
Update_Release:
needs: [Prepare_Patch,Patch_RouterOS]
if: |
needs.Prepare_Patch.outputs.SKIP_BUILD == 'false' &&
needs.Patch_RouterOS.result == 'success' &&
needs.Prepare_Patch.outputs.RELEASE == 'true'
runs-on: ubuntu-24.04
env:
CHANNEL: ${{ inputs.channel }}
VERSION: ${{ needs.Prepare_Patch.outputs.VERSION }}
steps:
- name: Update Release
run: |
sudo apt-get install -y knockd lftp > /dev/null
knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }}
echo "Knocking on server..."
for i in 1 2 3; do
if knock -4 ${{ secrets.SSH_SERVER }} ${{ secrets.SSH_KNOCK_PORT }} ; then
echo "Knock successful"
break
else
echo "Knock attempt $i failed, retrying..."
sleep 2
fi
done
echo "Waiting for firewall to open SSH port..."
sleep 3
REMOTE_PATH=${{ secrets.SSH_DIRECTORY }}
SERVER=${{ secrets.SSH_SERVER }}
USER=${{ secrets.SSH_USERNAME }}
PASS=${{ secrets.SSH_PASSWORD }}
PORT=${{ secrets.SSH_PORT }}
sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no -p $PORT $USER@$SERVER \
"bash /rw/disk/$REMOTE_PATH/packages.sh /rw/disk/$REMOTE_PATH/$VERSION; \
ln -sf /root/$REMOTE_PATH/$VERSION/info /rw/disk/$REMOTE_PATH/NEWESTa7.$CHANNEL; \
chown -R 32768:32768 /rw/disk/$REMOTE_PATH"
echo "✅ Updated: packages.csv"

2
.gitignore vendored
View File

@ -2,4 +2,6 @@ __pycache__/
.vscode/
venv/
app/
loader/
test/
test_*.py

8
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,8 @@
# WTF Code of Conduct (WTFCoC)
*"Do What The Fuck You Want, But Don't Be A Dick"*
1. **Do whatever you want** with the code
2. **Don't be a dick** to others
3. **No bureaucracy** - handle problems like adults
4. **You're on your own** - this isn't a safe space
5. **Final rule**: If this bothers you, re-read the WTFPL license name (words 2-4)

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

116
README.md
View File

@ -1,50 +1,78 @@
# MikroTik RouterOS Patch [[English](README_EN.md)]
# MikroTik RouterOS Patch
[![Patch Mikrotik RouterOS](https://github.com/elseif/MikroTikPatch/actions/workflows/main.yml/badge.svg)](https://github.com/elseif/MikroTikPatch/actions/workflows/main.yml)
![Cloud Status](https://img.shields.io/endpoint?url=https://mikrotik.ltd/status/cloud)
[![License: WTFPL](https://img.shields.io/badge/License-WTFPL-brightgreen.svg)](./LICENSE)
[![CoC:WTFCoC](https://img.shields.io/badge/CoC-WTFCoC-brightgreen.svg)](./CODE_OF_CONDUCT.md)
## ⚠️ 重要声明
**此项目及工具仅供测试用途。使用风险自负。生产环境请使用官方授权版本。**
## 架构x86、arm64
- **主页:** https://mikrotik.ltd/
- **演示:** https://demo.mikrotik.ltd/
- **授权: 安装option.npk后将自动授予最高级别许可证。**
- **源代码:** https://github.com/elseif/MikroTikPatch
- **授权BOT** https://t.me/ROS_Keygen_Bot
- **Docker** `docker pull ghcr.io/elseif/chr:latest`
## 架构arm64, arm, mipsbe, mmips, ppc, smips, x86
- **主页:** https://routeros.ltd/
- **授权: 需要赞助***CHR版本(x86/Arm64)支持在线直接获取授权*
- **功能:** 支持在线自定义制作品牌包
## 支持的云功能
| 功能 | 命令 |
|------|------|
| 在线升级 | `system/package/update/install` |
| DDNS | `ip/cloud/set ddns-enabled=yes` |
| 云备份 | `/system/backup/cloud/upload-file action=create-and-upload password=any` |
## 启用容器模式(无需物理重启)
1. 安装 `option.npk` 包;
2. 打开终端执行:`system/device-mode/update container=yes`
3. 打开新终端执行:`system/shell cmd="reboot -f"`
## 通过终端进入shell
- 安装 `option.npk`
- 打开终端执行:`/system/shell``/sh`
## 通过ssh或telnet进入shell
- 安装 `option.npk`
- ssh或telnet登录用户名为:`devel`,密码与`admin`密码相同。
## x86架构支持CHR和x86模式切换
- 安装 `option.npk`
- 进入shell执行`keygen chr``keygen x86`
## 支持开机自动运行脚本
- 安装 `option.npk`
- 创建或者编辑文件目录下的`rc.local`文件,即:`/rw/disk/rc.local`文件
- 写入脚本内容,例如:
```bash
#!/bin/sh
# This script will be executed *before* RouterOS *loader* start.
# You can put your own initialization stuff in here
echo "Hello, world!"
```
- 开机启动时将看到输出增加:`Starting rc.local...`
- 启动后进入shell执行`cat /ram/startup.catlog | grep 'Hello'` 查看输出。
### 更多关于RouterOS的信息请查看: https://manual.mikrotik.com/
### 感谢赞助
[DartNode(aff)](https://dartnode.com?aff=SnazzyLobster067) | [ZMTO(aff)](https://console.zmto.com/?affid=1588) | [Vultr(aff)](https://www.vultr.com/?ref=9807160-9J)
[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
### [[Discord](https://discord.gg/keV6MWQFtX)] [[Telegram](https://t.me/mikrotikpatch)] [[Keygen(Telegram Bot)](https://t.me/ROS_Keygen_Bot)]
### 国内镜像下载 密码: elseif
[ [123pan](https://www.123pan.com/s/IpxgTd-BYjQh.html) ]
[ [7.15.3](https://elseif.lanzouj.com/b00cre7g7a)]
[ [7.15.3-arm64](https://elseif.lanzouj.com/b00cre7g8b) ]
[ [6.49.15](https://elseif.lanzouj.com/b00crdq4ji) ]
[ [6.49.13](https://elseif.lanzouj.com/b00crdq4kj) ]
支持:在线更新、在线授权、云备份、DDNS
![](image/install.png)
![](image/routeros.png)
### x86模式授权许可
![](image/x86.png)
### x86模式在线授权(v6.x)
![](image/renew_v6.png)
### Chr模式在线授权
![](image/renew.png)
### Chr模式授权许可
![](image/chr.png)
![](image/arm.png)
![](image/mips.png)
## 如何使用Shell
安装 option-{version}.npk 包
telnet到RouterOS,用户名devel,密码与admin的密码相同
## 如何授权许可
进入shell
运行 keygen
参考上图。
Chr镜像支持在线授权许可
## 如何使用Python
安装 python3-{version}.npk 包
telnet到RouterOS,用户名devel,密码与admin的密码相同
运行 python -V
### npk.py
对npk文件进行解包修改创建签名和验证
### patch.py
替换公钥并签名
## 所有的修补操作都自动运行在[Github Action](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/)。

View File

@ -1,47 +0,0 @@
# MikroTik RouterOS Patch [[中文](README.md)]
### [[Discord](https://discord.gg/keV6MWQFtX)] [[Telegram](https://t.me/mikrotikpatch)] [[Keygen(Telegram Bot)](https://t.me/ROS_Keygen_Bot)]
### Download [Latest Patched](https://github.com/elseif/MikroTikPatch/releases/latest) iso file,install it and enjoy.
### CHR image is both support BIOS and UEFI boot mode.
### Support online upgrade,online license,cloud backup,cloud DDNS
![](image/install.png)
![](image/routeros.png)
### license RouterOS for x86.
![](image/x86.png)
### Renew license for x86 v6.x
![](image/renew_v6.png)
### Renew license for chr
![](image/renew.png)
### license RouterOS for chr
![](image/chr.png)
![](image/arm.png)
![](image/mips.png)
## How to use shell
install option-{version}.npk package
run telnet to routeros with username devel and password is same as admin
## How to license RouterOS
telnet to routeros with username devel and password is same as admin
run keygen
chr mode could use renew lincense online
## How to use python3
install python3-{version}.npk package
run telnet to routeros with username devel and password is same as admin
run python -V
### npk.py
SignVerifyCreate, Extract npk file.
### patch.py
Patch public key and sign NPK files
## all patches are applied automatically with [Github Action](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/).

Binary file not shown.

BIN
busybox/busybox_arm64 Normal file

Binary file not shown.

Binary file not shown.

364
chr.sh
View File

@ -1,26 +1,340 @@
#!/bin/bash
apt-get install -y gdisk extlinux
dd if=/dev/zero of=/dev/sda bs=1 count=512 conv=notrunc
sgdisk --clear --set-alignment=2 --new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 --new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" --gpttombr=1:2 /dev/sda
dd if=/dev/sda of=/tmp/pt.bin bs=1 count=66 skip=446
echo -e "\x80" | dd of=/tmp/pt.bin bs=1 count=1 conv=notrunc
dd if=/dev/zero of=/dev/sda bs=1 count=512 conv=notrunc
sgdisk --clear --set-alignment=2 --new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 --new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" /dev/sda
wget -O /tmp/mbr.bin https://raw.gitmirror.com/elseif/MikroTikPatch/main/mbr.bin
dd if=/tmp/mbr.bin of=/dev/sda bs=1 count=446 conv=notrunc
dd if=/tmp/pt.bin of=/dev/sda bs=1 count=66 seek=446 conv=notrunc
sync
mkfs.vfat -n "Boot" /dev/sda1
mkfs.ext4 -F -L "RouterOS" -m 0 /dev/sda2
mkdir -p /tmp/{boot,routeros}
mount -o loop,rw /dev/sda1 /tmp/boot
mkdir -p /tmp/boot/{BOOT,EFI/BOOT}
wget -O /tmp/boot/EFI/BOOT/BOOTX64.EFI https://hub.gitmirror.com/https://github.com/elseif/MikroTikPatch/releases/download/7.15.2/BOOTX64.EFI
extlinux --install -H 64 -S 32 /tmp/boot/BOOT
echo -e "default system\nlabel system\n\tkernel /EFI/BOOT/BOOTX64.EFI\n\tappend load_ramdisk=1 root=/dev/ram0 quiet" > /tmp/boot/BOOT/syslinux.cfg
umount /tmp/boot
mount -o loop,rw /dev/sda2 /tmp/routeros
mkdir -p /tmp/routeros/{var/pdb/{system,option},boot,rw}
wget -O /tmp/routeros/var/pdb/option/image https://hub.gitmirror.com/https://github.com/elseif/MikroTikPatch/releases/download/7.15.2/option-7.15.2.npk
wget -O /tmp/routeros/var/pdb/system/image https://hub.gitmirror.com/https://github.com/elseif/MikroTikPatch/releases/download/7.15.2/routeros-7.15.2.npk
umount /tmp/routeros
set -e
_ask() {
local _redo=0
read resp
case "$resp" in
!) echo "Type 'exit' to return to setup."
sh
_redo=1
;;
!*) eval "${resp#?}"
_redo=1
;;
esac
return $_redo
}
ask() {
local _question="$1" _default="$2"
while :; do
printf %s "$_question "
[ -z "$_default" ] || printf "[%s] " "$_default"
_ask && : ${resp:=$_default} && break
done
}
ask_until() {
resp=
while [ -z "$resp" ] ; do
ask "$1" "$2"
done
}
yesno() {
case $1 in
[Yy]) return 0;;
esac
return 1
}
ask_yesno() {
while true; do
ask "$1" "$2"
case "$resp" in
Y|y|N|n) break;;
esac
done
yesno "$resp"
}
select_language() {
while true; do
echo "Select your language:"
echo "1. English"
echo "2. 简体中文"
ask_until "Please choose an option" "1"
case $resp in
1)
MSG_ARCH="Arch:"
MSG_BOOTMODE="BootMode:"
MSG_STORAGE_DEVICE="Storage Device:"
MSG_ADDRESS="IP Address:"
MSG_GATEWAY="Gateway:"
MSG_DNS="Domain Name Server:"
MSG_SELECT_VERSION="Select the version you want to install:"
MSG_STABLE="stable (v7)"
MSG_TEST="testing (v7)"
MSG_LTS="long-term (v6)"
MSG_STABLE6="stable (v6)"
MSG_PLEASE_CHOOSE="Please choose an option:"
MSG_UNSUPPORTED_ARCH="Error: Unsupported architecture: "
MSG_INVALID_OPTION="Error: Invalid option!"
MSG_ARM64_NOT_SUPPORT_V6="arm64 does not support v6 version for now."
MSG_SELECTED_VERSION="Selected version:"
MSG_FILE_DOWNLOAD="Download file: "
MSG_DOWNLOAD_ERROR="Error: No wget nor curl is installed. Cannot download."
MSG_EXTRACT_ERROR="Error: No unzip nor gunzip is installed. Cannot uncompress."
MSG_DOWNLOAD_FAILED="Error: Download failed!"
MSG_OPERATION_ABORTED="Error: Operation aborted."
MSG_WARNING="Warn: All data on /dev/%s will be lost!"
MSG_REBOOTING="Ok, rebooting..."
MSG_ADMIN_PASSWORD="Admin Password:"
MSG_ERROR_MOUNT="Error: Failed to mount partition"
MSG_ERROR_LOOP="Error: Failed to setup loop device"
MSG_AUTO_RUN_FILE_CREATED="autorun.scr file created."
MSG_AUTO_RUN_FILE_NOT_CREATED="Warn: autorun.scr file create failed"
MSG_CONFIRM_CONTINUE="Do you want to continue? [y/n]"
;;
2)
MSG_ARCH="CPU架构:"
MSG_BOOTMODE="引导模式:"
MSG_STORAGE_DEVICE="存储设备名称:"
MSG_ADDRESS="IP地址:"
MSG_GATEWAY="网关地址:"
MSG_DNS="DNS服务器:"
MSG_SELECT_VERSION="请选择您要安装的版本:"
MSG_STABLE="稳定版 (v7)"
MSG_TEST="测试版 (v7)"
MSG_LTS="长期支持版 (v6)"
MSG_STABLE6="稳定版 (v6)"
MSG_PLEASE_CHOOSE="请选择一个选项:"
MSG_UNSUPPORTED_ARCH="错误: 不支持的架构: "
MSG_INVALID_OPTION="错误: 无效选项"
MSG_ARM64_NOT_SUPPORT_V6="ARM64架构暂不支持安装v6版本"
MSG_SELECTED_VERSION="已选择版本:"
MSG_FILE_DOWNLOAD="下载文件: "
MSG_DOWNLOAD_ERROR="错误: wget 或 curl 都未安装,无法下载文件。"
MSG_EXTRACT_ERROR="错误: unzip 或 gunzip 都未安装,无法解压文件。"
MSG_DOWNLOAD_FAILED="错误: 下载失败!"
MSG_OPERATION_ABORTED="错误: 操作已中止。"
MSG_WARNING="警告:/dev/%s 上的数据将会丢失!"
MSG_REBOOTING="正在重启..."
MSG_ADMIN_PASSWORD="管理员密码:"
MSG_ERROR_MOUNT="错误: 挂载分区失败"
MSG_ERROR_LOOP="错误: 设置 loop 设备失败"
MSG_AUTO_RUN_FILE_CREATED="autorun.scr 文件已创建。"
MSG_AUTO_RUN_FILE_NOT_CREATED="警告autorun.scr 文件创建失败!"
MSG_CONFIRM_CONTINUE="您是否确定继续? [y/n]"
;;
*)
echo "Error: Invalid option!"
continue
;;
esac
break
done
}
show_system_info() {
ARCH=$(uname -m)
BOOT_MODE=$( [ -d "/sys/firmware/efi" ] && echo "UEFI" || echo "BIOS" )
echo "$MSG_ARCH $ARCH"
echo "$MSG_BOOTMODE $BOOT_MODE"
}
confirm_storage() {
STORAGE=$(lsblk | grep disk | head -1 | awk '{print $1}')
ask_until "$MSG_STORAGE_DEVICE" "$STORAGE"
STORAGE=$resp
}
confirm_address() {
ETH=$(ip route show default | grep '^default' | sed -n 's/.* dev \([^\ ]*\) .*/\1/p')
MAC=$(cat /sys/class/net/$ETH/address | tr 'a-z' 'A-Z')
ADDRESS=$(ip addr show $ETH | grep global | cut -d' ' -f 6 | head -n 1)
GATEWAY=$(ip route list | grep default | cut -d' ' -f 3)
if [ -f "/etc/resolv.conf" ]; then
DNS=$(grep '^nameserver' /etc/resolv.conf | awk '{print $2}' | head -n 1)
fi
[ -z "$DNS" ] && DNS="8.8.8.8"
ask_until "$MSG_ADDRESS" "$ADDRESS"
ADDRESS=$resp
ask_until "$MSG_GATEWAY" "$GATEWAY"
GATEWAY=$resp
ask_until "$MSG_DNS" "$DNS"
DNS=$resp
}
http_get() {
local url=$1
local dest_file=$2
if command -v curl >/dev/null 2>&1; then
if [ -z "$dest_file" ]; then
curl -Ls "$url"
else
curl -L -# -o "$dest_file" "$url" || { echo "$MSG_DOWNLOAD_FAILED"; exit 1; }
fi
elif command -v wget >/dev/null 2>&1; then
if [ -z "$dest_file" ]; then
wget --no-check-certificate -qO- "$url"
else
wget --no-check-certificate -O "$dest_file" "$url" || { echo "$MSG_DOWNLOAD_FAILED"; exit 1; }
fi
else
echo "$MSG_DOWNLOAD_ERROR"
exit 1
fi
}
extract_zip() {
local zip_file=$1
local dest_file=$2
if command -v unzip >/dev/null 2>&1; then
unzip -p "$zip_file" > "$dest_file" || { echo "$MSG_EXTRACT_ERROR"; exit 1; }
elif command -v gunzip >/dev/null 2>&1; then
gunzip -c "$zip_file" > "$dest_file" || { echo "$MSG_EXTRACT_ERROR"; exit 1; }
else
echo "$MSG_EXTRACT_ERROR"
exit 1
fi
}
select_version() {
if [[ -n "$VERSION" ]]; then
if [[ "$VERSION" == 7.* ]]; then
V7=1
elif [[ "$VERSION" == 6.* ]]; then
V7=0
else
echo "Error: Unsupported version $VERSION"
exit 1
fi
echo "$MSG_SELECTED_VERSION $VERSION"
return
fi
while true; do
case $ARCH in
x86_64|i386|i486|i586|i686)
echo "$MSG_SELECT_VERSION"
echo "1. $MSG_STABLE"
echo "2. $MSG_TEST"
echo "3. $MSG_LTS"
echo "4. $MSG_STABLE6"
read -p "$MSG_PLEASE_CHOOSE [1-4]" version_choice
;;
aarch64)
echo "$MSG_SELECT_VERSION"
echo "1. $MSG_STABLE"
echo "2. $MSG_TEST"
read -p "$MSG_PLEASE_CHOOSE [1-2]" version_choice
;;
*)
echo "$MSG_UNSUPPORTED_ARCH $ARCH"
exit 1
;;
esac
case $version_choice in
1)
VERSION=$(http_get "https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable" | cut -d' ' -f1)
V7=1
;;
2)
VERSION=$(http_get "https://upgrade.mikrotik.ltd/routeros/NEWESTa7.testing" | cut -d' ' -f1)
V7=1
;;
3)
if [[ "$ARCH" == "aarch64" ]]; then
echo "$MSG_ARM64_NOT_SUPPORT_V6"
continue
fi
VERSION=$(http_get "https://upgrade.mikrotik.ltd/routeros/NEWEST6.long-term" | cut -d' ' -f1)
V7=0
;;
4)
if [[ "$ARCH" == "aarch64" ]]; then
echo "$MSG_ARM64_NOT_SUPPORT_V6"
continue
fi
VERSION=$(http_get "https://upgrade.mikrotik.ltd/routeros/NEWEST6.stable" | cut -d' ' -f1)
V7=0
;;
*)
echo "$MSG_INVALID_OPTION"
continue
;;
esac
echo "$MSG_SELECTED_VERSION $VERSION"
break
done
}
download_image(){
case $ARCH in
x86_64|i386|i486|i586|i686)
if [[ $V7 == 1 && $BOOT_MODE == "BIOS" ]]; then
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$VERSION/chr-$VERSION-legacy-bios.img.zip"
else
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$VERSION/chr-$VERSION.img.zip"
fi
;;
aarch64)
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$VERSION-arm64/chr-$VERSION-arm64.img.zip"
;;
*)
echo "$MSG_UNSUPPORTED_ARCH"
exit 1
;;
esac
echo "$MSG_FILE_DOWNLOAD $(basename "$IMG_URL")"
http_get "$IMG_URL" "/tmp/chr.img.zip"
cd /tmp
extract_zip "chr.img.zip" chr.img
}
create_autorun() {
LOOP=$(losetup -f)
if losetup -P "$LOOP" chr.img; then
sleep 1
MNT=/tmp/chr
mkdir -p $MNT
PARTITION=$([ "$V7" == 1 ] && echo "p2" || echo "p1")
if mount "${LOOP}${PARTITION}" "$MNT"; then
confirm_address
RANDOM_ADMIN_PASS=$(head -c 512 /dev/urandom | tr -dc 'A-HJ-KMNP-Za-hj-kmnp-z2-9' | head -c 16)
ask_until "$MSG_ADMIN_PASSWORD" "$RANDOM_ADMIN_PASS"
RANDOM_ADMIN_PASS=$resp
cat <<EOF > "$MNT/rw/autorun.scr"
/user set admin password="$RANDOM_ADMIN_PASS"
/ip dns set servers=$DNS
/ip address add address=$ADDRESS interface=[/interface ethernet get [find mac-address=$MAC] name]
/ip route add gateway=$GATEWAY
EOF
cat "$MNT/rw/autorun.scr"
echo "$MSG_AUTO_RUN_FILE_CREATED"
umount $MNT
losetup -d "$LOOP"
else
losetup -d "$LOOP"
echo "$MSG_ERROR_MOUNT $PARTITION"
echo "$MSG_AUTO_RUN_FILE_NOT_CREATED"
fi
else
echo "$MSG_ERROR_LOOP"
echo "$MSG_AUTO_RUN_FILE_NOT_CREATED"
fi
}
write_reboot() {
confirm_storage
printf "$MSG_WARNING\n" "$STORAGE"
ask_yesno "$MSG_CONFIRM_CONTINUE"
if [ $? -ne 0 ]; then
echo "$MSG_OPERATION_ABORTED"
exit 1
fi
sync
dd if=chr.img of=/dev/$STORAGE bs=4M conv=fsync
echo "$MSG_REBOOTING"
echo 1 > /proc/sys/kernel/sysrq 2>/dev/null || true
echo b > /proc/sysrq-trigger 2>/dev/null || true
reboot -f
}
select_language
show_system_info
select_version
download_image
create_autorun
write_reboot
exit 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

770
index.html Normal file
View File

@ -0,0 +1,770 @@
<!doctype html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MikroTik RouterOS Patched Versions</title>
<!-- Tailwind CSS framework for styling -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Iconify icon library for icons -->
<script src="https://code.iconify.design/3/3.1.1/iconify.min.js"></script>
<!-- Google Fonts preconnect for performance optimization -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Mirror fonts for Chinese users (fonts.loli.net) -->
<link rel="preconnect" href="https://fonts.loli.net">
<link rel="preconnect" href="https://gstatic.loli.net" crossorigin>
<!-- Main font stylesheet with Inter and Fira Code fonts -->
<link id="webfont-css" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap" rel="stylesheet">
<!-- Font loading fallback script for Chinese users -->
<script>
(function(){
var linkEl = document.getElementById('webfont-css');
if (!linkEl) return;
var mirrorHref = 'https://fonts.loli.net/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap';
// Check if a specific font is loaded
function isLoaded(name){
if (!('fonts' in document)) return false;
try { return document.fonts.check('1em "' + name + '"'); } catch (e) { return false; }
}
// Switch to mirror font server if Google Fonts fails
function switchToMirror(){
if (linkEl.href !== mirrorHref) { linkEl.href = mirrorHref; }
}
if (!('fonts' in document)) { switchToMirror(); return; }
var decided = false;
// Timeout fallback - switch to mirror after 2.5 seconds if fonts not loaded
var timer = setTimeout(function(){
if (!decided && (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
}
}, 2500);
// Check when fonts are ready
document.fonts.ready.then(function(){
if (!decided && (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
} else {
decided = true; clearTimeout(timer);
}
});
})();
</script>
<!-- Custom CSS styles using Tailwind CSS -->
<style type="text/tailwindcss">
/* Main body styling with gradient background */
body {
font-family: 'Inter', sans-serif;
background-image: radial-gradient(circle at top, #dde3ee 0%, #f1f5f9 60%);
}
/* Dark mode gradient background */
html.dark body {
background-image: radial-gradient(circle at top, #1e293b 0%, #0f172a 60%);
}
/* Monospace font for command code blocks */
#command pre, #command code {
font-family: 'Fira Code', monospace;
}
/* Custom scrollbar styling */
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #94a3b8;
border-radius: 10px;
}
/* Dark mode scrollbar */
html.dark .custom-scrollbar::-webkit-scrollbar-thumb {
background: #475569;
}
/* Active state for version selection buttons */
#version-buttons button.active {
@apply bg-blue-600 text-white shadow-md;
}
/* Hide default details marker for custom styling */
summary::-webkit-details-marker {
display: none;
}
/* Content card animation classes */
.content-card {
@apply transition-all duration-700;
}
/* Loading state animation */
.loading .content-card {
@apply opacity-0 translate-y-4;
}
</style>
<!-- Tailwind CSS configuration -->
<script>
tailwind.config = {
darkMode: 'class', // Enable class-based dark mode
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'], // Default sans-serif font
mono: ['Fira Code', 'monospace'], // Monospace font for code
},
},
},
}
</script>
</head>
<!-- Main body with dark mode support and loading animation -->
<body class="bg-slate-100 dark:bg-gray-900 text-slate-700 dark:text-slate-300 transition-colors duration-300 loading">
<!-- GitHub corner ribbon (top-right corner) -->
<a href="https://github.com/elseif/MikroTikPatch" class="github-corner fixed top-0 right-0 z-50" aria-label="View source on GitHub" target="_blank">
<svg width="80" height="80" viewBox="0 0 250 250" class="fill-slate-800 dark:fill-slate-50" style="position: absolute; top: 0; border: 0; right: 0;">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm text-slate-50 dark:text-gray-900"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.6,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body text-slate-50 dark:text-gray-900"></path>
</svg>
</a>
<!-- Main container with responsive padding -->
<div class="container mx-auto max-w-7xl px-4 py-8 md:py-16">
<!-- Page header section -->
<header class="text-center mb-12 content-card" style="transition-delay: 100ms;">
<!-- Main title with gradient text effect -->
<h1 class="text-4xl md:text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-sky-400 dark:from-blue-400 dark:to-sky-300 mb-3">MikroTik RouterOS</h1>
<p class="text-xl md:text-2xl text-slate-600 dark:text-slate-400">Patched Versions</p>
<!-- Status badges for build workflows and services -->
<div class="mt-6 flex justify-center items-center gap-4 flex-wrap">
<a href="https://github.com/elseif/MikroTikPatch/actions/workflows/main.yml" target="_blank"><img src="https://github.com/elseif/MikroTikPatch/actions/workflows/main.yml/badge.svg" alt="Patch Mikrotik RouterOS"></a>
<img src="https://img.shields.io/endpoint?logo=icloud&url=https://mikrotik.ltd/status/cloud" alt="Cloud Status">
</div>
<!-- GitHub social buttons -->
<div class="mt-4 flex justify-center items-center gap-2">
<script async defer src="https://buttons.github.io/buttons.js"></script>
<a class="github-button" href="https://github.com/elseif/MikroTikPatch" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star elseif/MikroTikPatch on GitHub">Star</a>
<a class="github-button" href="https://github.com/elseif/MikroTikPatch/fork" data-icon="octicon-repo-forked" data-size="large" data-show-count="true" aria-label="Fork elseif/MikroTikPatch on GitHub">Fork</a>
</div>
</header>
<!-- Main content area -->
<main class="space-y-12">
<!-- Information and settings card -->
<div class="bg-white/60 dark:bg-gray-800/60 rounded-2xl shadow-lg p-6 md:p-8 ring-1 ring-black/5 backdrop-blur-xl content-card" style="transition-delay: 200ms;">
<div class="grid md:grid-cols-2 gap-8">
<!-- Quick information section -->
<div>
<h2 class="text-lg font-semibold text-slate-800 dark:text-white mb-4 flex items-center"><span class="iconify mr-2 text-blue-500" data-icon="ph:info-bold"></span><span data-i18n="quickInformation"></span></h2>
<div class="space-y-3 text-sm">
<p data-i18n="infoLabel1"></p>
<p data-i18n="infoLabel2"></p>
</div>
</div>
<!-- Settings section -->
<div class="border-t md:border-t-0 md:border-l border-slate-200 dark:border-gray-700 pt-6 md:pt-0 md:pl-8">
<h2 class="text-lg font-semibold text-slate-800 dark:text-white mb-4 flex items-center"><span class="iconify mr-2 text-blue-500" data-icon="ph:gear-six-bold"></span><span data-i18n="settings"></span></h2>
<div class="space-y-4">
<!-- Theme toggle switch -->
<div class="flex items-center justify-between">
<label for="theme-toggle-btn" class="font-medium" data-i18n="theme"></label>
<button id="theme-toggle-btn" class="p-2 rounded-full text-slate-500 hover:bg-slate-200 dark:text-slate-400 dark:hover:bg-slate-700 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 focus:ring-offset-white dark:focus:ring-offset-gray-900">
<span class="sr-only">Toggle theme</span>
<span class="iconify text-xl sun-icon" data-icon="ph:sun-bold"></span>
<span class="iconify text-xl moon-icon" data-icon="ph:moon-bold"></span>
</button>
</div>
<!-- Language selector -->
<div class="flex items-center justify-between">
<label for="lang-switcher" data-i18n="language" class="font-medium"></label>
<select id="lang-switcher" class="bg-slate-100 dark:bg-gray-700 border border-slate-300 dark:border-gray-600 rounded-md p-1.5 text-sm focus:ring-blue-500 focus:border-blue-500">
<option value="en">English</option>
<option value="zh">中文</option>
</select>
</div>
<!-- GitHub proxy toggle -->
<div class="flex items-center justify-between">
<label for="gh-proxy" data-i18n="proxyLabel" class="font-medium text-sm"></label>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="gh-proxy" name="gh-proxy" class="sr-only peer">
<div class="w-11 h-6 bg-slate-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Install command section -->
<div class="bg-white/60 dark:bg-gray-800/60 rounded-2xl shadow-lg ring-1 ring-black/5 backdrop-blur-xl overflow-hidden content-card" style="transition-delay: 300ms;">
<div class="p-6 md:p-8">
<h2 class="text-lg font-semibold text-slate-800 dark:text-white mb-4 flex items-center" data-i18n="installCmdLabel"><span class="iconify mr-2 text-blue-500" data-icon="ph:terminal-window-bold"></span></h2>
<!-- Version and tool selection controls -->
<div class="flex flex-wrap items-center gap-2 mb-4">
<!-- Version selection buttons (populated by JavaScript) -->
<div id="version-buttons" class="flex flex-wrap items-center gap-2 p-1 bg-slate-100 dark:bg-gray-700/50 rounded-lg">
</div>
<!-- Tool selection (curl/wget) -->
<div class="flex items-center gap-2 p-1 bg-slate-100 dark:bg-gray-700/50 rounded-lg">
<label class="flex items-center px-2 cursor-pointer">
<input type="radio" name="tool" value="curl" checked class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<span class="ml-2 text-sm font-medium">curl</span>
</label>
<label class="flex items-center px-2 cursor-pointer">
<input type="radio" name="tool" value="wget" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<span class="ml-2 text-sm font-medium">wget</span>
</label>
</div>
</div>
<!-- Command display area with copy button -->
<div id="command-container" class="relative bg-slate-900 dark:bg-black/50 p-4 text-sm">
<button id="copy-btn" class="absolute top-3 right-3 p-1.5 bg-slate-700 hover:bg-slate-600 rounded-md text-slate-300 hover:text-white transition-colors">
<span class="sr-only">Copy command</span>
<span class="iconify text-lg copy-icon" data-icon="ph:copy-bold"></span>
<span class="iconify text-lg check-icon hidden" data-icon="ph:check-bold"></span>
</button>
<div id="command"></div>
</div>
</div>
</div>
<!-- Download sections container (populated by JavaScript) -->
<div id="download-rows" class="space-y-8 content-card" style="transition-delay: 400ms;">
<!-- Loading overlay shown while fetching version data -->
<div class="loading-overlay fixed inset-0 bg-slate-100/80 dark:bg-gray-900/80 flex flex-col items-center justify-center z-50" id="loading">
<span class="iconify text-4xl text-blue-600 animate-spin" data-icon="ph:spinner-gap-bold"></span>
<span data-i18n="loading" class="mt-4 text-lg font-medium"></span>
</div>
</div>
</main>
<!-- Footer with banner-style social links -->
<footer class="mt-16 pt-8 border-t border-slate-200 dark:border-gray-700/50">
<div class="text-center">
<!-- Social links without container background -->
<div class="flex justify-center items-center gap-4">
<!-- Telegram link -->
<a href="https://t.me/mikrotikpatch" target="_blank"
class="group flex items-center gap-3 px-6 py-3 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white shadow-md hover:shadow-lg transition-all duration-300 hover:scale-105">
<span class="iconify text-xl transition-transform duration-300 group-hover:rotate-12" data-icon="ph:telegram-logo-bold"></span>
<span class="font-semibold text-sm">Telegram</span>
</a>
<!-- GitHub link -->
<a href="https://github.com/elseif/MikroTikPatch" target="_blank"
class="group flex items-center gap-3 px-6 py-3 rounded-xl bg-gradient-to-r from-gray-800 to-gray-900 hover:from-gray-700 hover:to-gray-800 dark:from-slate-600 dark:to-slate-700 dark:hover:from-slate-500 dark:hover:to-slate-600 text-white shadow-md hover:shadow-lg transition-all duration-300 hover:scale-105">
<span class="iconify text-xl transition-transform duration-300 group-hover:rotate-12" data-icon="ph:github-logo-bold"></span>
<span class="font-semibold text-sm">GitHub</span>
</a>
<!-- Telegram Keyget Bot link -->
<a href="https://t.me/ROS_Keygen_Bot" target="_blank"
class="group flex items-center gap-3 px-6 py-3 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white shadow-md hover:shadow-lg transition-all duration-300 hover:scale-105">
<span class="iconify text-xl transition-transform duration-300 group-hover:rotate-12" data-icon="ph:telegram-logo-bold"></span>
<span class="font-semibold text-sm">Keygen Bot</span>
</a>
</div>
</div>
</footer>
</div>
<!-- Changelog popup modal -->
<div id="changelog-modal" class="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4 hidden" tabindex="-1">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-4xl max-h-[90vh] flex flex-col transform transition-all opacity-0 scale-95" id="changelog-modal-content">
<header class="p-4 border-b border-slate-200 dark:border-gray-700 flex justify-between items-center">
<h3 class="text-lg font-semibold text-slate-800 dark:text-white flex items-center gap-2">
<span class="iconify text-blue-500" data-icon="ph:newspaper-clipping-bold"></span>
<span data-i18n="changelog"></span>
</h3>
<button id="changelog-close" class="p-1 rounded-full text-slate-400 hover:bg-slate-100 dark:hover:bg-gray-700">
<span class="sr-only">Close modal</span>
<span class="iconify text-2xl" data-icon="ph:x-bold"></span>
</button>
</header>
<!-- Changelog content display area -->
<div id="changelog-content" class="p-6 text-sm text-slate-600 dark:text-slate-400 overflow-y-auto custom-scrollbar flex-grow bg-slate-50 dark:bg-gray-900/50 rounded-b-xl">
<!-- Loading state for changelog -->
<div class="flex items-center justify-center py-8">
<span class="iconify text-4xl text-blue-600 animate-spin" data-icon="ph:spinner-gap-bold"></span>
<span class="ml-4 text-lg font-medium" data-i18n="loadingChangelog"></span>
</div>
</div>
</div>
</div>
<!-- Main JavaScript functionality -->
<script>
document.addEventListener("DOMContentLoaded", async () => {
// Icon definitions for consistent icon usage
const ICONS = {
download: 'ph:download-simple-bold',
changelog: 'ph:newspaper-clipping-bold',
trash: 'ph:trash-bold',
copyAndOpen: 'ph:copy-simple-bold'
};
// Function to fetch latest version numbers with timeout fallback
async function fetch_latest_versions(url, defaultValue, timeout = 3000) {
try {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const res = await fetch(url, { signal: controller.signal });
clearTimeout(id);
if (res.ok) {
const text = await res.text();
return text.split(" ")[0]; // Extract version number from response
}
} catch (e) {
console.warn("fetch failed or timeout:", e);
}
return defaultValue; // Return fallback version if fetch fails
}
// Internationalization (i18n) translations for English and Chinese
const i18n = {
en: {
language: "Language",
infoLabel1:`If you are already running <b>patched</b> RouterOS, upgrading to the latest version can be done by clicking on <b>"Check For Updates"</b> in <b>QuickSet or System > Packages</b> menu in WebFig or WinBox.`,
infoLabel2:`For more information about <b>install and upgrade</b> see the <a href="https://help.mikrotik.com/docs/" target="_blank" class="text-blue-500 dark:text-blue-400 hover:underline">Documentation</a>. For more information about <b>patch</b> see the <a href="https://github.com/elseif/MikroTikPatch" target="_blank" class="text-blue-500 dark:text-blue-400 hover:underline">MikroTikPatch</a>.`,
installCmdLabel:"Install Command",
proxyLabel:"Enable GitHub Proxy Accelerator",
loading:"Loading latest versions...",
quickInformation:"Quick Information",
settings:"Settings",
theme:"Theme",
changelog:"Changelog",
loadingChangelog:"Loading changelog...",
changelogError:"Failed to load changelog. Please try again later.",
},
zh: {
language: "语言",
infoLabel1:`如果你已经在运行<b>Patch</b>过的RouterOS可以通过WebFig或WinBox中的<b>QuickSet或System > Packages</b>菜单点击<b>"Check For Updates"</b>来升级到最新版本。`,
infoLabel2:`有关<b>安装和升级</b>的更多信息,请参阅<a href="https://help.mikrotik.com/docs/" target="_blank" class="text-blue-500 dark:text-blue-400 hover:underline">文档</a><br>有关<b>Patch</b>的更多信息,请参阅<a href="https://github.com/elseif/MikroTikPatch" target="_blank" class="text-blue-500 dark:text-blue-400 hover:underline">MikroTikPatch</a>`,
installCmdLabel:"安装命令",
proxyLabel:"启用GitHub代理加速",
loading:"正在加载最新版本...",
quickInformation:"快速信息",
settings:"设置",
theme:"主题",
changelog:"更新日志",
loadingChangelog:"正在加载更新日志...",
changelogError:"加载更新日志失败,请稍后重试。",
}
};
// Function to set language and update all i18n elements
function setLanguage(lang) {
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute("data-i18n");
if (i18n[lang] && i18n[lang][key]) {
// Special handling for buttons that need icons
if (key === 'installCmdLabel') {
el.innerHTML = `<span class="iconify mr-2 text-blue-500" data-icon="ph:terminal-window-bold"></span>${i18n[lang][key]}`;
}
else {
el.innerHTML = i18n[lang][key];
}
}
});
localStorage.setItem("lang", lang); // Save language preference
}
// Initialize language based on saved preference or browser language
function initLanguage() {
let savedLang = localStorage.getItem("lang");
if (!savedLang) {
// Auto-detect Chinese language, default to English
savedLang = navigator.language.startsWith("zh") ? "zh" : "en";
}
document.getElementById("lang-switcher").value = savedLang;
setLanguage(savedLang);
}
// Language switcher event listener
document.getElementById("lang-switcher").addEventListener("change", e => {
setLanguage(e.target.value);
});
// Theme toggle functionality
const themeBtn = document.getElementById("theme-toggle-btn");
const htmlEl = document.documentElement;
const sunIcon = themeBtn.querySelector('.sun-icon');
const moonIcon = themeBtn.querySelector('.moon-icon');
// Update theme UI icons based on current theme
function updateThemeUI() {
if (htmlEl.classList.contains('dark')) {
sunIcon.classList.remove('hidden');
moonIcon.classList.add('hidden');
} else {
sunIcon.classList.add('hidden');
moonIcon.classList.remove('hidden');
}
}
// Initialize theme based on saved preference or system preference
function initializeTheme() {
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
htmlEl.classList.toggle("dark", savedTheme === "dark");
} else {
// Use system preference if no saved theme
htmlEl.classList.toggle("dark", window.matchMedia('(prefers-color-scheme: dark)').matches);
}
updateThemeUI();
}
// Theme toggle button event listener
themeBtn.addEventListener("click", () => {
htmlEl.classList.toggle("dark");
localStorage.setItem("theme", htmlEl.classList.contains("dark") ? "dark" : "light");
updateThemeUI();
});
// Initialize theme and language
initializeTheme();
initLanguage();
// Fetch latest version numbers from MikroTik servers
const loadingOverlay = document.getElementById("loading");
const [routeros7_stable, routeros7_testing, routeros6_longterm, routeros6_stable] = await Promise.all([
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable?ts=${Date.now()}`, "7.19.4"),
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWESTa7.testing?ts=${Date.now()}`, "7.20rc1"),
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWEST6.long-term?ts=${Date.now()}`, "6.49.18"),
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWEST6.stable?ts=${Date.now()}`, "6.49.19"),
]);
// Hide loading overlay and remove loading class
loadingOverlay.style.display = "none";
document.body.classList.remove('loading');
// Download sections configuration with versions and file types
const downloads =[
{
title:"RouterOS v7",
versions:[ { title:"Stable", version:routeros7_stable }, { title:"Testing", version:routeros7_testing } ],
groups: [
{ title:"ARM64 / AMPERE", arch:"arm64", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"ISO image for AMPERE", type:"iso" } ] },
{ title:"X86", arch:"x86", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"CD Image", type:"iso" }, { title:"Install image", type:"install" } ] },
{ title:"GENERAL", arch:"general", downloads:[ { title:"Netinstall (Windows)", type:"netinstall_windows" }, { title:"Netinstall (CLI Linux)", type:"netinstall_linux_cli" }, { title:"Changelog", type:"changlog" } ] }
]
},
{
title:"RouterOS v6",
versions:[ { title:"Long-Term", version:routeros6_longterm }, { title:"Stable", version:routeros6_stable } ],
groups: [
{ title:"X86", arch:"x86", downloads:[ { title:"Main package", type:"main" },{ title:"Extra packages", type:"extra" }, { title:"CD Image", type:"iso" }, { title:"Install image", type:"install" }, { title:"Changelog", type:"changlog" } ] }
]
},
{
title:"Cloud Hosted Router",
versions:[ { title:"Long-Term", version:routeros6_longterm }, { title:"Stable", version:routeros6_stable }, { title:"Stable", version:routeros7_stable }, { title:"Testing", version:routeros7_testing } ],
groups:[
{ title:"X86", arch:"x86", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"VHDX image", type:"vhdx" }, { title:"VMDK image", type:"vmdk" }, { title:"VDI image", type:"vdi" }, { title:"VirtualPC image", type:"vhd" }, { title:"Raw disk image", type:"img" }, { title:"OVA template", type:"ova" } ] },
{ title:"ARM64 / AMPERE", arch:"arm64", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"VHDX image", type:"vhdx" }, { title:"VMDK image", type:"vmdk" }, { title:"VDI image", type:"vdi" }, { title:"VirtualPC image", type:"vhd" }, { title:"Raw disk image", type:"img" } ] }
]
},
];
// Function to generate file names based on version, architecture, and type
const fileNames = (version, arch, type) => {
const files = [];
// RouterOS v6 only supports x86 architecture
if (version.charAt(0) === "6" && arch !== "x86") return files;
// Main package for RouterOS v7
if (type === "main" && version.charAt(0) === "7") files.push(`routeros-${version}${arch === "x86" ? "" : "-" + arch}.npk`);
// Main package for RouterOS v6
if (type === "main" && version.charAt(0) === "6") files.push(`routeros-x86-${version}.npk`);
// ISO images
if (type === "iso") files.push(`mikrotik-${version}${arch === "x86" ? "" : "-" + arch}.iso`);
// Extra packages
if (type === "extra") files.push(`all_packages-${arch}-${version}.zip`);
// Install images
if (type === "install") files.push(`install-image-${version}${arch === "x86" ? "" : "-" + arch}.zip`);
// Virtual machine images
if (type in { vhdx: 1, vmdk: 1, vdi: 1, vhd: 1, img: 1 }) {
files.push(`chr-${version}${arch === "x86" ? "" : "-" + arch}.${type}.zip`);
// Add legacy BIOS version for x86 RouterOS v7
if (arch === "x86" && version.charAt(0) !== "6") files.push(`chr-${version}-legacy-bios.${type}.zip`);
}
// OVA templates (RouterOS v7 only)
if (type === "ova" && version.charAt(0) !== "6") {
files.push(`chr-${version}${arch === "x86" ? "" : "-" + arch}.${type}.zip`);
if (arch === "x86") files.push(`chr-${version}-legacy-bios.${type}.zip`);
}
// Netinstall packages
if (type === "netinstall_windows") files.push(`netinstall-${version}.zip`);
if (type === "netinstall_linux_cli") files.push(`netinstall-${version}.tar.gz`);
return files;
};
// Generate base URL for downloads with optional GitHub proxy
const baseUrl = (version, arch) => `${localStorage.getItem("gh-proxy-enabled") === "true" ? "https://gh-proxy.com/" : ""}https://github.com/elseif/MikroTikPatch/releases/download/${version}${arch === "x86" || arch === "general" ? "" : "-" + arch}/`;
// Clear and populate download sections
const container = document.getElementById("download-rows");
container.innerHTML = '';
// Generate download sections dynamically
downloads.forEach((download, index) => {
const accordion = document.createElement("details");
accordion.className = "bg-white/60 dark:bg-gray-800/60 rounded-2xl shadow-lg ring-1 ring-black/5 backdrop-blur-xl overflow-hidden group";
accordion.open = index === 0; // Open first section by default
const summary = document.createElement("summary");
summary.className = "p-6 cursor-pointer text-xl font-bold text-slate-800 dark:text-white flex justify-between items-center";
summary.innerHTML = `<span>${download.title}</span><span class="iconify text-2xl transition-transform duration-300 group-open:rotate-180" data-icon="ph:caret-down-bold"></span>`;
const content = document.createElement("div");
content.className = "px-6 pb-6";
const table = document.createElement("div");
table.className = "overflow-x-auto";
table.innerHTML = `
<table class="w-full min-w-max text-sm text-left text-slate-500 dark:text-slate-400">
<thead class="text-xs text-slate-700 dark:text-slate-200 uppercase bg-slate-50/80 dark:bg-gray-700/80">
<tr>
<th scope="col" class="px-6 py-3 rounded-l-lg">Package / Architecture</th>
${download.versions.map(v => `<th scope="col" class="px-6 py-3 text-center">${v.version}<br><span class="font-normal normal-case">${v.title}</span></th>`).join("")}
<th class="rounded-r-lg"></th>
</tr>
</thead>
<tbody>
${download.groups.map(g => `
<tr class="bg-slate-100/80 dark:bg-gray-700/50">
<th colspan="${download.versions.length + 2}" class="px-6 py-2 text-base font-semibold text-slate-800 dark:text-white">${g.title}</th>
</tr>
${g.downloads.map(d => `
<tr class="bg-transparent border-b last:border-b-0 border-slate-200 dark:border-gray-700 hover:bg-slate-50/50 dark:hover:bg-gray-800/50 transition-colors">
<td class="px-6 py-4 font-medium text-slate-900 dark:text-white whitespace-nowrap">${d.title}</td>
${download.versions.map(v => {
if (d.type === "changlog") {
return `<td class="px-6 py-4 text-center"><button onclick="showChangelog('${v.version}')" class="inline-flex items-center gap-1.5 text-blue-500 dark:text-blue-400 hover:underline cursor-pointer" title="Changelog"><span class="iconify" data-icon="${ICONS.changelog}"></span> View</button></td>`;
}
const files = fileNames(v.version, g.arch, d.type);
if (!files || files.length === 0) return `<td class="px-6 py-4"></td>`;
const links = files.map(file => {
const url = `${baseUrl(v.version, g.arch)}${file}`;
let label = "Download";
if (download.title === "Cloud Hosted Router" && file.includes('chr')) {
if (v.version.charAt(0) === "6" ){
label = "Legacy";
} else {
label = file.includes("legacy-bios") ? "Legacy" : "UEFI";
}
}
return `<a href="${url}" class="inline-flex items-center gap-1.5 text-blue-500 dark:text-blue-400 hover:underline" title="${file}" target="_blank"><span class="iconify" data-icon="${ICONS.download}"></span> ${label}</a>`;
}).join("");
return `<td class="px-6 py-4 text-center space-x-2">${links}</td>`;
}).join("")}
<td></td>
</tr>
`).join("")}
`).join("")}
</tbody>
</table>
`;
content.appendChild(table);
accordion.appendChild(summary);
accordion.appendChild(content);
container.appendChild(accordion);
});
// GitHub proxy functionality
const ghProxyCheckbox = document.getElementById("gh-proxy");
if (localStorage.getItem("gh-proxy-enabled") === null && navigator.language.startsWith("zh")) {
// Enable GitHub proxy by default for Chinese users
localStorage.setItem("gh-proxy-enabled", "true");
}
ghProxyCheckbox.checked = localStorage.getItem("gh-proxy-enabled") === "true";
// Update all download links with/without GitHub proxy
function updateAllDownloadLinks() {
// Only apply proxy to download links (releases/download), not to other GitHub links
document.querySelectorAll('a[href*="github.com/elseif/MikroTikPatch/releases/download"]').forEach(link => {
const isProxyEnabled = localStorage.getItem("gh-proxy-enabled") === "true";
const hasProxy = link.href.includes('gh-proxy.com');
if (isProxyEnabled && !hasProxy) {
link.href = link.href.replace("https://github.com/", "https://gh-proxy.com/https://github.com/");
} else if (!isProxyEnabled && hasProxy) {
link.href = link.href.replace("https://gh-proxy.com/", "");
}
});
// Update command display if a version is selected
const activeBtn = document.querySelector("#version-buttons button.active");
activeBtn ? updateCommand(activeBtn.dataset.version) : updateCommand();
}
// GitHub proxy toggle event listener
ghProxyCheckbox.addEventListener("change", () => {
localStorage.setItem("gh-proxy-enabled", ghProxyCheckbox.checked ? "true" : "false");
updateAllDownloadLinks();
});
// Changelog modal functionality
const changelogModal = document.getElementById("changelog-modal");
const changelogModalContent = document.getElementById("changelog-modal-content");
const changelogContent = document.getElementById("changelog-content");
const changelogClose = document.getElementById("changelog-close");
// Fetch changelog content from MikroTik servers
async function fetchChangelog(version) {
try {
const response = await fetch(`https://upgrade.mikrotik.ltd/routeros/${version}/CHANGELOG`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const text = await response.text();
return text;
} catch (error) {
console.error("Failed to fetch changelog:", error);
return null;
}
}
// Global function to show changelog modal
window.showChangelog = function(version) {
changelogModal.classList.remove('hidden');
changelogContent.innerHTML = `
<div class="flex items-center justify-center py-8">
<span class="iconify text-4xl text-blue-600 animate-spin" data-icon="ph:spinner-gap-bold"></span>
<span class="ml-4 text-lg font-medium" data-i18n="loadingChangelog"></span>
</div>
`;
setLanguage(document.getElementById("lang-switcher").value);
setTimeout(() => {
changelogModalContent.classList.add('opacity-100', 'scale-100');
changelogModalContent.classList.remove('opacity-0', 'scale-95');
}, 10);
// Fetch and display changelog content
fetchChangelog(version).then(content => {
if (content) {
changelogContent.innerHTML = `
<div class="prose prose-slate dark:prose-invert max-w-none">
<pre class="whitespace-pre-wrap text-sm leading-relaxed">${content}</pre>
</div>
`;
} else {
// Show error state with retry button
changelogContent.innerHTML = `
<div class="flex flex-col items-center justify-center py-8 text-center">
<span class="iconify text-6xl text-red-500 mb-4" data-icon="ph:warning-circle-bold"></span>
<p class="text-lg font-medium text-slate-700 dark:text-slate-300 mb-2" data-i18n="changelogError"></p>
<button onclick="showChangelog('${version}')" class="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">
<span class="iconify mr-2" data-icon="ph:arrow-clockwise-bold"></span>
Retry
</button>
</div>
`;
setLanguage(document.getElementById("lang-switcher").value);
}
});
};
// Hide changelog modal with animation
function hideChangelog() {
changelogModalContent.classList.remove('opacity-100', 'scale-100');
changelogModalContent.classList.add('opacity-0', 'scale-95');
setTimeout(() => changelogModal.classList.add('hidden'), 200);
}
changelogClose.addEventListener("click", hideChangelog);
// Close modal when clicking outside
changelogModal.addEventListener("click", (e) => {
if (e.target === changelogModal) {
hideChangelog();
}
});
// Close modal with Escape key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && !changelogModal.classList.contains("hidden")) {
hideChangelog();
}
});
// Command generation and version selection
const versionBtnsContainer = document.getElementById("version-buttons");
const commandDiv = document.getElementById("command");
const copyBtn = document.getElementById("copy-btn");
// Generate version selection buttons
versionBtnsContainer.innerHTML = `
<button data-version="${routeros7_stable}" class="px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all">v7 Stable</button>
<button data-version="${routeros7_testing}" class="px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all">v7 Testing</button>
<button data-version="${routeros6_longterm}" class="px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all">v6 Long-Term</button>
<button data-version="${routeros6_stable}" class="px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all">v6 Stable</button>
`;
// Update command display based on selected tool and version
function updateCommand(version) {
const tool = document.querySelector('input[name="tool"]:checked').value;
const _version = version===undefined?"": `<span class="text-green-500 font-semibold">VERSION</span>=<span class="text-yellow-400">${version}</span> `
const cmd = tool === "curl"
? `<pre class="text-slate-300"><code>${_version}<span class="text-blue-400 font-bold">bash</span> <span class="text-pink-400">&lt;(</span><span class="text-blue-400 font-bold">curl</span> <span class="text-teal-400">https://mikrotik.ltd/chr.sh</span><span class="text-pink-400">)</span></code></pre>`
: `<pre class="text-slate-300"><code>${_version}<span class="text-blue-400 font-bold">bash</span> <span class="text-pink-400">&lt;(</span><span class="text-blue-400 font-bold">wget -O - </span><span class="text-teal-400">https://mikrotik.ltd/chr.sh</span><span class="text-pink-400">)</span></code></pre>`;
commandDiv.innerHTML = cmd;
}
// Tool selection event listeners
document.querySelectorAll('input[name="tool"]').forEach(radio => {
radio.addEventListener("change", () => {
const activeBtn = document.querySelector("#version-buttons button.active");
activeBtn ? updateCommand(activeBtn.dataset.version) : updateCommand();
});
});
// Version button event listeners
const buttons = document.querySelectorAll("#version-buttons button");
buttons.forEach(btn => {
btn.addEventListener("click", (e) => {
e.preventDefault();
if (btn.classList.contains("active")) {
updateCommand();
btn.classList.remove("active");
}else{
updateCommand(btn.dataset.version);
buttons.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
}
});
});
updateCommand();
// Copy command to clipboard functionality
copyBtn.addEventListener("click", () => {
const code = commandDiv.querySelector("code").innerText;
navigator.clipboard.writeText(code).then(() => {
copyBtn.querySelector('.copy-icon').classList.add('hidden');
copyBtn.querySelector('.check-icon').classList.remove('hidden');
setTimeout(() => {
copyBtn.querySelector('.copy-icon').classList.remove('hidden');
copyBtn.querySelector('.check-icon').classList.add('hidden');
}, 2000);
}).catch(err => console.error("Copy failed:", err));
});
});
</script>
</body>
</html>

Binary file not shown.

BIN
keygen/keygen_arm64 Normal file

Binary file not shown.

Binary file not shown.

BIN
mbr.bin

Binary file not shown.

View File

@ -1,8 +1,7 @@
import random
import struct
from sha256 import SHA256
from toyecc import AffineCurvePoint, getcurvebyname, FieldElement,ECPrivateKey,ECPublicKey,Tools
from toyecc.Random import secure_rand_int_between
MIKRO_BASE64_CHARACTER_TABLE = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
@ -39,8 +38,8 @@ def mikro_softwareid_encode(id:int)->str:
assert(isinstance(id, int))
ret = ''
for i in range(8):
ret += chr(SOFTWARE_ID_CHARACTER_TABLE[id % 0x23])
id //= 0x23
ret += chr(SOFTWARE_ID_CHARACTER_TABLE[id % len(SOFTWARE_ID_CHARACTER_TABLE)])
id //= len(SOFTWARE_ID_CHARACTER_TABLE)
if i == 3:
ret += '-'
return ret
@ -167,7 +166,7 @@ def mikro_kcdsa_sign(data:bytes,private_key:bytes)->bytes:
private_key:ECPrivateKey = ECPrivateKey(Tools.bytestoint_le(private_key), curve)
public_key:ECPublicKey = private_key.pubkey
while True:
nonce_secret = secure_rand_int_between(1, curve.n - 1)
nonce_secret = random.SystemRandom().randint(1, curve.n - 1)
nonce_point = nonce_secret * curve.G
nonce = int(nonce_point.x) % curve.n
nonce_hash = mikro_sha256(Tools.inttobytes_le(nonce,32))

169
npk.py
View File

@ -1,6 +1,6 @@
import struct,zlib
import argparse,os
import argparse,os,tempfile
from datetime import datetime
from dataclasses import dataclass
from enum import IntEnum
@ -51,7 +51,45 @@ class NpkInfo:
def name(self,value:str):
self._name = value[:16].encode().ljust(16,b'\x00')
@staticmethod
def decode_version(value:bytes):
def encode_version(version: str) -> bytes:
numbers = []
build = None
i = 0
n = len(version)
while i < n:
if version[i].isdigit():
num_str = ''
while i < n and version[i].isdigit():
num_str += version[i]
i += 1
numbers.append(int(num_str))
elif version[i] == '.':
i += 1
elif version[i].isalpha():
build = ''
while i < n and version[i].isalpha():
build += version[i]
i += 1
else:
i += 1
major = numbers[0] if len(numbers) > 0 else 0
minor = numbers[1] if len(numbers) > 1 else 0
revision = numbers[2] if len(numbers) > 2 else 0
if build == 'alpha':
build = 97
elif build == 'beta':
build = 98
elif build == 'rc':
build = 99
elif build == 'test':
build = 102
revision |= 0x80
else:# 'final'
build = 102
revision &= 0x7f
return struct.pack('4B',revision,build,minor,major)
@staticmethod
def decode_version(value:bytes)->str:
revision,build,minor,major = struct.unpack_from('4B',value)
if build == 97:
build = 'alpha'
@ -64,31 +102,10 @@ class NpkInfo:
build = 'test'
revision &= 0x7f
else:
build = 'final'
build = ''
else:
build = 'unknown'
return f'{major}.{minor}.{revision}.{build}'
@staticmethod
def encode_version(value:str):
s = value.split('.')
if 4 != len(s) and s[3] in [ 'alpha', 'beta', 'rc','final', 'test']:
raise ValueError('Invalid version string')
major = int(s[0])
minor = int(s[1])
revision = int(s[2])
if s[3] == 'alpha':
build = 97
elif s[3] == 'beta':
build = 98
elif s[3] == 'rc':
build = 99
elif s[3] == 'final':
build = 102
revision &= 0x7f
else: #'test'
build = 102
revision |= 0x80
return struct.pack('4B',revision,build,minor,major)
return f'{major}.{minor}{build + str(revision) if build !="" else "." + str(revision) if revision != 0 else ""}'
@property
def version(self)->str:
return self.decode_version(self._version)
@ -137,7 +154,7 @@ class NpkFileContainer:
self._items= items
def serialize(self)->bytes:
compressed_data = b''
compressor = zlib.compressobj()
compressor = zlib.compressobj(level=0)
for item in self._items:
data = struct.pack(self._format, item.perm,item.type,item.usr_or_grp, item.modify_time,item.revision,item.rc,item.minor,item.major,item.create_time,item.unknow,len(item.data),len(item.name))
data += item.name + item.data
@ -208,6 +225,47 @@ class NovaPackage(Package):
else:
self._parts.append(NpkPartItem(NpkPartID(part_id),part_data))
def set_null_block(self):
def rebuild_squashfs(data):
with tempfile.TemporaryDirectory() as tmp_dir:
squashfs_file = os.path.join(tmp_dir, 'squashfs.sfs')
squashfs_root = os.path.join(tmp_dir, 'squashfs-root')
with open(squashfs_file, 'wb') as f:
f.write(data)
os.system(f'unsquashfs -d {squashfs_root} {squashfs_file}')
os.remove(squashfs_file)
os.system(f'mksquashfs {squashfs_root} {squashfs_file} -no-recovery -noappend -exit-on-error -quiet -comp xz -no-xattrs -b 256k -all-root')
with open(squashfs_file, 'rb') as f:
return f.read()
def get_size(package,size):
for part in package._parts:
size += 6
size += len(part.data)
return size
def set_null(package,offset):
has_squashfs = False
for part in package._parts:
if part.id == NpkPartID.SQUASHFS and len(part.data) >= 4 and part.data[:4] in [b'hsqs',b'sqsh']:
part.data = rebuild_squashfs(part.data)
has_squashfs = True
if has_squashfs:
count = offset
for part in package._parts:
count += 6
if part.id == NpkPartID.NULL_BLOCK:
break
count += len(part.data)
count += 6
pad_len = (4096 - (count % 4096)) % 4096
package[NpkPartID.NULL_BLOCK].data = b'\x00' * pad_len
set_null(self,8)
offset = get_size(self,8)
for package in self._packages:
set_null(package,offset)
offset += get_size(package,0)
def get_digest(self,hash_fnc,package:Package=None)->bytes:
parts = package._parts if package else self._parts
for part in parts:
@ -228,18 +286,33 @@ class NovaPackage(Package):
def sign(self,kcdsa_private_key:bytes,eddsa_private_key:bytes):
import hashlib
from mikro import mikro_kcdsa_sign,mikro_eddsa_sign
build_time = os.environ['BUILD_TIME'] if 'BUILD_TIME' in os.environ else None
build_time = os.getenv('BUILD_TIME',None)
self.set_null_block()
if len(self._packages) > 0:
if build_time:
self[NpkPartID.PKG_INFO].data._build_time = int(build_time)
else:
build_time = self[NpkPartID.PKG_INFO].data._build_time
os.environ['BUILD_TIME'] = str(build_time)
github_env = os.getenv('GITHUB_ENV')
if github_env:
with open(github_env, 'a') as f:
f.write(f"BUILD_TIME={build_time}\n")
for package in self._packages:
if len(package[NpkPartID.SIGNATURE].data) != 20+48+64:
package[NpkPartID.SIGNATURE].data = b'\0'*(20+48+64)
if build_time:
package[NpkPartID.NAME_INFO].data._build_time = int(build_time)
else:
build_time = self[NpkPartID.NAME_INFO].data._build_time
os.environ['BUILD_TIME'] = str(build_time)
github_env = os.getenv('GITHUB_ENV')
if github_env:
with open(github_env, 'a') as f:
f.write(f"BUILD_TIME={build_time}\n")
sha1_digest = self.get_digest(hashlib.new('SHA1'),package)
sha256_digest = self.get_digest(hashlib.new('SHA256'),package)
kcdsa_signature = mikro_kcdsa_sign(sha256_digest[:20],kcdsa_private_key)
kcdsa_signature = mikro_kcdsa_sign(sha1_digest,kcdsa_private_key)
eddsa_signature = mikro_eddsa_sign(sha256_digest,eddsa_private_key)
package[NpkPartID.SIGNATURE].data = sha1_digest + kcdsa_signature + eddsa_signature
else:
@ -247,9 +320,16 @@ class NovaPackage(Package):
self[NpkPartID.SIGNATURE].data = b'\0'*(20+48+64)
if build_time:
self[NpkPartID.NAME_INFO].data._build_time = int(build_time)
else:
build_time = self[NpkPartID.NAME_INFO].data._build_time
os.environ['BUILD_TIME'] = str(build_time)
github_env = os.getenv('GITHUB_ENV')
if github_env:
with open(github_env, 'a') as f:
f.write(f"BUILD_TIME={build_time}\n")
sha1_digest = self.get_digest(hashlib.new('SHA1'))
sha256_digest = self.get_digest(hashlib.new('SHA256'))
kcdsa_signature = mikro_kcdsa_sign(sha256_digest[:20],kcdsa_private_key)
kcdsa_signature = mikro_kcdsa_sign(sha1_digest,kcdsa_private_key)
eddsa_signature = mikro_eddsa_sign(sha256_digest,eddsa_private_key)
self[NpkPartID.SIGNATURE].data = sha1_digest + kcdsa_signature + eddsa_signature
@ -263,9 +343,9 @@ class NovaPackage(Package):
signature = package[NpkPartID.SIGNATURE].data
if sha1_digest != signature[:20]:
return False
if not mikro_kcdsa_verify(sha256_digest[:20],signature[20:68],kcdsa_public_key):
if not mikro_kcdsa_verify(sha1_digest,signature[20:68],kcdsa_public_key):
return False
if not mikro_eddsa_verify(sha256_digest,signature[68:132],eddsa_public_key):
if len(signature) == 132 and not mikro_eddsa_verify(sha256_digest,signature[68:132],eddsa_public_key):
return False
else:
sha1_digest = self.get_digest(hashlib.new('SHA1'))
@ -273,11 +353,10 @@ class NovaPackage(Package):
signature = self[NpkPartID.SIGNATURE].data
if sha1_digest != signature[:20]:
return False
if not mikro_kcdsa_verify(sha256_digest[:20],signature[20:68],kcdsa_public_key):
if not mikro_kcdsa_verify(sha1_digest,signature[20:68],kcdsa_public_key):
return False
if not mikro_eddsa_verify(sha256_digest,signature[68:132],eddsa_public_key):
if len(signature) == 132 and not mikro_eddsa_verify(sha256_digest,signature[68:132],eddsa_public_key):
return False
return True
def save(self,file):
@ -325,6 +404,10 @@ if __name__=='__main__':
create_option_parser.add_argument('name',type=str,help='NPK name')
create_option_parser.add_argument('squashfs',type=str,help='NPK squashfs file')
create_option_parser.add_argument('-desc','--description',type=str,help='NPK description')
extract_parser = subparsers.add_parser('extract',help='Extract npk file')
extract_parser.add_argument('input',type=str, help='Input file')
extract_parser.add_argument('name',type=str,help='File to extract')
extract_parser.add_argument('output',type=str,help='Output to file')
args = parser.parse_args()
kcdsa_private_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PRIVATE_KEY'])
eddsa_private_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PRIVATE_KEY'])
@ -355,5 +438,23 @@ if __name__=='__main__':
option_npk.sign(kcdsa_private_key,eddsa_private_key)
option_npk.save(args.output)
print(f'Created {args.output}')
elif args.command =='extract':
print(f'Extracting {args.name} from {args.input}')
npk = NovaPackage.load(args.input)
file_container = None
if len(npk._packages) > 0:
for package in npk._packages:
if package[NpkPartID.NAME_INFO].data.name == 'system':
file_container = NpkFileContainer.unserialize_from(package[NpkPartID.FILE_CONTAINER].data)
else:
if npk[NpkPartID.NAME_INFO].data.name == 'system':
file_container = NpkFileContainer.unserialize_from(npk[NpkPartID.FILE_CONTAINER].data)
if file_container:
for item in file_container:
if item.name == args.name.encode():
with open(args.output,'wb') as f:
f.write(item.data)
print(f'Extracted {args.name} to {args.output}')
break
else:
parser.print_help()

View File

@ -1,32 +0,0 @@
def install_package(package, version="upgrade", index_url='https://mirrors.aliyun.com/pypi/simple/'):
from sys import executable
from subprocess import check_call
result = False
try:
if version.lower() == "upgrade":
result = check_call([executable, "-m", "pip", "install", package, "--upgrade", "-i", index_url])
else:
from pkg_resources import get_distribution
current_package_version = None
try:
current_package_version = get_distribution(package)
except Exception:
pass
if current_package_version is None or current_package_version != version:
installation_sign = "==" if ">=" not in version else ""
result = check_call([executable, "-m", "pip", "install", package + installation_sign + version, "-i", index_url])
except Exception as e:
print(e)
result = -1
return result
def check_package(package):
from importlib import import_module
try:
import_module(package)
return True
except ImportError:
return False
def check_install_package(packages):
for package in packages:
if not check_package(package):
install_package(package)

325
patch.py
View File

@ -1,7 +1,70 @@
import subprocess,lzma
import struct,os
import struct,os,re,tempfile
import pefile
from elftools.elf.elffile import ELFFile
from npk import NovaPackage,NpkPartID,NpkFileContainer
def replace_chunks(old_chunks,new_chunks,data,name):
pattern_parts = [re.escape(chunk) + b'(.{0,6})' for chunk in old_chunks[:-1]]
pattern_parts.append(re.escape(old_chunks[-1]))
pattern_bytes = b''.join(pattern_parts)
pattern = re.compile(pattern_bytes, flags=re.DOTALL)
def replace_match(match):
replaced = b''.join([new_chunks[i] + match.group(i+1) for i in range(len(new_chunks) - 1)])
replaced += new_chunks[-1]
print(f'{name} public key patched {b"".join(old_chunks)[:16].hex().upper()}...')
return replaced
return re.sub(pattern, replace_match, data)
def replace_key(old,new,data,name=''):
old_chunks = [old[i:i+4] for i in range(0, len(old), 4)]
new_chunks = [new[i:i+4] for i in range(0, len(new), 4)]
data = replace_chunks(old_chunks, new_chunks, data,name)
key_map = [28,19,25,16,14,3,24,15,22,8,6,17,11,7,9,23,18,13,10,0,26,21,2,5,20,30,31,4,27,29,1,12,]
old_chunks = [bytes([old[i]]) for i in key_map]
new_chunks = [bytes([new[i]]) for i in key_map]
data = replace_chunks(old_chunks, new_chunks, data,name)
arch = os.getenv('ARCH') or 'x86'
arch = arch.replace('-', '')
if arch in ['arm64','arm']:
old_chunks = [old[i:i+4] for i in range(0, len(old), 4)]
new_chunks = [new[i:i+4] for i in range(0, len(new), 4)]
old_bytes = old_chunks[4] + old_chunks[5] + old_chunks[2] + old_chunks[0] + old_chunks[1] + old_chunks[6] + old_chunks[7]
new_bytes = new_chunks[4] + new_chunks[5] + new_chunks[2] + new_chunks[0] + new_chunks[1] + new_chunks[6] + new_chunks[7]
if old_bytes in data:
print(f'{name} public key patched {old[:16].hex().upper()}...')
data = data.replace(old_bytes,new_bytes)
old_codes = [bytes.fromhex('793583E2'),bytes.fromhex('FD3A83E2'),bytes.fromhex('193D83E2')] #0x1e400000+0xfd000+0x640
new_codes = [bytes.fromhex('FF34A0E3'),bytes.fromhex('753C83E2'),bytes.fromhex('FC3083E2')] #0xff0075fc= 0xff000000+0x7500+0xfc
data = replace_chunks(old_codes, new_codes, data,name)
else:
def conver_chunks(data:bytes):
ret = [
(data[2] << 16) | (data[1] << 8) | data[0] | ((data[3] << 24) & 0x03000000),
(data[3] >> 2) | (data[4] << 6) | (data[5] << 14) | ((data[6] << 22) & 0x1C00000),
(data[6] >> 3) | (data[7] << 5) | (data[8] << 13) | ((data[9] << 21) & 0x3E00000),
(data[9] >> 5) | (data[10] << 3) | (data[11] << 11) | ((data[12] << 19) & 0x1F80000),
(data[12] >> 6) | (data[13] << 2) | (data[14] << 10) | (data[15] << 18),
data[16] | (data[17] << 8) | (data[18] << 16) | ((data[19] << 24) & 0x01000000),
(data[19] >> 1) | (data[20] << 7) | (data[21] << 15) | ((data[22] << 23) & 0x03800000),
(data[22] >> 3) | (data[23] << 5) | (data[24] << 13) | ((data[25] << 21) & 0x1E00000),
(data[25] >> 4) | (data[26] << 4) | (data[27] << 12) | ((data[28] << 20) & 0x3F00000),
(data[28] >> 6) | (data[29] << 2) | (data[30] << 10) | (data[31] << 18)
]
return [struct.pack('<I', x ) for x in ret]
old_chunks = conver_chunks(old)
new_chunks = conver_chunks(new)
old_bytes = b''.join([v for i,v in enumerate(old_chunks) if i != 8])
new_bytes = b''.join([v for i,v in enumerate(new_chunks) if i != 8])
if old_bytes in data:
print(f'{name} public key patched {old[:16].hex().upper()}...')
data = data.replace(old_bytes,new_bytes)
old_codes = [bytes.fromhex('713783E2'),bytes.fromhex('223A83E2'),bytes.fromhex('8D3F83E2')] #0x1C40000+0x22000+0x234
new_codes = [bytes.fromhex('973303E3'),bytes.fromhex('DD3883E3'),bytes.fromhex('033483E3')] #0x03DD3397 = 0x3397|0x00DD0000|0x03000000
data = replace_chunks(old_codes, new_codes, data,name)
return data
def patch_bzimage(data:bytes,key_dict:dict):
PE_TEXT_SECTION_OFFSET = 414
HEADER_PAYLOAD_OFFSET = 584
@ -22,9 +85,7 @@ def patch_bzimage(data:bytes,key_dict:dict):
initramfs = initramfs[:cpio_offset2]
new_initramfs = initramfs
for old_public_key,new_public_key in key_dict.items():
if old_public_key in new_initramfs:
print(f'initramfs public key patched {old_public_key[:16].hex().upper()}...')
new_initramfs = new_initramfs.replace(old_public_key,new_public_key)
new_initramfs = replace_key(old_public_key,new_public_key,new_initramfs,'initramfs')
new_vmlinux = vmlinux.replace(initramfs,new_initramfs)
new_vmlinux_xz = lzma.compress(new_vmlinux,check=lzma.CHECK_CRC32,filters=[
{"id": lzma.FILTER_X86},
@ -45,51 +106,31 @@ def patch_bzimage(data:bytes,key_dict:dict):
new_data = new_data.replace(vmlinux_xz,new_vmlinux_xz)
return new_data
def patch_block(dev:str,file:str,key_dict):
BLOCK_SIZE = 4096
#sudo debugfs /dev/nbd0p1 -R 'stats' | grep "Block size" | sed -n '1p' | cut -d ':' -f 2
#sudo debugfs /dev/nbd0p1 -R 'stat boot/initrd.rgz' 2> /dev/null | sed -n '11p'
stdout,_ = run_shell_command(f"debugfs {dev} -R 'stat {file}' 2> /dev/null | sed -n '11p' ")
#(0-11):1592-1603, (IND):1173, (12-15):1604-1607, (16-26):1424-1434
blocks_info = stdout.decode().strip().split(',')
blocks = []
ind_block_id = None
for block_info in blocks_info:
_tmp = block_info.strip().split(':')
if _tmp[0].strip() == '(IND)':
ind_block_id = int(_tmp[1])
else:
id_range = _tmp[0].strip().replace('(','').replace(')','').split('-')
block_range = _tmp[1].strip().replace('(','').replace(')','').split('-')
blocks += [id for id in range(int(block_range[0]),int(block_range[1])+1)]
print(f' blocks : {len(blocks)} ind_block_id : {ind_block_id}')
#sudo debugfs /dev/nbd0p1 -R 'cat boot/initrd.rgz' > data
data,stderr = run_shell_command(f"debugfs {dev} -R 'cat {file}' 2> /dev/null")
new_data = patch_kernel(data,key_dict)
print(f'write block {len(blocks)} : [',end="")
with open(dev,'wb') as f:
for index,block_id in enumerate(blocks):
print('#',end="")
f.seek(block_id*BLOCK_SIZE)
f.write(new_data[index*BLOCK_SIZE:(index+1)*BLOCK_SIZE])
f.flush()
print(']')
def patch_initrd_xz(initrd_xz:bytes,key_dict:dict,ljust=True):
try:
initrd = lzma.decompress(initrd_xz)
except Exception as e:
print(f'size:{len(initrd_xz)},header:{initrd_xz[:20].hex().upper()},footer:{initrd_xz[-20:].hex().upper()}\n')
raise Exception(f'failed to decompress initrd_xz: {e}')
new_initrd = initrd
for old_public_key,new_public_key in key_dict.items():
if old_public_key in new_initrd:
print(f'initrd public key patched {old_public_key[:16].hex().upper()}...')
new_initrd = new_initrd.replace(old_public_key,new_public_key)
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": 9,}] )
new_initrd = replace_key(old_public_key,new_public_key,new_initrd,'initrd')
preset = 6
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": preset }] )
while len(new_initrd_xz) > len(initrd_xz) and preset < 9:
print(f'preset:{preset}')
print(f'new initrd xz size:{len(new_initrd_xz)}')
print(f'old initrd xz size:{len(initrd_xz)}')
preset += 1
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": preset }] )
if len(new_initrd_xz) > len(initrd_xz):
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": 9 | lzma.PRESET_EXTREME,'dict_size': 32*1024*1024,"lc": 4,"lp": 0, "pb": 0,}] )
if ljust:
assert len(new_initrd_xz) <= len(initrd_xz),'new initrd xz size is too big'
print(f'preset:{preset}')
print(f'new initrd xz size:{len(new_initrd_xz)}')
print(f'old initrd xz size:{len(initrd_xz)}')
print(f'ljust size:{len(initrd_xz)-len(new_initrd_xz)}')
assert len(new_initrd_xz) <= len(initrd_xz),'new initrd xz size is too big'
new_initrd_xz = new_initrd_xz.ljust(len(initrd_xz),b'\0')
return new_initrd_xz
@ -130,12 +171,58 @@ def patch_pe(data: bytes,key_dict:dict):
new_data = data.replace(vmlinux_xz,new_vmlinux_xz)
return new_data
def build_efi(input_file, output_file):
def find_xz_streams(data:bytes):
streams = []
XZ_HEADER_MAGIC = b'\xFD7zXZ\x00\x00\x01'
XZ_FOOTER_MAGIC = b'\x00\x00\x00\x00\x01\x59\x5A'
i = 0
while True:
start = data.find(XZ_HEADER_MAGIC, i)
if start == -1:
break
end = data.find(XZ_FOOTER_MAGIC, start)
assert end != -1, 'XZ footer not found'
end += len(XZ_FOOTER_MAGIC)
streams.append((start, end))
i = end
return streams
with open(input_file, 'rb') as f:
elf = ELFFile(f)
initrd_section =elf.get_section_by_name('initrd')
assert initrd_section is not None,'initrd section not found'
initrd_data = initrd_section.data()
xz_streams = find_xz_streams(initrd_data)
assert len(xz_streams) == 2,'only support 2 xz streams'
efi_xz = initrd_data[xz_streams[0][0]:xz_streams[0][1]]
cpio_xz = initrd_data[xz_streams[1][0]:xz_streams[1][1]]
try:
efi = lzma.decompress(efi_xz)
except Exception as e:
print(f'size:{len(efi_xz)},header:{efi_xz[:20].hex().upper()},footer:{efi_xz[-20:].hex().upper()}\n')
raise Exception(f'failed to decompress efi: {e}')
with pefile.PE(data = efi) as pe:
data_section =[section for section in pe.sections if section.Name == b'.data\x00\x00\x00'][0]
rva = data_section.VirtualAddress
addr = data_section.PointerToRawData
data = data_section.get_data()
size = len(data)
alignment = ((rva + size) + 4096 - 1) & ~(4096 - 1) #4096对齐
alignment = alignment - (rva+size)
new_data = data + b'\x00'*alignment
new_data += struct.pack('<I',len(cpio_xz))
new_data += cpio_xz
data_section.SizeOfRawData = len(new_data)
new_file_data = pe.write()
new_file_data = new_file_data[:addr]
new_file_data += new_data
with open(output_file, 'wb') as f:
f.write(new_file_data)
def patch_netinstall(key_dict: dict,input_file,output_file=None):
netinstall = open(input_file,'rb').read()
if netinstall[:2] == b'MZ':
from package import check_install_package
check_install_package(['pefile'])
import pefile
ROUTEROS_BOOT = {
129:{'arch':'power','name':'Powerboot'},
130:{'arch':'e500','name':'e500_boot'},
@ -174,7 +261,6 @@ def patch_netinstall(key_dict: dict,input_file,output_file=None):
pe.set_bytes_at_rva(rva,new_data)
pe.write(output_file or input_file)
elif netinstall[:4] == b'\x7FELF':
import re
# 83 00 00 00 C4 68 C4 0B 5A C2 04 08 10 9E 52 00
# 8A 00 00 00 C3 68 C4 0B 6A 60 57 08 C0 3D 54 00
# 81 00 00 00 D3 68 C4 0B 2A 9E AB 08 5C 1B 78 00
@ -235,43 +321,97 @@ def patch_kernel(data:bytes,key_dict):
return patch_elf(data,key_dict)
elif data[:5] == b'\xFD7zXZ':
print('patching initrd')
return patch_initrd_xz(data,key_dict)
return patch_initrd_xz(data,key_dict,False)
else:
raise Exception('unknown kernel format')
def patch_squashfs(path,key_dict):
for root, dirs, files in os.walk(path):
for file in files:
file = os.path.join(root,file)
if os.path.isfile(file):
data = open(file,'rb').read()
for old_public_key,new_public_key in key_dict.items():
if old_public_key in data:
print(f'{file} public key patched {old_public_key[:16].hex().upper()}...')
data = data.replace(old_public_key,new_public_key)
open(file,'wb').write(data)
data = open(file,'rb').read()
url_dict = {
os.environ['MIKRO_LICENCE_URL'].encode():os.environ['CUSTOM_LICENCE_URL'].encode(),
os.environ['MIKRO_UPGRADE_URL'].encode():os.environ['CUSTOM_UPGRADE_URL'].encode(),
os.environ['MIKRO_CLOUD_URL'].encode():os.environ['CUSTOM_CLOUD_URL'].encode(),
os.environ['MIKRO_CLOUD_PUBLIC_KEY'].encode():os.environ['CUSTOM_CLOUD_PUBLIC_KEY'].encode(),
}
for old_url,new_url in url_dict.items():
if old_url in data:
print(f'{file} url patched {old_url.decode()[:7]}...')
data = data.replace(old_url,new_url)
open(file,'wb').write(data)
def patch_loader(loader_file):
try:
from loader.patch_loader import patch_loader as do_patch_loader
arch = os.getenv('ARCH') or 'x86'
arch = arch.replace('-', '')
do_patch_loader(loader_file,loader_file,arch)
except ImportError as e:
print(e)
print("loader module import failed. cannot run patch_loader.py")
if os.path.split(file)[1] == 'licupgr':
url_dict = {
os.environ['MIKRO_RENEW_URL'].encode():os.environ['CUSTOM_RENEW_URL'].encode(),
def patch_squashfs(path,key_dict):
url_replacements = {
os.environ.get('MIKRO_LICENCE_URL', '').encode(): os.environ.get('CUSTOM_LICENCE_URL', '').encode(),
os.environ.get('MIKRO_UPGRADE_URL', '').encode(): os.environ.get('CUSTOM_UPGRADE_URL', '').encode(),
os.environ.get('MIKRO_CLOUD_URL', '').encode(): os.environ.get('CUSTOM_CLOUD_URL', '').encode(),
os.environ.get('MIKRO_CLOUD_PUBLIC_KEY', '').encode(): os.environ.get('CUSTOM_CLOUD_PUBLIC_KEY', '').encode(),
}
for old_url,new_url in url_dict.items():
url_replacements = {k: v for k, v in url_replacements.items() if k and v}
renew_replacements = {
os.environ.get('MIKRO_RENEW_URL', '').encode(): os.environ.get('CUSTOM_RENEW_URL', '').encode(),
}
renew_replacements = {k: v for k, v in renew_replacements.items() if k and v}
for root, dirs, files in os.walk(path):
if 'mode' in files and 'keyman' in files:
for file_path in [os.path.join(root, 'mode'),os.path.join(root, 'keyman')]:
with open(file_path, 'rb') as f:
data = f.read()
for old_url,new_url in url_replacements.items():
if old_url in data:
print(f'{file} url patched {old_url.decode()[:7]}...')
print(f'{file_path} url patched {old_url.decode()[:7]}...')
data = data.replace(old_url,new_url)
open(file,'wb').write(data)
modified = False
for old_public_key,new_public_key in key_dict.items():
new_data = replace_key(old_public_key,new_public_key,data,file_path)
if new_data != data:
data = new_data
modified = True
with open(f'{file_path}_', 'wb') as f:
f.write(data)
if 'loader' in files and os.path.isfile(os.path.join(root, 'loader')):
loader_file = os.path.join(root, 'loader')
patch_loader(loader_file)
if 'BOOTX64.EFI' in files and os.path.isfile(os.path.join(root, 'BOOTX64.EFI')):
efi_file = os.path.join(root, 'BOOTX64.EFI')
with open(efi_file, 'rb') as f:
data = f.read()
new_data = patch_kernel(data,key_dict)
assert new_data != data, f'{file_path} key not patched'
with open(efi_file, 'wb') as f:
f.write(new_data)
for filename in files:
if filename in ['mode','keyman','loader','BOOTX64.EFI']:
continue
file_path = os.path.join(root,filename)
if not os.path.isfile(file_path):
continue
modified = False
with open(file_path, 'rb') as f:
data = f.read()
for old_public_key,new_public_key in key_dict.items():
new_data = replace_key(old_public_key,new_public_key,data,file_path)
if new_data != data:
data = new_data
modified = True
for old_url,new_url in url_replacements.items():
if old_url in data:
print(f'{file_path} url patched {old_url.decode()[:7]}...')
data = data.replace(old_url,new_url)
modified = True
if filename == 'licupgr':
for old_url,new_url in renew_replacements.items():
if old_url in data:
print(f'{file_path} url patched {old_url.decode()[:7]}...')
data = data.replace(old_url,new_url)
modified = True
if modified:
with open(file_path, 'wb') as f:
f.write(data)
def run_shell_command(command):
process = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -285,22 +425,21 @@ def patch_npk_package(package,key_dict):
print(f'patch {item.name} ...')
item.data = patch_kernel(item.data,key_dict)
package[NpkPartID.FILE_CONTAINER].data = file_container.serialize()
squashfs_file = 'squashfs-root.sfs'
extract_dir = 'squashfs-root'
open(squashfs_file,'wb').write(package[NpkPartID.SQUASHFS].data)
with tempfile.TemporaryDirectory() as tmp_dir:
squashfs_file = os.path.join(tmp_dir, 'squashfs-root.sfs')
extract_dir = os.path.join(tmp_dir, 'squashfs-root')
with open(squashfs_file,'wb') as f:
f.write(package[NpkPartID.SQUASHFS].data)
print(f"extract {squashfs_file} ...")
run_shell_command(f"unsquashfs -d {extract_dir} {squashfs_file}")
patch_squashfs(extract_dir,key_dict)
logo = os.path.join(extract_dir,"nova/lib/console/logo.txt")
run_shell_command(f"sudo sed -i '1d' {logo}")
run_shell_command(f"sudo sed -i '8s#.*# elseif@live.cn https://github.com/elseif/MikroTikPatch#' {logo}")
run_shell_command(f"sed -i '1d' {logo}")
run_shell_command(f"sed -i '8s#.*# elseif@live.cn https://github.com/elseif/MikroTikPatch#' {logo}")
print(f"pack {extract_dir} ...")
run_shell_command(f"rm -f {squashfs_file}")
run_shell_command(f"mksquashfs {extract_dir} {squashfs_file} -quiet -comp xz -no-xattrs -b 256k")
print(f"clean ...")
run_shell_command(f"rm -rf {extract_dir}")
package[NpkPartID.SQUASHFS].data = open(squashfs_file,'rb').read()
run_shell_command(f"rm -f {squashfs_file}")
run_shell_command(f"mksquashfs {extract_dir} {squashfs_file} -no-recovery -noappend -exit-on-error -quiet -comp xz -no-xattrs -b 256k -all-root")
with open(squashfs_file,'rb') as f:
package[NpkPartID.SQUASHFS].data = f.read()
def patch_npk_file(key_dict,kcdsa_private_key,eddsa_private_key,input_file,output_file=None):
npk = NovaPackage.load(input_file)
@ -322,16 +461,16 @@ if __name__ == '__main__':
kernel_parser = subparsers.add_parser('kernel',help='patch kernel file')
kernel_parser.add_argument('input',type=str, help='Input file')
kernel_parser.add_argument('-O','--output',type=str,help='Output file')
block_parser = subparsers.add_parser('block',help='patch block file')
block_parser.add_argument('dev',type=str, help='block device')
block_parser.add_argument('file',type=str, help='file path')
buildefi_parser = subparsers.add_parser('buildefi',help='build efi file')
buildefi_parser.add_argument('input',type=str, help='kernel file')
buildefi_parser.add_argument('output',type=str,help='Output to file')
netinstall_parser = subparsers.add_parser('netinstall',help='patch netinstall file')
netinstall_parser.add_argument('input',type=str, help='Input file')
netinstall_parser.add_argument('-O','--output',type=str,help='Output file')
args = parser.parse_args()
key_dict = {
bytes.fromhex(os.environ['MIKRO_LICENSE_PUBLIC_KEY']):bytes.fromhex(os.environ['CUSTOM_LICENSE_PUBLIC_KEY']),
bytes.fromhex(os.environ['MIKRO_NPK_SIGN_PUBLIC_LKEY']):bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PUBLIC_KEY'])
bytes.fromhex(os.environ['MIKRO_NPK_SIGN_PUBLIC_KEY']):bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PUBLIC_KEY'])
}
kcdsa_private_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PRIVATE_KEY'])
eddsa_private_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PRIVATE_KEY'])
@ -342,9 +481,9 @@ if __name__ == '__main__':
print(f'patching {args.input} ...')
data = patch_kernel(open(args.input,'rb').read(),key_dict)
open(args.output or args.input,'wb').write(data)
elif args.command == 'block':
print(f'patching {args.file} in {args.dev} ...')
patch_block(args.dev,args.file,key_dict)
elif args.command == 'buildefi':
print(f'building EFI from {args.input} ...')
build_efi(args.input,args.output)
elif args.command == 'netinstall':
print(f'patching {args.input} ...')
patch_netinstall(key_dict,args.input,args.output)

View File

@ -87,7 +87,7 @@ class ShortWeierstrassCurve(EllipticCurve, CurveOpIsomorphism, CurveOpExportSage
@property
def is_koblitz(self):
"""Returns whether the curve allows for efficient computation of a map
\phi in the field (i.e. that the curve is commonly known as a 'Koblitz
phi in the field (i.e. that the curve is commonly known as a 'Koblitz
Curve'). This corresponds to examples 3 and 4 of the paper "Faster
Point Multiplication on Elliptic Curves with Efficient Endomorphisms"
by Gallant, Lambert and Vanstone."""