Compare commits

..

325 Commits

Author SHA1 Message Date
elseif
f750442687 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:56 +08:00
elseif
06bd69f331 Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:42 +08:00
elseif
e35cf5f74e Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:27 +08:00
elseif
e12e1aa67b Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:45:06 +08:00
elseif
6a8aaa89d8 Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 09:44:53 +08:00
elseif
495927d0de Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 08:57:23 +08:00
elseif
c73c6bacc8 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-11 08:56:31 +08:00
elseif
8e7134e0a6 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 17:27:26 +08:00
elseif
f4cc728789 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 17:26:58 +08:00
elseif
8c3278f477 Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 17:07:20 +08:00
elseif
04a831b463 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 16:30:01 +08:00
elseif
c75dd48261 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:36:11 +08:00
elseif
6519ab2e12 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:26:20 +08:00
elseif
ec1c0f1719 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:16:03 +08:00
elseif
3de34ef556 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 15:09:47 +08:00
elseif
8499e4591a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:23:18 +08:00
elseif
f520ff799c Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:12:45 +08:00
elseif
ccb4220a61 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:10:02 +08:00
elseif
8159018e3c Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 12:09:07 +08:00
elseif
dc30d89afb Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-10 07:47:39 +08:00
elseif
4472649719 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 10:23:13 +08:00
elseif
fc469087be Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 10:22:15 +08:00
elseif
477fd66fe0 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 10:04:22 +08:00
elseif
cd32348c52 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-09-08 08:37:29 +08:00
elseif
37f92c3def Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 01:39:38 +08:00
elseif
2bb452b300 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:16:44 +08:00
elseif
710921ef23 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:13:21 +08:00
elseif
8bfcdfe5a8 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:12:51 +08:00
elseif
2baf264aec Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:11:15 +08:00
elseif
fd6f36d484 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:08:06 +08:00
elseif
2ab01e5328 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-09-01 00:06:52 +08:00
elseif
16854dadf9 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-31 23:56:16 +08:00
elseif
d981a4b137 Delete keygen/tmp
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:53:58 +08:00
elseif
378c9009b8 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:53:34 +08:00
elseif
73f9ba0506 Create tmp
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:52:28 +08:00
elseif
646f479290 Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:49:53 +08:00
elseif
e3b46ed7c3 Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:49:41 +08:00
elseif
1a66685cc4 Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 21:49:29 +08:00
elseif
cbd6ffa50c Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 19:38:57 +08:00
elseif
73d90b27e0 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 19:31:24 +08:00
elseif
60728d6fe2 Update index.html
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 19:27:34 +08:00
elseif
556cc81474 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 18:35:01 +08:00
elseif
2286063b65 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 17:29:36 +08:00
elseif
907b335d3a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 17:29:10 +08:00
elseif
567819bec4 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 17:19:41 +08:00
elseif
bbc6b854cd Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:53:43 +08:00
elseif
6f4c9b43fc Delete keygen/keygen_arm
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:52:57 +08:00
elseif
659de9d659 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:34:23 +08:00
elseif
78707050a0 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:31:09 +08:00
elseif
cdb378b45d Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:25:09 +08:00
elseif
9b40d04e38 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:17:43 +08:00
elseif
110cda779c Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 16:12:13 +08:00
elseif
36be15859e Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-27 15:56:02 +08:00
elseif
c40954115b Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 17:22:44 +08:00
elseif
2babd89f27 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 17:19:46 +08:00
elseif
7a6e538834 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 14:10:29 +08:00
elseif
a2f9af37ad Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 14:05:54 +08:00
elseif
6e630b852a Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:49:44 +08:00
elseif
4987501168 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:35:49 +08:00
elseif
c2ec6d439c Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:27:00 +08:00
elseif
c5712825ba Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:22:40 +08:00
elseif
072417bf1b Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:17:09 +08:00
elseif
f2d18e32f6 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-26 00:10:04 +08:00
elseif
7758330905 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:25:45 +08:00
elseif
0ff47ed53e Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:24:17 +08:00
elseif
90dfb10d32 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:21:53 +08:00
elseif
a217ae8df0 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:18:07 +08:00
elseif
fa9c53bef1 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:07:27 +08:00
elseif
18b6449701 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 22:05:07 +08:00
elseif
d7517cb9f4 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 21:55:55 +08:00
elseif
3a17176568 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 21:54:18 +08:00
elseif
da3653fcb7 更新 mikrotik_patch_7.yml 2025-08-25 15:21:45 +08:00
elseif
48bd5e9601 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 14:26:08 +08:00
elseif
c0f53a242a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 13:05:25 +08:00
elseif
8d18551f5b Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 12:54:44 +08:00
elseif
f29d91d1a4 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 12:39:16 +08:00
elseif
89c13ae6fa Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-25 12:34:02 +08:00
elseif
e028fdd880 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 05:14:47 +08:00
elseif
88ff1f7130 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 05:07:35 +08:00
elseif
92f26c270e Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:59:06 +08:00
elseif
c1163894f3 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:45:03 +08:00
elseif
5196f20043 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:43:38 +08:00
elseif
bba676a2d6 Rename chr_busybox.sh to chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:40:23 +08:00
elseif
259051906c Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:40:09 +08:00
elseif
2162e76e64 Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:19:30 +08:00
elseif
7c22877423 Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:14:22 +08:00
elseif
6a486adfc6 Update chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:11:51 +08:00
elseif
49608606f2 Update and rename chr.sh to chr_busybox.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 04:08:09 +08:00
elseif
85a2526bcd Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 03:58:06 +08:00
elseif
b5049c0ebb Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 03:56:41 +08:00
elseif
15273578e8 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-08-23 03:31:43 +08:00
elseif
a85cbf1548 Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-22 00:26:03 +08:00
elseif
7195dae015 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 21:47:12 +08:00
elseif
5ef845c148 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 21:35:32 +08:00
elseif
00a510fc1e Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 21:01:44 +08:00
elseif
3d491261f3 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:58:57 +08:00
elseif
4ba3cc3f11 Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:57:56 +08:00
elseif
b5904ff5ae Delete keygen/keygen_aarch64
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:57:46 +08:00
elseif
5a8e9d19b3 Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:56:41 +08:00
elseif
3c5ed64380 Update npk.py
Signed-off-by: elseif <elseif@live.cn>
2025-08-21 20:52:04 +08:00
elseif
808b637ebd Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 17:35:33 +08:00
elseif
ced9e23040 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:40:31 +08:00
elseif
4623e18824 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:39:59 +08:00
elseif
87e7644916 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:39:34 +08:00
elseif
40676b67af Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:10:20 +08:00
elseif
e674ef263a Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 16:09:39 +08:00
elseif
9cd8640e0a Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 15:55:17 +08:00
elseif
fcf7fddd2e Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 15:54:36 +08:00
elseif
bdb3de0777 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 07:36:05 +08:00
elseif
923225902b Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-19 07:35:48 +08:00
elseif
3b4391d582 Delete keygen/rc.local
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 19:04:13 +08:00
elseif
6c6899386b Update rc.local
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 19:03:54 +08:00
elseif
5220cb9d8e Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 19:02:20 +08:00
elseif
cfe551fdda Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 18:59:43 +08:00
elseif
241c81356b Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 17:13:20 +08:00
elseif
9cc2f9063d Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 17:08:29 +08:00
elseif
399c670504 Create rc.local
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:40:09 +08:00
elseif
ee6d299e9f Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:38:31 +08:00
elseif
0234cf6f75 Delete busybox/busybox_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:38:09 +08:00
elseif
9df0184656 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:37:46 +08:00
elseif
5623d2215c Delete keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:37:17 +08:00
elseif
7239cd4cdf Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:36:27 +08:00
elseif
b60cab8b45 Delete keygen/keygen_x86
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:34:19 +08:00
elseif
e8b9bef9aa Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-18 15:31:09 +08:00
elseif
dc959f9b16 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 21:19:16 +08:00
elseif
2ea2d3fbdf Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:46:46 +08:00
elseif
3d3f3fee33 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:46:03 +08:00
elseif
8d411a43c6 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:44:59 +08:00
elseif
d483639d46 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:44:47 +08:00
elseif
4d09aa8aab Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:41:00 +08:00
elseif
8f59b90921 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 20:40:34 +08:00
elseif
5d08e5308d Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:31:22 +08:00
elseif
18e256ff41 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:30:34 +08:00
elseif
855fbc85d1 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:30:00 +08:00
elseif
9715f13b6a Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 19:14:57 +08:00
elseif
b2893b91b7 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:57:48 +08:00
elseif
988ded50c3 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:56:30 +08:00
elseif
950505e0ab Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:50:20 +08:00
elseif
96c0fd251e Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:49:17 +08:00
elseif
d4dcf6dfeb Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 18:48:04 +08:00
elseif
65b7c57616 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 14:15:12 +08:00
elseif
edb40667ef Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 14:04:49 +08:00
elseif
f98f599cb7 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 07:08:18 +08:00
elseif
f28acb89ae Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 07:05:08 +08:00
elseif
259fb4bf2a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-08-13 06:54:12 +08:00
elseif
8f15749897 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-29 11:52:44 +08:00
elseif
9712d7d464 Update CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:59:12 +08:00
elseif
7bebf605a0 Update LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:58:30 +08:00
elseif
f018e0de99 Update LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:58:05 +08:00
elseif
7ef69a1e31 Update README_PT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:56:07 +08:00
elseif
1ba7057fc7 Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:55:53 +08:00
elseif
8a52ef8df4 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:55:38 +08:00
elseif
7292ad7a80 Update README_PT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:47:06 +08:00
elseif
88de7162b7 Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:46:51 +08:00
elseif
f3497a9a4b Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:46:34 +08:00
elseif
702efdb828 Update CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:43:37 +08:00
elseif
a2038ece24 Update CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:40:04 +08:00
elseif
0b3fd16ba7 Update LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-07-28 20:35:14 +08:00
elseif
8367df6ef0 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-17 22:07:56 +08:00
zyb
2b9db1436c modified: patch.py 2025-07-13 14:27:34 +08:00
zyb
6dccdda7bb modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 21:10:15 +08:00
zyb
1d0b4f9563 modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 20:48:53 +08:00
zyb
ce13b4b959 modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 20:42:06 +08:00
zyb
3f98072f2d modified: .github/workflows/mikrotik_patch_7.yml 2025-07-11 20:38:55 +08:00
zyb
ceb85aaf72 modified: .github/workflows/mikrotik_patch_7.yml
modified:   busybox/busybox_aarch64
	new file:   busybox/busybox_arm
	modified:   busybox/busybox_x86
	modified:   keygen/keygen_aarch64
	new file:   keygen/keygen_arm
	modified:   patch.py
2025-07-11 20:36:23 +08:00
elseif
ad132d5a8d Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-10 18:49:16 +08:00
elseif
0e6364e06a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-10 18:46:53 +08:00
elseif
c7f31476e7 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-07-08 00:58:38 +08:00
elseif
674370280d Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-07-06 01:30:56 +08:00
elseif
9a7ea04b45 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-07-05 14:40:35 +08:00
elseif
4965f266db Create CODE_OF_CONDUCT.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-05 06:07:33 +08:00
elseif
6c33b4c2c9 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-07-04 00:26:48 +08:00
zyb
5729c4746a modified: README.md
modified:   README_EN.md
	modified:   README_PT.md
2025-06-29 01:36:00 +08:00
elseif
0e89b026b8 Create LICENSE
Signed-off-by: elseif <elseif@live.cn>
2025-06-23 12:41:14 +08:00
zyb
88925b0057 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-23 12:37:27 +08:00
zyb
18f7b4cf36 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 14:49:16 +08:00
zyb
77b0644c95 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 14:32:03 +08:00
zyb
e72436306b modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 10:37:46 +08:00
zyb
d32177f1e7 modified: .github/workflows/mikrotik_patch_6.yml 2025-06-21 09:49:51 +08:00
zyb
2ad3d69a4d modified: .github/workflows/mikrotik_patch_7.yml 2025-06-21 09:47:49 +08:00
zyb
70819de5a4 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 14:31:23 +08:00
zyb
f22db63a10 modified: npk.py 2025-06-19 13:48:23 +08:00
zyb
178ffcbadb modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2025-06-19 13:45:31 +08:00
zyb
70bca68232 modified: npk.py
modified:   patch.py
2025-06-19 13:38:45 +08:00
zyb
4cdd6d5619 modified: patch.py 2025-06-19 13:31:48 +08:00
zyb
af97ad2a07 modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2025-06-19 13:28:29 +08:00
zyb
fd50885bf4 modified: patch.py 2025-06-19 13:23:52 +08:00
zyb
2551551828 modified: .github/workflows/mikrotik_patch_7.yml
modified:   patch.py
2025-06-19 13:21:08 +08:00
zyb
d8f092eb80 modified: patch.py 2025-06-19 13:16:46 +08:00
zyb
fe68e753c6 modified: patch.py 2025-06-19 13:15:26 +08:00
zyb
9ca203dcad modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 13:11:50 +08:00
zyb
939e01725c modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 13:07:24 +08:00
zyb
1935f35864 modified: .github/workflows/mikrotik_patch_7.yml
modified:   npk.py
2025-06-19 12:48:56 +08:00
zyb
676f0e0f82 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:43:23 +08:00
zyb
b50ba21932 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:34:03 +08:00
zyb
78f63469c9 modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:30:13 +08:00
zyb
511392906b modified: .github/workflows/mikrotik_patch_7.yml 2025-06-19 12:22:59 +08:00
elseif
2d2ca3e493 Merge pull request #118 from PauloNRocha/main
Add Portuguese translation of README
2025-06-19 12:12:44 +08:00
zyb
3f988f27ed modified: .github/workflows/mikrotik_patch_7.yml
deleted:    .github/workflows/self_hosted_patch.yml
	modified:   .gitignore
2025-06-19 12:09:51 +08:00
elseif
8a2b8f629c Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:26:00 +08:00
elseif
2ce0f85f8a Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:17:52 +08:00
elseif
a07fe281f1 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:07:34 +08:00
elseif
70321348ad Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:04:58 +08:00
Paulo Rocha
6b1d9a4a62 minor spelling corrections 2025-06-18 23:04:09 -03:00
elseif
9f5f72bb03 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 10:00:30 +08:00
Paulo Rocha
173dfd69e3 Add Portuguese translation of README
Added README_PT.md with full Brazilian Portuguese translation of the original README. Helpful for Portuguese-speaking users.
2025-06-18 22:57:15 -03:00
elseif
f0698d8814 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:50:58 +08:00
elseif
db75ad1711 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:47:03 +08:00
elseif
f54f9f15b6 Update chr.sh
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:13:42 +08:00
elseif
17b37b7124 Delete mbr.bin
Signed-off-by: elseif <elseif@live.cn>
2025-06-19 09:09:13 +08:00
elseif
48ce77cf80 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:32:27 +08:00
elseif
202dab0201 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:23:34 +08:00
elseif
ef13a0cf68 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:22:07 +08:00
elseif
cc05886c0a Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 19:16:56 +08:00
elseif
018f060462 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:52:44 +08:00
elseif
fcd6926272 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:37:08 +08:00
elseif
6cd71c9efe Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:33:59 +08:00
elseif
a350e2badd Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:24:26 +08:00
elseif
9bad9f06f1 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:15:06 +08:00
elseif
a5db54eca5 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 18:11:41 +08:00
elseif
dedfe06698 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:55:56 +08:00
elseif
cdae26e673 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:46:50 +08:00
elseif
33cd5313f0 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:41:27 +08:00
elseif
2b0548ba76 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:33:59 +08:00
elseif
4a9d5672c1 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:22:06 +08:00
elseif
23dd4c7f43 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:10:48 +08:00
elseif
80bda12745 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 14:01:56 +08:00
elseif
954fcfdbe3 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:53:19 +08:00
elseif
78516e3695 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:29:47 +08:00
elseif
e43686eca2 Update self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:25:01 +08:00
elseif
3f3bd3978b self_hosted_patch.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-18 13:04:42 +08:00
elseif
d6c9e7c528 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-17 15:54:00 +08:00
elseif
476691ac29 Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-06-17 15:20:57 +08:00
elseif
8e3ff90b2d Add files via upload
Signed-off-by: elseif <elseif@live.cn>
2025-06-17 15:20:15 +08:00
elseif
50a2f3f03b Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-06-14 20:19:25 +08:00
elseif
317904dfbf Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-14 19:48:15 +08:00
elseif
6e0c8a52bd Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:29:26 +08:00
elseif
2fe1832095 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:17:39 +08:00
elseif
7a15204f3a Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:15:02 +08:00
elseif
6e0c64cd73 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-06-12 00:10:46 +08:00
elseif
f4a4187e23 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-03-04 11:23:03 +08:00
elseif
66323bbfea Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 13:14:44 +08:00
elseif
7f2ec84067 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 13:13:36 +08:00
elseif
5589babdcc Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 13:09:00 +08:00
elseif
18bae23bf3 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 12:47:17 +08:00
elseif
3d52d65eb5 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 12:39:41 +08:00
elseif
a951a224b0 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-25 12:37:48 +08:00
elseif
343fe653ee Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-24 14:37:08 +08:00
elseif
bfb4e91be7 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-24 14:19:57 +08:00
elseif
898d1c1789 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:34:42 +08:00
elseif
e4167afbec Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:33:07 +08:00
elseif
aeaf99729f Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:27:06 +08:00
elseif
d125a063ce Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-22 15:22:40 +08:00
elseif
ce926f7cc9 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:45:20 +08:00
elseif
aea2da2a36 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:31:26 +08:00
elseif
c869dca3c1 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:23:14 +08:00
elseif
cbe8254d84 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:18:39 +08:00
elseif
331df17ee7 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 17:12:01 +08:00
elseif
35852182b2 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:30:46 +08:00
elseif
e0d1eb1a98 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:26:17 +08:00
elseif
0717a39c02 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:24:30 +08:00
elseif
4993be82a6 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:22:42 +08:00
elseif
0dbed4777f Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 16:17:23 +08:00
elseif
3b529862d1 Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:43:29 +08:00
elseif
6da98653fc Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:43:02 +08:00
elseif
27e023fb28 Update README_EN.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:42:28 +08:00
elseif
df6b25a30e Update README.md
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 15:42:06 +08:00
elseif
60294067c7 Merge pull request #80 from emam06/patch-3
Update
2025-02-19 15:41:19 +08:00
elseif
d309253b06 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 11:12:54 +08:00
elseif
b3579cb363 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-19 11:09:18 +08:00
elseif
0f2c5e6081 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:50:25 +08:00
elseif
8f59465bf0 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:43:39 +08:00
elseif
736af6817b Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:33:03 +08:00
elseif
10a56eb673 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 20:31:07 +08:00
elseif
6518dda504 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:42:28 +08:00
elseif
74e812fa2b Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:40:17 +08:00
elseif
810909828a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:16:22 +08:00
elseif
49f4ad0a59 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:02:01 +08:00
elseif
ece607c8f7 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-18 15:01:10 +08:00
elseif
1426fc061a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-17 12:44:05 +08:00
elseif
7579dadaea Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-17 12:29:48 +08:00
elseif
6b6669fb23 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-17 12:27:07 +08:00
elseif
5f603dcebc Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-11 17:47:22 +08:00
elseif
e1994f69d6 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 13:43:33 +08:00
elseif
39af3326bd Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 03:39:14 +08:00
elseif
c657f2e736 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 03:08:29 +08:00
elseif
a8dd672a66 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 00:51:42 +08:00
elseif
cf01bbda4d Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 00:43:57 +08:00
elseif
1c9207d498 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-07 00:40:47 +08:00
elseif
f1ed27cb52 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:32:42 +08:00
elseif
8d38a80dd8 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:30:14 +08:00
elseif
f4af3671be Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:19:28 +08:00
elseif
2f0f0b1abb Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 23:01:27 +08:00
elseif
ea10a1a4c7 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-02-05 22:47:38 +08:00
emam06
bd03ecb963 Update
Signed-off-by: emam06 <egr7yco@gmail.com>
2025-01-27 15:17:41 +02:00
elseif
5b909283a0 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 21:35:52 +08:00
elseif
289ea3a34f Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 03:15:17 +08:00
elseif
aaf997c20d Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 03:06:18 +08:00
elseif
ab61c4937f Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 03:04:50 +08:00
elseif
8790f8f322 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:51:46 +08:00
elseif
5137e190ac Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:46:33 +08:00
elseif
97974bd9c6 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:39:29 +08:00
elseif
a99490184d Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:22:55 +08:00
elseif
da97108900 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-25 02:06:07 +08:00
elseif
ba72caaead Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 23:28:49 +08:00
elseif
8e8f42831f Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 23:16:16 +08:00
elseif
aea034bcf6 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 22:55:09 +08:00
elseif
a7ddc582b1 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:57:12 +08:00
elseif
cb0310bbcd Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:50:09 +08:00
elseif
38b73158f4 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:42:02 +08:00
elseif
ac9c61924d Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:32:29 +08:00
elseif
073e8811d8 Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-19 21:24:07 +08:00
elseif
3207b0ccd3 Update mikrotik_patch_6.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 11:13:51 +08:00
elseif
65dff5204a Update mikrotik_patch_7.yml
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 11:13:31 +08:00
elseif
10349947e6 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 11:11:16 +08:00
elseif
2e7b31abf5 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 10:55:50 +08:00
elseif
32ef5f3aeb Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 10:53:36 +08:00
elseif
3ba9e027e3 Update patch.py
Signed-off-by: elseif <elseif@live.cn>
2025-01-18 10:46:54 +08:00
elseif
f6fea00c16 Merge pull request #74 from vnxme/system-random
Use SystemRandom to ensure crossplatform compatibility
2024-12-30 11:21:29 +08:00
vnxme
391d019a42 Use random.SystemRandom().randint
instead of toyecc.Random.secure_rand_int_between
2024-12-28 10:57:32 +03:00
elseif
4c6ab80b09 Merge pull request #66 from vnxme/env-consistency-fixes
Env consistency fixes
2024-12-14 12:40:16 +08:00
elseif
098f967cfd Merge pull request #69 from loskiq/patch-1
Update mikro.py
2024-12-14 12:40:05 +08:00
Amir
e61c981f84 Update mikro.py
Signed-off-by: Amir <loskiq@gmail.com>
2024-12-13 17:23:54 +03:00
vnxme
29663ed175 Fix *_LICENSE_URL spelling 2024-12-12 11:45:40 +03:00
vnxme
fd2baf25a0 Fix MIKRO_NPK_SIGN_PUBLIC_KEY spelling 2024-12-12 11:45:40 +03:00
20 changed files with 2073 additions and 287 deletions

View File

@@ -3,10 +3,33 @@ name: Patch Mikrotik RouterOS 6.x
on:
# push:
# branches: [ "main" ]
schedule:
- cron: "0 0 * * *"
# schedule:
# - cron: "0 0 * * *"
workflow_dispatch:
inputs:
channel:
description: 'Channel (stable, long-term)'
required: true
default: 'stable'
type: choice
options:
- stable
- long-term
version:
description: "Specify version (e.g., 6.49.19), empty for latest"
required: false
default: ''
type: string
buildtime:
description: "Custom build time, leave empty for 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
@@ -17,7 +40,7 @@ env:
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }}
CUSTOM_CLOUD_PUBLIC_KEY: ${{ secrets.CUSTOM_CLOUD_PUBLIC_KEY }}
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }}
MIKRO_NPK_SIGN_PUBLIC_LKEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_LKEY }}
MIKRO_NPK_SIGN_PUBLIC_KEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_KEY }}
MIKRO_CLOUD_PUBLIC_KEY: ${{ secrets.MIKRO_CLOUD_PUBLIC_KEY }}
MIKRO_LICENCE_URL: ${{ secrets.MIKRO_LICENCE_URL }}
CUSTOM_LICENCE_URL: ${{ secrets.CUSTOM_LICENCE_URL }}
@@ -29,24 +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
run: echo "BUILD_TIME=$(date +'%s')" >> $GITHUB_OUTPUT
- name: Set variables
id: set_vars
run: |
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
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: [long-term,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
@@ -60,10 +109,19 @@ jobs:
id: get_latest
run: |
echo $(uname -a)
LATEST_VERSION=$(wget -nv -O - https://upgrade.mikrotik.com/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
@@ -71,14 +129,44 @@ 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
- 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'
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
sudo ln -sf /pckg/option/bin/busybox ./option-root/bin/$cmd
done
sudo mksquashfs option-root option.sfs -quiet -comp xz -no-xattrs -b 256k
sudo rm -rf option-root
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
@@ -93,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: |
@@ -107,12 +195,25 @@ jobs:
sudo rm -rf ./iso
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 \
-V "MikroTik $LATEST_VERSION" \
-sysid "" -preparer "MiKroTiK" \
@@ -129,13 +230,9 @@ jobs:
sudo mkdir ./all_packages
sudo cp ./new_iso/*.npk ./all_packages/
sudo rm -rf ./new_iso
cd ./all_packages
sudo zip ../all_packages-x86-$LATEST_VERSION.zip *.npk
cd ..
- name: Create install-image-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true'
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo modprobe nbd
sudo apt-get update
@@ -184,6 +281,8 @@ jobs:
- name: Create chr-${{ env.LATEST_VERSION }}.img
if: steps.get_latest.outputs.has_new_version == 'true'
run: |
sudo modprobe nbd
sudo apt-get install -y qemu-utils extlinux > /dev/null
sudo mkdir ./chr
sudo cp chr.img chr-$LATEST_VERSION.img
sudo qemu-nbd -c /dev/nbd0 -f raw chr-$LATEST_VERSION.img
@@ -191,12 +290,12 @@ jobs:
sudo mount /dev/nbd0p1 ./chr
sudo cp ./all_packages/dude-$LATEST_VERSION.npk ./chr/var/pdb/dude/image
sudo -E python3 patch.py npk ./chr/var/pdb/routeros-x86/image
sudo cp ./chr/var/pdb/routeros-x86/image ./all_packages/routeros-x86-$LATEST_VERSION.npk
sudo cp ./all_packages/routeros-x86-$LATEST_VERSION.npk routeros-x86-$LATEST_VERSION.npk
sudo 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 umount ./chr
sudo qemu-nbd -d /dev/nbd0
sudo rm -rf ./chr
sudo qemu-img convert -f raw -O qcow2 chr-$LATEST_VERSION.img chr-$LATEST_VERSION.qcow2
sudo qemu-img convert -f raw -O vmdk chr-$LATEST_VERSION.img chr-$LATEST_VERSION.vmdk
sudo qemu-img convert -f raw -O vpc chr-$LATEST_VERSION.img chr-$LATEST_VERSION.vhd
@@ -210,31 +309,69 @@ jobs:
sudo zip chr-$LATEST_VERSION.vdi.zip chr-$LATEST_VERSION.vdi
sudo zip chr-$LATEST_VERSION.img.zip chr-$LATEST_VERSION.img
sudo rm chr-$LATEST_VERSION$ARCH.qcow2
sudo rm chr-$LATEST_VERSION$ARCH.vmdk
sudo rm chr-$LATEST_VERSION$ARCH.vhd
sudo rm chr-$LATEST_VERSION$ARCH.vhdx
sudo rm chr-$LATEST_VERSION$ARCH.vdi
sudo rm chr-$LATEST_VERSION$ARCH.img
sudo rm chr-$LATEST_VERSION.qcow2
sudo rm chr-$LATEST_VERSION.vmdk
sudo rm chr-$LATEST_VERSION.vhd
sudo rm chr-$LATEST_VERSION.vhdx
sudo rm chr-$LATEST_VERSION.vdi
sudo rm chr-$LATEST_VERSION.img
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
echo $LATEST_VERSION $BUILD_TIME | sudo tee ./publish/NEWEST6.${{ matrix.channel }}
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: |
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 }} "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" \
@@ -242,23 +379,25 @@ jobs:
--data '{"purge_everything": true}'
- name: Delete Release tag ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == '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$ARCH)
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')
echo "Release ID: $RELEASE_ID"
if [ "$RELEASE_ID" != "null" ]; then
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$LATEST_VERSION$ARCH
echo "Tag $LATEST_VERSION$ARCH deleted successfully."
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/$LATEST_VERSION
echo "Tag $LATEST_VERSION deleted successfully."
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID
echo "Release with tag $LATEST_VERSION$ARCH deleted successfully."
echo "Release with tag $LATEST_VERSION deleted successfully."
else
echo "Release not found for tag: $LATEST_VERSION)"
fi
- name: Create Release tag ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true'
- 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 }}"
@@ -272,3 +411,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

@@ -3,8 +3,41 @@ on:
# push:
# branches: [ "main" ]
schedule:
- cron: "0 0 * * *"
- cron: "0 0 * * *" # At UTC 00:00 on every day
workflow_dispatch:
inputs:
arch:
description: 'Architecture (x86, arm64, arm)'
required: true
default: 'x86'
type: choice
options:
- x86
- arm64
- arm
channel:
description: 'Channel (stable, testing)'
required: true
default: 'stable'
type: choice
options:
- stable
- testing
version:
description: "Specify version (e.g., 7.19.2), empty for latest"
required: false
default: ''
type: string
buildtime:
description: "Custom build time, leave empty for now"
required: false
default: ''
type: string
release:
description: "Release to GitHub & Upload "
required: false
default: false
type: boolean
permissions:
contents: write
@@ -16,7 +49,7 @@ env:
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }}
CUSTOM_CLOUD_PUBLIC_KEY: ${{ secrets.CUSTOM_CLOUD_PUBLIC_KEY }}
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }}
MIKRO_NPK_SIGN_PUBLIC_LKEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_LKEY }}
MIKRO_NPK_SIGN_PUBLIC_KEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_KEY }}
MIKRO_CLOUD_PUBLIC_KEY: ${{ secrets.MIKRO_CLOUD_PUBLIC_KEY }}
MIKRO_LICENCE_URL: ${{ secrets.MIKRO_LICENCE_URL }}
CUSTOM_LICENCE_URL: ${{ secrets.CUSTOM_LICENCE_URL }}
@@ -28,27 +61,51 @@ 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
run: echo "BUILD_TIME=$(date +'%s')" >> $GITHUB_OUTPUT
- name: Set variables
id: set_vars
run: |
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
BUILD_TIME=$(date +'%s')
RELEASE=true
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
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:
arch: [x86,arm64]
channel: [stable, testing]
matrix:
include: ${{ fromJSON(needs.Set_Variables.outputs.MATRIX_JSON) }}
env:
TZ: 'Asia/Shanghai'
LATEST_VERSION: ""
ARCH: ""
BUILD_TIME: ${{ needs.Set_Variables.outputs.BUILD_TIME }}
RELEASE: ${{ needs.Set_Variables.outputs.RELEASE }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -62,7 +119,16 @@ jobs:
id: get_latest
run: |
echo $(uname -a)
LATEST_VERSION=$(wget -nv -O - https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWESTa7.${{ matrix.channel }} | cut -d ' ' -f1)
echo Build Time:$BUILD_TIME
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [ -n "${{ github.event.inputs.version }}" ]; then
LATEST_VERSION="${{ github.event.inputs.version }}"
else
LATEST_VERSION=$(wget -nv -O - https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWESTa7.${{ matrix.channel }} | cut -d ' ' -f1)
fi
else
LATEST_VERSION=$(wget -nv -O - https://${{ env.MIKRO_UPGRADE_URL }}/routeros/NEWESTa7.${{ matrix.channel }} | cut -d ' ' -f1)
fi
echo Latest Version:$LATEST_VERSION
if [ "${{ github.event_name }}" == "schedule" ]; then
_LATEST_VERSION=$(wget -nv -O - https://${{ env.CUSTOM_UPGRADE_URL }}/routeros/NEWESTa7.${{ matrix.channel }} | cut -d ' ' -f1)
@@ -73,33 +139,28 @@ 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
if [ "${{ matrix.arch }}" == "x86" ]; then
ARCH=''
echo "ARCH=$ARCH" >> $GITHUB_ENV
elif [ "${{ matrix.arch }}" == "arm64" ]; then
ARCH='-arm64'
echo "ARCH=$ARCH" >> $GITHUB_ENV
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
echo "LATEST_VERSION=${LATEST_VERSION}" >> $GITHUB_ENV
sudo apt-get update > /dev/null
- name: Cache Squashfs
- name: Get loader patch files
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 }}
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' && 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
@@ -112,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)
@@ -121,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
@@ -132,68 +203,53 @@ jobs:
sudo mksquashfs python python3.sfs -quiet -comp xz -no-xattrs -b 256k
sudo rm -rf ./python
- name: Cache NetInstall ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
id: cache-netinstall
uses: actions/cache@v4
with:
path: |
netinstall.zip
netinstall.tar.gz
key: netinstall-${{ env.LATEST_VERSION }}
- name: Get netinstall ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && steps.cache-netinstall.outputs.cache-hit != 'true'
run: |
sudo wget -nv -O netinstall.zip https://download.mikrotik.com/routeros/$LATEST_VERSION/netinstall-$LATEST_VERSION.zip
sudo wget -nv -O netinstall.tar.gz https://download.mikrotik.com/routeros/$LATEST_VERSION/netinstall-$LATEST_VERSION.tar.gz
- name: Patch netinstall ${{ env.LATEST_VERSION }}
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
run: |
sudo unzip netinstall.zip
sudo -E python3 patch.py netinstall netinstall.exe
sudo zip netinstall-$LATEST_VERSION.zip ./netinstall.exe
sudo tar -xvf netinstall.tar.gz
sudo -E python3 patch.py netinstall netinstall-cli
sudo tar -czvf netinstall-$LATEST_VERSION.tar.gz ./netinstall-cli
- name: Cache mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
if: steps.get_latest.outputs.has_new_version == 'true'
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
sudo mount -o loop,ro mikrotik.iso ./iso
sudo mkdir ./new_iso
sudo cp -r ./iso/* ./new_iso/
sudo rsync -a ./iso/ ./new_iso/
sudo umount ./iso
sudo rm -rf ./iso
sudo mv ./new_iso/routeros-$LATEST_VERSION$ARCH.npk ./
sudo -E python3 patch.py npk routeros-$LATEST_VERSION$ARCH.npk
NPK_FILES=$(find ./new_iso/*.npk)
for file in $NPK_FILES; do
sudo -E python3 npk.py sign $file $file
done
sudo cp routeros-$LATEST_VERSION$ARCH.npk ./new_iso/
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
@@ -238,20 +294,322 @@ jobs:
sudo zip ../all_packages-${{ matrix.arch }}-$LATEST_VERSION.zip *.npk
cd ..
- name: Cache chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img.zip
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 }}${{ 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' && (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
sudo rm chr.img.zip
sudo mv chr-$LATEST_VERSION$ARCH.img chr.img
- name: Create chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && (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
sgdisk --clear --set-alignment=2 \
--new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
chr-$LATEST_VERSION$ARCH.img
dd if=chr-$LATEST_VERSION$ARCH.img of=pt.bin bs=1 count=66 skip=446
echo -e "\x80" | dd of=pt.bin bs=1 count=1 conv=notrunc
sgdisk --mbrtogpt --clear --set-alignment=2 \
--new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
chr-$LATEST_VERSION$ARCH.img
dd if=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
sudo mkfs.ext4 -F -L "RouterOS" -m 0 /dev/nbd0p2
sudo mkdir -p ./img/{boot,routeros}
sudo mount /dev/nbd0p1 ./img/boot/
if [ "${{ matrix.arch }}" == "x86" ]; then
sudo cp chr.img chr-$LATEST_VERSION$ARCH-legacy-bios.img
sudo qemu-nbd -c /dev/nbd1 -f raw chr-$LATEST_VERSION$ARCH-legacy-bios.img
sudo -E python3 patch.py block /dev/nbd1p1 EFI/BOOT/BOOTX64.EFI
sudo mkdir -p ./chr/{boot,routeros}
sudo mount /dev/nbd1p1 ./chr/boot/
sudo mkdir -p ./img/boot/EFI/BOOT
sudo cp ./chr/boot/EFI/BOOT/BOOTX64.EFI ./img/boot/EFI/BOOT/BOOTX64.EFI
sudo umount /dev/nbd1p1
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/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
sudo qemu-img convert -f raw -O qcow2 chr-$LATEST_VERSION$ARCH-legacy-bios.img chr-$LATEST_VERSION$ARCH-legacy-bios.qcow2
sudo qemu-img convert -f raw -O vmdk chr-$LATEST_VERSION$ARCH-legacy-bios.img chr-$LATEST_VERSION$ARCH-legacy-bios.vmdk
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
sudo zip chr-$LATEST_VERSION$ARCH-legacy-bios.vhdx.zip chr-$LATEST_VERSION$ARCH-legacy-bios.vhdx
sudo zip chr-$LATEST_VERSION$ARCH-legacy-bios.vdi.zip chr-$LATEST_VERSION$ARCH-legacy-bios.vdi
sudo zip chr-$LATEST_VERSION$ARCH-legacy-bios.img.zip chr-$LATEST_VERSION$ARCH-legacy-bios.img
sudo rm chr-$LATEST_VERSION$ARCH-legacy-bios.qcow2
sudo rm chr-$LATEST_VERSION$ARCH-legacy-bios.vmdk
sudo rm chr-$LATEST_VERSION$ARCH-legacy-bios.vhd
sudo rm chr-$LATEST_VERSION$ARCH-legacy-bios.vhdx
sudo rm chr-$LATEST_VERSION$ARCH-legacy-bios.vdi
sudo rm chr-$LATEST_VERSION$ARCH-legacy-bios.img
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo qemu-nbd -c /dev/nbd1 -f raw chr.img
sudo mkdir -p ./chr/boot
sudo mount /dev/nbd1p1 ./chr/boot/
sudo -E python3 patch.py kernel ./chr/boot/EFI/BOOT/BOOTAA64.EFI -O ./BOOTAA64.EFI
sudo mkdir -p ./img/boot/EFI/BOOT
sudo cp ./BOOTAA64.EFI ./img/boot/EFI/BOOT/BOOTAA64.EFI
sudo umount /dev/nbd1p1
sudo rm -rf ./chr
sudo qemu-nbd -d /dev/nbd1
fi
sudo umount /dev/nbd0p1
sudo mount /dev/nbd0p2 ./img/routeros/
sudo mkdir -p ./img/routeros/{var/pdb/{system,option},boot,rw}
sudo cp ./all_packages/option-$LATEST_VERSION$ARCH.npk ./img/routeros/var/pdb/option/image
sudo cp ./all_packages/routeros-$LATEST_VERSION$ARCH.npk ./img/routeros/var/pdb/system/image
sudo umount /dev/nbd0p2
sudo rm -rf ./img
sudo qemu-nbd -d /dev/nbd0
sudo qemu-img convert -f raw -O qcow2 chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.qcow2
sudo qemu-img convert -f raw -O vmdk chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vmdk
sudo qemu-img convert -f raw -O vpc chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vhd
sudo qemu-img convert -f raw -O vhdx chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vhdx
sudo qemu-img convert -f raw -O vdi chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vdi
sudo 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
sudo zip chr-$LATEST_VERSION$ARCH.vhdx.zip chr-$LATEST_VERSION$ARCH.vhdx
sudo zip chr-$LATEST_VERSION$ARCH.vdi.zip chr-$LATEST_VERSION$ARCH.vdi
sudo zip chr-$LATEST_VERSION$ARCH.img.zip chr-$LATEST_VERSION$ARCH.img
sudo rm chr-$LATEST_VERSION$ARCH.qcow2
sudo rm chr-$LATEST_VERSION$ARCH.vmdk
sudo rm chr-$LATEST_VERSION$ARCH.vhd
sudo rm chr-$LATEST_VERSION$ARCH.vhdx
sudo rm chr-$LATEST_VERSION$ARCH.vdi
sudo rm chr-$LATEST_VERSION$ARCH.img
- name: 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'
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86' && env.RELEASE == 'true'
id: cache-refind
uses: actions/cache@v4
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'
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
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'x86'
- 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
sudo apt-get install -y qemu-utils extlinux > /dev/null
@@ -288,124 +646,100 @@ jobs:
sudo zip install-image-$LATEST_VERSION.zip ./install-image-$LATEST_VERSION.img
sudo rm ./install-image-$LATEST_VERSION.img
- name: Cache chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img.zip
if: steps.get_latest.outputs.has_new_version == 'true' && matrix.arch == 'arm64'
id: cache-chr-img
- 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
with:
path: |
chr.img
key: chr-${{ env.LATEST_VERSION }}-${{ matrix.arch }}.img
netinstall.zip
netinstall.tar.gz
key: netinstall-${{ env.LATEST_VERSION }}
restore-keys: netinstall-${{ env.LATEST_VERSION }}
- name: Get chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
if: steps.get_latest.outputs.has_new_version == 'true' && steps.cache-chr-img.outputs.cache-hit != 'true' && matrix.arch == 'arm64'
- 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'
run: |
sudo wget -nv -O chr.img.zip https://download.mikrotik.com/routeros/$LATEST_VERSION/chr-$LATEST_VERSION$ARCH.img.zip
sudo unzip chr.img.zip
sudo rm chr.img.zip
sudo mv chr-$LATEST_VERSION$ARCH.img chr.img
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: Create chr-${{ env.LATEST_VERSION }}${{ env.ARCH }}.img
if: steps.get_latest.outputs.has_new_version == 'true'
- 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 modprobe nbd
sudo apt-get install -y qemu-utils extlinux > /dev/null
truncate --size 128M chr-$LATEST_VERSION$ARCH.img
sgdisk --clear --set-alignment=2 \
--new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
--gpttombr=1:2 \
chr-$LATEST_VERSION$ARCH.img
dd if=chr-$LATEST_VERSION$ARCH.img of=pt.bin bs=1 count=66 skip=446
echo -e "\x80" | dd of=pt.bin bs=1 count=1 conv=notrunc
sgdisk --mbrtogpt --clear --set-alignment=2 \
--new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 \
--new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" \
chr-$LATEST_VERSION$ARCH.img
dd if=mbr.bin of=chr-$LATEST_VERSION$ARCH.img bs=1 count=446 conv=notrunc
dd if=pt.bin of=chr-$LATEST_VERSION$ARCH.img bs=1 count=66 seek=446 conv=notrunc
sudo qemu-nbd -c /dev/nbd0 -f raw chr-$LATEST_VERSION$ARCH.img
sudo mkfs.vfat -n "Boot" /dev/nbd0p1
sudo mkfs.ext4 -F -L "RouterOS" -m 0 /dev/nbd0p2
sudo mkdir -p ./img/{boot,routeros}
sudo mount /dev/nbd0p1 ./img/boot/
if [ "${{ matrix.arch }}" == "x86" ]; then
sudo mkdir -p ./img/boot/{BOOT,EFI/BOOT}
sudo cp BOOTX64.EFI ./img/boot/EFI/BOOT/BOOTX64.EFI
sudo extlinux --install -H 64 -S 32 ./img/boot/BOOT
echo -e "default system\nlabel system\n\tkernel /EFI/BOOT/BOOTX64.EFI\n\tappend load_ramdisk=1 root=/dev/ram0 quiet" > syslinux.cfg
sudo cp syslinux.cfg ./img/boot/BOOT/
sudo rm syslinux.cfg
elif [ "${{ matrix.arch }}" == "arm64" ]; then
sudo qemu-nbd -c /dev/nbd1 -f raw chr.img
sudo mkdir -p ./chr/boot
sudo mount /dev/nbd1p1 ./chr/boot/
sudo -E python3 patch.py kernel ./chr/boot/EFI/BOOT/BOOTAA64.EFI -O ./BOOTAA64.EFI
sudo mkdir -p ./img/boot/EFI/BOOT
sudo cp ./BOOTAA64.EFI ./img/boot/EFI/BOOT/BOOTAA64.EFI
sudo umount /dev/nbd1p1
sudo rm -rf ./chr
sudo qemu-nbd -d /dev/nbd1
fi
sudo umount /dev/nbd0p1
sudo mount /dev/nbd0p2 ./img/routeros/
sudo mkdir -p ./img/routeros/{var/pdb/{system,option},boot,rw}
sudo cp ./all_packages/option-$LATEST_VERSION$ARCH.npk ./img/routeros/var/pdb/option/image
sudo cp ./all_packages/routeros-$LATEST_VERSION$ARCH.npk ./img/routeros/var/pdb/system/image
sudo umount /dev/nbd0p2
sudo rm -rf ./img
sudo qemu-nbd -d /dev/nbd0
sudo unzip netinstall.zip
sudo -E python3 patch.py netinstall netinstall.exe
sudo zip netinstall-$LATEST_VERSION.zip ./netinstall.exe
sudo tar -xvf netinstall.tar.gz
sudo -E python3 patch.py netinstall netinstall-cli
sudo tar -czvf netinstall-$LATEST_VERSION.tar.gz ./netinstall-cli
sudo qemu-img convert -f raw -O qcow2 chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.qcow2
sudo qemu-img convert -f raw -O vmdk chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vmdk
sudo qemu-img convert -f raw -O vpc chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vhd
sudo qemu-img convert -f raw -O vhdx chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vhdx
sudo qemu-img convert -f raw -O vdi chr-$LATEST_VERSION$ARCH.img chr-$LATEST_VERSION$ARCH.vdi
sudo zip chr-$LATEST_VERSION$ARCH.qcow2.zip chr-$LATEST_VERSION$ARCH.qcow2
sudo zip chr-$LATEST_VERSION$ARCH.vmdk.zip chr-$LATEST_VERSION$ARCH.vmdk
sudo zip chr-$LATEST_VERSION$ARCH.vhd.zip chr-$LATEST_VERSION$ARCH.vhd
sudo zip chr-$LATEST_VERSION$ARCH.vhdx.zip chr-$LATEST_VERSION$ARCH.vhdx
sudo zip chr-$LATEST_VERSION$ARCH.vdi.zip chr-$LATEST_VERSION$ARCH.vdi
sudo zip chr-$LATEST_VERSION$ARCH.img.zip chr-$LATEST_VERSION$ARCH.img
sudo rm chr-$LATEST_VERSION$ARCH.qcow2
sudo rm chr-$LATEST_VERSION$ARCH.vmdk
sudo rm chr-$LATEST_VERSION$ARCH.vhd
sudo rm chr-$LATEST_VERSION$ARCH.vhdx
sudo rm chr-$LATEST_VERSION$ARCH.vdi
sudo rm chr-$LATEST_VERSION$ARCH.img
- name: Upload Files
if: steps.get_latest.outputs.has_new_version == 'true'
- name: Prepare for Upload
if: steps.get_latest.outputs.has_new_version == 'true' && env.RELEASE == 'true'
run: |
sudo mkdir -p ./publish/$LATEST_VERSION
echo $LATEST_VERSION $BUILD_TIME | sudo tee ./publish/NEWESTa7.${{ matrix.channel }}
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 }}
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
sudo apt-get install ssh sshpass
sshpass -p "${{ secrets.SSH_PASSWORD }}" ssh -o StrictHostKeyChecking=no -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_SERVER }} "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/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/"
# - 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" \
--header "Content-Type:application/json" \
--data '{"purge_everything": true}'
- name: Delete Release tag ${{ env.LATEST_VERSION }} ${{ matrix.arch }}
if: steps.get_latest.outputs.has_new_version == 'true'
- 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_INFO=$(curl -s -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/tags/$LATEST_VERSION$ARCH)
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id')
echo "Release ID: $RELEASE_ID"
if [ "$RELEASE_ID" != "null" ]; then
@@ -417,11 +751,11 @@ jobs:
echo "Release not found for tag: $LATEST_VERSION)"
fi
- name: Create Release tag ${{ env.LATEST_VERSION }} ${{ matrix.arch }}
if: steps.get_latest.outputs.has_new_version == 'true'
- 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'}}
@@ -433,4 +767,13 @@ jobs:
install-image-${{ env.LATEST_VERSION }}.zip
routeros-${{ env.LATEST_VERSION }}${{ env.ARCH }}.npk
all_packages-*-${{ 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 }}${{ env.ARCH }}
path: |
all_packages-*-${{ env.LATEST_VERSION }}.zip
mikrotik-${{ env.LATEST_VERSION }}${{ env.ARCH }}.iso
chr-${{ env.LATEST_VERSION }}*.zip

1
.gitignore vendored
View File

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

8
CODE_OF_CONDUCT.md Normal file
View File

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

13
LICENSE Normal file
View File

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

View File

@@ -1,16 +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)
@@ -29,6 +43,7 @@
## 如何使用Shell
安装 option-{version}.npk 包
telnet到RouterOS,用户名devel,密码与admin的密码相同
要使用devel用户名登录必须安装option-{version}.npk包且启用。
## 如何授权许可
进入shell
运行 keygen
@@ -42,10 +57,16 @@
对npk文件进行解包修改创建签名和验证
### patch.py
替换公钥并签名
## 所有的修补操作都自动运行在[Github Action](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/)。
### 感谢赞助
[ZMTO](https://console.zmto.com)

View File

@@ -1,4 +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)]
@@ -38,10 +42,12 @@
### 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/).

66
README_PT.md Normal file
View File

@@ -0,0 +1,66 @@
[![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)
# 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)]
### Baixe a [ISO modificada mais recente](https://github.com/elseif/MikroTikPatch/releases/latest), instale e aproveite.
### A imagem CHR suporta modo de boot tanto BIOS quanto UEFI.
### Suporte a atualização online, licença online, backup em nuvem e DDNS em nuvem
![](image/install.png)
![](image/routeros.png)
### Licenciar o RouterOS para x86
![](image/x86.png)
### Renovar a licença para x86 v6.x
![](image/renew_v6.png)
### Renovar a licença para CHR
![](image/renew.png)
### Licenciar o RouterOS para CHR
![](image/chr.png)
![](image/arm.png)
![](image/mips.png)
## Como usar o shell
```bash
instale o pacote option-{versão}.npk
acesse via telnet o routeros com o usuário devel e a senha igual a do usuário admin
```
## Como licenciar o RouterOS
```bash
acesse via telnet o routeros com o usuário devel e a senha igual a do admin
execute o keygen
modo CHR pode usar a renovação de licença online
```
## Como usar o Python 3
```bash
instale o pacote python3-{versão}.npk
acesse via telnet o routeros com o usuário devel e a senha igual a do admin
execute `python -V`
```
### npk.py
```bash
Assina, verifica, cria e extrai arquivos .npk
```
### patch.py
```bash
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.

106
chr.sh
View File

@@ -1,26 +1,82 @@
#!/bin/bash
apt-get install -y gdisk extlinux
dd if=/dev/zero of=/dev/sda bs=1 count=512 conv=notrunc
sgdisk --clear --set-alignment=2 --new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 --new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" --gpttombr=1:2 /dev/sda
dd if=/dev/sda of=/tmp/pt.bin bs=1 count=66 skip=446
echo -e "\x80" | dd of=/tmp/pt.bin bs=1 count=1 conv=notrunc
dd if=/dev/zero of=/dev/sda bs=1 count=512 conv=notrunc
sgdisk --clear --set-alignment=2 --new=1::+32M --typecode=1:8300 --change-name=1:"RouterOS Boot" --attributes=1:set:2 --new=2::-0 --typecode=2:8300 --change-name=2:"RouterOS" /dev/sda
wget -O /tmp/mbr.bin https://raw.gitmirror.com/elseif/MikroTikPatch/main/mbr.bin
dd if=/tmp/mbr.bin of=/dev/sda bs=1 count=446 conv=notrunc
dd if=/tmp/pt.bin of=/dev/sda bs=1 count=66 seek=446 conv=notrunc
sync
mkfs.vfat -n "Boot" /dev/sda1
mkfs.ext4 -F -L "RouterOS" -m 0 /dev/sda2
mkdir -p /tmp/{boot,routeros}
mount -o loop,rw /dev/sda1 /tmp/boot
mkdir -p /tmp/boot/{BOOT,EFI/BOOT}
wget -O /tmp/boot/EFI/BOOT/BOOTX64.EFI https://hub.gitmirror.com/https://github.com/elseif/MikroTikPatch/releases/download/7.15.2/BOOTX64.EFI
extlinux --install -H 64 -S 32 /tmp/boot/BOOT
echo -e "default system\nlabel system\n\tkernel /EFI/BOOT/BOOTX64.EFI\n\tappend load_ramdisk=1 root=/dev/ram0 quiet" > /tmp/boot/BOOT/syslinux.cfg
umount /tmp/boot
mount -o loop,rw /dev/sda2 /tmp/routeros
mkdir -p /tmp/routeros/{var/pdb/{system,option},boot,rw}
wget -O /tmp/routeros/var/pdb/option/image https://hub.gitmirror.com/https://github.com/elseif/MikroTikPatch/releases/download/7.15.2/option-7.15.2.npk
wget -O /tmp/routeros/var/pdb/system/image https://hub.gitmirror.com/https://github.com/elseif/MikroTikPatch/releases/download/7.15.2/routeros-7.15.2.npk
umount /tmp/routeros
set -e
LATEST_VERSION="${1:-7.19.4}"
echo "VERSION: $LATEST_VERSION"
ARCH=$(uname -m)
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
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

990
index.html Normal file
View File

@@ -0,0 +1,990 @@
<!doctype html>
<html lang="en" >
<head>
<meta charset="utf-8" />
<title>MikroTik RouterOS Patched Versions</title>
<link href="https://unpkg.com/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="https://unpkg.com/firacode/distr/fira_code.css" rel="stylesheet">
<style>
body,html {
height: 100%;
font-size: 100%
}
*,:after,:before {box-sizing: border-box}
body {
background: #F7F7F7;
color: #757575;
margin: 0;
font-family: arial,sans-serif;
line-height: 1.5;
}
.wrapper { background:white; }
table { background: #fff; }
table thead, table tfoot { background: #f5f5f5; }
/* --- DARK THEME --- */
body.dark {
background: #1e1e1e;
color: #ddd;
}
body.dark .wrapper {
background: #2a2a2a;
}
body.dark h1,h2,h3,h4,h5,h6 {
color: #f0f0f0;
}
body.dark a { color:#4ea3ff; }
body.dark a:hover { color:#81c8ff; }
body.dark table {
background: #2a2a2a;
border-color: #444;
}
body.dark table thead, body.dark table tfoot {
background: #333;
}
body.dark table tr td,
body.dark table tr th {
color: #ddd;
}
body.dark .downloadTable tbody tr:hover {
background-color: #333 !important;
}
body.dark .downloadTable thead tr {
background-color: #444 !important;
}
body.dark .downloadTable thead th {
color: #eee !important;
}
body.dark #command pre {
background: #111;
color: #eaeaea;
}
/* دکمه تغییر تم */
#theme-toggle {
position: fixed;
top: 60px;
right: 15px;
background: #0992c9;
color: #fff;
border: none;
padding: 8px 14px;
cursor: pointer;
border-radius: 6px;
font-size: 14px;
z-index: 2000;
}
#theme-toggle:hover {
background: #0775a1;
}
body,html {
height: 100%;
font-size: 100%
}
*,:after,:before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box
}
body {
background: #F7F7F7;
color: #757575;
padding-top:80px;
font-family:arial,sans-serif;
padding: 0;
margin: 0;
font-style: normal;
line-height: 1.5;
cursor: auto;
opacity:1;
}
a {-webkit-transition: all 200ms ease-in-out;transition: all 200ms ease-in-out; color:#4984AE;text-decoration: none;}
a:hover{color: #2F5B7B}
h1,h2,h3,h4,h5,h6{font-family:'Open Sans',sans-serif;font-weight:normal;margin-bottom:18px;margin-top:26px;color:#626262;}
h1 {font-weight: 300}
h2{font-size:1.875rem}
h3{font-size:1.5rem}
.text-center{text-align:center}
.button {
border-style: solid;
border-width: 0;
cursor: pointer;
line-height: normal;
margin: 0 0 1.25rem;
position: relative;
text-align: center;
border-radius: 0;
display: inline-block;
font-size: 1rem;
background-color: #0992c9;
border-color: #0775a1;
color: #fff;
transition: background-color .3s ease-out;
padding: 1rem 2rem 1.0625rem
}
.button:focus,.button:hover,button:focus,button:hover {
background-color: #0775a1;
color: #fff
}
.button.secondary,button.secondary {
background-color: #e7e7e7;
border-color: #b9b9b9;
color: #333
}
.button.secondary:focus,.button.secondary:hover,button.secondary:focus,button.secondary:hover {
background-color: #b9b9b9;
color: #333
}
.wrapper{background:white;max-width:1600px;margin:auto;overflow:auto;padding-top:25px; padding-bottom: 35px;position: relative;}
.row {
max-width: 62.5rem;
margin: 0 auto
}
.row.collapse>.column,.row.collapse>.columns {
padding-left: 0;
padding-right: 0
}
.row.collapse .row {
margin-left: 0;
margin-right: 0
}
.row .row {
width: auto;
max-width: none;
margin: 0 -.9375rem
}
.row .row.collapse {
width: auto;
margin: 0;
max-width: none
}
.column,.columns {
padding-left: .9375rem;
padding-right: .9375rem;
width: 100%;
float: left
}
[class*=column]+[class*=column]:last-child {float: right}
[class*=column]+[class*=column].end { float: left}
hr {
border: solid #ddd;
border-width: 1px 0 0;
margin: 1.25rem 0 1.1875rem;
height: 0
}
b,strong { font-weight: 700}
small {font-size: 60%}
table {
background: #fff;
margin-bottom: 1.25rem;
border: 1px solid #ddd;
table-layout: auto
}
table caption {
background: 0 0;
color: #222;
font-size: 1rem;
font-weight: 700
}
table tfoot,table thead {
background: #f5f5f5;
}
table tfoot tr td,table tfoot tr th,table thead tr td,table thead tr th {
padding: .5rem .625rem .625rem;
font-size: .875rem;
font-weight: 700;
color: #222
}
table tr td,table tr th {
padding: .5625rem .625rem;
font-size: .875rem;
color: #222;
text-align: left;
}
table tr.alt,table tr.even,table tr:nth-of-type(even) {
background: #f9f9f9
}
table tbody tr td,table tbody tr th,table tfoot tr td,table tfoot tr th,table thead tr th,table tr td {
display: table-cell;
line-height: 1.125rem;
}
.table{width:100%;border:0;border-collapse:collapse;}
.table tr:nth-of-type(even){background-color:transparent;}
.table td{padding:4px 10px 4px 0;color:#757575}
.table thead {background:transparent;}
.table thead th{padding:4px 10px 4px 0;color:#747474}
.downloadTable, .fullWidth{width:100%;}
.downloadTable tbody tr{border-bottom:1px solid #e3e3e3}
.downloadTable tbody tr:hover{background-color:#f7f7f7}
.downloadTable td{padding:6px 10px}
.downloadTable tbody tr.ht td{background-color:#888;border-bottom:0;}
.downloadTable tbody tr td.boldy{font-weight:bold;color:#ddd}
.downloadTable tbody tr:last-child{border-bottom:0;}
.downloadTable thead tr{background-color:#4caf50;}
.downloadTable thead th{color:white;padding:12px 5px}
.downloadTable tbody tr:first-child td:first-child{font-weight:bold;width:230px;}
.downloadTable tbody tr.hv td:not(:first-child){min-width:150px;}
.downloadTable td {padding: 0 4px; }
.downloadTable tr td:first-child {padding: 6px 10px;}
.download-btn, .changelog-btn, .checksums-btn { padding: 10px; color: grey;font-size: 1.2em;}
.download-btn:hover {color: #007bff;}
.downloadTable tr:hover .checksum-btn {
visibility: visible;
}
.download_version_row:hover{
background: #f0f3f7;
cursor: pointer;
}
.small-1 {
width: 8.33333%
}
.small-11 {
width: 91.66667%
}
.filter-blue {
filter: invert(39%) sepia(88%) saturate(5000%) hue-rotate(190deg) brightness(95%) contrast(90%);
}
.table-wrapper { position: relative; }
thead { position: relative; }
.collapse-btn {
position: absolute;
top: 50%;
right: 8px;
transform: translateY(-50%);
background: #fff;
border: 1px solid #ccc;
border-radius: 2px;
padding: 2px 6px;
cursor: pointer;
font-size: 0.85em;
line-height: 1;
}
.collapse-btn:hover {
background: gray;
}
.github-corner svg {clip-path: polygon(0 0, 100% 0, 100% 100%);}
.github-corner:hover .octo-arm {animation: octocat-wave 560ms ease-in-out;}
@keyframes octocat-wave {
0% { transform: rotate(0deg);}
20% {transform: rotate(-25deg);}
40% {transform: rotate(10deg);}
60% {transform: rotate(-25deg);}
80% {transform: rotate(10deg);}
100% {transform: rotate(0deg);}
}
@media (max-width: 500px) {
.github-corner:hover .octo-arm {animation: none;}
.github-corner .octo-arm {animation: octocat-wave 560ms ease-in-out;}
}
.reveal-modal {
display: none;
position: fixed;
z-index: 1005;
width: 60%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: auto;
background-color: #fff;
padding: 1.875rem;
border: 1px solid #666;
box-shadow: 0 0 10px rgba(0,0,0,.4)
}
.reveal-modal .close-reveal-modal {
font-size: 2.5rem;
line-height: 1;
position: absolute;
top: .625rem;
right: 1.375rem;
color: #aaa;
font-weight: 700;
cursor: pointer
}
#version-buttons {margin-bottom: 12px;}
#version-buttons button {
background-color: #b9b9b9;
color: #fff;
border: none;
padding: 6px 12px;
margin-left: 6px;
border-radius: 1px;
cursor: pointer;
}
#version-buttons button:hover {background-color: #555;}
#version-buttons button.active {background-color: #4caf50;}
#command-container {
position: relative;
margin-top: 12px;
}
#copy-btn {
position: absolute;
top: 10px;
right: 10px;
background-color: #4caf50;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
z-index: 10;
}
#copy-btn i {margin-right: 6px;}
#copy-btn:hover {background-color: #45a049;}
#command pre {
font-family: 'Fira Code', monospace !important;
font-size: 13px;
line-height: 1.5;
background-color: #1e1e1e;
color: #d4d4d4;
padding: 12px;
border-radius: 3px;
overflow-x: auto;
}
#command .bash { color: #c586c0; font-weight: bold; }
#command .url { color: #569cd6; }
#command .version { color: #4ec9b0; font-weight: bold; }
#command pre:hover {background-color: #2c2c2c;}
.loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-style: italic;
color: gray;
background: rgba(255,255,255,0.9);
padding: 15px 25px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
z-index: 1000;
}
.loading i {
margin-right: 10px;
animation: fa-spin 1s linear infinite;
}
</style>
</head>
<body>
<script>
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;
}
async function init() {
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">Documentation</a><br>
For more information about <b>patch</b> see the <a href="https://github.com/elseif/MikroTikPatch" target="_blank">MikroTikPatch</a>`,
installCmdLabel:"<b>Install Command</b>",
proxyLabel:"Use a proxy service to accelerate access to GitHub files",
clearCache:"Clear Cache",
copyUrlsClearCache:"Copy URLs to Clear Cache",
loading:"Loading...",
},
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">文档</a><br>有关<b>Patch</b>的更多信息,请参阅<a href="https://github.com/elseif/MikroTikPatch" target="_blank">MikroTikPatch</a>`,
installCmdLabel:"<b>安装命令</b>",
proxyLabel:"使用代理服务加速访问GitHub文件",
clearCache:"清除缓存",
copyUrlsClearCache:"复制链接以清除缓存",
loading:"加载中...",
}
};
function setLanguage(lang) {
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute("data-i18n");
if (i18n[lang] && i18n[lang][key]) {
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);
});
initLanguage();
const loading = document.getElementById("loading");
loading.style.display = "inline";
const routeros7_stable = await fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable", "7.19.4");
const routeros7_testing = await fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWESTa7.testing", "7.20rc1");
const routeros6_longterm = await fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWEST6.long-term", "6.49.18");
const routeros6_stable = await fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWEST6.stable", "6.49.19");
loading.style.display = "none";
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");
downloads.forEach((download, index) => {
const wrapper = document.createElement("div");
wrapper.className = "table-wrapper";
const table = document.createElement("table");
table.className = "table tablev downloadTable"
table.innerHTML = `
<thead>
<tr>
<th>${download.title}</th>
${download.versions.map(v => `<th>${v.version} ${v.title}</th>`).join("")}
</tr>
</thead>
<tbody>
${download.groups.map(g => {
return `
<tr class="ht">
<td class="boldy">${g.title}</td>
<td colspan="${download.versions.length}"></td>
</tr>
${g.downloads.map(d => {
return `
<tr class="hv">
<td>${d.title}</td>
${download.versions.map(v => {
if(d.type === "changlog"){
return `<td><a href="https://upgrade.mikrotik.ltd/routeros/${v.version}/CHANGELOG" class="download-btn" title="${d.title}" target="_blank"><i class="fa fa-file-text"></i></a></td>`;
}else{
const files = fileNames(v.version,g.arch,d.type);
if (files && files.length > 0) {
const links = files.map(file => {
const url = `${baseUrl(v.version, g.arch)}${file}`;
if (download.versions.length === 4 && d.type in {vhdx:1,vmdk:1,vdi:1,vhd:1,img:1,ova:1} ){
if (v.version.charAt(0) === "6" ){
return `<a href="${url}" class="download-btn" title="${file}" target="_blank"><i class="fa fa-save"></i> Legacy</a>`;
}else{
if (file.includes("legacy-bios")){
return `<a href="${url}" class="download-btn" title="${file}" target="_blank"><i class="fa fa-save"></i> Legacy</a>`;
}else{
return `<a href="${url}" class="download-btn" title="${file}" target="_blank"><i class="fa fa-save"></i> UEFI</a>`;
}
}
}else{
return `<a href="${url}" class="download-btn" title="${file}" target="_blank"><i class="fa fa-save"></i></a>`;
}
}).join(" ");
return `<td>${links}</td>`;
}else{
return `<td></td>`;
}
}
}).join("")}
</tr>
`;
}).join("")}
`;
}).join("")}
</tbody>
`
const btn = document.createElement("button");
btn.className = "collapse-btn";
btn.innerHTML = `<i class="fa fa-minus"></i>`;
table.querySelector("thead").appendChild(btn);
wrapper.appendChild(table);
container.appendChild(wrapper);
const tbody = table.querySelector("tbody");
if (index === 0) {
tbody.style.display = "";
btn.innerHTML = `<i class="fa fa-minus"></i>`;
} else {
tbody.style.display = "none";
btn.innerHTML = `<i class="fa fa-plus"></i>`;
}
btn.onclick = () => {
if (tbody.style.display === "none") {
tbody.style.display = "";
btn.innerHTML = `<i class="fa fa-minus"></i>`;
} else {
tbody.style.display = "none";
btn.innerHTML = `<i class="fa fa-plus"></i>`;
}
};
});
document.getElementById("clear-cache").onclick = e => {
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");
document.getElementById("urls-pannel").style.display = "block";
};
document.getElementById("urls-close").onclick = () => {
document.getElementById("urls-pannel").style.display = "none";
};
document.getElementById("copy-to-clear-cache").onclick = () => {
const el = document.getElementById("urls-list");
const range = document.createRange();
range.selectNodeContents(el);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
document.execCommand("copy");
window.open("https://cache.gh-proxy.com/", "_blank");
document.getElementById("urls-pannel").style.display = "none";
}
const ghProxyCheckbox = document.querySelector('input[name="gh-proxy"]');
ghProxyCheckbox.checked = localStorage.getItem("gh-proxy-enabled") === "true";
document.getElementById("clear-cache").style.display = ghProxyCheckbox.checked ?"inline-block":"none";
ghProxyCheckbox.addEventListener("change", () => {
if (ghProxyCheckbox.checked) {
localStorage.setItem("gh-proxy-enabled", "true");
const links = document.querySelectorAll("a[href^='https://github.com/elseif/MikroTikPatch/']");
links.forEach(link => {
link.href = link.href.replace(/^https:\/\/github\.com\//, "https://gh-proxy.com/https://github.com/");
});
document.getElementById("clear-cache").style.display = "inline-block";
} else {
localStorage.setItem("gh-proxy-enabled", "false");
const links = document.querySelectorAll("a[href^='https://gh-proxy.com/https://github.com/elseif/MikroTikPatch/']");
links.forEach(link => {
link.href = link.href.replace(/^https:\/\/gh-proxy\.com\//, "");
});
document.getElementById("clear-cache").style.display = "none";
}
const activeBtn = document.querySelector("#version-buttons button.active");
if (activeBtn) updateCommand(activeBtn.dataset.version);
});
const versionBtns = document.getElementById("version-buttons");
const commandDiv = document.getElementById("command");
const copyBtn = document.getElementById("copy-btn");
versionBtns.innerHTML = `
<span data-i18n="installCmdLabel"></span>
<label><input type="radio" name="tool" value="curl" checked>curl</label>
<label><input type="radio" name="tool" value="wget">wget</label>
<button data-version="${routeros7_stable}">RouterOS v7 Stable</button>
<button data-version="${routeros7_testing}">RouterOS v7 Testing</button>
<button data-version="${routeros6_longterm}">RouterOS v6 Long-Term</button>
<button data-version="${routeros6_stable}">RouterOS v6 Stable</button>
`
function updateCommand(version) {
const tool = document.querySelector('input[name="tool"]:checked').value;
let cmd;
const proxy = `${localStorage.getItem("gh-proxy-enabled") === "true"?" | sed 's#https://github.com#https:/gh-proxy.com/https://github.com#g'":""}`;
if (tool === "curl") {
cmd = `<pre ${proxy === ""?"":"style='font-size:12px'"}><span class="bash">curl</span> <span class="url">https://mikrotik.ltd/chr.sh</span>${proxy} | <span class="bash">bash</span> -s <span class="version">${version}</span></pre>`;
} else {
cmd = `<pre ${proxy === ""?"":"style='font-size:11px'"}><span class="bash">wget</span> -O - <span class="url">https://mikrotik.ltd/chr.sh</span>${proxy} | <span class="bash">bash</span> -s <span class="version">${version}</span></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", () => {
updateCommand(btn.dataset.version);
buttons.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
});
});
updateCommand(buttons[0].dataset.version);
buttons[0].classList.add("active");
copyBtn.addEventListener("click", () => {
const code = commandDiv.querySelector("pre").innerText;
navigator.clipboard.writeText(code).then(() => {
copyBtn.innerHTML = `<i class="fa fa-copy"></i>Copied!`;
setTimeout(() => { copyBtn.innerHTML = `<i class="fa fa-copy"></i>Copy`; }, 1500);
}).catch(err => {
console.error("Copy failed:", err);
});
});
const themeBtn = document.getElementById("theme-toggle");
const currentTheme = localStorage.getItem("theme");
if(currentTheme === "dark"){
document.body.classList.add("dark");
themeBtn.innerHTML = '<i class="fa fa-sun-o"></i> Light';
}
themeBtn.addEventListener("click", ()=>{
document.body.classList.toggle("dark");
if(document.body.classList.contains("dark")){
themeBtn.innerHTML = '<i class="fa fa-sun-o"></i> Light';
localStorage.setItem("theme","dark");
} else {
themeBtn.innerHTML = '<i class="fa fa-moon-o"></i> Dark';
localStorage.setItem("theme","light");
}
});
}
window.addEventListener("DOMContentLoaded", init);
</script>
<script async defer src="https://buttons.github.io/buttons.js"></script>
<div class="wrapper">
<button id="theme-toggle"><i class="fa fa-moon-o"></i> Dark</button>
<a href="https://github.com/elseif/MikroTikPatch" class="github-corner" aria-label="View source on GitHub" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 250 250" fill="#151513" style="position: absolute; top: 0; right: 0">
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#151513"/>
<path class="octo-arm" fill="#fff" d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16" style="-webkit-transform-origin: 130px 106px; transform-origin: 130px 106px"/>
<path class="octo-body" fill="#fff" d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"/>
</svg>
</a>
<div class="row">
<div class="text-center">
<h1 class="filter-blue" style="margin-top: 20px;"><b>MikroTik RouterOS Patched Versions</b></h1>
</div>
</div>
<div class="row text-center">
<div>
<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>
</div>
<div>
<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>
<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>
<div class="row">
<p data-i18n="infoLabel1"></p>
<p data-i18n="infoLabel2"></p>
<div id="version-buttons"></div >
<div id="command-container">
<button id="copy-btn"><i class="fa fa-copy"></i>Copy</button>
<div id="command"></div>
</div>
<div style="height: 30px;">
<label><input type="checkbox" name="gh-proxy"><span data-i18n="proxyLabel"></span></label>
<a data-i18n="clearCache" id="clear-cache" class="button secondary" style="margin: 0px 10px !important;padding: 6px;font-size: .8125rem;height: 30px;" href="#" title="gh-proxy"></a>
</div>
</div>
<div class="row" style="display: flex; align-items: center; justify-content: center; ">
<div id="urls-pannel" class="reveal-modal" style="display: none;" tabindex="0">
<a id="urls-close" class="close-reveal-modal" aria-label="Close">×</a>
<div id="urls-list" style="margin: 20px; font-size: 16px;overflow:auto;height: 300px;"></div>
<div style="display: flex; align-items: center; justify-content: center; ">
<a data-i18n="copyUrlsClearCache" id="copy-to-clear-cache" class="button" style="margin-bottom: 0;padding: 8px;; width:180px;height:30px;font-size: .8125rem;" href="#" title="gh-proxy"></a>
</div>
</div>
</div>
<div class="row" id="download-rows" style="margin-top: 2vh;">
<div class="loading" id="loading"><i class="fa fa-spinner"></i><span data-i18n="loading"></span></div>
</div>
<div class="row">
<ul style="display: flex; align-items: center; justify-content: center; gap: 20px; list-style: none; padding: 0; margin: 0; ">
<li>
<a href="https://t.me/mikrotikpatch" target="_blank" title="Telegram">
<i class="fa fa-telegram fa-2x"></i>
</a>
</li>
<li>
<a href="https://github.com/elseif/MikroTikPatch" target="_blank" title="GitHub">
<i class="fa fa-github fa-2x"></i>
</a>
</li>
<li style="display: flex; gap: 6px;">
<span data-i18n="language"></span>
<select id="lang-switcher">
<option value="en">English</option>
<option value="zh">中文</option>
</select>
</li>
</ul>
</div>
</div>
</body>
</html>

Binary file not shown.

BIN
keygen/keygen_arm Normal file

Binary file not shown.

Binary file not shown.

BIN
mbr.bin

Binary file not shown.

View File

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

57
npk.py
View File

@@ -207,7 +207,57 @@ 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):
if len(part.data) < 4 or part.data[:4] != b'hsqs':
return part.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()
if len(self._packages) > 0:
has_squashfs = False
for package in self._packages:
for part in package._parts:
if part.id == NpkPartID.SQUASHFS:
part.data = rebuild_squashfs(part.data)
has_squashfs = True
if not has_squashfs:
return
for package in self._packages:
count = 8
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
else:
has_squashfs = False
for part in self._parts:
if part.id == NpkPartID.SQUASHFS:
part.data = rebuild_squashfs(part.data)
has_squashfs = True
if not has_squashfs:
return
count = 8
for part in self._parts:
count += 6
if part.id == NpkPartID.NULL_BLOCK:
break
count += len(part.data)
count += 6
pad_len = (4096 - (count % 4096)) % 4096
self[NpkPartID.NULL_BLOCK].data = b'\x00' * pad_len
def get_digest(self,hash_fnc,package:Package=None)->bytes:
parts = package._parts if package else self._parts
for part in parts:
@@ -228,7 +278,8 @@ class NovaPackage(Package):
def sign(self,kcdsa_private_key:bytes,eddsa_private_key:bytes):
import hashlib
from mikro import mikro_kcdsa_sign,mikro_eddsa_sign
build_time = os.environ['BUILD_TIME'] if 'BUILD_TIME' in os.environ else None
build_time = os.getenv('BUILD_TIME',None)
self.set_null_block()
if len(self._packages) > 0:
if build_time:
self[NpkPartID.PKG_INFO].data._build_time = int(build_time)
@@ -356,4 +407,4 @@ if __name__=='__main__':
option_npk.save(args.output)
print(f'Created {args.output}')
else:
parser.print_help()
parser.print_help()

117
patch.py
View File

@@ -1,7 +1,68 @@
import subprocess,lzma
import struct,os
import struct,os,re
from npk import NovaPackage,NpkPartID,NpkFileContainer
def replace_chunks(old_chunks,new_chunks,data,name):
pattern_parts = [re.escape(chunk) + b'(.{0,6})' for chunk in old_chunks[:-1]]
pattern_parts.append(re.escape(old_chunks[-1]))
pattern_bytes = b''.join(pattern_parts)
pattern = re.compile(pattern_bytes, flags=re.DOTALL)
def replace_match(match):
replaced = b''.join([new_chunks[i] + match.group(i+1) for i in range(len(new_chunks) - 1)])
replaced += new_chunks[-1]
print(f'{name} public key patched {b"".join(old_chunks)[:16].hex().upper()}...')
return replaced
return re.sub(pattern, replace_match, data)
def replace_key(old,new,data,name=''):
old_chunks = [old[i:i+4] for i in range(0, len(old), 4)]
new_chunks = [new[i:i+4] for i in range(0, len(new), 4)]
data = replace_chunks(old_chunks, new_chunks, data,name)
key_map = [28,19,25,16,14,3,24,15,22,8,6,17,11,7,9,23,18,13,10,0,26,21,2,5,20,30,31,4,27,29,1,12,]
old_chunks = [bytes([old[i]]) for i in key_map]
new_chunks = [bytes([new[i]]) for i in key_map]
data = replace_chunks(old_chunks, new_chunks, data,name)
arch = os.getenv('ARCH') or 'x86'
arch = arch.replace('-', '')
if arch in ['arm64','arm']:
old_chunks = [old[i:i+4] for i in range(0, len(old), 4)]
new_chunks = [new[i:i+4] for i in range(0, len(new), 4)]
old_bytes = old_chunks[4] + old_chunks[5] + old_chunks[2] + old_chunks[0] + old_chunks[1] + old_chunks[6] + old_chunks[7]
new_bytes = new_chunks[4] + new_chunks[5] + new_chunks[2] + new_chunks[0] + new_chunks[1] + new_chunks[6] + new_chunks[7]
if old_bytes in data:
print(f'{name} public key patched {old[:16].hex().upper()}...')
data = data.replace(old_bytes,new_bytes)
old_codes = [bytes.fromhex('793583E2'),bytes.fromhex('FD3A83E2'),bytes.fromhex('193D83E2')] #0x1e400000+0xfd000+0x640
new_codes = [bytes.fromhex('FF34A0E3'),bytes.fromhex('753C83E2'),bytes.fromhex('FC3083E2')] #0xff0075fc= 0xff000000+0x7500+0xfc
data = replace_chunks(old_codes, new_codes, data,name)
else:
def conver_chunks(data:bytes):
ret = [
(data[2] << 16) | (data[1] << 8) | data[0] | ((data[3] << 24) & 0x03000000),
(data[3] >> 2) | (data[4] << 6) | (data[5] << 14) | ((data[6] << 22) & 0x1C00000),
(data[6] >> 3) | (data[7] << 5) | (data[8] << 13) | ((data[9] << 21) & 0x3E00000),
(data[9] >> 5) | (data[10] << 3) | (data[11] << 11) | ((data[12] << 19) & 0x1F80000),
(data[12] >> 6) | (data[13] << 2) | (data[14] << 10) | (data[15] << 18),
data[16] | (data[17] << 8) | (data[18] << 16) | ((data[19] << 24) & 0x01000000),
(data[19] >> 1) | (data[20] << 7) | (data[21] << 15) | ((data[22] << 23) & 0x03800000),
(data[22] >> 3) | (data[23] << 5) | (data[24] << 13) | ((data[25] << 21) & 0x1E00000),
(data[25] >> 4) | (data[26] << 4) | (data[27] << 12) | ((data[28] << 20) & 0x3F00000),
(data[28] >> 6) | (data[29] << 2) | (data[30] << 10) | (data[31] << 18)
]
return [struct.pack('<I', x ) for x in ret]
old_chunks = conver_chunks(old)
new_chunks = conver_chunks(new)
old_bytes = b''.join([v for i,v in enumerate(old_chunks) if i != 8])
new_bytes = b''.join([v for i,v in enumerate(new_chunks) if i != 8])
if old_bytes in data:
print(f'{name} public key patched {old[:16].hex().upper()}...')
data = data.replace(old_bytes,new_bytes)
old_codes = [bytes.fromhex('713783E2'),bytes.fromhex('223A83E2'),bytes.fromhex('8D3F83E2')] #0x1C40000+0x22000+0x234
new_codes = [bytes.fromhex('973303E3'),bytes.fromhex('DD3883E3'),bytes.fromhex('033483E3')] #0x03DD3397 = 0x3397|0x00DD0000|0x03000000
data = replace_chunks(old_codes, new_codes, data,name)
return data
def patch_bzimage(data:bytes,key_dict:dict):
PE_TEXT_SECTION_OFFSET = 414
HEADER_PAYLOAD_OFFSET = 584
@@ -22,9 +83,7 @@ def patch_bzimage(data:bytes,key_dict:dict):
initramfs = initramfs[:cpio_offset2]
new_initramfs = initramfs
for old_public_key,new_public_key in key_dict.items():
if old_public_key in new_initramfs:
print(f'initramfs public key patched {old_public_key[:16].hex().upper()}...')
new_initramfs = new_initramfs.replace(old_public_key,new_public_key)
new_initramfs = replace_key(old_public_key,new_public_key,new_initramfs,'initramfs')
new_vmlinux = vmlinux.replace(initramfs,new_initramfs)
new_vmlinux_xz = lzma.compress(new_vmlinux,check=lzma.CHECK_CRC32,filters=[
{"id": lzma.FILTER_X86},
@@ -53,6 +112,7 @@ def patch_block(dev:str,file:str,key_dict):
stdout,_ = run_shell_command(f"debugfs {dev} -R 'stat {file}' 2> /dev/null | sed -n '11p' ")
#(0-11):1592-1603, (IND):1173, (12-15):1604-1607, (16-26):1424-1434
blocks_info = stdout.decode().strip().split(',')
print(f'blocks_info : {blocks_info}')
blocks = []
ind_block_id = None
for block_info in blocks_info:
@@ -60,6 +120,7 @@ def patch_block(dev:str,file:str,key_dict):
if _tmp[0].strip() == '(IND)':
ind_block_id = int(_tmp[1])
else:
print(f'block_info : {block_info}')
id_range = _tmp[0].strip().replace('(','').replace(')','').split('-')
block_range = _tmp[1].strip().replace('(','').replace(')','').split('-')
blocks += [id for id in range(int(block_range[0]),int(block_range[1])+1)]
@@ -81,15 +142,23 @@ def patch_initrd_xz(initrd_xz:bytes,key_dict:dict,ljust=True):
initrd = lzma.decompress(initrd_xz)
new_initrd = initrd
for old_public_key,new_public_key in key_dict.items():
if old_public_key in new_initrd:
print(f'initrd public key patched {old_public_key[:16].hex().upper()}...')
new_initrd = new_initrd.replace(old_public_key,new_public_key)
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": 9,}] )
new_initrd = replace_key(old_public_key,new_public_key,new_initrd,'initrd')
preset = 6
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": preset }] )
while len(new_initrd_xz) > len(initrd_xz) and preset < 9:
print(f'preset:{preset}')
print(f'new initrd xz size:{len(new_initrd_xz)}')
print(f'old initrd xz size:{len(initrd_xz)}')
preset += 1
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": preset }] )
if len(new_initrd_xz) > len(initrd_xz):
new_initrd_xz = lzma.compress(new_initrd,check=lzma.CHECK_CRC32,filters=[{"id": lzma.FILTER_LZMA2, "preset": 9 | lzma.PRESET_EXTREME,'dict_size': 32*1024*1024,"lc": 4,"lp": 0, "pb": 0,}] )
if ljust:
assert len(new_initrd_xz) <= len(initrd_xz),'new initrd xz size is too big'
print(f'preset:{preset}')
print(f'new initrd xz size:{len(new_initrd_xz)}')
print(f'old initrd xz size:{len(initrd_xz)}')
print(f'ljust size:{len(initrd_xz)-len(new_initrd_xz)}')
assert len(new_initrd_xz) <= len(initrd_xz),'new initrd xz size is too big'
new_initrd_xz = new_initrd_xz.ljust(len(initrd_xz),b'\0')
return new_initrd_xz
@@ -239,24 +308,38 @@ def patch_kernel(data:bytes,key_dict):
else:
raise Exception('unknown kernel format')
def patch_loader(loader_file):
try:
from package import check_install_package
check_install_package(['pyelftools'])
from loader.patch_loader import patch_loader as do_patch_loader
arch = os.getenv('ARCH') or 'x86'
arch = arch.replace('-', '')
do_patch_loader(loader_file,loader_file,arch)
except ImportError as e:
print(e)
print("loader module import failed. cannot run patch_loader.py")
def patch_squashfs(path,key_dict):
for root, dirs, files in os.walk(path):
for file in files:
file = os.path.join(root,file)
for _file in files:
file = os.path.join(root,_file)
if os.path.isfile(file):
if _file =='loader':
patch_loader(file)
continue
data = open(file,'rb').read()
for old_public_key,new_public_key in key_dict.items():
if old_public_key in data:
print(f'{file} public key patched {old_public_key[:16].hex().upper()}...')
data = data.replace(old_public_key,new_public_key)
open(file,'wb').write(data)
data = open(file,'rb').read()
_data = replace_key(old_public_key,new_public_key,data,file)
if _data != data:
open(file,'wb').write(_data)
url_dict = {
os.environ['MIKRO_LICENCE_URL'].encode():os.environ['CUSTOM_LICENCE_URL'].encode(),
os.environ['MIKRO_UPGRADE_URL'].encode():os.environ['CUSTOM_UPGRADE_URL'].encode(),
os.environ['MIKRO_CLOUD_URL'].encode():os.environ['CUSTOM_CLOUD_URL'].encode(),
os.environ['MIKRO_CLOUD_PUBLIC_KEY'].encode():os.environ['CUSTOM_CLOUD_PUBLIC_KEY'].encode(),
}
data = open(file,'rb').read()
for old_url,new_url in url_dict.items():
if old_url in data:
print(f'{file} url patched {old_url.decode()[:7]}...')
@@ -331,7 +414,7 @@ if __name__ == '__main__':
args = parser.parse_args()
key_dict = {
bytes.fromhex(os.environ['MIKRO_LICENSE_PUBLIC_KEY']):bytes.fromhex(os.environ['CUSTOM_LICENSE_PUBLIC_KEY']),
bytes.fromhex(os.environ['MIKRO_NPK_SIGN_PUBLIC_LKEY']):bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PUBLIC_KEY'])
bytes.fromhex(os.environ['MIKRO_NPK_SIGN_PUBLIC_KEY']):bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PUBLIC_KEY'])
}
kcdsa_private_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PRIVATE_KEY'])
eddsa_private_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PRIVATE_KEY'])