Compare commits

..

195 Commits

Author SHA1 Message Date
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
17 changed files with 1248 additions and 172 deletions

View File

@@ -16,15 +16,20 @@ on:
- stable
- long-term
version:
description: "Version ('', 6.49.17, 6.49.18,...)"
description: "Specify version (e.g., 6.49.19), empty for latest"
required: false
default: ''
type: string
buildtime:
description: "Build Time"
description: "Custom build time, leave empty for now,when set specify version"
required: false
default: ''
type: string
release:
description: "Release to GitHub & Upload "
required: false
default: false
type: boolean
permissions:
contents: write
@@ -47,30 +52,50 @@ env:
CUSTOM_CLOUD_URL: ${{ secrets.CUSTOM_CLOUD_URL }}
jobs:
Set_BuildTime:
Set_Variables:
runs-on: ubuntu-22.04
outputs:
BUILD_TIME: ${{ steps.set_buildtime.outputs.BUILD_TIME }}
BUILD_TIME: ${{ steps.set_vars.outputs.BUILD_TIME }}
RELEASE: ${{ steps.set_vars.outputs.RELEASE }}
MATRIX_JSON: ${{ steps.set_vars.outputs.MATRIX_JSON }}
steps:
- name: Set build time
id: set_buildtime
- name: Set variables
id: set_vars
run: |
_BUILD_TIME=${{ github.event.inputs.buildtime }}
if [ -z "$_BUILD_TIME" ]; then
echo "BUILD_TIME=$(date +'%s')" >> $GITHUB_OUTPUT
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
BUILD_TIME="${{ github.event.inputs.buildtime }}"
RELEASE="${{ github.event.inputs.release }}"
if [ -z "$BUILD_TIME" ]; then
BUILD_TIME=$(date +'%s')
fi
if [ -z "$RELEASE" ]; then
RELEASE=false
fi
ARCH="${{ github.event.inputs.arch }}"
CHANNEL="${{ github.event.inputs.channel }}"
MATRIX_JSON=$(jq -nc --arg arch "$ARCH" --arg channel "$CHANNEL" '[{arch: $arch, channel: $channel}]')
else
echo "BUILD_TIME=$_BUILD_TIME" >> $GITHUB_OUTPUT
BUILD_TIME=$(date +'%s')
RELEASE=true
MATRIX_JSON='[{"arch":"x86","channel":"stable"},{"arch":"x86","channel":"long-term"}]'
fi
echo "BUILD_TIME=$BUILD_TIME" >> $GITHUB_OUTPUT
echo "RELEASE=$RELEASE" >> $GITHUB_OUTPUT
echo "MATRIX_JSON=$MATRIX_JSON" >> $GITHUB_OUTPUT
echo "BUILD_TIME: $BUILD_TIME"
echo "RELEASE: $RELEASE"
echo "MATRIX_JSON: $MATRIX_JSON"
Patch_RouterOS:
needs: Set_BuildTime
needs: Set_Variables
runs-on: ubuntu-22.04
strategy:
matrix:
channel: ${{ fromJSON(format('["{0}"]', github.event.inputs.channel || 'stable')) }}
matrix:
include: ${{ fromJSON(needs.Set_Variables.outputs.MATRIX_JSON) }}
env:
TZ: 'Asia/Shanghai'
LATEST_VERSION: ""
BUILD_TIME: ${{ needs.Set_Variables.outputs.BUILD_TIME }}
RELEASE: ${{ needs.Set_Variables.outputs.RELEASE }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -84,13 +109,19 @@ jobs:
id: get_latest
run: |
echo $(uname -a)
LATEST_VERSION=${{ github.event.inputs.version }}
if [ -z "$_LATEST_VERSION" ]; then
LATEST_VERSION=$(wget -nv -O - https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWEST6.${{ matrix.channel }} | cut -d ' ' -f1)
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [ -n "${{ github.event.inputs.version }}" ]; then
LATEST_VERSION="${{ github.event.inputs.version }}"
else
read LATEST_VERSION BUILD_TIME <<< "$(wget -nv -O - "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWEST6.${{ matrix.channel }}")"
fi
else
read LATEST_VERSION BUILD_TIME <<< "$(wget -nv -O - "https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWEST6.${{ matrix.channel }}")"
fi
echo Build Time:$BUILD_TIME
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)
_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
@@ -98,46 +129,29 @@ jobs:
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
sudo apt-get update > /dev/null
has_patched_npk=false
if wget -nv --spider https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/$LATEST_VERSION/routeros-x86-$LATEST_VERSION.npk; then
echo "RouterOS NPK file exists, attempting to download..."
if wget -nv -O routeros-x86-$LATEST_VERSION.npk https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/$LATEST_VERSION/routeros-x86-$LATEST_VERSION.npk; then
echo "RouterOS NPK file downloaded successfully."
if wget -nv --spider https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/$LATEST_VERSION/system-$LATEST_VERSION.npk; then
echo "System NPK file exists, attempting to download..."
if wget -nv -O system-$LATEST_VERSION.npk https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/$LATEST_VERSION/system-$LATEST_VERSION.npk; then
has_patched_npk=true
fi
fi
fi
fi
echo "has_patched_npk=$has_patched_npk" >> $GITHUB_OUTPUT
- name: Cache Squashfs
- name: Get loader patch files
if: steps.get_latest.outputs.has_new_version == 'true'
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: Create Squashfs for option and python3
if: steps.get_latest.outputs.has_new_version == 'true'
id: cache-squashfs
uses: actions/cache@v4
with:
path: |
option.sfs
key: busybox-squashfs
- name: Create Squashfs for option
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-squashfs.outputs.cache-hit != 'true'
run: |
sudo mkdir -p ./option-root/bin/
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
sudo chmod +x ./busybox/busybox_x86
COMMANDS=$(./busybox/busybox_x86 --list)
for cmd in $COMMANDS; do
@@ -145,7 +159,14 @@ jobs:
done
sudo mksquashfs option-root option.sfs -quiet -comp xz -no-xattrs -b 256k
sudo rm -rf option-root
sudo wget -O cpython.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20250708/cpython-3.11.13+20250708-x86_64-unknown-linux-musl-install_only_stripped.tar.gz
sudo tar -xf cpython.tar.gz
sudo rm cpython.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 mikrotik-${{ env.LATEST_VERSION }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
id: cache-mikrotik
@@ -160,7 +181,7 @@ jobs:
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: |
@@ -172,18 +193,25 @@ jobs:
sudo rsync -a ./iso/ ./new_iso/
sudo umount ./iso
sudo rm -rf ./iso
if [ "${{ steps.get_latest.outputs.has_patched_npk }}" == "false" ]; then
sudo mv ./new_iso/system-$LATEST_VERSION.npk ./
sudo -E python3 patch.py npk system-$LATEST_VERSION.npk
else
sudo -E python3 npk.py sign system-$LATEST_VERSION.npk system-$LATEST_VERSION.npk
sudo mv ./new_iso/system-$LATEST_VERSION.npk ./
sudo -E python3 patch.py npk system-$LATEST_VERSION.npk
NPK_FILES=$(find ./new_iso -type f -name "*.npk")
if [ -z "$NPK_FILES" ]; then
echo "No .npk files found in iso, downloading..."
URL="https://download.mikrotik.com/routeros/$LATEST_VERSION/all_packages-${{ matrix.arch }}-$LATEST_VERSION.zip"
TMP_ZIP="./all_packages.zip"
sudo wget -nv -O "$TMP_ZIP" "$URL"
sudo unzip -o "$TMP_ZIP" -d ./new_iso
sudo rm -f "$TMP_ZIP"
NPK_FILES=$(find ./new_iso -type f -name "*.npk")
fi
if [ -n "$NPK_FILES" ]; then
for file in $NPK_FILES; do
sudo -E python3 npk.py sign "$file" "$file"
done
fi
sudo cp -f system-$LATEST_VERSION.npk ./new_iso/
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 -E python3 npk.py create ./new_iso/gps-$LATEST_VERSION.npk ./option-$LATEST_VERSION.npk option ./option.sfs -desc="busybox"
sudo cp option-$LATEST_VERSION.npk ./new_iso/
sudo mkisofs -o mikrotik-$LATEST_VERSION.iso \
@@ -204,7 +232,7 @@ jobs:
sudo rm -rf ./new_iso
- name: Create install-image-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && steps.get_latest.outputs.has_patched_npk == 'true'
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo modprobe nbd
sudo apt-get update
@@ -261,16 +289,12 @@ jobs:
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
if [ "${{ steps.get_latest.outputs.has_patched_npk }}" == "false" ]; then
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
else
sudo -E python3 npk.py sign routeros-x86-$LATEST_VERSION.npk routeros-x86-$LATEST_VERSION.npk
sudo cp -f routeros-x86-$LATEST_VERSION.npk ./chr/var/pdb/routeros-x86/image
sudo cp routeros-x86-$LATEST_VERSION.npk ./all_packages/routeros-x86-$LATEST_VERSION.npk
fi
#sudo mkdir -p ./chr/var/pdb/option
#sudo cp ./all_packages/option-$LATEST_VERSION.npk ./chr/var/pdb/option/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 mkdir -p ./chr/var/pdb/option
sudo cp ./all_packages/option-$LATEST_VERSION.npk ./chr/var/pdb/option/image
sudo mkdir -p ./chr/rw/disk
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 ./chr/rw/disk/rc.local
sudo umount ./chr
sudo qemu-nbd -d /dev/nbd0
sudo rm -rf ./chr
@@ -297,29 +321,59 @@ jobs:
cd ./all_packages
sudo zip ../all_packages-x86-$LATEST_VERSION.zip *.npk
cd ..
- name: Upload Files
if: steps.get_latest.outputs.has_new_version == 'true'
- name: Prepare for Upload
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
sudo mkdir -p ./publish/$LATEST_VERSION
sudo cp CHANGELOG ./publish/$LATEST_VERSION/
sudo cp ./all_packages/*.npk ./publish/$LATEST_VERSION/
# sudo cp mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso ./publish/$LATEST_VERSION/ 2>/dev/null || true
# sudo cp chr-${{ env.LATEST_VERSION }}*.zip ./publish/$LATEST_VERSION/ 2>/dev/null || true
# sudo cp netinstall-${{ env.LATEST_VERSION }}.* ./publish/$LATEST_VERSION/ 2>/dev/null || true
# sudo cp install-image-${{ env.LATEST_VERSION }}.zip ./publish/$LATEST_VERSION/ 2>/dev/null || true
echo $LATEST_VERSION $BUILD_TIME | sudo tee ./publish/NEWEST6.${{ matrix.channel }}
- name: Upload Files
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
if [ "${{ steps.get_latest.outputs.has_patched_npk }}" == "false" ]; then
OUTDIR="$LATEST_VERSION-UNDONE"
else
OUTDIR="$LATEST_VERSION"
fi
sudo mkdir -p ./publish/$OUTDIR
sudo cp CHANGELOG ./publish/$OUTDIR/
sudo cp ./all_packages/*.npk ./publish/$OUTDIR/
sudo chown -R root:root ./publish/
LOCAL_PATH=./publish/$LATEST_VERSION
REMOTE_PATH=${{ secrets.SSH_DIRECTORY }}
SERVER=${{ secrets.SSH_SERVER }}
USER=${{ secrets.SSH_USERNAME }}
PASS=${{ secrets.SSH_PASSWORD }}
PORT=${{ secrets.SSH_PORT }}
CHANNEL=${{ matrix.channel }}
sudo apt-get install -y lftp ssh sshpass > /dev/null 2>&1
sudo -E lftp -u ${{ secrets.SSH_USERNAME }},'${{ secrets.SSH_PASSWORD }}' sftp://${{ secrets.SSH_SERVER }}:${{ secrets.SSH_PORT }} <<EOF
sudo -E lftp -u "$USER","$PASS" sftp://$SERVER:$PORT <<EOF
set sftp:auto-confirm yes
mirror --reverse --verbose --only-newer ./publish ${{ secrets.SSH_DIRECTORY }}
mirror --reverse --verbose --only-newer ./publish "$REMOTE_PATH"
bye
EOF
sshpass -p "${{ secrets.SSH_PASSWORD }}" ssh -o StrictHostKeyChecking=no -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_SERVER }} "echo $LATEST_VERSION $BUILD_TIME | tee /rw/disk/${{ secrets.SSH_DIRECTORY }}/NEWEST6.${{ matrix.channel }}; chown -R 32768:32768 /rw/disk/${{ secrets.SSH_DIRECTORY }}/"
sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no -p $PORT $USER@$SERVER \
"echo $LATEST_VERSION $BUILD_TIME | tee /rw/disk/$REMOTE_PATH/NEWEST6.$CHANNEL; chown -R 32768:32768 /rw/disk/$REMOTE_PATH/"
# - name: Upload to R2 Bucket
# if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
# uses: ryand56/r2-upload-action@latest
# with:
# r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
# r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
# r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
# r2-bucket: routeros
# source-dir: ./publish
# destination-dir: ./routeros
# keep-file-fresh: true
# output-file-url: false
- name: Clear Cloudflare cache
if: steps.get_latest.outputs.has_new_version == 'true'
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
curl --request POST --url "https://api.cloudflare.com/client/v4/zones/fe6831ed6dc9e6235e69ef2a31f2e7fe/purge_cache" \
--header "Authorization: Bearer 9GDQkzU51QXaqzp1qMjyFKpyeJyOdnNoG9GZQaGP" \
@@ -327,8 +381,9 @@ jobs:
--data '{"purge_everything": true}'
- name: Delete Release tag ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && steps.get_latest.outputs.has_patched_npk == 'true'
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
sed -i "1i Build Time:$BUILD_TIME" CHANGELOG
HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
RELEASE_INFO=$(curl -s -H $HEADER https://api.github.com/repos/${{ github.repository }}/releases/tags/$LATEST_VERSION)
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id')
@@ -342,8 +397,9 @@ jobs:
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' && steps.get_latest.outputs.has_patched_npk == 'true'
- name: Create Release tag ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
uses: softprops/action-gh-release@v2
with:
name: "RouterOS ${{ env.LATEST_VERSION }}"
@@ -357,3 +413,13 @@ jobs:
install-image-${{ env.LATEST_VERSION }}.zip
routeros-x86-${{ env.LATEST_VERSION }}.npk
all_packages-x86-${{ env.LATEST_VERSION }}.zip
- name: Upload Files as Artifact (No Release)
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'false'
uses: actions/upload-artifact@v4
with:
name: mikrotik-${{ env.LATEST_VERSION }}
path: |
all_packages-x86-${{ env.LATEST_VERSION }}.zip
mikrotik-${{ env.LATEST_VERSION }}.iso
chr-${{ env.LATEST_VERSION }}*.zip

View File

@@ -2,18 +2,19 @@ name: Patch Mikrotik RouterOS 7.x
on:
# push:
# branches: [ "main" ]
# schedule:
# - cron: "0 0 * * *"
schedule:
- cron: "0 0 * * *" # At UTC 00:00 on every day
workflow_dispatch:
inputs:
arch:
description: 'Architecture (x86, arm64)'
description: 'Architecture (x86, arm64, arm)'
required: true
default: 'x86'
type: choice
options:
- x86
- arm64
- arm
channel:
description: 'Channel (stable, testing)'
required: true
@@ -23,7 +24,7 @@ on:
- stable
- testing
version:
description: "Specify version (e.g., 7.17.2), empty for latest"
description: "Specify version (e.g., 7.19.2), empty for latest"
required: false
default: ''
type: string
@@ -35,7 +36,7 @@ on:
release:
description: "Release to GitHub & Upload "
required: false
default: 'false'
default: false
type: boolean
permissions:
@@ -85,12 +86,8 @@ jobs:
else
BUILD_TIME=$(date +'%s')
RELEASE=true
MATRIX_JSON='[
{"arch":"x86","channel":"stable"},
{"arch":"arm64","channel":"stable"},
{"arch":"x86","channel":"testing"},
{"arch":"arm64","channel":"testing"}
]'
MATRIX_JSON='[{"arch":"x86","channel":"stable"},{"arch":"arm64","channel":"stable"},{"arch":"arm","channel":"stable"},{"arch":"x86","channel":"testing"},{"arch":"arm64","channel":"testing"},{"arch":"arm","channel":"testing"}]'
MATRIX_JSON='[{"arch":"x86","channel":"stable"},{"arch":"arm64","channel":"stable"},{"arch":"x86","channel":"testing"},{"arch":"arm64","channel":"testing"}]'
fi
echo "BUILD_TIME=$BUILD_TIME" >> $GITHUB_OUTPUT
echo "RELEASE=$RELEASE" >> $GITHUB_OUTPUT
@@ -146,6 +143,8 @@ jobs:
cat CHANGELOG
if [[ "${{ matrix.arch }}" == "arm64" ]]; then
echo "ARCH=-arm64" >> $GITHUB_ENV
elif [[ "${{ matrix.arch }}" == "arm" ]]; then
echo "ARCH=-arm" >> $GITHUB_ENV
else
echo "ARCH=" >> $GITHUB_ENV
fi
@@ -160,18 +159,8 @@ jobs:
7z x -p"${{ secrets.LOADER_7Z_PASSWORD }}" loader.7z >> /dev/null
rm loader.7z
- 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'
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo mkdir -p ./option-root/bin/
if [ "${{ matrix.arch }}" == "x86" ]; then
@@ -184,6 +173,14 @@ jobs:
sudo chmod +x ./option-root/bin/busybox
sudo cp keygen/keygen_aarch64 ./option-root/bin/keygen
sudo chmod +x ./option-root/bin/keygen
elif [ "${{ matrix.arch }}" == "arm" ]; then
sudo cp busybox/busybox_arm ./option-root/bin/busybox
sudo chmod +x ./option-root/bin/busybox
sudo cp keygen/keygen_arm ./option-root/bin/keygen
sudo chmod +x ./option-root/bin/keygen
else
echo "Unsupported architecture: ${{ matrix.arch }}"
exit 1
fi
sudo chmod +x ./busybox/busybox_x86
COMMANDS=$(./busybox/busybox_x86 --list)
@@ -193,9 +190,11 @@ jobs:
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.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10+20241016-x86_64-unknown-linux-musl-install_only_stripped.tar.gz
sudo wget -O cpython.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20250708/cpython-3.11.13+20250708-x86_64-unknown-linux-musl-install_only_stripped.tar.gz
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo wget -O cpython.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20241016/cpython-3.11.10+20241016-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz
sudo wget -O cpython.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20250708/cpython-3.11.13+20250708-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz
elif [ "${{ matrix.arch }}" == "arm" ]; then
sudo wget -O cpython.tar.gz -nv https://github.com/indygreg/python-build-standalone/releases/download/20250708/cpython-3.11.13+20250708-armv7-unknown-linux-gnueabi-install_only_stripped.tar.gz
fi
sudo tar -xf cpython.tar.gz
sudo rm cpython.tar.gz
@@ -205,22 +204,23 @@ jobs:
sudo rm -rf ./python
- name: Cache mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
if: steps.get_latest.outputs.has_new_version == 'true' && (matrix.arch == 'x86' || matrix.arch == 'arm64')
id: cache-mikrotik
uses: actions/cache@v4
with:
path: |
mikrotik.iso
key: mikrotik-${{ env.LATEST_VERSION }}-${{ matrix.arch }}
key: mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}
restore-keys: mikrotik-${{ env.LATEST_VERSION }}${{ env.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'
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-mikrotik.outputs.cache-hit != 'true' && (matrix.arch == 'x86' || matrix.arch == 'arm64')
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'
if: steps.get_latest.outputs.has_new_version == 'true' && (matrix.arch == 'x86' || matrix.arch == 'arm64')
run: |
sudo apt-get install -y mkisofs xorriso > /dev/null
sudo mkdir ./iso
@@ -231,14 +231,25 @@ jobs:
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
NPK_FILES=$(find ./new_iso -type f -name "*.npk")
if [ -z "$NPK_FILES" ]; then
echo "No .npk files found in iso, downloading..."
URL="https://download.mikrotik.com/routeros/$LATEST_VERSION/all_packages-${{ matrix.arch }}-$LATEST_VERSION.zip"
TMP_ZIP="./all_packages.zip"
sudo wget -nv -O "$TMP_ZIP" "$URL"
sudo unzip -o "$TMP_ZIP" -d ./new_iso
sudo rm -f "$TMP_ZIP"
NPK_FILES=$(find ./new_iso -type f -name "*.npk")
fi
if [ -n "$NPK_FILES" ]; then
for file in $NPK_FILES; do
sudo -E python3 npk.py sign "$file" "$file"
done
fi
sudo cp -f 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 -E python3 npk.py create ./new_iso/gps-$LATEST_VERSION$ARCH.npk ./python3-$LATEST_VERSION$ARCH.npk python3 ./python3.sfs -desc="python 3.11.13"
sudo cp python3-$LATEST_VERSION$ARCH.npk ./new_iso/
sudo mkdir ./efiboot
sudo mount -o loop ./new_iso/efiboot.img ./efiboot
@@ -284,16 +295,17 @@ jobs:
cd ..
- name: Cache chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img.zip
if: steps.get_latest.outputs.has_new_version == 'true'
if: steps.get_latest.outputs.has_new_version == 'true' && (matrix.arch == 'x86' || matrix.arch == 'arm64')
id: cache-chr-img
uses: actions/cache@v4
with:
path: |
chr.img
key: chr-${{ env.LATEST_VERSION }}-${{ matrix.arch }}.img
key: chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
restore-keys: chr-${{ env.LATEST_VERSION }}${{ env.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'
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-chr-img.outputs.cache-hit != 'true' && (matrix.arch == 'x86' || 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
@@ -301,8 +313,101 @@ jobs:
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'
if: steps.get_latest.outputs.has_new_version == 'true' && (matrix.arch == 'x86' || matrix.arch == 'arm64')
run: |
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="{CAPACITY_MB}" ovf:capacityAllocationUnits="byte * 2^20" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="{POPULATED_SIZE}"/>
</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 v7.19.4</Annotation>
</AnnotationSection>
</VirtualSystem>
</Envelope>'
sudo modprobe nbd
sudo apt-get install -y qemu-utils > /dev/null
truncate --size 128M chr-$LATEST_VERSION$ARCH.img
@@ -311,14 +416,13 @@ jobs:
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
chr-$LATEST_VERSION$ARCH.img
dd if=chr.img of=mbr.bin bs=1 count=446 skip=0
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=chr.img 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
@@ -337,9 +441,10 @@ jobs:
sudo shred -v -n 1 -z /dev/nbd1p2
sudo mkfs.ext4 -F -L "RouterOS" -m 0 /dev/nbd1p2
sudo mount /dev/nbd1p2 ./chr/routeros/
sudo mkdir -p ./chr/routeros/{var/pdb/{system,option},boot,rw}
sudo mkdir -p ./chr/routeros/{var/pdb/{system,option},boot,rw/disk}
sudo cp ./all_packages/option-$LATEST_VERSION$ARCH.npk ./chr/routeros/var/pdb/option/image
sudo cp ./all_packages/routeros-$LATEST_VERSION$ARCH.npk ./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 ./chr/routeros/rw/disk/rc.local
sudo umount /dev/nbd1p2
sudo qemu-nbd -d /dev/nbd1
sudo rm -rf ./chr
@@ -349,7 +454,27 @@ jobs:
sudo qemu-img convert -f raw -O vpc chr-$LATEST_VERSION$ARCH-legacy-bios.img chr-$LATEST_VERSION$ARCH-legacy-bios.vhd
sudo qemu-img convert -f raw -O vhdx chr-$LATEST_VERSION$ARCH-legacy-bios.img chr-$LATEST_VERSION$ARCH-legacy-bios.vhdx
sudo qemu-img convert -f raw -O vdi chr-$LATEST_VERSION$ARCH-legacy-bios.img chr-$LATEST_VERSION$ARCH-legacy-bios.vdi
sudo qemu-img convert -f raw -O vmdk -o subformat=streamOptimized chr-$LATEST_VERSION$ARCH-legacy-bios.img chr-$LATEST_VERSION$ARCH-legacy-bios-disk1.vmdk
VMDK=chr-$LATEST_VERSION$ARCH-legacy-bios-disk1.vmdk
OVF=chr-$LATEST_VERSION$ARCH-legacy-bios.ovf
OVA=chr-$LATEST_VERSION$ARCH-legacy-bios.ova
FILE_SIZE=$(stat -c %s "$VMDK")
VIRTUAL_SIZE=$(qemu-img info --output=json "$VMDK" | jq '.["virtual-size"]' | head -n 1)
CAPACITY_MB=$((VIRTUAL_SIZE / 1024 / 1024))
POPULATED_SIZE=$FILE_SIZE
FIRMWARE_TYPE="bios"
echo "$OVF_TEMPLATE" | sudo sed -e "s|{VMDK_FILE}|$VMDK|g" \
-e "s|{FILE_SIZE}|$FILE_SIZE|g" \
-e "s|{CAPACITY_MB}|$CAPACITY_MB|g" \
-e "s|{POPULATED_SIZE}|$POPULATED_SIZE|g" \
-e "s|{FIRMWARE_TYPE}|$FIRMWARE_TYPE|g" |sudo tee $OVF
sudo tar -cvf "$OVA" "$OVF" "$VMDK"
sudo rm $VMDK
sudo rm $OVF
sudo zip $OVA.zip $OVA
sudo rm $OVA
sudo zip chr-$LATEST_VERSION$ARCH-legacy-bios.qcow2.zip chr-$LATEST_VERSION$ARCH-legacy-bios.qcow2
sudo zip chr-$LATEST_VERSION$ARCH-legacy-bios.vmdk.zip chr-$LATEST_VERSION$ARCH-legacy-bios.vmdk
sudo zip chr-$LATEST_VERSION$ARCH-legacy-bios.vhd.zip chr-$LATEST_VERSION$ARCH-legacy-bios.vhd
@@ -385,11 +510,31 @@ jobs:
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 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 qemu-img convert -f raw -O vmdk -o subformat=streamOptimized chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH-disk1.vmdk
VMDK=chr-$LATEST_VERSION$ARCH-disk1.vmdk
OVF=chr-$LATEST_VERSION$ARCH.ovf
OVA=chr-$LATEST_VERSION$ARCH.ova
FILE_SIZE=$(stat -c %s "$VMDK")
VIRTUAL_SIZE=$(qemu-img info --output=json "$VMDK" | jq '.["virtual-size"]' | head -n 1)
CAPACITY_MB=$((VIRTUAL_SIZE / 1024 / 1024))
POPULATED_SIZE=$FILE_SIZE
FIRMWARE_TYPE="efi"
echo "$OVF_TEMPLATE" | sudo sed -e "s|{VMDK_FILE}|$VMDK|g" \
-e "s|{FILE_SIZE}|$FILE_SIZE|g" \
-e "s|{CAPACITY_MB}|$CAPACITY_MB|g" \
-e "s|{POPULATED_SIZE}|$POPULATED_SIZE|g" \
-e "s|{FIRMWARE_TYPE}|$FIRMWARE_TYPE|g" |sudo tee $OVF
sudo tar -cvf "$OVA" "$OVF" "$VMDK"
sudo rm $VMDK
sudo rm $OVF
sudo zip $OVA.zip $OVA
sudo rm $OVA
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
@@ -404,6 +549,51 @@ jobs:
sudo rm chr-$LATEST_VERSION$ARCH.vdi
sudo rm chr-$LATEST_VERSION$ARCH.img
- name: Cache all_packages${{ env.ARCH }}-${{ env.LATEST_VERSION }}.zip
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch != 'x86' && matrix.arch != 'arm64'
id: cache-all-packages
uses: actions/cache@v4
with:
path: all_packages.zip
key: all_packages${{ env.ARCH }}-${{ env.LATEST_VERSION }}.zip
restore-keys: all_packages${{ env.ARCH }}-${{ env.LATEST_VERSION }}.zip
- name: Get all_packages{{ env.ARCH }}-${{ env.LATEST_VERSION }}.zip
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-all-packages.outputs.cache-hit != 'true' && matrix.arch != 'x86' && matrix.arch != 'arm64'
run: |
sudo wget -nv -O all_packages.zip https://download.mikrotik.com/routeros/$LATEST_VERSION/all_packages-${{ matrix.arch }}-$LATEST_VERSION.zip
- name: Cache Main package routeros-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch != 'x86' && matrix.arch != 'arm64'
id: cache-main-package
uses: actions/cache@v4
with:
path: routeros.npk
key: routeros-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
restore-keys: routeros-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
- name: Get Main package routeros-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-main-package.outputs.cache-hit != 'true' && matrix.arch != 'x86' && matrix.arch != 'arm64'
run: |
sudo wget -nv -O routeros.npk https://download.mikrotik.com/routeros/$LATEST_VERSION/routeros-$LATEST_VERSION$ARCH.npk
- name: Patch all_packages{{ env.ARCH }}-${{ env.LATEST_VERSION }}.zip
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch != 'x86' && matrix.arch != 'arm64'
run: |
sudo unzip all_packages.zip -d ./all_packages
sudo cp routeros.npk routeros-$LATEST_VERSION$ARCH.npk
sudo -E python3 patch.py npk routeros-$LATEST_VERSION$ARCH.npk
NPK_FILES=$(find ./all_packages/*.npk)
for file in $NPK_FILES; do
sudo -E python3 npk.py sign $file $file
done
sudo cp -f routeros-$LATEST_VERSION$ARCH.npk ./all_packages/routeros-$LATEST_VERSION$ARCH.npk
sudo -E python3 npk.py create ./all_packages/gps-$LATEST_VERSION$ARCH.npk ./all_packages/option-$LATEST_VERSION$ARCH.npk option ./option.sfs -desc="busybox"
sudo -E python3 npk.py create ./all_packages/gps-$LATEST_VERSION$ARCH.npk ./all_packages/python3-$LATEST_VERSION$ARCH.npk python3 ./python3.sfs -desc="python 3.11.13"
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' && env.RELEASE == 'true'
@@ -412,12 +602,13 @@ jobs:
with:
path: refind-bin-0.14.2.zip
key: refind
restore-keys: refind
- name: Get refind
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && steps.cache-refind.outputs.cache-hit != 'true' && env.RELEASE == '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
- name: Create install-image-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && env.RELEASE == 'true'
run: |
sudo modprobe nbd
@@ -456,7 +647,7 @@ jobs:
sudo rm ./install-image-$LATEST_VERSION.img
- name: Cache NetInstall ${{ env.LATEST_VERSION }}
- name: Cache NetInstall ${{ env.LATEST_VERSION }}${{ env.ARCH }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && env.RELEASE == 'true'
id: cache-netinstall
uses: actions/cache@v4
@@ -465,6 +656,7 @@ jobs:
netinstall.zip
netinstall.tar.gz
key: netinstall-${{ env.LATEST_VERSION }}
restore-keys: 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' && env.RELEASE == 'true'
@@ -472,7 +664,7 @@ jobs:
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 }}
- name: Patch netinstall ${{ env.LATEST_VERSION }}${{ env.ARCH }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && env.RELEASE == 'true'
run: |
sudo unzip netinstall.zip
@@ -482,14 +674,22 @@ jobs:
sudo -E python3 patch.py netinstall netinstall-cli
sudo tar -czvf netinstall-$LATEST_VERSION.tar.gz ./netinstall-cli
- name: Upload Files
- name: Prepare for Upload
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
sudo mkdir -p ./publish/$LATEST_VERSION
sudo cp CHANGELOG ./publish/$LATEST_VERSION/
sudo cp ./all_packages/*.npk ./publish/$LATEST_VERSION/
# sudo cp mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso ./publish/$LATEST_VERSION/ 2>/dev/null || true
# sudo cp chr-${{ env.LATEST_VERSION }}*.zip ./publish/$LATEST_VERSION/ 2>/dev/null || true
# sudo cp netinstall-${{ env.LATEST_VERSION }}.* ./publish/$LATEST_VERSION/ 2>/dev/null || true
# sudo cp install-image-${{ env.LATEST_VERSION }}.zip ./publish/$LATEST_VERSION/ 2>/dev/null || true
echo $LATEST_VERSION $BUILD_TIME | sudo tee ./publish/NEWESTa7.${{ matrix.channel }}
- name: Upload Files
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
sudo chown -R root:root ./publish/
LOCAL_PATH=./publish/$LATEST_VERSION
REMOTE_PATH=${{ secrets.SSH_DIRECTORY }}
SERVER=${{ secrets.SSH_SERVER }}
@@ -507,13 +707,25 @@ jobs:
EOF
sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no -p $PORT $USER@$SERVER \
"echo $LATEST_VERSION $BUILD_TIME | tee /rw/disk/$REMOTE_PATH/NEWESTa7.$CHANNEL; /rw/disk/$REMOTE_PATH/packages.sh /rw/disk/$REMOTE_PATH/$LATEST_VERSION"
"echo $LATEST_VERSION $BUILD_TIME | tee /rw/disk/$REMOTE_PATH/NEWESTa7.$CHANNEL; bash /rw/disk/$REMOTE_PATH/packages.sh /rw/disk/$REMOTE_PATH/$LATEST_VERSION"
sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no -p $PORT $USER@$SERVER \
"chown -R 32768:32768 /rw/disk/$REMOTE_PATH/"
"chown -R 32768:32768 /rw/disk/$REMOTE_PATH/"
# - name: Upload to R2 Bucket
# if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
# uses: ryand56/r2-upload-action@latest
# with:
# r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
# r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
# r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
# r2-bucket: routeros
# source-dir: ./publish
# destination-dir: ./routeros
# keep-file-fresh: true
# output-file-url: false
- name: Clear Cloudflare cache
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
@@ -522,9 +734,10 @@ jobs:
--header "Content-Type:application/json" \
--data '{"purge_everything": true}'
- name: Delete Release tag ${{ env.LATEST_VERSION }} ${{ matrix.arch }}
- name: Delete Release tag ${{ env.LATEST_VERSION }} ${{ env.ARCH }}
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
sed -i "1i Build Time:$BUILD_TIME" CHANGELOG
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')
@@ -538,11 +751,11 @@ jobs:
echo "Release not found for tag: $LATEST_VERSION)"
fi
- name: Create Release tag ${{ env.LATEST_VERSION }} ${{ matrix.arch }}
- name: Create Release tag ${{ env.LATEST_VERSION }} ${{ env.ARCH }}
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
uses: softprops/action-gh-release@v2
with:
name: "RouterOS ${{ env.LATEST_VERSION }} ${{ matrix.arch }}"
name: "RouterOS ${{ env.LATEST_VERSION }} ${{ env.ARCH }}"
body_path: "CHANGELOG"
tag_name: ${{ env.LATEST_VERSION }}${{ env.ARCH }}
make_latest: ${{ matrix.channel == 'stable'}} && ${{ matrix.arch == 'x86'}}
@@ -559,8 +772,8 @@ jobs:
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'false'
uses: actions/upload-artifact@v4
with:
name: mikrotik-${{ env.LATEST_VERSION }}-${{ matrix.arch }}
name: mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}
path: |
all_packages-*-${{ env.LATEST_VERSION }}.zip
mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
chr-${{ env.LATEST_VERSION }}*.zip
chr-${{ env.LATEST_VERSION }}*.zip

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.

View File

@@ -1,18 +1,30 @@
[![Patch Mikrotik RouterOS 6.x](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_6.yml/badge.svg)](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_6.yml)
[![Patch Mikrotik RouterOS 7.x](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml/badge.svg)](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml)
# MikroTik RouterOS Patch [[English](README_EN.md)]
[![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)
### [[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
![Cloud Status](https://img.shields.io/endpoint?url=https://mikrotik.ltd/status/cloud)
![VPS Status](https://img.shields.io/endpoint?url=https://mikrotik.ltd/status/dartnode)
*如果云服务或部署云服务的虚拟主机都不在线那么在线更新、在线授权、云备份、DDNS以及ROS_Keygen_Bot都暂时不能使用*
### 从7.19.4和7.20beta8开始安装option包以后会自动激活授权如果有rc.local文件会自动加载运行。
```mermaid
graph TD
A[启动] --> B[检查 keygen 文件是否存在 ]
B -->|是| C[fork 执行 keygen]
B -->|否| D[检查 rc.local 文件是否存在]
C --> D
D -->|是| E[fork 执行 /bin/sh rc.local]
D -->|否| F[启动服务]
E --> F
```
![](image/install.png)
![](image/routeros.png)
@@ -31,6 +43,7 @@
## 如何使用Shell
安装 option-{version}.npk 包
telnet到RouterOS,用户名devel,密码与admin的密码相同
要使用devel用户名登录必须安装option-{version}.npk包且启用。
## 如何授权许可
进入shell
运行 keygen
@@ -44,10 +57,15 @@
对npk文件进行解包修改创建签名和验证
### patch.py
替换公钥并签名
## 所有的修补操作都自动运行在[Github Action](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/)。
### 感谢赞助
[ZMTO](https://console.zmto.com)

View File

@@ -1,6 +1,8 @@
[![Patch Mikrotik RouterOS 6.x](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_6.yml/badge.svg)](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_6.yml)
[![Patch Mikrotik RouterOS 7.x](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml/badge.svg)](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml)
# MikroTik RouterOS Patch [[中文](README.md)]
[![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)
### [[Discord](https://discord.gg/keV6MWQFtX)] [[Telegram](https://t.me/mikrotikpatch)] [[Keygen(Telegram Bot)](https://t.me/ROS_Keygen_Bot)]
@@ -40,6 +42,9 @@
### patch.py
Patch public key and sign NPK files
## Thanks for sponsoring
[ZMTO](https://console.zmto.com/)
## all patches are applied automatically with [Github Action](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/).

View File

@@ -2,6 +2,8 @@
[![Patch Mikrotik RouterOS 7.x](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml/badge.svg)](https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml)
# Patch para MikroTik RouterOS [[中文](README.md)]
[![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)
### [[Discord](https://discord.gg/keV6MWQFtX)] [[Telegram](https://t.me/mikrotikpatch)] [[Keygen (Bot do Telegram)](https://t.me/ROS_Keygen_Bot)]
@@ -58,4 +60,7 @@ Assina, verifica, cria e extrai arquivos .npk
Altera a chave pública e assina arquivos .npk
```
## Thanks for sponsoring
[ZMTO](https://console.zmto.com/)
## Todos os patches são aplicados automaticamente com [GitHub Actions](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/)

Binary file not shown.

BIN
busybox/busybox_arm Normal file

Binary file not shown.

Binary file not shown.

112
chr.sh
View File

@@ -1,15 +1,97 @@
curl -# -L https://github.com/elseif/MikroTikPatch/releases/download/7.19.1/chr-7.19.1-legacy-bios.img.zip -o /tmp/chr.img.zip && cd /tmp && \
gunzip -c chr.img.zip > chr.img && \
STORAGE=`lsblk | grep disk | cut -d ' ' -f 1 | head -n 1` && \
echo STORAGE is $STORAGE && \
ETH=`ip route show default | sed -n 's/.* dev \([^\ ]*\) .*/\1/p'` && \
echo ETH is $ETH && \
ADDRESS=`ip addr show $ETH | grep global | cut -d' ' -f 6 | head -n 1` && \
echo ADDRESS is $ADDRESS && \
GATEWAY=`ip route list | grep default | cut -d' ' -f 3` && \
echo GATEWAY is $GATEWAY && \
sleep 5 && \
dd if=chr.img of=/dev/$STORAGE bs=4M oflag=sync && \
echo "Ok, reboot" && \
echo 1 > /proc/sys/kernel/sysrq && \
echo b > /proc/sysrq-trigger
#!/bin/bash
set -e
LATEST_VERSION="${1:-7.19.4}"
echo "VERSION: $LATEST_VERSION"
ARCH=$(uname -m)
if [[ $LATEST_VERSION == 7.* ]]; then
case $ARCH in
x86_64|i386|i486|i586|i686)
echo "ARCH: $ARCH"
if [ -d /sys/firmware/efi ]; then
echo "BOOT MODE: UEFI"
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$LATEST_VERSION/chr-$LATEST_VERSION.img.zip"
else
echo "BOOT MODE: BIOS/MBR"
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$LATEST_VERSION/chr-$LATEST_VERSION-legacy-bios.img.zip"
fi
;;
aarch64)
echo "ARCH: $ARCH"
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$LATEST_VERSION-arm64/chr-$LATEST_VERSION-arm64.img.zip"
;;
*)
echo "Unsupported architecture: $ARCH"
exit 1
;;
esac
else
case $ARCH in
x86_64|i386|i486|i586|i686)
echo "ARCH: $ARCH"
IMG_URL="https://github.com/elseif/MikroTikPatch/releases/download/$LATEST_VERSION/chr-$LATEST_VERSION.img.zip"
;;
*)
echo "Unsupported architecture: $ARCH"
exit 1
;;
esac
fi
STORAGE=$(lsblk -d -n -o NAME,TYPE | awk '$2=="disk"{print $1; exit}')
echo "STORAGE: $STORAGE"
ETH=$(ip route show default | grep '^default' | sed -n 's/.* dev \([^\ ]*\) .*/\1/p')
echo "ETH: $ETH"
ADDRESS=$(ip addr show $ETH | grep global | cut -d' ' -f 6 | head -n 1)
echo "ADDRESS: $ADDRESS"
GATEWAY=$(ip route list | grep default | cut -d' ' -f 3)
echo "GATEWAY: $GATEWAY"
DNS=$(grep '^nameserver' /etc/resolv.conf | awk '{print $2}' | head -n 1)
[ -z "$DNS" ] && DNS="8.8.8.8"
echo "DNS: $DNS"
echo "FILE: $(basename $IMG_URL)"
if command -v wget >/dev/null 2>&1; then
wget --no-check-certificate -O /tmp/chr.img.zip "$IMG_URL" || { echo "Download failed!"; exit 1; }
elif command -v curl >/dev/null 2>&1; then
curl -L --insecure -o /tmp/chr.img.zip "$IMG_URL" || { echo "Download failed!"; exit 1; }
else
echo "Neither wget nor curl is installed. Cannot download $url"
exit 1
fi
cd /tmp
gunzip -c chr.img.zip > chr.img
RANDOM_PASS=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 8)
if LOOP=$(losetup -Pf --show chr.img 2>/dev/null); then
sleep 3
MNT=/tmp/chr
mkdir -p $MNT
if mount ${LOOP}p2 $MNT 2>/dev/null; then
cat <<EOF | tee $MNT/rw/autorun.scr
/ip address add address=$ADDRESS interface=ether1
/ip route add gateway=$GATEWAY
/ip dns set servers=$DNS
/user set admin password="$RANDOM_PASS"
EOF
echo "autorun.scr file created."
echo -e "admin password: \e[31m$RANDOM_PASS\e[0m"
umount $MNT
else
echo "Failed to mount partition 2, skipping autorun.scr creation."
fi
losetup -d $LOOP
fi
echo "WARNING: All data on /dev/$STORAGE will be lost!"
read -p "Do you want to continue? [Y/n]: " confirm < /dev/tty
confirm=${confirm:-Y}
if [[ "$confirm" =~ ^[Nn]$ ]]; then
echo "Operation aborted."
exit 1
fi
dd if=chr.img of=/dev/$STORAGE bs=4M conv=fsync
echo "Ok, rebooting..."
echo 1 > /proc/sys/kernel/sysrq 2>/dev/null || true
echo b > /proc/sysrq-trigger 2>/dev/null || true
reboot -f

600
index.html Normal file
View File

@@ -0,0 +1,600 @@
<!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>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://code.iconify.design/3/3.1.1/iconify.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.loli.net">
<link rel="preconnect" href="https://gstatic.loli.net" crossorigin>
<link id="webfont-css" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap" rel="stylesheet">
<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';
function isLoaded(name){
if (!('fonts' in document)) return false;
try { return document.fonts.check('1em "' + name + '"'); } catch (e) { return false; }
}
function switchToMirror(){
if (linkEl.href !== mirrorHref) { linkEl.href = mirrorHref; }
}
if (!('fonts' in document)) { switchToMirror(); return; }
var decided = false;
var timer = setTimeout(function(){
if (!decided && (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
}
}, 2500);
document.fonts.ready.then(function(){
if (!decided && (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
} else {
decided = true; clearTimeout(timer);
}
});
})();
</script>
<style type="text/tailwindcss">
body {
font-family: 'Inter', sans-serif;
background-image: radial-gradient(circle at top, #dde3ee 0%, #f1f5f9 60%);
}
html.dark body {
background-image: radial-gradient(circle at top, #1e293b 0%, #0f172a 60%);
}
#command pre, #command code {
font-family: 'Fira Code', monospace;
}
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #94a3b8;
border-radius: 10px;
}
html.dark .custom-scrollbar::-webkit-scrollbar-thumb {
background: #475569;
}
#version-buttons button.active {
@apply bg-blue-600 text-white shadow-md;
}
summary::-webkit-details-marker {
display: none;
}
.content-card {
@apply transition-all duration-700;
}
.loading .content-card {
@apply opacity-0 translate-y-4;
}
</style>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['Fira Code', 'monospace'],
},
},
},
}
</script>
</head>
<body class="bg-slate-100 dark:bg-gray-900 text-slate-700 dark:text-slate-300 transition-colors duration-300 loading">
<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>
<div class="container mx-auto max-w-7xl px-4 py-8 md:py-16">
<header class="text-center mb-12 content-card" style="transition-delay: 100ms;">
<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>
<div class="mt-6 flex justify-center items-center gap-4 flex-wrap">
<a href="https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_6.yml" target="_blank"><img src="https://mikrotik.ltd/badge/mikrotik_patch_6.yml" alt="Patch Mikrotik RouterOS 6.x"></a>
<a href="https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml" target="_blank"><img src="https://mikrotik.ltd/badge/mikrotik_patch_7.yml" alt="Patch Mikrotik RouterOS 7.x"></a>
<img src="https://img.shields.io/endpoint?logo=icloud&url=https://mikrotik.ltd/status/cloud" alt="Cloud Status">
<img src="https://img.shields.io/endpoint?logo=googlecloudstorage&url=https://mikrotik.ltd/status/dartnode" alt="VPS Status">
</div>
<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 class="space-y-12">
<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">
<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>
<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">
<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>
<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>
<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>
<button data-i18n="clearCache" id="clear-cache" class="w-full text-sm font-semibold bg-slate-100 hover:bg-slate-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-slate-700 dark:text-slate-200 py-2 px-4 rounded-lg transition-colors flex items-center justify-center gap-2"></button>
</div>
</div>
</div>
</div>
</div>
<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>
<div class="flex flex-wrap items-center gap-2 mb-4">
<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>
<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>
</div>
<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 id="download-rows" class="space-y-8 content-card" style="transition-delay: 400ms;">
<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 class="mt-16 pt-8 border-t border-slate-200 dark:border-gray-700/50 text-center text-sm text-slate-500 dark:text-slate-400">
<div class="flex justify-center items-center gap-6">
<a href="https://t.me/mikrotikpatch" target="_blank" title="Telegram" class="text-slate-500 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
<span class="iconify text-3xl" data-icon="ph:telegram-logo-bold"></span>
</a>
<a href="https://github.com/elseif/MikroTikPatch" target="_blank" title="GitHub" class="text-slate-500 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
<span class="iconify text-3xl" data-icon="ph:github-logo-bold"></span>
</a>
</div>
</footer>
</div>
<div id="urls-pannel" 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-2xl max-h-[90vh] flex flex-col transform transition-all opacity-0 scale-95" id="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" data-i18n="copyUrlsClearCache"></h3>
<button id="urls-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>
<div id="urls-list" 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 whitespace-pre-wrap break-all"></div>
<footer class="p-4 border-t border-slate-200 dark:border-gray-700">
<button id="copy-to-clear-cache" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg 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-800 flex items-center justify-center gap-2" data-i18n="copyUrlsClearCache" href="#"></button>
</footer>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", async () => {
const ICONS = {
download: 'ph:download-simple-bold',
changelog: 'ph:newspaper-clipping-bold',
trash: 'ph:trash-bold',
copyAndOpen: 'ph:copy-simple-bold'
};
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];
}
} catch (e) {
console.warn("fetch failed or timeout:", e);
}
return defaultValue;
}
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",
clearCache:"Clear gh-proxy Cache",
copyUrlsClearCache:"Copy URLs & Open Clear Cache Page",
loading:"Loading latest versions...",
quickInformation:"Quick Information",
settings:"Settings",
theme:"Theme",
},
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代理加速",
clearCache:"清除gh-proxy缓存",
copyUrlsClearCache:"复制链接并打开缓存清除页面",
loading:"正在加载最新版本...",
quickInformation:"快速信息",
settings:"设置",
theme:"主题",
}
};
function setLanguage(lang) {
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute("data-i18n");
if (i18n[lang] && i18n[lang][key]) {
if (key === 'clearCache') {
el.innerHTML = `<span class="iconify" data-icon="${ICONS.trash}"></span> ${i18n[lang][key]}`;
} else if (key === 'copyUrlsClearCache' && el.id === 'copy-to-clear-cache') {
el.innerHTML = `<span class="iconify" data-icon="${ICONS.copyAndOpen}"></span> ${i18n[lang][key]}`;
} else 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);
}
function initLanguage() {
let savedLang = localStorage.getItem("lang");
if (!savedLang) {
savedLang = navigator.language.startsWith("zh") ? "zh" : "en";
}
document.getElementById("lang-switcher").value = savedLang;
setLanguage(savedLang);
}
document.getElementById("lang-switcher").addEventListener("change", e => {
setLanguage(e.target.value);
});
const themeBtn = document.getElementById("theme-toggle-btn");
const htmlEl = document.documentElement;
const sunIcon = themeBtn.querySelector('.sun-icon');
const moonIcon = themeBtn.querySelector('.moon-icon');
function updateThemeUI() {
if (htmlEl.classList.contains('dark')) {
sunIcon.classList.remove('hidden');
moonIcon.classList.add('hidden');
} else {
sunIcon.classList.add('hidden');
moonIcon.classList.remove('hidden');
}
}
function initializeTheme() {
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
htmlEl.classList.toggle("dark", savedTheme === "dark");
} else {
htmlEl.classList.toggle("dark", window.matchMedia('(prefers-color-scheme: dark)').matches);
}
updateThemeUI();
}
themeBtn.addEventListener("click", () => {
htmlEl.classList.toggle("dark");
localStorage.setItem("theme", htmlEl.classList.contains("dark") ? "dark" : "light");
updateThemeUI();
});
initializeTheme();
initLanguage();
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", "7.19.4"),
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWESTa7.testing", "7.20rc1"),
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWEST6.long-term", "6.49.18"),
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWEST6.stable", "6.49.19"),
]);
loadingOverlay.style.display = "none";
document.body.classList.remove('loading');
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:"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" } ] }
]
},
];
const fileNames = (version, arch, type) => {
const files = [];
if (version.charAt(0) === "6" && arch !== "x86") return files;
if (type === "main" && version.charAt(0) === "7") files.push(`routeros-${version}${arch === "x86" ? "" : "-" + arch}.npk`);
if (type === "iso") files.push(`mikrotik-${version}${arch === "x86" ? "" : "-" + arch}.iso`);
if (type === "extra") files.push(`all_packages-${arch}-${version}.zip`);
if (type === "install") files.push(`install-image-${version}${arch === "x86" ? "" : "-" + arch}.zip`);
if (type in { vhdx: 1, vmdk: 1, vdi: 1, vhd: 1, img: 1 }) {
files.push(`chr-${version}${arch === "x86" ? "" : "-" + arch}.${type}.zip`);
if (arch === "x86" && version.charAt(0) !== "6") files.push(`chr-${version}-legacy-bios.${type}.zip`);
}
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`);
}
if (type === "netinstall_windows") files.push(`netinstall-${version}.zip`);
if (type === "netinstall_linux_cli") files.push(`netinstall-${version}.tar.gz`);
return files;
};
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}/`;
const container = document.getElementById("download-rows");
container.innerHTML = '';
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;
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"><a href="https://upgrade.mikrotik.ltd/routeros/${v.version}/CHANGELOG" class="inline-flex items-center gap-1.5 text-blue-500 dark:text-blue-400 hover:underline" title="Changelog" target="_blank"><span class="iconify" data-icon="${ICONS.changelog}"></span> View</a></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("<br>");
return `<td class="px-6 py-4 text-center space-y-2">${links}</td>`;
}).join("")}
<td></td>
</tr>
`).join("")}
`).join("")}
</tbody>
</table>
`;
content.appendChild(table);
accordion.appendChild(summary);
accordion.appendChild(content);
container.appendChild(accordion);
});
const ghProxyCheckbox = document.getElementById("gh-proxy");
const clearCacheBtn = document.getElementById("clear-cache");
ghProxyCheckbox.checked = localStorage.getItem("gh-proxy-enabled") === "true";
clearCacheBtn.style.display = ghProxyCheckbox.checked ? "block" : "none";
function updateAllDownloadLinks() {
document.querySelectorAll('a[href*="github.com/elseif/MikroTikPatch"]').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/", "");
}
});
const activeBtn = document.querySelector("#version-buttons button.active");
if (activeBtn) updateCommand(activeBtn.dataset.version);
}
ghProxyCheckbox.addEventListener("change", () => {
localStorage.setItem("gh-proxy-enabled", ghProxyCheckbox.checked ? "true" : "false");
clearCacheBtn.style.display = ghProxyCheckbox.checked ? "block" : "none";
updateAllDownloadLinks();
});
const modal = document.getElementById("urls-pannel");
const modalContent = document.getElementById("modal-content");
clearCacheBtn.addEventListener("click", () => {
const links = document.querySelectorAll('a[href^="https://gh-proxy.com/https://github.com/elseif/MikroTikPatch/"]');
document.getElementById("urls-list").innerText = Array.from(links).map(a => a.href).filter(href => !href.includes(".yml")).join("\n");
modal.classList.remove('hidden');
setTimeout(() => { modalContent.classList.add('opacity-100', 'scale-100'); modalContent.classList.remove('opacity-0', 'scale-95'); }, 10);
});
document.getElementById("urls-close").addEventListener("click", () => {
modalContent.classList.remove('opacity-100', 'scale-100');
modalContent.classList.add('opacity-0', 'scale-95');
setTimeout(() => modal.classList.add('hidden'), 200);
});
document.getElementById("copy-to-clear-cache").addEventListener("click", () => {
navigator.clipboard.writeText(document.getElementById("urls-list").innerText);
window.open("https://cache.gh-proxy.com/", "_blank");
document.getElementById("urls-close").click();
});
const versionBtnsContainer = document.getElementById("version-buttons");
const commandDiv = document.getElementById("command");
const copyBtn = document.getElementById("copy-btn");
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>
`;
function updateCommand(version) {
const tool = document.querySelector('input[name="tool"]:checked').value;
const proxy = localStorage.getItem("gh-proxy-enabled") === "true" ? " | sed 's#https://github.com#https://gh-proxy.com/https://github.com#g'" : "";
const cmd = tool === "curl"
? `<pre class="text-slate-300"><code><span class="text-pink-400">curl</span> <span class="text-cyan-400">https://mikrotik.ltd/chr.sh</span>${proxy} | <span class="text-pink-400">bash</span> -s <span class="text-green-400">${version}</span></code></pre>`
: `<pre class="text-slate-300"><code><span class="text-pink-400">wget</span> -O - <span class="text-cyan-400">https://mikrotik.ltd/chr.sh</span>${proxy} | <span class="text-pink-400">bash</span> -s <span class="text-green-400">${version}</span></code></pre>`;
commandDiv.innerHTML = cmd;
}
document.querySelectorAll('input[name="tool"]').forEach(radio => {
radio.addEventListener("change", () => {
const activeBtn = document.querySelector("#version-buttons button.active");
if (activeBtn) updateCommand(activeBtn.dataset.version);
});
});
const buttons = document.querySelectorAll("#version-buttons button");
buttons.forEach(btn => {
btn.addEventListener("click", (e) => {
e.preventDefault();
updateCommand(btn.dataset.version);
buttons.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
});
});
buttons[0].click();
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_arm Normal file

Binary file not shown.

Binary file not shown.

44
npk.py
View File

@@ -207,7 +207,46 @@ class NovaPackage(Package):
self._parts.append(NpkPartItem(NpkPartID(part_id),NpkInfo.unserialize_from(part_data)))
else:
self._parts.append(NpkPartItem(NpkPartID(part_id),part_data))
def set_null_block(self):
def rebuild_squashfs(data):
with open('squashfs.sfs', 'wb') as f:
f.write(data)
os.system('unsquashfs -d squashfs-root squashfs.sfs')
os.system('rm squashfs.sfs')
os.system('mksquashfs squashfs-root squashfs.sfs -comp xz -no-xattrs -b 256k')
os.system('rm -rf squashfs-root')
with open('squashfs.sfs', '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:
@@ -229,6 +268,7 @@ class NovaPackage(Package):
import hashlib
from mikro import mikro_kcdsa_sign,mikro_eddsa_sign
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)
@@ -356,4 +396,4 @@ if __name__=='__main__':
option_npk.save(args.output)
print(f'Created {args.output}')
else:
parser.print_help()
parser.print_help()

View File

@@ -24,7 +24,7 @@ def replace_key(old,new,data,name=''):
data = replace_chunks(old_chunks, new_chunks, data,name)
arch = os.getenv('ARCH') or 'x86'
arch = arch.replace('-', '')
if arch == 'arm64':
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]
@@ -32,9 +32,35 @@ def replace_key(old,new,data,name=''):
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')]
new_codes = [bytes.fromhex('FF34A0E3'),bytes.fromhex('753C83E2'),bytes.fromhex('FC3083E2')]
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):