mirror of
https://gitee.com/johng/gf
synced 2026-06-08 02:27:42 +08:00
Compare commits
762 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea5d52cb2f | |||
| b1754f8254 | |||
| 83f08b3135 | |||
| d3d1f94e40 | |||
| fc8572e8dd | |||
| 85acd3d31d | |||
| 9694a68211 | |||
| 0be8b29e42 | |||
| 5580ac9475 | |||
| 84ed66018e | |||
| 4d7f9552fe | |||
| d4b14fd717 | |||
| 0bd15bdab5 | |||
| 0b407c5e6d | |||
| 5f5b82188c | |||
| a17849bc39 | |||
| f3e3a5af5a | |||
| dc71c0d28f | |||
| eb99f5ebfe | |||
| 007fe0ea1a | |||
| 50b04c658c | |||
| cbea89e6f7 | |||
| e2e12fadb0 | |||
| 47eaa513c0 | |||
| 3bbc0fdd21 | |||
| 9393072745 | |||
| e42aa64d75 | |||
| b6935a7285 | |||
| b0367e4a62 | |||
| 0b71bc43a8 | |||
| afeca8cf9d | |||
| 33c396fb74 | |||
| 4903ef7887 | |||
| ab5ab4c675 | |||
| 3ea61d084e | |||
| 2433b2ce99 | |||
| 927758a2a9 | |||
| cb0d018fa4 | |||
| 49f69f6ade | |||
| bcb479aabb | |||
| db59f4232f | |||
| aa625d24bc | |||
| 36cfbaaa22 | |||
| f8462b5218 | |||
| b2bd12a371 | |||
| acf2307eb9 | |||
| 93964ee231 | |||
| 943939ba2b | |||
| f1b5a223bf | |||
| bb9cd37fab | |||
| 972f8c3aff | |||
| 34e522306d | |||
| 7c92c2f7e8 | |||
| 5204bdb60d | |||
| 6ab8d065d1 | |||
| d4e686226b | |||
| 19d7714503 | |||
| f464f30e6f | |||
| 0e7d7d1eee | |||
| 6af66a0201 | |||
| 81f3ad043d | |||
| ca7450f595 | |||
| 65192a7f92 | |||
| 8c309ac9fe | |||
| 7f1ce2aff3 | |||
| 10b67fc7b0 | |||
| 3cd005911d | |||
| d10d96800f | |||
| 35e5f1f204 | |||
| 1efdb72990 | |||
| 8d925d4741 | |||
| d6362cad16 | |||
| 00e83fed3f | |||
| 02f1cc7b40 | |||
| 32a60c2e96 | |||
| 30040332a7 | |||
| 569cbb09bf | |||
| 130191f4ed | |||
| e6732039c6 | |||
| df92c483d0 | |||
| 7c9eefef3d | |||
| 42de1c1dbe | |||
| 395df940d7 | |||
| ef1e18df19 | |||
| e684eae878 | |||
| 5219c5c37e | |||
| 5059abd88e | |||
| e2ed058e3b | |||
| ccc959a2d3 | |||
| a5a7d23792 | |||
| 5bc9acdab3 | |||
| ab1970e7d6 | |||
| 1582714325 | |||
| 7391a4d45a | |||
| 7e16d9b63e | |||
| d49dccb147 | |||
| 6cddfdb313 | |||
| 912316d765 | |||
| b9e2b05f04 | |||
| 887803e495 | |||
| eb11061bd2 | |||
| 000c7a92ed | |||
| 097b26f318 | |||
| 3da5e5e865 | |||
| e60262fec9 | |||
| 74bf1b4bc3 | |||
| 3f69e0db36 | |||
| 7d4c59ac5a | |||
| 3841f05e02 | |||
| bcd409ab1c | |||
| 4dd43aa018 | |||
| aed695313a | |||
| cf299273c4 | |||
| 53323f3cf9 | |||
| 24ee5341ec | |||
| ed4d1554ab | |||
| ac3481ed43 | |||
| 3df5969348 | |||
| ea6a773d60 | |||
| 35a326e169 | |||
| 4020eb9b4c | |||
| 243fe73c57 | |||
| 932f8c48ef | |||
| 7c1be3eb63 | |||
| 5de2cfbfa1 | |||
| b593c00d97 | |||
| 7798e96190 | |||
| 55ac18d90a | |||
| a409db5540 | |||
| dd5d56674e | |||
| 79617570ca | |||
| a4e7cc4700 | |||
| 2fbe4125dd | |||
| 47915816b5 | |||
| 0f53660453 | |||
| f3437dc00f | |||
| 574d63b4fd | |||
| 53e5a04073 | |||
| c51785125e | |||
| 5230f8304e | |||
| e0e00434cc | |||
| b95cb3180d | |||
| dec66dbd05 | |||
| cb44c40a9c | |||
| 2c22f4e17d | |||
| 83fa3593b1 | |||
| 4ad508c04d | |||
| 0d52386236 | |||
| 16a5318d32 | |||
| b1da02dff6 | |||
| b99db92113 | |||
| c0dff1dc16 | |||
| ef6ef506d6 | |||
| b71ac868b7 | |||
| d72997da04 | |||
| 41c0dde9bf | |||
| 498b72f75a | |||
| 5e231f3d61 | |||
| a2fec50500 | |||
| 6d7edb1479 | |||
| ce72f9a84b | |||
| 4fc24e1391 | |||
| 160bddecd3 | |||
| 39810a520c | |||
| 0dc47609b5 | |||
| 8fb4636cb4 | |||
| 30cf3dbbe6 | |||
| c90e9311e3 | |||
| ba2a7e4417 | |||
| 740dfa58a6 | |||
| 9620b15ea1 | |||
| 3fab7a341d | |||
| 5804547bc5 | |||
| 70d0d20750 | |||
| 92e21c275c | |||
| 8aff08581f | |||
| 6eb0de42f8 | |||
| 8c4a0b61b8 | |||
| 22696566d6 | |||
| c5c2938380 | |||
| d6433de4a6 | |||
| 15eaac35a8 | |||
| 46f3196297 | |||
| 842e5a6774 | |||
| 2ece368810 | |||
| 1bbfc56121 | |||
| 835b252b5d | |||
| 9bc9fc4545 | |||
| 8cc5338870 | |||
| 9191003391 | |||
| 99236fd93b | |||
| b34d560bb7 | |||
| 7bb4ddcfd5 | |||
| 00190abad8 | |||
| aa8eabd853 | |||
| 4914517f6b | |||
| 0865dbbf75 | |||
| d3a3ad0228 | |||
| e075432c40 | |||
| e816ab05de | |||
| a4762d0e02 | |||
| 2329622564 | |||
| 368312c816 | |||
| f6dbe1a40c | |||
| 032d085619 | |||
| a6fff37be8 | |||
| 879283685d | |||
| 0f4f2a6672 | |||
| ff6f5ce237 | |||
| dc0467e934 | |||
| f0d22fe570 | |||
| 05ee4d489a | |||
| 3c1ded22fd | |||
| bda5d252b2 | |||
| 0c3e66d6a0 | |||
| f31bf76f94 | |||
| 0afedee49d | |||
| 43d439a1cd | |||
| b8aeb4f0ee | |||
| 1408385612 | |||
| d6471d7d51 | |||
| aa9d66c53c | |||
| d9a7ee3e29 | |||
| 04c70f2037 | |||
| 2198f0cefe | |||
| c6b9b4d326 | |||
| 4c6ebe7808 | |||
| a9090e4a72 | |||
| 0126eb5470 | |||
| ed63617aa0 | |||
| 30f483a524 | |||
| 894f202b75 | |||
| 9171585b2c | |||
| 23d8ef32a3 | |||
| a031e112e5 | |||
| da8297d770 | |||
| 3991eb053c | |||
| 6fb26c44d7 | |||
| fdc027734c | |||
| 32a7f6a0f0 | |||
| 5bbec48679 | |||
| d4b9ee4c61 | |||
| 50b5cd50bc | |||
| 53afbd0f05 | |||
| b6874eb66d | |||
| 7ff7de4643 | |||
| 5f148632d2 | |||
| c1325ef9a3 | |||
| d788b7ff5e | |||
| 66d0663dc5 | |||
| a56524ee05 | |||
| 1aa9f2809e | |||
| 261672e84c | |||
| 6a4e39e815 | |||
| 4af9ce8a81 | |||
| 5f146720fe | |||
| 67e6772d88 | |||
| 12e9febe9e | |||
| 676022eeb6 | |||
| 3a8fc1e70d | |||
| 0b6798acb5 | |||
| e721124b6c | |||
| b32eb30212 | |||
| 967a39ecbe | |||
| dfb7f5abfb | |||
| 56f5d5125b | |||
| 5083174a92 | |||
| 4a278dfd79 | |||
| 80d57ed8f9 | |||
| b742e222d6 | |||
| ae86f66545 | |||
| 45e4c9e16c | |||
| 8c07f1a42c | |||
| e6c97410ef | |||
| b1a55c7a32 | |||
| 1cd1449085 | |||
| 55690f3738 | |||
| 13f6fb1929 | |||
| e8088a6563 | |||
| e8051bad9a | |||
| d0d41a63a6 | |||
| 853b038a47 | |||
| 34946f6105 | |||
| 15d88c269d | |||
| cbbfd85eeb | |||
| adf90c876f | |||
| b4f76b8448 | |||
| ed858ebd4b | |||
| 272b9c7afd | |||
| 8dc8dd9756 | |||
| a64d1001e2 | |||
| ad737ded3c | |||
| ac6b0c0980 | |||
| b69e0ff9f7 | |||
| 0361f9f7de | |||
| 005668aca8 | |||
| 013f8b216a | |||
| 8ecfa91e5d | |||
| 117fc6eda2 | |||
| d66af122c7 | |||
| a7467945ca | |||
| 81d8aa55cd | |||
| 4a6630138d | |||
| 3adae3a9aa | |||
| 21ebf48072 | |||
| 2b90bcfab6 | |||
| 5f0641f348 | |||
| 38c9cac578 | |||
| 9ba49fa454 | |||
| 39fede66e6 | |||
| d984f1a9d8 | |||
| 28b8efe00c | |||
| 7b0fd6de9b | |||
| c0fa2e3a73 | |||
| 3f6669e2b7 | |||
| 6ff4ed84e5 | |||
| 5e72b03b0a | |||
| 4bb88027d8 | |||
| ae4f14c2e2 | |||
| 5a8b33fa09 | |||
| 5884a0e05f | |||
| 31e44062a8 | |||
| 87cb1c9b8e | |||
| 0266d24d0a | |||
| 0876e00eb8 | |||
| 85c4794ceb | |||
| e007bf35b2 | |||
| 74e968e93b | |||
| 18507fb836 | |||
| 3b245837b9 | |||
| a853984f52 | |||
| 00c544ee99 | |||
| e7b9e41a5e | |||
| e254b4f3c0 | |||
| b0c9c68c9c | |||
| 1030434ce6 | |||
| 2f08c4b00f | |||
| 4553f90a83 | |||
| ef7fec7e24 | |||
| 0a76b9c61b | |||
| fbeb8f81ac | |||
| 62af4f1c2c | |||
| ed43efe4fb | |||
| 1cb42c32e3 | |||
| 628b454ebc | |||
| 38a858d7d3 | |||
| 83b92ddfa4 | |||
| 7cd415b1df | |||
| d2113b4d23 | |||
| d445987f95 | |||
| 14d2d747f6 | |||
| 73dc8c9c4b | |||
| 576f1a798c | |||
| 78fa2d2e3b | |||
| 9402cc8c6a | |||
| 7d1a508ea9 | |||
| b84034b667 | |||
| 84b7cbd992 | |||
| bc8ca912ce | |||
| b61baa1efc | |||
| c4a5bc8584 | |||
| 8c71d579b5 | |||
| 2e8d8f63ff | |||
| 91b94878d3 | |||
| b000aa3dfe | |||
| 582c6eaef9 | |||
| 4f4109cdb6 | |||
| 9f12673631 | |||
| d37b75442d | |||
| ee58255418 | |||
| 033ba588c9 | |||
| 60d8283971 | |||
| ad90bc2809 | |||
| a1b9eca7b6 | |||
| c4a5b8ca94 | |||
| ab79134309 | |||
| 63c3e8396b | |||
| 1793bf0863 | |||
| 8e0e87877a | |||
| 425b31c6fd | |||
| 4da469325d | |||
| 33fdde6afd | |||
| ee6103418b | |||
| 038548c188 | |||
| ce8b536fca | |||
| 8e5a03f6c9 | |||
| 1c5fd1a1f7 | |||
| 2598745e50 | |||
| e756f284be | |||
| c6a02b850d | |||
| 55e8dbe9fd | |||
| 5efa5ebd2f | |||
| 4ebe4233fc | |||
| 35623b5abe | |||
| b617d399b6 | |||
| b96b5c3f7c | |||
| 847f016cc9 | |||
| c613dc8c5c | |||
| 182a393050 | |||
| 6cb91021cf | |||
| 2be9bb970b | |||
| ea396a3925 | |||
| b1611fee1b | |||
| dba903c13b | |||
| 7cb5fbe684 | |||
| d7ae5624c8 | |||
| f1455ad37a | |||
| 127e8af6a6 | |||
| d9be1d0b52 | |||
| 6cd84e8276 | |||
| ceaeceadd9 | |||
| cd5bf7c504 | |||
| 66aa0c7050 | |||
| 141ca62c6d | |||
| 9dc97f4b0d | |||
| 714bda3e0f | |||
| 2b4598f65b | |||
| 5e9ef8ada4 | |||
| cf7c07cc34 | |||
| 508062f8dc | |||
| e5c63c7e16 | |||
| 7a11f00eb4 | |||
| faf09c586c | |||
| c866b5005f | |||
| a0619f7ff0 | |||
| 37aee19bfa | |||
| 27609d8da8 | |||
| c083b333d8 | |||
| ee376883d1 | |||
| 98169784b1 | |||
| 9d1c6f2daa | |||
| 25d4ba320a | |||
| 3988a7ff6b | |||
| 26e3c7aeb8 | |||
| eff46bd1db | |||
| 7a3176ea77 | |||
| a656ad0941 | |||
| 299573dd19 | |||
| 43b84f4044 | |||
| 897d6d9ad0 | |||
| e4c8cfc16b | |||
| 95888e0b77 | |||
| 4ded89d453 | |||
| 82a3391937 | |||
| f580b7a488 | |||
| 9df0a9da0a | |||
| 6172862061 | |||
| 1ae037f515 | |||
| 6f7cd96a7f | |||
| e00d3ff7ff | |||
| 390b936153 | |||
| 863bea1ad1 | |||
| b7794a8783 | |||
| bb3c51c6cc | |||
| c3c82cebd5 | |||
| 5d51e9fa2c | |||
| 2c70bb6a00 | |||
| 98b2e8ab18 | |||
| 675ae9bade | |||
| 3e7e8ba6f2 | |||
| f1766bdbdc | |||
| e3665cedaf | |||
| dd7caea910 | |||
| 8ed57c6468 | |||
| 0e6becc36d | |||
| e38c455252 | |||
| 4c1cf73005 | |||
| 384fb3c4d5 | |||
| afb90b0af3 | |||
| 1530ffc926 | |||
| f876a56d2a | |||
| d26eadf5be | |||
| 13fc0cb9eb | |||
| 59b3f6e962 | |||
| 80442efe94 | |||
| ab929e465b | |||
| 047c90466d | |||
| 40e6b2b0f1 | |||
| 9159f00014 | |||
| 59a9484970 | |||
| 8a853b1bb7 | |||
| 2bcd6c4771 | |||
| 1acc1b8230 | |||
| 2c169e2330 | |||
| f57d71b6fa | |||
| 796bc008f8 | |||
| d7faae0531 | |||
| f0511592b5 | |||
| 68efab79ef | |||
| 1ede8c77ba | |||
| 48f95d0009 | |||
| 0bb57b8989 | |||
| 0503c17867 | |||
| 19779cd342 | |||
| 141f3512a9 | |||
| 3b9e5c71bf | |||
| 2bcee014f7 | |||
| f0568b4e22 | |||
| 8670f29c4e | |||
| 33a528af76 | |||
| 52056644d4 | |||
| 3ae23df4b3 | |||
| 1b327b8abd | |||
| bb5cd3e224 | |||
| 1e8237446e | |||
| b2b2044786 | |||
| 64c5222623 | |||
| 1597291ac3 | |||
| c2e742335b | |||
| cf5884bc60 | |||
| cbf5ee9649 | |||
| 8ac177a6de | |||
| cdd4473df5 | |||
| aaebaa7250 | |||
| 7443a8baa1 | |||
| c6ff95a3f4 | |||
| 7957016ae2 | |||
| 17ab0e2ced | |||
| b0650f3402 | |||
| f4f73f2765 | |||
| babc69e13d | |||
| 481c50f233 | |||
| b62b2f3598 | |||
| 4f37abac6a | |||
| 31a23e724d | |||
| 7d5ab1f8db | |||
| 0d8952dcde | |||
| bd7ec5d0b0 | |||
| 9e6e8001ca | |||
| 0639becccc | |||
| 88844649eb | |||
| 31c5d5a5f5 | |||
| 6abbc57c96 | |||
| 39af6e51c4 | |||
| ef04c8a09e | |||
| 4505d61604 | |||
| 04d32e7a91 | |||
| 26066965c3 | |||
| 7f199527f8 | |||
| ea79b3cbb8 | |||
| 0ca81bd11a | |||
| 4d13ffdc26 | |||
| 331a29024e | |||
| 6aa5c2b2ef | |||
| 8c969b2a84 | |||
| 0d7e28ee75 | |||
| ab5062663e | |||
| 896b9fa105 | |||
| 6a01275499 | |||
| 61bf0a0092 | |||
| d7c5a08d20 | |||
| 350ee9f0a2 | |||
| 7753fc6fe1 | |||
| 24f5b2782d | |||
| 853d7aaf8f | |||
| 07509e9847 | |||
| ab82599ee2 | |||
| 10e2b60ad9 | |||
| 965476c7f4 | |||
| 9536b33a6a | |||
| 61271e4f7b | |||
| 57941b6df9 | |||
| 1102de5a66 | |||
| 6d33a73617 | |||
| cdcb0cdc14 | |||
| 6176028176 | |||
| c871bb3a1e | |||
| 3430cf1f17 | |||
| 4556dda038 | |||
| 49042d480c | |||
| b7295a1558 | |||
| 62d91438f2 | |||
| c4c3620c5f | |||
| ec0cbab47e | |||
| 8e3c66584d | |||
| 84e75129a5 | |||
| 26d460241d | |||
| ede54b392e | |||
| d12542d78e | |||
| 3a014dcb09 | |||
| cb27f26e64 | |||
| a3ad294d6a | |||
| cf57ea3ef0 | |||
| 94dd590fc4 | |||
| 27836feb47 | |||
| 96e48e1de6 | |||
| d5c06664b5 | |||
| e4edbe25b2 | |||
| 31bc30bb27 | |||
| a2905977ec | |||
| b63e01adf6 | |||
| 2680666f52 | |||
| f82f53f5f6 | |||
| e27ca17b0e | |||
| 066b1026a2 | |||
| c7cf72e7bc | |||
| 0e2a0075ef | |||
| 6dccaf802c | |||
| de5224689a | |||
| f9ec01c647 | |||
| 4902eb73b7 | |||
| 513c8605fb | |||
| 84148bbbb0 | |||
| c2e91edca8 | |||
| 17fc1ce174 | |||
| be9377a496 | |||
| c40a4d8a66 | |||
| 6d0b4faeb0 | |||
| 3297924992 | |||
| 059c62a6c0 | |||
| bef942b19f | |||
| 30140fb229 | |||
| df3ae386cb | |||
| baf4cc1d1c | |||
| 9fcd3374c1 | |||
| d5d56e51d7 | |||
| 3b8853736d | |||
| 16b22e7505 | |||
| da7eac03ad | |||
| 060f67c2c8 | |||
| 273b81d60f | |||
| 72b58ff8a1 | |||
| 900e0b2751 | |||
| 9026fd2c13 | |||
| f6f0c8fd1e | |||
| 4bdaacab91 | |||
| 60ca7d7246 | |||
| e4e312c4f8 | |||
| 7bcdbae7b8 | |||
| b16cd2dc85 | |||
| 0826f8ba35 | |||
| ead284e20b | |||
| 59023f9f09 | |||
| 17e48ba9f2 | |||
| e60b42470e | |||
| 9c42ba187d | |||
| c6f14dc1b1 | |||
| 02e3240bb1 | |||
| 54f0968f86 | |||
| 0e75d39811 | |||
| 6cf6414da2 | |||
| 89f77a2412 | |||
| a400d8b2f3 | |||
| dc6a9237d7 | |||
| 2c73ba2f76 | |||
| 2cbfdf43cf | |||
| 5e0e6f356b | |||
| cbff244d88 | |||
| 2e405342ca | |||
| 583d576cdb | |||
| 3db97ba0dd | |||
| e81d6a859b | |||
| 534abb7f17 | |||
| 7198eb3b66 | |||
| 32f33b9f8c | |||
| 03ad6a5728 | |||
| c1308475f3 | |||
| e4ec1be948 | |||
| c90f91dcbe | |||
| 5332ce4c79 | |||
| eaae7f46d2 | |||
| 8c40a53b80 | |||
| 25c091df7f | |||
| ec3a4532b8 | |||
| ad04adccea | |||
| cfd2636f13 | |||
| 7e854f88ca | |||
| 3628b1e9d2 | |||
| 68e75c589b | |||
| 9ad9292321 | |||
| 7fcf7d31a0 | |||
| 0cf28c0f07 | |||
| abbc96a873 | |||
| b7201e111d | |||
| a31553468c | |||
| ce89b440bb | |||
| 3a72c4a507 | |||
| c82e612258 | |||
| ae5891068e | |||
| f326dc4eaa | |||
| 48fddcd5e7 | |||
| 33c9204d58 | |||
| 99f1e69469 | |||
| ed0b3c039a | |||
| eef25c28b4 | |||
| a32847f0c5 | |||
| 0fc193faa3 | |||
| 65077a224c | |||
| c256d2d4af | |||
| 215a50675e | |||
| 023c4a19ae | |||
| ac5d399906 | |||
| 7fd0e5b3bc | |||
| 64ff651d57 | |||
| d260de15ba | |||
| be77779aff | |||
| e119f2a534 | |||
| a09c8497bc | |||
| ebad3eb93e | |||
| e4e4534c7c | |||
| b412fc6516 | |||
| f9c9750108 | |||
| 5dee3bb4d9 | |||
| 1e3d8cdadd | |||
| c5bf45f1ae | |||
| bf674060c0 | |||
| 878dbe4ab9 | |||
| d8b383719a | |||
| 9ff5f39701 | |||
| 5144cc0e08 | |||
| ee29b28575 | |||
| 7785082f19 | |||
| edf40ba430 | |||
| a228495ced | |||
| ed9dc70769 | |||
| e8581d4fd5 | |||
| 2d6fcf5d06 | |||
| 55e0262c37 | |||
| d5e5a48170 | |||
| d0f2928cec | |||
| 190a53647e | |||
| f9a3fa3c23 | |||
| f1fee72d6d | |||
| 0b4ae6b116 | |||
| a1ec7cb896 | |||
| 1935412db9 | |||
| c90a9d45ee | |||
| a594592151 | |||
| 119d8bf98c | |||
| 1e141d9f64 | |||
| 587af6dec8 | |||
| 793e862e5a | |||
| 09c3425dd3 | |||
| 4ca168412b | |||
| 66f24db6da | |||
| c39a58f812 | |||
| 5034f231a9 | |||
| 1a271ce627 | |||
| 64afd5f64c | |||
| 0e0d2e1c45 | |||
| 52d8371ba9 | |||
| 1d74b58d36 | |||
| 66803fd664 | |||
| c7f51b8e77 | |||
| b57cbacc82 | |||
| 325887fa18 | |||
| 73ca527b0a | |||
| 147348e0d1 | |||
| ad202ea735 | |||
| d7bd1b74e8 | |||
| 5e3c0bd9aa | |||
| a6bbb8424c | |||
| 00daeb318c | |||
| 65341141fe | |||
| fe353c5fe3 | |||
| 008e5ea196 | |||
| 157e936f24 | |||
| 455d724c01 | |||
| ea60f7e054 | |||
| daf2b649ef | |||
| aa87d234e3 | |||
| 61c9d5fb3f |
2
.codecov.yml
Normal file
2
.codecov.yml
Normal file
@ -0,0 +1,2 @@
|
||||
ignore:
|
||||
- "cmd" # ignore cmd folders and all its contents
|
||||
BIN
.github/logo.png
vendored
Normal file
BIN
.github/logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
42
.github/workflows/apollo/docker-compose.yml
vendored
Normal file
42
.github/workflows/apollo/docker-compose.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
apollo-quick-start:
|
||||
image: "loads/apollo-quick-start:latest"
|
||||
container_name: apollo-quick-start
|
||||
depends_on:
|
||||
- apollo-db
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "8070:8070"
|
||||
- "8060:8060"
|
||||
links:
|
||||
- apollo-db
|
||||
#environment:
|
||||
#JAVA_OPTS: '-Xms100m -Xmx1000m -Xmn100m -Xss256k -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=250m'
|
||||
#APOLLO_CONFIG_DB_USERNAME: 'root'
|
||||
#APOLLO_CONFIG_DB_PASSWORD: 'apollo'
|
||||
#APOLLO_PORTAL_DB_USERNAME: 'root'
|
||||
#APOLLO_PORTAL_DB_PASSWORD: 'apollo'
|
||||
|
||||
apollo-db:
|
||||
image: "loads/mysql:5.7"
|
||||
container_name: apollo-db
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
||||
#MYSQL_ROOT_PASSWORD: 'apollo'
|
||||
depends_on:
|
||||
- apollo-dbdata
|
||||
ports:
|
||||
- "13306:3306"
|
||||
volumes:
|
||||
- ./sql:/docker-entrypoint-initdb.d
|
||||
volumes_from:
|
||||
- apollo-dbdata
|
||||
|
||||
apollo-dbdata:
|
||||
image: "loads/alpine:3.8"
|
||||
container_name: apollo-dbdata
|
||||
volumes:
|
||||
- /var/lib/mysql
|
||||
467
.github/workflows/apollo/sql/apolloconfigdb.sql
vendored
Normal file
467
.github/workflows/apollo/sql/apolloconfigdb.sql
vendored
Normal file
@ -0,0 +1,467 @@
|
||||
--
|
||||
-- Copyright 2022 Apollo Authors
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
# Create Database
|
||||
# ------------------------------------------------------------
|
||||
CREATE DATABASE IF NOT EXISTS ApolloConfigDB DEFAULT CHARACTER SET = utf8mb4;
|
||||
|
||||
Use ApolloConfigDB;
|
||||
|
||||
# Dump of table app
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `App`;
|
||||
|
||||
CREATE TABLE `App` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名',
|
||||
`OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id',
|
||||
`OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字',
|
||||
`OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName',
|
||||
`OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_Name` (`Name`(191))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表';
|
||||
|
||||
|
||||
|
||||
# Dump of table appnamespace
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `AppNamespace`;
|
||||
|
||||
CREATE TABLE `AppNamespace` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一',
|
||||
`AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id',
|
||||
`Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型',
|
||||
`IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共',
|
||||
`Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`),
|
||||
KEY `Name_AppId` (`Name`,`AppId`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义';
|
||||
|
||||
|
||||
|
||||
# Dump of table audit
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Audit`;
|
||||
|
||||
CREATE TABLE `Audit` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`EntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名',
|
||||
`EntityId` int(10) unsigned DEFAULT NULL COMMENT '记录ID',
|
||||
`OpName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型',
|
||||
`Comment` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日志审计表';
|
||||
|
||||
|
||||
|
||||
# Dump of table cluster
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Cluster`;
|
||||
|
||||
CREATE TABLE `Cluster` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`Name` varchar(32) NOT NULL DEFAULT '' COMMENT '集群名字',
|
||||
`AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'App id',
|
||||
`ParentClusterId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父cluster',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`),
|
||||
KEY `IX_ParentClusterId` (`ParentClusterId`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='集群';
|
||||
|
||||
|
||||
|
||||
# Dump of table commit
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Commit`;
|
||||
|
||||
CREATE TABLE `Commit` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`ChangeSets` longtext NOT NULL COMMENT '修改变更集',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName',
|
||||
`NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName',
|
||||
`Comment` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `AppId` (`AppId`(191)),
|
||||
KEY `ClusterName` (`ClusterName`(191)),
|
||||
KEY `NamespaceName` (`NamespaceName`(191))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='commit 历史表';
|
||||
|
||||
# Dump of table grayreleaserule
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `GrayReleaseRule`;
|
||||
|
||||
CREATE TABLE `GrayReleaseRule` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name',
|
||||
`NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name',
|
||||
`BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'branch name',
|
||||
`Rules` varchar(16000) DEFAULT '[]' COMMENT '灰度规则',
|
||||
`ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '灰度对应的release',
|
||||
`BranchStatus` tinyint(2) DEFAULT '1' COMMENT '灰度分支状态: 0:删除分支,1:正在使用的规则 2:全量发布',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='灰度规则表';
|
||||
|
||||
|
||||
# Dump of table instance
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Instance`;
|
||||
|
||||
CREATE TABLE `Instance` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName',
|
||||
`DataCenter` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Data Center Name',
|
||||
`Ip` varchar(32) NOT NULL DEFAULT '' COMMENT 'instance ip',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `IX_UNIQUE_KEY` (`AppId`,`ClusterName`,`Ip`,`DataCenter`),
|
||||
KEY `IX_IP` (`Ip`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用配置的应用实例';
|
||||
|
||||
|
||||
|
||||
# Dump of table instanceconfig
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `InstanceConfig`;
|
||||
|
||||
CREATE TABLE `InstanceConfig` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`InstanceId` int(11) unsigned DEFAULT NULL COMMENT 'Instance Id',
|
||||
`ConfigAppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Config App Id',
|
||||
`ConfigClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Cluster Name',
|
||||
`ConfigNamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Namespace Name',
|
||||
`ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key',
|
||||
`ReleaseDeliveryTime` timestamp NULL DEFAULT NULL COMMENT '配置获取时间',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `IX_UNIQUE_KEY` (`InstanceId`,`ConfigAppId`,`ConfigNamespaceName`),
|
||||
KEY `IX_ReleaseKey` (`ReleaseKey`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_Valid_Namespace` (`ConfigAppId`,`ConfigClusterName`,`ConfigNamespaceName`,`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用实例的配置信息';
|
||||
|
||||
|
||||
|
||||
# Dump of table item
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Item`;
|
||||
|
||||
CREATE TABLE `Item` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId',
|
||||
`Key` varchar(128) NOT NULL DEFAULT 'default' COMMENT '配置项Key',
|
||||
`Value` longtext NOT NULL COMMENT '配置项值',
|
||||
`Comment` varchar(1024) DEFAULT '' COMMENT '注释',
|
||||
`LineNum` int(10) unsigned DEFAULT '0' COMMENT '行号',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `IX_GroupId` (`NamespaceId`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置项目';
|
||||
|
||||
|
||||
|
||||
# Dump of table namespace
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Namespace`;
|
||||
|
||||
CREATE TABLE `Namespace` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name',
|
||||
`NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_ClusterName_NamespaceName_DeletedAt` (`AppId`(191),`ClusterName`(191),`NamespaceName`(191),`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_NamespaceName` (`NamespaceName`(191))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='命名空间';
|
||||
|
||||
|
||||
|
||||
# Dump of table namespacelock
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `NamespaceLock`;
|
||||
|
||||
CREATE TABLE `NamespaceLock` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
|
||||
`NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
`IsDeleted` bit(1) DEFAULT b'0' COMMENT '软删除',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_NamespaceId_DeletedAt` (`NamespaceId`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='namespace的编辑锁';
|
||||
|
||||
|
||||
|
||||
# Dump of table release
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Release`;
|
||||
|
||||
CREATE TABLE `Release` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key',
|
||||
`Name` varchar(64) NOT NULL DEFAULT 'default' COMMENT '发布名字',
|
||||
`Comment` varchar(256) DEFAULT NULL COMMENT '发布说明',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName',
|
||||
`NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName',
|
||||
`Configurations` longtext NOT NULL COMMENT '发布配置',
|
||||
`IsAbandoned` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否废弃',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_ReleaseKey_DeletedAt` (`ReleaseKey`,`DeletedAt`),
|
||||
KEY `AppId_ClusterName_GroupName` (`AppId`(191),`ClusterName`(191),`NamespaceName`(191)),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布';
|
||||
|
||||
|
||||
# Dump of table releasehistory
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ReleaseHistory`;
|
||||
|
||||
CREATE TABLE `ReleaseHistory` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName',
|
||||
`NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'namespaceName',
|
||||
`BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT '发布分支名',
|
||||
`ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '关联的Release Id',
|
||||
`PreviousReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '前一次发布的ReleaseId',
|
||||
`Operation` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '发布类型,0: 普通发布,1: 回滚,2: 灰度发布,3: 灰度规则更新,4: 灰度合并回主分支发布,5: 主分支发布灰度自动发布,6: 主分支回滚灰度自动发布,7: 放弃灰度',
|
||||
`OperationContext` longtext NOT NULL COMMENT '发布上下文信息',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`,`BranchName`),
|
||||
KEY `IX_ReleaseId` (`ReleaseId`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布历史';
|
||||
|
||||
|
||||
# Dump of table releasemessage
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ReleaseMessage`;
|
||||
|
||||
CREATE TABLE `ReleaseMessage` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`Message` varchar(1024) NOT NULL DEFAULT '' COMMENT '发布的消息内容',
|
||||
`DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_Message` (`Message`(191))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布消息';
|
||||
|
||||
|
||||
|
||||
# Dump of table serverconfig
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ServerConfig`;
|
||||
|
||||
CREATE TABLE `ServerConfig` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key',
|
||||
`Cluster` varchar(32) NOT NULL DEFAULT 'default' COMMENT '配置对应的集群,default为不针对特定的集群',
|
||||
`Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值',
|
||||
`Comment` varchar(1024) DEFAULT '' COMMENT '注释',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_Key_Cluster_DeletedAt` (`Key`,`Cluster`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置';
|
||||
|
||||
# Dump of table accesskey
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `AccessKey`;
|
||||
|
||||
CREATE TABLE `AccessKey` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`Secret` varchar(128) NOT NULL DEFAULT '' COMMENT 'Secret',
|
||||
`IsEnabled` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: enabled, 0: disabled',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_Secret_DeletedAt` (`AppId`,`Secret`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问密钥';
|
||||
|
||||
# Config
|
||||
# ------------------------------------------------------------
|
||||
INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`)
|
||||
VALUES
|
||||
('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url,多个service以英文逗号分隔'),
|
||||
('namespace.lock.switch', 'default', 'false', '一次发布只能有一个人修改开关'),
|
||||
('item.value.length.limit', 'default', '20000', 'item value最大长度限制'),
|
||||
('config-service.cache.enabled', 'default', 'false', 'ConfigService是否开启缓存,开启后能提高性能,但是会增大内存消耗!'),
|
||||
('item.key.length.limit', 'default', '128', 'item key 最大长度限制');
|
||||
|
||||
# Sample Data
|
||||
# ------------------------------------------------------------
|
||||
INSERT INTO `App` (`AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`)
|
||||
VALUES
|
||||
('SampleApp', 'Sample App', 'TEST1', '样例部门1', 'apollo', 'apollo@acme.com');
|
||||
|
||||
INSERT INTO `AppNamespace` (`Name`, `AppId`, `Format`, `IsPublic`, `Comment`)
|
||||
VALUES
|
||||
('application', 'SampleApp', 'properties', 0, 'default app namespace');
|
||||
|
||||
INSERT INTO `Cluster` (`Name`, `AppId`)
|
||||
VALUES
|
||||
('default', 'SampleApp');
|
||||
|
||||
INSERT INTO `Namespace` (`Id`, `AppId`, `ClusterName`, `NamespaceName`)
|
||||
VALUES
|
||||
(1, 'SampleApp', 'default', 'application');
|
||||
|
||||
|
||||
INSERT INTO `Item` VALUES (1,1,'timeout','100','sample timeout配置',1,_binary '\0',0,'default','2022-09-28 15:43:17','','2022-09-28 15:43:17'),
|
||||
(2,1,'','','',2,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(3,1,'server.address',':8000','',3,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(4,1,'server.dumpRouterMap','true','',4,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(5,1,'server.routeOverWrite','true','',5,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(6,1,'server.accessLogEnabled','true','',6,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(7,1,'server.openapiPath','/api.json','',7,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(8,1,'server.swaggerPath','/swagger','',8,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(9,1,'','','',9,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(10,1,'','','# Global logging.',10,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(11,1,'logger.level','all','',11,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55'),
|
||||
(12,1,'logger.stdout','true','',12,_binary '\0',0,'apollo','2022-09-28 15:47:55','apollo','2022-09-28 15:47:55');
|
||||
|
||||
INSERT INTO `Release` VALUES (1,'20161009155425-d3a0749c6e20bc15','20161009155424-release','Sample发布','SampleApp','default','application','{\"timeout\":\"100\"}',_binary '\0',_binary '\0',0,'default','2022-09-28 15:59:38','','2022-09-28 15:59:38'),
|
||||
(2,'20220929000151-1dc5634459e19171','20220929000148-release','','SampleApp','default','application','{\"timeout\":\"100\",\"server.address\":\":8000\",\"server.dumpRouterMap\":\"true\",\"server.routeOverWrite\":\"true\",\"server.accessLogEnabled\":\"true\",\"server.openapiPath\":\"/api.json\",\"server.swaggerPath\":\"/swagger\",\"logger.level\":\"all\",\"logger.stdout\":\"true\"}',_binary '\0',_binary '\0',0,'apollo','2022-09-28 16:01:51','apollo','2022-09-28 16:01:51');
|
||||
|
||||
INSERT INTO `ReleaseHistory` VALUES (1,'SampleApp','default','application','default',1,0,0,'{}',_binary '\0',0,'apollo','2022-09-28 15:59:38','apollo','2022-09-28 15:59:38'),
|
||||
(2,'SampleApp','default','application','default',2,1,0,'{\"isEmergencyPublish\":false}',_binary '\0',0,'apollo','2022-09-28 16:01:51','apollo','2022-09-28 16:01:51');
|
||||
|
||||
INSERT INTO `ReleaseMessage` VALUES (1,'SampleApp+default+application','2022-09-28 15:59:38'),
|
||||
(2,'SampleApp+default+application','2022-09-28 16:01:51');
|
||||
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
420
.github/workflows/apollo/sql/apolloportaldb.sql
vendored
Normal file
420
.github/workflows/apollo/sql/apolloportaldb.sql
vendored
Normal file
@ -0,0 +1,420 @@
|
||||
--
|
||||
-- Copyright 2022 Apollo Authors
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
# Create Database
|
||||
# ------------------------------------------------------------
|
||||
CREATE DATABASE IF NOT EXISTS ApolloPortalDB DEFAULT CHARACTER SET = utf8mb4;
|
||||
|
||||
Use ApolloPortalDB;
|
||||
|
||||
# Dump of table app
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `App`;
|
||||
|
||||
CREATE TABLE `App` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名',
|
||||
`OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id',
|
||||
`OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字',
|
||||
`OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName',
|
||||
`OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_Name` (`Name`(191))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表';
|
||||
|
||||
|
||||
|
||||
# Dump of table appnamespace
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `AppNamespace`;
|
||||
|
||||
CREATE TABLE `AppNamespace` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一',
|
||||
`AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id',
|
||||
`Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型',
|
||||
`IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共',
|
||||
`Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`),
|
||||
KEY `Name_AppId` (`Name`,`AppId`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义';
|
||||
|
||||
|
||||
|
||||
# Dump of table consumer
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Consumer`;
|
||||
|
||||
CREATE TABLE `Consumer` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名',
|
||||
`OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id',
|
||||
`OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字',
|
||||
`OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName',
|
||||
`OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放API消费者';
|
||||
|
||||
|
||||
|
||||
# Dump of table consumeraudit
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ConsumerAudit`;
|
||||
|
||||
CREATE TABLE `ConsumerAudit` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id',
|
||||
`Uri` varchar(1024) NOT NULL DEFAULT '' COMMENT '访问的Uri',
|
||||
`Method` varchar(16) NOT NULL DEFAULT '' COMMENT '访问的Method',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_ConsumerId` (`ConsumerId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer审计表';
|
||||
|
||||
|
||||
|
||||
# Dump of table consumerrole
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ConsumerRole`;
|
||||
|
||||
CREATE TABLE `ConsumerRole` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id',
|
||||
`RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_ConsumerId_RoleId_DeletedAt` (`ConsumerId`,`RoleId`,`DeletedAt`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_RoleId` (`RoleId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer和role的绑定表';
|
||||
|
||||
|
||||
|
||||
# Dump of table consumertoken
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ConsumerToken`;
|
||||
|
||||
CREATE TABLE `ConsumerToken` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'ConsumerId',
|
||||
`Token` varchar(128) NOT NULL DEFAULT '' COMMENT 'token',
|
||||
`Expires` datetime NOT NULL DEFAULT '2099-01-01 00:00:00' COMMENT 'token失效时间',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_Token_DeletedAt` (`Token`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer token表';
|
||||
|
||||
# Dump of table favorite
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Favorite`;
|
||||
|
||||
CREATE TABLE `Favorite` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`UserId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '收藏的用户',
|
||||
`AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID',
|
||||
`Position` int(32) NOT NULL DEFAULT '10000' COMMENT '收藏顺序',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_UserId_AppId_DeletedAt` (`UserId`,`AppId`,`DeletedAt`),
|
||||
KEY `AppId` (`AppId`(191)),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COMMENT='应用收藏表';
|
||||
|
||||
# Dump of table permission
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Permission`;
|
||||
|
||||
CREATE TABLE `Permission` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`PermissionType` varchar(32) NOT NULL DEFAULT '' COMMENT '权限类型',
|
||||
`TargetId` varchar(256) NOT NULL DEFAULT '' COMMENT '权限对象类型',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_TargetId_PermissionType_DeletedAt` (`TargetId`,`PermissionType`,`DeletedAt`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='permission表';
|
||||
|
||||
|
||||
|
||||
# Dump of table role
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Role`;
|
||||
|
||||
CREATE TABLE `Role` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`RoleName` varchar(256) NOT NULL DEFAULT '' COMMENT 'Role name',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_RoleName_DeletedAt` (`RoleName`,`DeletedAt`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
|
||||
|
||||
|
||||
|
||||
# Dump of table rolepermission
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `RolePermission`;
|
||||
|
||||
CREATE TABLE `RolePermission` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id',
|
||||
`PermissionId` int(10) unsigned DEFAULT NULL COMMENT 'Permission Id',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_RoleId_PermissionId_DeletedAt` (`RoleId`,`PermissionId`,`DeletedAt`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_PermissionId` (`PermissionId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和权限的绑定表';
|
||||
|
||||
|
||||
|
||||
# Dump of table serverconfig
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `ServerConfig`;
|
||||
|
||||
CREATE TABLE `ServerConfig` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key',
|
||||
`Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值',
|
||||
`Comment` varchar(1024) DEFAULT '' COMMENT '注释',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_Key_DeletedAt` (`Key`,`DeletedAt`),
|
||||
KEY `DataChange_LastTime` (`DataChange_LastTime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置';
|
||||
|
||||
|
||||
|
||||
# Dump of table userrole
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `UserRole`;
|
||||
|
||||
CREATE TABLE `UserRole` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`UserId` varchar(128) DEFAULT '' COMMENT '用户身份标识',
|
||||
`RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id',
|
||||
`IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal',
|
||||
`DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds',
|
||||
`DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀',
|
||||
`DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀',
|
||||
`DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_UserId_RoleId_DeletedAt` (`UserId`,`RoleId`,`DeletedAt`),
|
||||
KEY `IX_DataChange_LastTime` (`DataChange_LastTime`),
|
||||
KEY `IX_RoleId` (`RoleId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和role的绑定表';
|
||||
|
||||
# Dump of table Users
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Users`;
|
||||
|
||||
CREATE TABLE `Users` (
|
||||
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`Username` varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户登录账户',
|
||||
`Password` varchar(512) NOT NULL DEFAULT 'default' COMMENT '密码',
|
||||
`UserDisplayName` varchar(512) NOT NULL DEFAULT 'default' COMMENT '用户名称',
|
||||
`Email` varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址',
|
||||
`Enabled` tinyint(4) DEFAULT NULL COMMENT '是否有效',
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `UK_Username` (`Username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
|
||||
|
||||
# Dump of table Authorities
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `Authorities`;
|
||||
|
||||
CREATE TABLE `Authorities` (
|
||||
`Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
`Username` varchar(64) NOT NULL,
|
||||
`Authority` varchar(50) NOT NULL,
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
# Config
|
||||
# ------------------------------------------------------------
|
||||
INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`)
|
||||
VALUES
|
||||
('apollo.portal.envs', 'dev', '可支持的环境列表'),
|
||||
('organizations', '[{\"orgId\":\"TEST1\",\"orgName\":\"样例部门1\"},{\"orgId\":\"TEST2\",\"orgName\":\"样例部门2\"}]', '部门列表'),
|
||||
('superAdmin', 'apollo', 'Portal超级管理员'),
|
||||
('api.readTimeout', '10000', 'http接口read timeout'),
|
||||
('consumer.token.salt', 'someSalt', 'consumer token salt'),
|
||||
('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'),
|
||||
('configView.memberOnly.envs', 'dev', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'),
|
||||
('apollo.portal.meta.servers', '{}', '各环境Meta Service列表');
|
||||
|
||||
INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`)
|
||||
VALUES
|
||||
('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1);
|
||||
|
||||
INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user');
|
||||
|
||||
# Sample Data
|
||||
# ------------------------------------------------------------
|
||||
INSERT INTO `App` (`AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`)
|
||||
VALUES
|
||||
('SampleApp', 'Sample App', 'TEST1', '样例部门1', 'apollo', 'apollo@acme.com');
|
||||
|
||||
INSERT INTO `AppNamespace` (`Name`, `AppId`, `Format`, `IsPublic`, `Comment`)
|
||||
VALUES
|
||||
('application', 'SampleApp', 'properties', 0, 'default app namespace');
|
||||
|
||||
INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`)
|
||||
VALUES
|
||||
(1, 'CreateCluster', 'SampleApp'),
|
||||
(2, 'CreateNamespace', 'SampleApp'),
|
||||
(3, 'AssignRole', 'SampleApp'),
|
||||
(4, 'ModifyNamespace', 'SampleApp+application'),
|
||||
(5, 'ReleaseNamespace', 'SampleApp+application');
|
||||
|
||||
INSERT INTO `Role` (`Id`, `RoleName`)
|
||||
VALUES
|
||||
(1, 'Master+SampleApp'),
|
||||
(2, 'ModifyNamespace+SampleApp+application'),
|
||||
(3, 'ReleaseNamespace+SampleApp+application');
|
||||
|
||||
INSERT INTO `RolePermission` (`RoleId`, `PermissionId`)
|
||||
VALUES
|
||||
(1, 1),
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(2, 4),
|
||||
(3, 5);
|
||||
|
||||
INSERT INTO `UserRole` (`UserId`, `RoleId`)
|
||||
VALUES
|
||||
('apollo', 1),
|
||||
('apollo', 2),
|
||||
('apollo', 3);
|
||||
|
||||
-- spring session (https://github.com/spring-projects/spring-session/blob/faee8f1bdb8822a5653a81eba838dddf224d92d6/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql)
|
||||
CREATE TABLE SPRING_SESSION (
|
||||
PRIMARY_ID CHAR(36) NOT NULL,
|
||||
SESSION_ID CHAR(36) NOT NULL,
|
||||
CREATION_TIME BIGINT NOT NULL,
|
||||
LAST_ACCESS_TIME BIGINT NOT NULL,
|
||||
MAX_INACTIVE_INTERVAL INT NOT NULL,
|
||||
EXPIRY_TIME BIGINT NOT NULL,
|
||||
PRINCIPAL_NAME VARCHAR(100),
|
||||
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
|
||||
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
|
||||
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
|
||||
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
|
||||
|
||||
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
|
||||
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
|
||||
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
|
||||
ATTRIBUTE_BYTES BLOB NOT NULL,
|
||||
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
|
||||
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
|
||||
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
6
.github/workflows/before_script.sh
vendored
Normal file
6
.github/workflows/before_script.sh
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || if [ $? != 0 ]; then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi
|
||||
echo "gofmt check pass."
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
88
.github/workflows/ci-main.sh
vendored
Normal file
88
.github/workflows/ci-main.sh
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
coverage=$1
|
||||
|
||||
# find all path that contains go.mod.
|
||||
for file in `find . -name go.mod`; do
|
||||
dirpath=$(dirname $file)
|
||||
echo $dirpath
|
||||
|
||||
if [[ $file =~ "/testdata/" ]]; then
|
||||
echo "ignore testdata path $file"
|
||||
continue 1
|
||||
fi
|
||||
|
||||
# package kuhecm needs golang >= v1.19
|
||||
if [ "kubecm" = $(basename $dirpath) ]; then
|
||||
continue 1
|
||||
if ! go version|grep -qE "go1.19|go1.[2-9][0-9]"; then
|
||||
echo "ignore kubecm as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# package consul needs golang >= v1.19
|
||||
if [ "consul" = $(basename $dirpath) ]; then
|
||||
continue 1
|
||||
if ! go version|grep -qE "go1.19|go1.[2-9][0-9]"; then
|
||||
echo "ignore consul as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# package etcd needs golang >= v1.19
|
||||
if [ "etcd" = $(basename $dirpath) ]; then
|
||||
if ! go version|grep -qE "go1.19|go1.[2-9][0-9]"; then
|
||||
echo "ignore etcd as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# package polaris needs golang >= v1.19
|
||||
if [ "polaris" = $(basename $dirpath) ]; then
|
||||
if ! go version|grep -qE "go1.19|go1.[2-9][0-9]"; then
|
||||
echo "ignore polaris as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# package example needs golang >= v1.20
|
||||
if [ "example" = $(basename $dirpath) ]; then
|
||||
if ! go version|grep -qE "go1.[2-9][0-9]"; then
|
||||
echo "ignore example as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# package otlpgrpc needs golang >= v1.20
|
||||
if [ "otlpgrpc" = $(basename $dirpath) ]; then
|
||||
if ! go version|grep -qE "go1.[2-9][0-9]"; then
|
||||
echo "ignore otlpgrpc as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# package otlphttp needs golang >= v1.20
|
||||
if [ "otlphttp" = $(basename $dirpath) ]; then
|
||||
if ! go version|grep -qE "go1.[2-9][0-9]"; then
|
||||
echo "ignore otlphttp as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cd $dirpath
|
||||
go mod tidy
|
||||
go build ./...
|
||||
# check coverage
|
||||
if [ "${coverage}" = "coverage" ]; then
|
||||
go test ./... -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
|
||||
if grep -q "/gogf/gf/.*/v2" go.mod; then
|
||||
sed -i "s/gogf\/gf\(\/.*\)\/v2/gogf\/gf\/v2\1/g" coverage.out
|
||||
fi
|
||||
else
|
||||
go test ./... -race || exit 1
|
||||
fi
|
||||
|
||||
cd -
|
||||
done
|
||||
220
.github/workflows/ci-main.yml
vendored
Normal file
220
.github/workflows/ci-main.yml
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
# The main codes build and unit testing running workflow.
|
||||
name: GoFrame Main CI
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- personal/**
|
||||
- feature/**
|
||||
- enhance/**
|
||||
- fix/**
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- personal/**
|
||||
- feature/**
|
||||
- enhance/**
|
||||
- fix/**
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
TZ: "Asia/Shanghai"
|
||||
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
# Etcd service.
|
||||
# docker run -d --name etcd -p 2379:2379 -e ALLOW_NONE_AUTHENTICATION=yes loads/etcd:3.4.24
|
||||
etcd:
|
||||
image: loads/etcd:3.4.24
|
||||
env:
|
||||
ALLOW_NONE_AUTHENTICATION: yes
|
||||
ports:
|
||||
- 2379:2379
|
||||
|
||||
# Redis backend server.
|
||||
redis:
|
||||
image : loads/redis:7.0
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
|
||||
# MySQL backend server.
|
||||
mysql:
|
||||
image: loads/mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
# PostgreSQL backend server.
|
||||
# docker run -d --name postgres -p 5432:5432 \
|
||||
# -e POSTGRES_PASSWORD=12345678 -e POSTGRES_USER=postgres -e POSTGRES_DB=test \
|
||||
# -v postgres:/Users/john/Temp/postgresql/data loads/postgres:13
|
||||
postgres:
|
||||
image: loads/postgres:13
|
||||
env:
|
||||
POSTGRES_PASSWORD: 12345678
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: test
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- 5432:5432
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
# MSSQL backend server.
|
||||
# docker run -d --name mssql -p 1433:1433 \
|
||||
# -e ACCEPT_EULA=Y \
|
||||
# -e SA_PASSWORD=LoremIpsum86 \
|
||||
# -e MSSQL_DB=test \
|
||||
# -e MSSQL_USER=root \
|
||||
# -e MSSQL_PASSWORD=LoremIpsum86 \
|
||||
# loads/mssqldocker:14.0.3391.2
|
||||
mssql:
|
||||
image: loads/mssqldocker:14.0.3391.2
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: LoremIpsum86
|
||||
MSSQL_DB: test
|
||||
MSSQL_USER: root
|
||||
MSSQL_PASSWORD: LoremIpsum86
|
||||
ports:
|
||||
- 1433:1433
|
||||
options: >-
|
||||
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
|
||||
--health-start-period 10s
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
# ClickHouse backend server.
|
||||
# docker run -d --name clickhouse \
|
||||
# -p 9000:9000 -p 8123:8123 -p 9001:9001 \
|
||||
# loads/clickhouse-server:22.1.3.7
|
||||
clickhouse-server:
|
||||
image: loads/clickhouse-server:22.1.3.7
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 8123:8123
|
||||
- 9001:9001
|
||||
|
||||
# Polaris backend server.
|
||||
# docker run -d --name polaris -p 8090:8090 -p 8091:8091 -p 8093:8093 -p 9090:9090 -p 9091:9091 loads/polaris-server-standalone:1.11.2
|
||||
# docker run -d --name polaris -p 8090:8090 -p 8091:8091 -p 8093:8093 -p 9090:9090 -p 9091:9091 loads/polaris-standalone:v1.16.3
|
||||
polaris:
|
||||
image: loads/polaris-standalone:v1.17.2
|
||||
ports:
|
||||
- 8090:8090
|
||||
- 8091:8091
|
||||
- 8093:8093
|
||||
- 9090:9090
|
||||
- 9091:9091
|
||||
|
||||
# Oracle 11g server
|
||||
oracle-server:
|
||||
image: loads/oracle-xe-11g-r2:11.2.0
|
||||
env:
|
||||
ORACLE_ALLOW_REMOTE: true
|
||||
ORACLE_SID: XE
|
||||
ORACLE_DB_USER_NAME: system
|
||||
ORACLE_DB_PASSWORD: oracle
|
||||
ports:
|
||||
- 1521:1521
|
||||
|
||||
# dm8 server
|
||||
# docker run -d --name dm -p 5236:5236 loads/dm:v8.1.2.128_ent_x86_64_ctm_pack4
|
||||
dm-server:
|
||||
image: loads/dm:v8.1.2.128_ent_x86_64_ctm_pack4
|
||||
ports:
|
||||
- 5236:5236
|
||||
|
||||
zookeeper:
|
||||
image: loads/zookeeper:3.8
|
||||
ports:
|
||||
- 2181:2181
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ "1.18", "1.19", "1.20", "1.21" ]
|
||||
goarch: [ "386", "amd64" ]
|
||||
|
||||
steps:
|
||||
# TODO: szenius/set-timezone update to node16
|
||||
- name: Setup Timezone
|
||||
uses: szenius/set-timezone@v1.1
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Start Apollo Containers
|
||||
run: docker-compose -f ".github/workflows/apollo/docker-compose.yml" up -d --build
|
||||
|
||||
- name: Start Nacos Containers
|
||||
run: docker-compose -f ".github/workflows/nacos/docker-compose.yml" up -d --build
|
||||
|
||||
- name: Start Redis Cluster Containers
|
||||
run: docker-compose -f ".github/workflows/redis/docker-compose.yml" up -d --build
|
||||
|
||||
- name: Start Consul Containers
|
||||
run: docker-compose -f ".github/workflows/consul/docker-compose.yml" up -d --build
|
||||
|
||||
- name: Setup Golang ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
cache-dependency-path: '**/go.sum'
|
||||
|
||||
- name: Before Script
|
||||
run: bash .github/workflows/before_script.sh
|
||||
|
||||
- name: Build & Test
|
||||
if: ${{ (github.event_name == 'push' && github.ref != 'refs/heads/master') || github.event_name == 'pull_request' }}
|
||||
run: bash .github/workflows/ci-main.sh
|
||||
|
||||
- name: Build & Test & Coverage
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
run: bash .github/workflows/ci-main.sh coverage
|
||||
|
||||
- name: Stop Redis Cluster Containers
|
||||
run: docker-compose -f ".github/workflows/redis/docker-compose.yml" down
|
||||
|
||||
- name: Stop Apollo Containers
|
||||
run: docker-compose -f ".github/workflows/apollo/docker-compose.yml" down
|
||||
|
||||
- name: Stop Nacos Containers
|
||||
run: docker-compose -f ".github/workflows/nacos/docker-compose.yml" down
|
||||
|
||||
- name: Stop Consul Containers
|
||||
run: docker-compose -f ".github/workflows/consul/docker-compose.yml" down
|
||||
|
||||
- name: Report Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
with:
|
||||
flags: go-${{ matrix.go-version }}-${{ matrix.goarch }}
|
||||
27
.github/workflows/ci-sub.sh
vendored
Normal file
27
.github/workflows/ci-sub.sh
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
coverage=$1
|
||||
|
||||
# find all path that contains go.mod.
|
||||
for file in `find . -name go.mod`; do
|
||||
dirpath=$(dirname $file)
|
||||
echo $dirpath
|
||||
|
||||
# package kuhecm needs golang >= v1.19
|
||||
if [ "kubecm" = $(basename $dirpath) ]; then
|
||||
if ! go version|grep -qE "go1.19|go1.[2-9][0-9]"; then
|
||||
echo "ignore kubecm as go version: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
else
|
||||
continue 1
|
||||
fi
|
||||
|
||||
cd $dirpath
|
||||
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./... -race || exit 1
|
||||
|
||||
cd -
|
||||
done
|
||||
67
.github/workflows/ci-sub.yml
vendored
Normal file
67
.github/workflows/ci-sub.yml
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
# The sub codes build and unit testing running workflow.
|
||||
# It maintains the ci of unimportant packages.
|
||||
name: GoFrame Sub CI
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- personal/**
|
||||
- feature/**
|
||||
- enhance/**
|
||||
- fix/**
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- personal/**
|
||||
- feature/**
|
||||
- enhance/**
|
||||
- fix/**
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
TZ: "Asia/Shanghai"
|
||||
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ "1.18", "1.19", "1.20", "1.21" ]
|
||||
goarch: [ "386", "amd64" ]
|
||||
|
||||
steps:
|
||||
- name: Setup Timezone
|
||||
uses: szenius/set-timezone@v1.1
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Start Minikube
|
||||
uses: medyagh/setup-minikube@master
|
||||
|
||||
- name: Setup Golang ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
cache-dependency-path: '**/go.sum'
|
||||
|
||||
- name: Before Script
|
||||
run: bash .github/workflows/before_script.sh
|
||||
|
||||
- name: Build & Test
|
||||
run: bash .github/workflows/ci-sub.sh
|
||||
|
||||
|
||||
7
.github/workflows/consul/client.json
vendored
Normal file
7
.github/workflows/consul/client.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"node_name": "consul-client",
|
||||
"data_dir": "/consul/data",
|
||||
"retry_join":[
|
||||
"consul-server"
|
||||
]
|
||||
}
|
||||
31
.github/workflows/consul/docker-compose.yml
vendored
Normal file
31
.github/workflows/consul/docker-compose.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
|
||||
consul-server:
|
||||
image: loads/consul:1.15
|
||||
container_name: consul-server
|
||||
restart: always
|
||||
volumes:
|
||||
- ./server.json:/consul/config/server.json:ro
|
||||
networks:
|
||||
- consul
|
||||
ports:
|
||||
- "8500:8500"
|
||||
- "8600:8600/tcp"
|
||||
- "8600:8600/udp"
|
||||
command: "agent"
|
||||
|
||||
consul-client:
|
||||
image: loads/consul:1.15
|
||||
container_name: consul-client
|
||||
restart: always
|
||||
volumes:
|
||||
- ./client.json:/consul/config/client.json:ro
|
||||
networks:
|
||||
- consul
|
||||
command: "agent"
|
||||
|
||||
networks:
|
||||
consul:
|
||||
driver: bridge
|
||||
12
.github/workflows/consul/server.json
vendored
Normal file
12
.github/workflows/consul/server.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"node_name": "consul-server",
|
||||
"server": true,
|
||||
"bootstrap" : true,
|
||||
"ui_config": {
|
||||
"enabled" : true
|
||||
},
|
||||
"data_dir": "/consul/data",
|
||||
"addresses": {
|
||||
"http" : "0.0.0.0"
|
||||
}
|
||||
}
|
||||
149
.github/workflows/gf.yml
vendored
149
.github/workflows/gf.yml
vendored
@ -1,149 +0,0 @@
|
||||
name: GoFrame CI
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
|
||||
env:
|
||||
GF_DEBUG: 0
|
||||
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
redis:
|
||||
image : redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
postgres:
|
||||
image: postgres:13
|
||||
env:
|
||||
POSTGRES_PASSWORD: 12345678
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_DB: test
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- 5432:5432
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
mssql:
|
||||
image: mcmoe/mssqldocker:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: LoremIpsum86
|
||||
MSSQL_DB: test
|
||||
MSSQL_USER: root
|
||||
MSSQL_PASSWORD: LoremIpsum86
|
||||
ports:
|
||||
- 1433:1433
|
||||
options: >-
|
||||
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
|
||||
--health-start-period 10s
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
|
||||
# strategy set
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.15", "1.16", "1.17"]
|
||||
|
||||
|
||||
steps:
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Before Script
|
||||
run: |
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || if [ $? != 0 ]; then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi
|
||||
echo "gofmt check pass."
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: CLI Build & Test
|
||||
run: |
|
||||
cd cmd/gf
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
|
||||
- name: Example Build & Test
|
||||
run: |
|
||||
cd example
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
|
||||
- name: Contrib Build & Test
|
||||
run: |
|
||||
cd contrib
|
||||
for file in `find . -name go.mod`; do
|
||||
path=$(dirname $file)
|
||||
# Ignore oracle database driver build&test.
|
||||
if [ "oracle" = $(basename $path) ]; then
|
||||
continue 1
|
||||
fi
|
||||
cd $path
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./...
|
||||
cd -
|
||||
done
|
||||
|
||||
- name: Run i386 Arch Test
|
||||
run: |
|
||||
GOARCH=386 go test -v ./... || exit 1
|
||||
|
||||
- name: Run amd64 Arch Test
|
||||
run: |
|
||||
GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Report Coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: go-${{ matrix.go }}
|
||||
|
||||
28
.github/workflows/gitee-sync.yml
vendored
Normal file
28
.github/workflows/gitee-sync.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
name: Sync to Gitee
|
||||
jobs:
|
||||
run:
|
||||
name: Run
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
- name: Mirror Github to Gitee
|
||||
uses: Yikun/hub-mirror-action@v1.2
|
||||
with:
|
||||
src: github/gogf
|
||||
dst: gitee/johng
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
src_account_type: org
|
||||
dst_account_type: user
|
||||
timeout: 600
|
||||
debug: true
|
||||
force_update: true
|
||||
static_list: "gf"
|
||||
54
.github/workflows/golangci-lint.yml
vendored
Normal file
54
.github/workflows/golangci-lint.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
# Tencent is pleased to support the open source community by making Polaris available.
|
||||
#
|
||||
# Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
#
|
||||
# Licensed under the BSD 3-Clause License (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://opensource.org/licenses/BSD-3-Clause
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
name: GolangCI-Lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- personal/**
|
||||
- feature/**
|
||||
- enhance/**
|
||||
- fix/**
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- personal/**
|
||||
- feature/**
|
||||
- enhance/**
|
||||
- fix/**
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ '1.18','1.19','1.20','1.21' ]
|
||||
name: golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Golang ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.52.2
|
||||
args: --timeout 3m0s
|
||||
28
.github/workflows/issue-check-inactive.yml
vendored
Normal file
28
.github/workflows/issue-check-inactive.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# 规则描述:每天凌晨3点(GMT+8)执行一次,将最近7天没有活跃且非BUG的ISSUE设置标签:inactive
|
||||
name: Issue Check Inactive
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 19 * * *"
|
||||
|
||||
env: # 设置环境变量
|
||||
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
issue-check-inactive:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
# pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: check-inactive
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'check-inactive'
|
||||
inactive-label: 'inactive'
|
||||
inactive-day: 7
|
||||
issue-state: open
|
||||
exclude-labels: 'bug,planned,$exclude-empty'
|
||||
23
.github/workflows/issue-close-inactive.yml
vendored
Normal file
23
.github/workflows/issue-close-inactive.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# 规则描述:每天凌晨 4 点 (GMT+8) 执行一次,将最近 30 天没有活跃且非 BUG 的 ISSUE 关闭
|
||||
name: Issue Close Inactive
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 20 * * *"
|
||||
|
||||
env: # 设置环境变量
|
||||
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: need close
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: "close-issues"
|
||||
# token: ${{ secrets.GF_TOKEN }}
|
||||
labels: 'inactive'
|
||||
inactive-day: 30
|
||||
exclude-labels: 'bug,$exclude-empty'
|
||||
close-reason: 'not active'
|
||||
25
.github/workflows/issue-labeled.yml
vendored
Normal file
25
.github/workflows/issue-labeled.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
## 规则描述:当 issue 被标记为 help wanted 时,增加评论
|
||||
|
||||
name: Issue Labeled
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
env: # 设置环境变量
|
||||
TZ: Asia/Shanghai # 时区(设置时区可使页面中的`最近更新时间`使用时区时间)
|
||||
|
||||
jobs:
|
||||
reply-labeled:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: contribution welcome
|
||||
if: github.event.label.name == 'help wanted'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: "create-comment, remove-labels"
|
||||
# token: ${{ secrets.GF_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}. We like your proposal/feedback and would appreciate a contribution via a Pull Request by you or another community member. We thank you in advance for your contribution and are looking forward to reviewing it!
|
||||
你好 @${{ github.event.issue.user.login }}。我们喜欢您的提案/反馈,并希望您或其他社区成员通过拉取请求做出贡献。我们提前感谢您的贡献,并期待对其进行审查。
|
||||
29
.github/workflows/issue-remove-inactive.yml
vendored
Normal file
29
.github/workflows/issue-remove-inactive.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# 规则描述:在 issue 没有活跃且尚未被关闭期间,若 issue 作者更新或评论该 ISSUE,则移除其 inactive 标签
|
||||
name: Issue Remove Inactive
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [edited]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
env: # 设置环境变量
|
||||
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
issue-remove-inactive:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
# pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: remove inactive
|
||||
if: github.event.issue.state == 'open'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: 'inactive'
|
||||
29
.github/workflows/issue-remove-need-more-details.yml
vendored
Normal file
29
.github/workflows/issue-remove-need-more-details.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# 规则描述:将需要提供更多细节且暂未关闭的 issue,在 issue 作者评论后,移除 need more details 标签
|
||||
name: Issue Remove Need More Details
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [edited]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
env: # 设置环境变量
|
||||
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
issue-remove-need-more-details:
|
||||
permissions:
|
||||
issues: write # for actions-cool/issues-helper to update issues
|
||||
# pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: remove need more details
|
||||
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: 'need more details'
|
||||
19
.github/workflows/issue-translator.yml
vendored
Normal file
19
.github/workflows/issue-translator.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# https://github.com/usthe/issues-translate-action
|
||||
name: 'Issue Translator'
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
IS_MODIFY_TITLE: true
|
||||
# not require, default false. Decide whether to modify the issue title
|
||||
# if true, the robot account @Issues-translate-bot must have modification permissions,
|
||||
# invite @Issues-translate-bot to your project or use your custom bot.
|
||||
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
|
||||
24
.github/workflows/nacos/docker-compose.yml
vendored
Normal file
24
.github/workflows/nacos/docker-compose.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
nacos:
|
||||
image: loads/nacos-server:v2.1.2
|
||||
container_name: nacos
|
||||
env_file:
|
||||
- ./env/nacos.env
|
||||
ports:
|
||||
- "8848:8848"
|
||||
- "9848:9848"
|
||||
- "9555:9555"
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl" ,"http://localhost:8848/nacos" ]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
|
||||
initializer:
|
||||
image: loads/curl:latest
|
||||
depends_on:
|
||||
nacos:
|
||||
condition: service_healthy
|
||||
command: [ "sh", "-c", "curl -X POST 'http://nacos:8848/nacos/v1/cs/configs?dataId=config.toml&group=test&content=%5Bserver%5D%0A%09address%3D%22%3A8000%22'" ]
|
||||
2
.github/workflows/nacos/env/nacos.env
vendored
Normal file
2
.github/workflows/nacos/env/nacos.env
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
PREFER_HOST_MODE=hostname
|
||||
MODE=standalone
|
||||
95
.github/workflows/redis/docker-compose.yml
vendored
Normal file
95
.github/workflows/redis/docker-compose.yml
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
version: "2"
|
||||
|
||||
services:
|
||||
redis-master:
|
||||
container_name: redis-master
|
||||
image: "loads/redis:7.0-sentinel"
|
||||
environment:
|
||||
- REDIS_REPLICATION_MODE=master
|
||||
- REDIS_PASSWORD=111111
|
||||
ports:
|
||||
- 6380:6379
|
||||
|
||||
redis-slave1:
|
||||
container_name: redis-slave1
|
||||
image: "loads/redis:7.0-sentinel"
|
||||
environment:
|
||||
- REDIS_REPLICATION_MODE=slave
|
||||
- REDIS_MASTER_HOST=redis-master
|
||||
- REDIS_MASTER_PASSWORD=111111
|
||||
- REDIS_PASSWORD=111111
|
||||
ports:
|
||||
- 6381:6379
|
||||
depends_on:
|
||||
- redis-master
|
||||
links:
|
||||
- redis-master
|
||||
|
||||
redis-slave2:
|
||||
container_name: redis-slave2
|
||||
image: "loads/redis:7.0-sentinel"
|
||||
environment:
|
||||
- REDIS_REPLICATION_MODE=slave
|
||||
- REDIS_MASTER_HOST=redis-master
|
||||
- REDIS_MASTER_PASSWORD=111111
|
||||
- REDIS_PASSWORD=111111
|
||||
ports:
|
||||
- 6382:6379
|
||||
depends_on:
|
||||
- redis-master
|
||||
links:
|
||||
- redis-master
|
||||
|
||||
redis-sentinel-1:
|
||||
container_name: redis-sentinel-1
|
||||
image: "loads/redis-sentinel:7.0"
|
||||
environment:
|
||||
- REDIS_MASTER_HOST=redis-master
|
||||
- REDIS_MASTER_PORT_NUMBER=6379
|
||||
- REDIS_MASTER_PASSWORD=111111
|
||||
depends_on:
|
||||
- redis-master
|
||||
- redis-slave1
|
||||
- redis-slave2
|
||||
ports:
|
||||
- 26379:26379
|
||||
links:
|
||||
- redis-master
|
||||
- redis-slave1
|
||||
- redis-slave2
|
||||
|
||||
redis-sentinel-2:
|
||||
container_name: redis-sentinel-2
|
||||
image: "loads/redis-sentinel:7.0"
|
||||
environment:
|
||||
- REDIS_MASTER_HOST=redis-master
|
||||
- REDIS_MASTER_PORT_NUMBER=6379
|
||||
- REDIS_MASTER_PASSWORD=111111
|
||||
depends_on:
|
||||
- redis-master
|
||||
- redis-slave1
|
||||
- redis-slave2
|
||||
links:
|
||||
- redis-master
|
||||
- redis-slave1
|
||||
- redis-slave2
|
||||
ports:
|
||||
- 26380:26379
|
||||
|
||||
redis-sentinel-3:
|
||||
container_name: redis-sentinel-3
|
||||
image: "loads/redis-sentinel:7.0"
|
||||
environment:
|
||||
- REDIS_MASTER_HOST=redis-master
|
||||
- REDIS_MASTER_PORT_NUMBER=6379
|
||||
- REDIS_MASTER_PASSWORD=111111
|
||||
depends_on:
|
||||
- redis-master
|
||||
- redis-slave1
|
||||
- redis-slave2
|
||||
ports:
|
||||
- 26381:26379
|
||||
links:
|
||||
- redis-master
|
||||
- redis-slave1
|
||||
- redis-slave2
|
||||
@ -1,4 +1,4 @@
|
||||
name: GoFrame CLI Build Release
|
||||
name: GoFrame Release
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -16,12 +16,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Github Code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set Up Golang Environment
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.20.8
|
||||
|
||||
- name: Build CLI Binary
|
||||
run: |
|
||||
@ -47,18 +47,18 @@ jobs:
|
||||
|
||||
- name: Create Github Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: GoFrame CLI Release ${{ github.ref }}
|
||||
name: GoFrame Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: alexellis/upload-assets@0.2.2
|
||||
uses: alexellis/upload-assets@0.4.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
39
.github/workflows/tag.yml
vendored
Normal file
39
.github/workflows/tag.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: GoFrame AutoCreating SubMod Tags
|
||||
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
env:
|
||||
TZ: Asia/Shanghai
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Auto Creating Tags
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Github Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Auto Creating Tags For Contrib Packages
|
||||
run: |
|
||||
git config --global user.email "tagrobot@goframe.org"
|
||||
git config --global user.name "TagRobot"
|
||||
|
||||
# auto create tags for contrib packages.
|
||||
for file in `find contrib -name go.mod`; do
|
||||
tag=$(dirname $file)/$GITHUB_REF_NAME
|
||||
git tag $tag
|
||||
git push origin $tag
|
||||
done
|
||||
|
||||
# auto create tag for cli tool
|
||||
for file in `find cmd -name go.mod`; do
|
||||
tag=$(dirname $file)/$GITHUB_REF_NAME
|
||||
git tag $tag
|
||||
git push origin $tag
|
||||
done
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@ -7,12 +7,13 @@
|
||||
.settings/
|
||||
.vscode/
|
||||
vendor/
|
||||
composer.lock
|
||||
gitpush.sh
|
||||
pkg/
|
||||
bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.test/
|
||||
main
|
||||
gf
|
||||
cmd/gf/main
|
||||
cmd/gf/gf
|
||||
temp/
|
||||
go.work
|
||||
go.work.sum
|
||||
!cmd/gf/go.work
|
||||
1159
.golangci.yml
1159
.golangci.yml
File diff suppressed because it is too large
Load Diff
82
.set_version.sh
Executable file
82
.set_version.sh
Executable file
@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Parameter exception, please execute in the format of $0 [directory] [version number]"
|
||||
echo "PS:$0 ./ v2.4.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$1" ]; then
|
||||
echo "Error: Directory does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$2" != v* ]]; then
|
||||
echo "Error: Version number must start with v"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
workdir=.
|
||||
newVersion=$2
|
||||
echo "Prepare to replace the GF library version numbers in all go.mod files in the ${workdir} directory with ${newVersion}"
|
||||
|
||||
# check find command support or not
|
||||
output=$(find "${workdir}" -name go.mod 2>&1)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: please use bash or zsh to run!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ true ]]; then
|
||||
echo "package gf" > version.go
|
||||
echo "" >> version.go
|
||||
echo "const (" >> version.go
|
||||
echo -e "\t// VERSION is the current GoFrame version." >> version.go
|
||||
echo -e "\tVERSION = \"${newVersion}\"" >> version.go
|
||||
echo ")" >> version.go
|
||||
fi
|
||||
|
||||
if [ -f "go.work" ]; then
|
||||
mv go.work go.work.version.bak
|
||||
echo "Back up the go.work file to avoid affecting the upgrade"
|
||||
fi
|
||||
|
||||
for file in `find ${workdir} -name go.mod`; do
|
||||
goModPath=$(dirname $file)
|
||||
echo ""
|
||||
echo "processing dir: $goModPath"
|
||||
cd $goModPath
|
||||
if [ $goModPath = "./cmd/gf" ]; then
|
||||
mv go.work go.work.version.bak
|
||||
go mod edit -replace github.com/gogf/gf/v2=../../
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/clickhouse/v2=../../contrib/drivers/clickhouse
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/mssql/v2=../../contrib/drivers/mssql
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/mysql/v2=../../contrib/drivers/mysql
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/oracle/v2=../../contrib/drivers/oracle
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/pgsql/v2=../../contrib/drivers/pgsql
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/sqlite/v2=../../contrib/drivers/sqlite
|
||||
# else
|
||||
# cd -
|
||||
# continue 1
|
||||
fi
|
||||
go mod tidy
|
||||
# Upgrading only GF related libraries, sometimes even if a version number is specified, it may not be possible to successfully upgrade. Please confirm before submitting the code
|
||||
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf"
|
||||
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf" | xargs -L1 go get -v
|
||||
go mod tidy
|
||||
if [ $goModPath = "./cmd/gf" ]; then
|
||||
go mod edit -dropreplace github.com/gogf/gf/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/clickhouse/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/mssql/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/mysql/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/oracle/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/pgsql/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/sqlite/v2
|
||||
mv go.work.version.bak go.work
|
||||
fi
|
||||
cd -
|
||||
done
|
||||
|
||||
if [ -f "go.work.version.bak" ]; then
|
||||
mv go.work.version.bak go.work
|
||||
echo "Restore the go.work file"
|
||||
fi
|
||||
26
Makefile
Normal file
26
Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
$(eval files=$(shell find . -name go.mod))
|
||||
@set -e; \
|
||||
for file in ${files}; do \
|
||||
goModPath=$$(dirname $$file); \
|
||||
cd $$goModPath; \
|
||||
go mod tidy; \
|
||||
cd -; \
|
||||
done
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
# make version to=v2.4.0
|
||||
.PHONY: version
|
||||
version:
|
||||
@set -e; \
|
||||
newVersion=$(to); \
|
||||
./.set_version.sh ./ $$newVersion; \
|
||||
echo "make version to=$(to) done"
|
||||
|
||||
|
||||
55
README.MD
55
README.MD
@ -1,18 +1,20 @@
|
||||
# GoFrame
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/statics/image/gf-head-large.png" width="100"/>
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/actions/workflows/gf.yml)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/statics/image/logo2.png?v=1" width="300"/>
|
||||
|
||||
[](https://pkg.go.dev/github.com/gogf/gf/v2)
|
||||
[](https://github.com/gogf/gf/actions/workflows/ci-main.yml)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf/v2)
|
||||
[](https://codecov.io/gh/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
</div>
|
||||
|
||||
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework of Golang.
|
||||
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework of Golang.
|
||||
|
||||
# Features
|
||||
|
||||
- modular, loosely coupled design
|
||||
- rich components, out-of-the-box
|
||||
- automatic codes generating for efficiency
|
||||
@ -27,34 +29,31 @@
|
||||
- much, much more...ready to explore?
|
||||
|
||||
# Installation
|
||||
|
||||
Enter your repo. directory and execute following command:
|
||||
|
||||
## primary module
|
||||
|
||||
```bash
|
||||
go get -u -v github.com/gogf/gf/v2
|
||||
```
|
||||
|
||||
## cli tool
|
||||
|
||||
```bash
|
||||
go install github.com/gogf/gf/cmd/gf/v2
|
||||
go install github.com/gogf/gf/cmd/gf/v2@latest
|
||||
```
|
||||
|
||||
# Limitation
|
||||
```
|
||||
golang version >= 1.15
|
||||
```
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
```
|
||||
golang version >= 1.18
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
* Chinese Official Site(中文官网): [https://goframe.org](https://goframe.org/display/gf)
|
||||
* GoDoc API: [https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
|
||||
|
||||
- Chinese Official Site(中文官网): [https://goframe.org](https://goframe.org/display/gf)
|
||||
- GoDoc API: [https://pkg.go.dev/github.com/gogf/gf/v2](https://pkg.go.dev/github.com/gogf/gf/v2)
|
||||
|
||||
# License
|
||||
|
||||
@ -76,32 +75,20 @@ golang version >= 1.15
|
||||
|
||||
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://goframe.org/pages/viewpage.action?pageId=1114415).
|
||||
|
||||
|
||||
# Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://contributors-img.web.app/image?repo=gogf/gf" /></a>
|
||||
|
||||
|
||||
# Donators
|
||||
|
||||
If you love `GoFrame`, why not [buy developer a cup of coffee](https://goframe.org/pages/viewpage.action?pageId=1115633)?
|
||||
|
||||
# Sponsors
|
||||
|
||||
We appreciate any kind of sponsorship for `GoFrame` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/download/thumbnails/1114119/jetbrains.png" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://goframe.org/download/attachments/1114119/atlassian.jpg" height="120" alt="Atlassian"/></a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -2,39 +2,56 @@
|
||||
|
||||
`gf` is a powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience.
|
||||
|
||||
|
||||
## 1. Install
|
||||
|
||||
## 1) PreCompiled Binary
|
||||
You can also install `gf` tool using pre-built binaries: https://github.com/gogf/gf/releases
|
||||
|
||||
You can also install `gf` tool using pre-built binaries: <https://github.com/gogf/gf/releases>
|
||||
|
||||
1. `Mac` & `Linux`
|
||||
```shell
|
||||
|
||||
```shell
|
||||
wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH) && chmod +x gf && ./gf install -y && rm ./gf
|
||||
```
|
||||
```
|
||||
|
||||
> If you're using `zsh`, you might need rename your alias by command `alias gf=gf` to resolve the conflicts between `gf` and `git fetch`.
|
||||
|
||||
2. `Windows`
|
||||
Manually download, execute in command line it and then follow the instruction.
|
||||
|
||||
Manually download, execute it and then follow the instruction.
|
||||
3. Database support
|
||||
|
||||
3. Database `sqlite` and `oracle` are not support in `gf gen` command in default as it needs `cgo` and `gcc`, you can manually make some changes to the source codes and do the building.
|
||||
| DB | builtin support | remarks |
|
||||
|:----------:|:---------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||
| mysql | yes | - |
|
||||
| mariadb | yes | - |
|
||||
| tidb | yes | - |
|
||||
| mssql | yes | - |
|
||||
| oracle | yes | - |
|
||||
| pgsql | yes | - |
|
||||
| sqlite | yes | - |
|
||||
| sqlitecgo | no | to support sqlite database on 32bit architecture systems, manually add package import to the [source codes](./internal/cmd/cmd_gen_dao.go) and do the building. |
|
||||
| clickhouse | no | manually add package import to the [source codes](./internal/cmd/cmd_gen_dao.go) and do the building. |
|
||||
| dm | no | manually add package import to the [source codes](./internal/cmd/cmd_gen_dao.go) and do the building. |
|
||||
|
||||
## 2) Manually Install
|
||||
|
||||
```shell
|
||||
git clone https://github.com/gogf/gf && cd cmd/gf && go install
|
||||
```
|
||||
|
||||
```shell
|
||||
go install github.com/gogf/gf/cmd/gf/v2@latest # latest version
|
||||
go install github.com/gogf/gf/cmd/gf/v2@v2.5.5 # certain version(should be >= v2.5.5)
|
||||
```
|
||||
|
||||
## 2. Commands
|
||||
|
||||
```html
|
||||
$ gf
|
||||
USAGE
|
||||
gf COMMAND [OPTION]
|
||||
|
||||
COMMAND
|
||||
up upgrade GoFrame version/tool to latest one in current project
|
||||
env show current Golang environment variables
|
||||
fix auto fixing codes after upgrading to new GoFrame version
|
||||
run running go codes with hot-compiled-like feature
|
||||
gen automatically generate go files for dao/do/entity/pb/pbentity
|
||||
tpl template parsing and building commands
|
||||
@ -60,10 +77,3 @@ ADDITIONAL
|
||||
### 1). Command `gf run` returns `pipe: too many open files`
|
||||
|
||||
Please use `ulimit -n 65535` to enlarge your system configuration for max open files for current terminal shell session, and then `gf run`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
114
cmd/gf/gfcmd/gfcmd.go
Normal file
114
cmd/gf/gfcmd/gfcmd.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gfcmd
|
||||
|
||||
import (
|
||||
_ "github.com/gogf/gf/cmd/gf/v2/internal/packed"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
const (
|
||||
cliFolderName = `hack`
|
||||
)
|
||||
|
||||
// Command manages the CLI command of `gf`.
|
||||
// This struct can be globally accessible and extended with custom struct.
|
||||
type Command struct {
|
||||
*gcmd.Command
|
||||
}
|
||||
|
||||
// Run starts running the command according the command line arguments and options.
|
||||
func (c *Command) Run(ctx context.Context) {
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
if err, ok := exception.(error); ok {
|
||||
mlog.Print(err.Error())
|
||||
} else {
|
||||
panic(gerror.NewCodef(gcode.CodeInternalPanic, "%+v", exception))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// CLI configuration, using the `hack/config.yaml` in priority.
|
||||
if path, _ := gfile.Search(cliFolderName); path != "" {
|
||||
if adapter, ok := g.Cfg().GetAdapter().(*gcfg.AdapterFile); ok {
|
||||
if err := adapter.SetPath(path); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// zsh alias "git fetch" conflicts checks.
|
||||
handleZshAlias()
|
||||
|
||||
// -y option checks.
|
||||
allyes.Init()
|
||||
|
||||
// just run.
|
||||
if err := c.RunWithError(ctx); err != nil {
|
||||
// Exit with error message and exit code 1.
|
||||
// It is very important to exit the command process with code 1.
|
||||
mlog.Fatalf(`%+v`, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCommand retrieves and returns the root command of CLI `gf`.
|
||||
func GetCommand(ctx context.Context) (*Command, error) {
|
||||
root, err := gcmd.NewFromObject(cmd.GF)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = root.AddObject(
|
||||
cmd.Up,
|
||||
cmd.Env,
|
||||
cmd.Fix,
|
||||
cmd.Run,
|
||||
cmd.Gen,
|
||||
cmd.Tpl,
|
||||
cmd.Init,
|
||||
cmd.Pack,
|
||||
cmd.Build,
|
||||
cmd.Docker,
|
||||
cmd.Install,
|
||||
cmd.Version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
command := &Command{
|
||||
root,
|
||||
}
|
||||
return command, nil
|
||||
}
|
||||
|
||||
// zsh alias "git fetch" conflicts checks.
|
||||
func handleZshAlias() {
|
||||
if home, err := gfile.Home(); err == nil {
|
||||
zshPath := gfile.Join(home, ".zshrc")
|
||||
if gfile.Exists(zshPath) {
|
||||
var (
|
||||
aliasCommand = `alias gf=gf`
|
||||
content = gfile.GetContents(zshPath)
|
||||
)
|
||||
if !gstr.Contains(content, aliasCommand) {
|
||||
_ = gfile.PutContentsAppend(zshPath, "\n"+aliasCommand+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,60 @@
|
||||
module github.com/gogf/gf/cmd/gf/v2
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.0-rc2
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.0.0-rc2
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.0.0-rc2
|
||||
github.com/gogf/gf/v2 v2.0.0
|
||||
github.com/longbridgeapp/sqlparser v0.3.1 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.5.7
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.5.7
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.5.7
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.5.7
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.5.7
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.5.7
|
||||
github.com/gogf/gf/v2 v2.5.7
|
||||
github.com/minio/selfupdate v0.6.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 // indirect
|
||||
golang.org/x/mod v0.9.0
|
||||
golang.org/x/tools v0.7.0
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 => ../../contrib/drivers/mssql/
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 => ../../contrib/drivers/pgsql/
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 => ../../contrib/drivers/sqlite/
|
||||
github.com/gogf/gf/v2 => ../../
|
||||
require (
|
||||
aead.dev/minisign v0.2.0 // indirect
|
||||
github.com/BurntSushi/toml v1.2.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/paulmach/orb v0.7.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.7.10 // indirect
|
||||
go.opentelemetry.io/otel v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
||||
276
cmd/gf/go.sum
276
cmd/gf/go.sum
@ -1,164 +1,214 @@
|
||||
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
|
||||
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15 h1:lLAZliqrZEygkxosLaW1qHyeTb4Ho7fVCZ0WKCpLocU=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15/go.mod h1:Z21o82zD8FFqefOQDg93c0XITlxGbTsWQuRm588Azkk=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
|
||||
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.5.6 h1:yziPSf9AycEWphv9WiNjcRAVPOJtUauMMvP6pHQB4jY=
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.5.6/go.mod h1:yOlpwhFXgW+P2sf4goA20PUtxdVLliBx4dJRyJeOtto=
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.5.6 h1:LGQIe5IvYVr4hZ/vUAFiqWssxE7QeILyVPJ9swo1Cmk=
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.5.6/go.mod h1:EcF8v8jqCV61/YqN6DXxdo3kh8waGmEj6WpFqbLkkrM=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.5.6 h1:oR9F4LVoKa/fjf/o6Y/CQRNiYy35Bszo07WwvMWYMxo=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.5.6/go.mod h1:gvHSRqCpv2c+N0gDHsEldHgU/yM9tcCBdIEKZ32/TaE=
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.5.6 h1:3Y3lEoO9SoG1AmfaKjgTsDt93+T2q/qTMog8wBvIIGM=
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.5.6/go.mod h1:cR3lFoU6ZtSaMQ3DpCJwWnYW6EvHPYGGeqv/kzgH4gw=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.5.6 h1:0WHVzqITqIBu/NNPXt3tN2eiWAGiNjs9sg6wh+WbUvY=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.5.6/go.mod h1:qZCTNQ0n2gHcuBwM9wUl3pelync3xK0gTnChJZD6f0I=
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.5.6 h1:6clfLvFoHXHdw+skmXg4yxw+cLwgAG8gRiS/6f9Y9Xc=
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.5.6/go.mod h1:QV6Rrj+4G4OaJVkP9XXRZ1LWL+ls6qH7ebeMcxsulqA=
|
||||
github.com/gogf/gf/v2 v2.5.6 h1:a1UK1yUP3s+l+vPxmV91+8gTarAP9b1IEOw0W7LNl6E=
|
||||
github.com/gogf/gf/v2 v2.5.6/go.mod h1:17K/gBYrp0bHGC3XYC7bSPoywmZ6MrZHrZakTfh4eIQ=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/longbridgeapp/sqlparser v0.3.1 h1:iWOZWGIFgQrJRgobLXUNJdvqGRpbVXkyKUKUA5CNJBE=
|
||||
github.com/longbridgeapp/sqlparser v0.3.1/go.mod h1:GIHaUq8zvYyHLCLMJJykx1CdM6LHtkUih/QaJXySSx4=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
|
||||
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
|
||||
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
|
||||
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sijms/go-ora/v2 v2.7.10 h1:GSLdj0PYYgSndhsnm7b6p32OqgnwnUZSkFb3j+htfhI=
|
||||
github.com/sijms/go-ora/v2 v2.7.10/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ=
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
|
||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0 h1:BNPMYUONPNbLneMttKSjQhOTlFLOD9U22HNG1KrIN2Y=
|
||||
go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM=
|
||||
go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
|
||||
go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
|
||||
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
|
||||
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
|
||||
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
|
||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
|
||||
20
cmd/gf/go.work
Normal file
20
cmd/gf/go.work
Normal file
@ -0,0 +1,20 @@
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
./
|
||||
)
|
||||
|
||||
// =====================================================================================================
|
||||
// NOTE:
|
||||
// Please update associated commands in ../../.set_version.sh if any of the follows replacements change.
|
||||
// =====================================================================================================
|
||||
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 => ../../contrib/drivers/clickhouse
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 => ../../contrib/drivers/mssql
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 => ../../contrib/drivers/mysql
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 => ../../contrib/drivers/oracle
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 => ../../contrib/drivers/pgsql
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 => ../../contrib/drivers/sqlite
|
||||
github.com/gogf/gf/v2 => ../../
|
||||
)
|
||||
@ -1,14 +1,22 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -38,6 +46,7 @@ type cGFInput struct {
|
||||
Version bool `short:"v" name:"version" brief:"show version information of current binary" orphan:"true"`
|
||||
Debug bool `short:"d" name:"debug" brief:"show internal detailed debugging information" orphan:"true"`
|
||||
}
|
||||
|
||||
type cGFOutput struct{}
|
||||
|
||||
func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error) {
|
||||
@ -46,18 +55,24 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error)
|
||||
_, err = Version.Index(ctx, cVersionInput{})
|
||||
return
|
||||
}
|
||||
|
||||
answer := "n"
|
||||
// No argument or option, do installation checks.
|
||||
if !service.Install.IsInstalled() {
|
||||
if data, isInstalled := service.Install.IsInstalled(); !isInstalled {
|
||||
mlog.Print("hi, it seams it's the first time you installing gf cli.")
|
||||
s := gcmd.Scanf("do you want to install gf binary to your system? [y/n]: ")
|
||||
if strings.EqualFold(s, "y") {
|
||||
if err = service.Install.Run(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
answer = gcmd.Scanf("do you want to install gf(%s) binary to your system? [y/n]: ", gf.VERSION)
|
||||
} else if !data.IsSelf {
|
||||
mlog.Print("hi, you have installed gf cli.")
|
||||
answer = gcmd.Scanf("do you want to install gf(%s) binary to your system? [y/n]: ", gf.VERSION)
|
||||
}
|
||||
if strings.EqualFold(answer, "y") {
|
||||
if err = service.Install.Run(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
return
|
||||
}
|
||||
|
||||
// Print help content.
|
||||
gcmd.CommandFromCtx(ctx).Print()
|
||||
return
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@ -9,9 +15,9 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/encoding/gbase64"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@ -20,12 +26,14 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
Build = cBuild{
|
||||
nodeNameInConfigFile: "gfcli.build",
|
||||
packedGoFileName: "build_pack_data.go",
|
||||
packedGoFileName: "internal/packed/build_pack_data.go",
|
||||
}
|
||||
)
|
||||
|
||||
@ -39,7 +47,7 @@ const (
|
||||
cBuildBrief = `cross-building go project for lots of platforms`
|
||||
cBuildEg = `
|
||||
gf build main.go
|
||||
gf build main.go --pack public,template
|
||||
gf build main.go --ps public,template
|
||||
gf build main.go --cgo
|
||||
gf build main.go -m none
|
||||
gf build main.go -n my-app -a all -s all
|
||||
@ -108,27 +116,35 @@ func init() {
|
||||
}
|
||||
|
||||
type cBuildInput struct {
|
||||
g.Meta `name:"build" config:"gfcli.build"`
|
||||
File string `name:"FILE" arg:"true" brief:"building file path"`
|
||||
Name string `short:"n" name:"name" brief:"output binary name"`
|
||||
Version string `short:"v" name:"version" brief:"output binary version"`
|
||||
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
|
||||
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
|
||||
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
|
||||
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
|
||||
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
|
||||
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
|
||||
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
|
||||
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
|
||||
Exit bool `name:"exit" brief:"exit building when any error occurs, default is false" orphan:"true"`
|
||||
Pack string `name:"pack" brief:"pack specified folder into temporary go file before building and removes it after built"`
|
||||
g.Meta `name:"build" config:"gfcli.build"`
|
||||
File string `name:"FILE" arg:"true" brief:"building file path"`
|
||||
Name string `short:"n" name:"name" brief:"output binary name"`
|
||||
Version string `short:"v" name:"version" brief:"output binary version"`
|
||||
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
|
||||
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
|
||||
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
|
||||
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
|
||||
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
|
||||
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
|
||||
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
|
||||
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
|
||||
PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"`
|
||||
PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"`
|
||||
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, specially for multiple arch and system buildings. default is false" orphan:"true"`
|
||||
DumpENV bool `short:"de" name:"dumpEnv" brief:"dump current go build environment before building binary" orphan:"true"`
|
||||
}
|
||||
|
||||
type cBuildOutput struct{}
|
||||
|
||||
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
|
||||
// print used go env
|
||||
if in.DumpENV {
|
||||
_, _ = Env.Index(ctx, cEnvInput{})
|
||||
}
|
||||
|
||||
mlog.SetHeaderPrint(true)
|
||||
|
||||
mlog.Debugf(`build input: %+v`, in)
|
||||
mlog.Debugf(`build command input: %+v`, in)
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
@ -143,7 +159,7 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
if gfile.Exists("main.go") {
|
||||
file = "main.go"
|
||||
} else {
|
||||
mlog.Fatal("build file path cannot be empty")
|
||||
mlog.Fatal("build file path is empty or main.go not found in current working directory")
|
||||
}
|
||||
}
|
||||
if in.Name == "" {
|
||||
@ -189,22 +205,32 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
platformMap[system][arch] = true
|
||||
}
|
||||
// Auto packing.
|
||||
if len(in.Pack) > 0 {
|
||||
dataFilePath := fmt.Sprintf(`packed/%s`, c.packedGoFileName)
|
||||
if !gfile.Exists(dataFilePath) {
|
||||
if in.PackSrc != "" {
|
||||
if in.PackDst == "" {
|
||||
mlog.Fatal(`parameter "packDst" should not be empty when "packSrc" is used`)
|
||||
}
|
||||
if gfile.Exists(in.PackDst) && !gfile.IsFile(in.PackDst) {
|
||||
mlog.Fatalf(`parameter "packDst" path "%s" should be type of file not directory`, in.PackDst)
|
||||
}
|
||||
if !gfile.Exists(in.PackDst) {
|
||||
// Remove the go file that is automatically packed resource.
|
||||
defer func() {
|
||||
_ = gfile.Remove(dataFilePath)
|
||||
mlog.Printf(`remove the automatically generated resource go file: %s`, dataFilePath)
|
||||
_ = gfile.Remove(in.PackDst)
|
||||
mlog.Printf(`remove the automatically generated resource go file: %s`, in.PackDst)
|
||||
}()
|
||||
}
|
||||
packCmd := fmt.Sprintf(`gf pack %s %s`, in.Pack, dataFilePath)
|
||||
// remove black space in separator.
|
||||
in.PackSrc, _ = gregex.ReplaceString(`,\s+`, `,`, in.PackSrc)
|
||||
packCmd := fmt.Sprintf(`gf pack %s %s --keepPath=true`, in.PackSrc, in.PackDst)
|
||||
mlog.Print(packCmd)
|
||||
gproc.MustShellRun(packCmd)
|
||||
gproc.MustShellRun(ctx, packCmd)
|
||||
}
|
||||
|
||||
// Injected information by building flags.
|
||||
ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`, c.getBuildInVarStr(in))
|
||||
ldFlags := fmt.Sprintf(
|
||||
`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`,
|
||||
c.getBuildInVarStr(ctx, in),
|
||||
)
|
||||
|
||||
// start building
|
||||
mlog.Print("start building...")
|
||||
@ -223,22 +249,28 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
|
||||
continue
|
||||
}
|
||||
for arch, _ := range item {
|
||||
for arch := range item {
|
||||
if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) {
|
||||
continue
|
||||
}
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
// Single binary building, output the binary to current working folder.
|
||||
// For example:
|
||||
// `gf build`
|
||||
// `gf build -o main.exe`
|
||||
if runtime.GOOS == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
// Single binary building, output the binary to current working folder.
|
||||
output := ""
|
||||
var outputPath string
|
||||
if len(in.Output) > 0 {
|
||||
output = "-o " + in.Output + ext
|
||||
outputPath = "-o " + in.Output
|
||||
} else {
|
||||
output = "-o " + in.Name + ext
|
||||
outputPath = "-o " + in.Name + ext
|
||||
}
|
||||
cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, in.Extra, file)
|
||||
cmd = fmt.Sprintf(
|
||||
`go build %s -ldflags "%s" %s %s`,
|
||||
outputPath, ldFlags, in.Extra, file,
|
||||
)
|
||||
} else {
|
||||
// Cross-building, output the compiled binary to specified path.
|
||||
if system == "windows" {
|
||||
@ -246,22 +278,33 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
}
|
||||
genv.MustSet("GOOS", system)
|
||||
genv.MustSet("GOARCH", arch)
|
||||
|
||||
var outputPath string
|
||||
if len(in.Output) > 0 {
|
||||
outputPath = "-o " + in.Output
|
||||
} else {
|
||||
outputPath = fmt.Sprintf(
|
||||
"-o %s/%s/%s%s",
|
||||
in.Path, system+"_"+arch, in.Name, ext,
|
||||
)
|
||||
}
|
||||
cmd = fmt.Sprintf(
|
||||
`go build -o %s/%s/%s%s -ldflags "%s" %s%s`,
|
||||
in.Path, system+"_"+arch, in.Name, ext, ldFlags, in.Extra, file,
|
||||
`go build %s -ldflags "%s" %s%s`,
|
||||
outputPath, ldFlags, in.Extra, file,
|
||||
)
|
||||
}
|
||||
mlog.Debug(fmt.Sprintf("build for GOOS=%s GOARCH=%s", system, arch))
|
||||
mlog.Debug(cmd)
|
||||
// It's not necessary printing the complete command string.
|
||||
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
|
||||
mlog.Print(cmdShow)
|
||||
if result, err := gproc.ShellExec(cmd); err != nil {
|
||||
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
|
||||
mlog.Printf(
|
||||
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
|
||||
system, arch, gstr.Trim(result),
|
||||
`you may use command option "--debug" to enable debug info and check the details`,
|
||||
)
|
||||
if in.Exit {
|
||||
if in.ExitWhenError {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
@ -279,15 +322,16 @@ buildDone:
|
||||
return
|
||||
}
|
||||
|
||||
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
|
||||
// getBuildInVarStr retrieves and returns the custom build-in variables in configuration
|
||||
// file as json.
|
||||
func (c cBuild) getBuildInVarStr(in cBuildInput) string {
|
||||
func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {
|
||||
buildInVarMap := in.VarMap
|
||||
if buildInVarMap == nil {
|
||||
buildInVarMap = make(g.Map)
|
||||
}
|
||||
buildInVarMap["builtGit"] = c.getGitCommit()
|
||||
buildInVarMap["builtTime"] = gtime.Now().String()
|
||||
buildInVarMap[gbuild.BuiltGit] = c.getGitCommit(ctx)
|
||||
buildInVarMap[gbuild.BuiltTime] = gtime.Now().String()
|
||||
buildInVarMap[gbuild.BuiltVersion] = in.Version
|
||||
b, err := json.Marshal(buildInVarMap)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
@ -296,13 +340,13 @@ func (c cBuild) getBuildInVarStr(in cBuildInput) string {
|
||||
}
|
||||
|
||||
// getGitCommit retrieves and returns the latest git commit hash string if present.
|
||||
func (c cBuild) getGitCommit() string {
|
||||
func (c cBuild) getGitCommit(ctx context.Context) string {
|
||||
if gproc.SearchBinary("git") == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
|
||||
s, _ = gproc.ShellExec(cmd)
|
||||
s, _ = gproc.ShellExec(ctx, cmd)
|
||||
)
|
||||
mlog.Debug(cmd)
|
||||
if s != "" {
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -30,6 +39,7 @@ gf docker main.go
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -p -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -p -tp ["hub.docker.com/john","hub.docker.com/smith"] -tn image:tag
|
||||
`
|
||||
cDockerDc = `
|
||||
The "docker" command builds the GF project to a docker images.
|
||||
@ -37,41 +47,48 @@ It runs "gf build" firstly to compile the project to binary file.
|
||||
It then runs "docker build" command automatically to generate the docker image.
|
||||
You should have docker installed, and there must be a Dockerfile in the root of the project.
|
||||
`
|
||||
cDockerMainBrief = `main file path for "gf build", it's "main.go" in default. empty string for no binary build`
|
||||
cDockerBuildBrief = `binary build options before docker image build, it's "-a amd64 -s linux" in default`
|
||||
cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default`
|
||||
cDockerShellBrief = `path of the shell file which is executed before docker build`
|
||||
cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed`
|
||||
cDockerTagBrief = `tag name for this docker, which is usually used for docker push`
|
||||
cDockerExtraBrief = `extra build options passed to "docker image"`
|
||||
cDockerMainBrief = `main file path for "gf build", it's "main.go" in default. empty string for no binary build`
|
||||
cDockerBuildBrief = `binary build options before docker image build, it's "-a amd64 -s linux" in default`
|
||||
cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default`
|
||||
cDockerShellBrief = `path of the shell file which is executed before docker build`
|
||||
cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed`
|
||||
cDockerTagBrief = `full tag for this docker, pattern like "xxx.xxx.xxx/image:tag"`
|
||||
cDockerTagNameBrief = `tag name for this docker, pattern like "image:tag". this option is required with TagPrefixes`
|
||||
cDockerTagPrefixesBrief = `tag prefixes for this docker, which are used for docker push. this option is required with TagName`
|
||||
cDockerExtraBrief = `extra build options passed to "docker image"`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cDockerUsage`: cDockerUsage,
|
||||
`cDockerBrief`: cDockerBrief,
|
||||
`cDockerEg`: cDockerEg,
|
||||
`cDockerDc`: cDockerDc,
|
||||
`cDockerMainBrief`: cDockerMainBrief,
|
||||
`cDockerFileBrief`: cDockerFileBrief,
|
||||
`cDockerShellBrief`: cDockerShellBrief,
|
||||
`cDockerBuildBrief`: cDockerBuildBrief,
|
||||
`cDockerPushBrief`: cDockerPushBrief,
|
||||
`cDockerTagBrief`: cDockerTagBrief,
|
||||
`cDockerExtraBrief`: cDockerExtraBrief,
|
||||
`cDockerUsage`: cDockerUsage,
|
||||
`cDockerBrief`: cDockerBrief,
|
||||
`cDockerEg`: cDockerEg,
|
||||
`cDockerDc`: cDockerDc,
|
||||
`cDockerMainBrief`: cDockerMainBrief,
|
||||
`cDockerFileBrief`: cDockerFileBrief,
|
||||
`cDockerShellBrief`: cDockerShellBrief,
|
||||
`cDockerBuildBrief`: cDockerBuildBrief,
|
||||
`cDockerPushBrief`: cDockerPushBrief,
|
||||
`cDockerTagBrief`: cDockerTagBrief,
|
||||
`cDockerTagNameBrief`: cDockerTagNameBrief,
|
||||
`cDockerTagPrefixesBrief`: cDockerTagPrefixesBrief,
|
||||
`cDockerExtraBrief`: cDockerExtraBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cDockerInput struct {
|
||||
g.Meta `name:"docker" config:"gfcli.docker"`
|
||||
Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"`
|
||||
File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"`
|
||||
Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"`
|
||||
Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}" d:"-a amd64 -s linux"`
|
||||
Tag string `name:"tag" short:"t" brief:"{cDockerTagBrief}"`
|
||||
Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cDockerExtraBrief}"`
|
||||
g.Meta `name:"docker" config:"gfcli.docker"`
|
||||
Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"`
|
||||
File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"`
|
||||
Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"`
|
||||
Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}"`
|
||||
Tag string `name:"tag" short:"t" brief:"{cDockerTagBrief}"`
|
||||
TagName string `name:"tagName" short:"tn" brief:"{cDockerTagNameBrief}" v:"required-with:TagPrefixes"`
|
||||
TagPrefixes []string `name:"tagPrefixes" short:"tp" brief:"{cDockerTagPrefixesBrief}" v:"required-with:TagName"`
|
||||
Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cDockerExtraBrief}"`
|
||||
}
|
||||
|
||||
type cDockerOutput struct{}
|
||||
|
||||
func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput, err error) {
|
||||
@ -80,37 +97,83 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
mlog.Fatalf(`command "docker" not found in your environment, please install docker first to proceed this command`)
|
||||
}
|
||||
|
||||
mlog.Debugf(`docker command input: %+v`, in)
|
||||
|
||||
// Binary build.
|
||||
in.Build += " --exit"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
return
|
||||
if in.Main != "" && in.Build != "" {
|
||||
in.Build += " --exitWhenError"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(ctx, fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
mlog.Debugf(`build binary failed with error: %+v`, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shell executing.
|
||||
if gfile.Exists(in.Shell) {
|
||||
if err = gproc.ShellRun(gfile.GetContents(in.Shell)); err != nil {
|
||||
if in.Shell != "" && gfile.Exists(in.Shell) {
|
||||
if err = c.exeDockerShell(ctx, in.Shell); err != nil {
|
||||
mlog.Debugf(`build docker failed with error: %+v`, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Docker build.
|
||||
dockerBuildOptions := ""
|
||||
if in.Tag != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`-t %s`, in.Tag)
|
||||
var (
|
||||
dockerBuildOptions string
|
||||
dockerTags []string
|
||||
dockerTagBase string
|
||||
)
|
||||
if len(in.TagPrefixes) > 0 {
|
||||
for _, tagPrefix := range in.TagPrefixes {
|
||||
tagPrefix = gstr.TrimRight(tagPrefix, "/")
|
||||
dockerTags = append(dockerTags, fmt.Sprintf(`%s/%s`, tagPrefix, in.TagName))
|
||||
}
|
||||
}
|
||||
if in.Extra != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`%s %s`, dockerBuildOptions, in.Extra)
|
||||
if len(dockerTags) == 0 {
|
||||
dockerTags = []string{in.Tag}
|
||||
}
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions)); err != nil {
|
||||
return
|
||||
for i, dockerTag := range dockerTags {
|
||||
if i > 0 {
|
||||
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker tag %s %s`, dockerTagBase, dockerTag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
dockerTagBase = dockerTag
|
||||
dockerBuildOptions = ""
|
||||
if dockerTag != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`-t %s`, dockerTag)
|
||||
}
|
||||
if in.Extra != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`%s %s`, dockerBuildOptions, in.Extra)
|
||||
}
|
||||
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Docker push.
|
||||
if in.Tag == "" || !in.Push {
|
||||
if !in.Push {
|
||||
return
|
||||
}
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, in.Tag)); err != nil {
|
||||
return
|
||||
for _, dockerTag := range dockerTags {
|
||||
if dockerTag == "" {
|
||||
continue
|
||||
}
|
||||
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker push %s`, dockerTag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cDocker) exeDockerShell(ctx context.Context, shellFilePath string) error {
|
||||
if gfile.ExtName(shellFilePath) == "sh" && runtime.GOOS == "windows" {
|
||||
mlog.Debugf(`ignore shell file "%s", as it cannot be run on windows system`, shellFilePath)
|
||||
return nil
|
||||
}
|
||||
return gproc.ShellRun(ctx, gfile.GetContents(shellFilePath))
|
||||
}
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -23,10 +31,11 @@ type cEnv struct {
|
||||
type cEnvInput struct {
|
||||
g.Meta `name:"env"`
|
||||
}
|
||||
|
||||
type cEnvOutput struct{}
|
||||
|
||||
func (c cEnv) Index(ctx context.Context, in cEnvInput) (out *cEnvOutput, err error) {
|
||||
result, err := gproc.ShellExec("go env")
|
||||
result, err := gproc.ShellExec(ctx, "go env")
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
|
||||
152
cmd/gf/internal/cmd/cmd_fix.go
Normal file
152
cmd/gf/internal/cmd/cmd_fix.go
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
var (
|
||||
Fix = cFix{}
|
||||
)
|
||||
|
||||
type cFix struct {
|
||||
g.Meta `name:"fix" brief:"auto fixing codes after upgrading to new GoFrame version" usage:"gf fix" `
|
||||
}
|
||||
|
||||
type cFixInput struct {
|
||||
g.Meta `name:"fix"`
|
||||
Path string `name:"path" short:"p" brief:"directory path, it uses current working directory in default"`
|
||||
Version string `name:"version" short:"v" brief:"custom specified version to fix, leave it empty to auto detect"`
|
||||
}
|
||||
|
||||
type cFixOutput struct{}
|
||||
|
||||
type cFixItem struct {
|
||||
Version string
|
||||
Func func(version string) error
|
||||
}
|
||||
|
||||
func (c cFix) Index(ctx context.Context, in cFixInput) (out *cFixOutput, err error) {
|
||||
|
||||
if in.Path == "" {
|
||||
in.Path = gfile.Pwd()
|
||||
}
|
||||
if in.Version == "" {
|
||||
in.Version, err = c.autoDetectVersion(in)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if in.Version == "" {
|
||||
mlog.Print(`no GoFrame usage found, exit fixing`)
|
||||
return
|
||||
}
|
||||
mlog.Debugf(`current GoFrame version auto detect "%s"`, in.Version)
|
||||
}
|
||||
|
||||
if !gproc.IsChild() {
|
||||
mlog.Printf(`start auto fixing directory path "%s"...`, in.Path)
|
||||
defer mlog.Print(`done!`)
|
||||
}
|
||||
|
||||
err = c.doFix(in)
|
||||
return
|
||||
}
|
||||
|
||||
func (c cFix) doFix(in cFixInput) (err error) {
|
||||
|
||||
var items = []cFixItem{
|
||||
{Version: "v2.3", Func: c.doFixV23},
|
||||
{Version: "v2.5", Func: c.doFixV25},
|
||||
}
|
||||
for _, item := range items {
|
||||
if gstr.CompareVersionGo(in.Version, item.Version) < 0 {
|
||||
mlog.Debugf(
|
||||
`current GoFrame or contrib package version "%s" is lesser than "%s", nothing to do`,
|
||||
in.Version, item.Version,
|
||||
)
|
||||
continue
|
||||
}
|
||||
if err = item.Func(in.Version); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// doFixV23 fixes code when upgrading to GoFrame v2.3.
|
||||
func (c cFix) doFixV23(version string) error {
|
||||
replaceFunc := func(path, content string) string {
|
||||
// gdb.TX from struct to interface.
|
||||
content = gstr.Replace(content, "*gdb.TX", "gdb.TX")
|
||||
// function name changes for package gtcp/gudp.
|
||||
if gstr.Contains(content, "/gf/v2/net/gtcp") || gstr.Contains(content, "/gf/v2/net/gudp") {
|
||||
content = gstr.ReplaceByMap(content, g.MapStrStr{
|
||||
".SetSendDeadline": ".SetDeadlineSend",
|
||||
".SetReceiveDeadline": ".SetDeadlineRecv",
|
||||
".SetReceiveBufferWait": ".SetBufferWaitRecv",
|
||||
})
|
||||
}
|
||||
return content
|
||||
}
|
||||
return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
|
||||
}
|
||||
|
||||
// doFixV25 fixes code when upgrading to GoFrame v2.5.
|
||||
func (c cFix) doFixV25(version string) (err error) {
|
||||
replaceFunc := func(path, content string) string {
|
||||
content, err = c.doFixV25Content(content)
|
||||
return content
|
||||
}
|
||||
return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
|
||||
}
|
||||
|
||||
func (c cFix) doFixV25Content(content string) (newContent string, err error) {
|
||||
newContent = content
|
||||
if gstr.Contains(content, `.BindHookHandlerByMap(`) {
|
||||
var pattern = `\.BindHookHandlerByMap\((.+?), map\[string\]ghttp\.HandlerFunc`
|
||||
newContent, err = gregex.ReplaceString(
|
||||
pattern,
|
||||
`.BindHookHandlerByMap($1, map[ghttp.HookName]ghttp.HandlerFunc`,
|
||||
content,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cFix) autoDetectVersion(in cFixInput) (string, error) {
|
||||
var (
|
||||
err error
|
||||
path = gfile.Join(in.Path, "go.mod")
|
||||
version string
|
||||
)
|
||||
if !gfile.Exists(path) {
|
||||
return "", gerror.Newf(`"%s" not found in current working directory`, path)
|
||||
}
|
||||
err = gfile.ReadLines(path, func(line string) error {
|
||||
array := gstr.SplitAndTrim(line, " ")
|
||||
if len(array) > 0 {
|
||||
if gstr.HasPrefix(array[0], gfPackage) {
|
||||
version = array[1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@ -11,6 +17,12 @@ var (
|
||||
|
||||
type cGen struct {
|
||||
g.Meta `name:"gen" brief:"{cGenBrief}" dc:"{cGenDc}"`
|
||||
cGenDao
|
||||
cGenEnums
|
||||
cGenCtrl
|
||||
cGenPb
|
||||
cGenPbEntity
|
||||
cGenService
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
15
cmd/gf/internal/cmd/cmd_gen_ctrl.go
Normal file
15
cmd/gf/internal/cmd/cmd_gen_ctrl.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/genctrl"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenCtrl = genctrl.CGenCtrl
|
||||
)
|
||||
@ -1,801 +1,22 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
// _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
// _ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/gendao"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDaoPath = `service/internal/dao`
|
||||
defaultDoPath = `service/internal/do`
|
||||
defaultEntityPath = `model/entity`
|
||||
cGenDaoConfig = `gfcli.gen.dao`
|
||||
cGenDaoUsage = `gf gen dao [OPTION]`
|
||||
cGenDaoBrief = `automatically generate go files for dao/do/entity`
|
||||
cGenDaoEg = `
|
||||
gf gen dao
|
||||
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen dao -p ./model -g user-center -t user,user_detail,user_login
|
||||
gf gen dao -r user_
|
||||
`
|
||||
|
||||
cGenDaoAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
dao:
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
tables: "order,products"
|
||||
jsonCase: "CamelLower"
|
||||
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "./my-app"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
`
|
||||
cGenDaoBriefPath = `directory path for generated files`
|
||||
cGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
|
||||
cGenDaoBriefPrefix = `add prefix for all table of specified link/database tables`
|
||||
cGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
|
||||
cGenDaoBriefWithTime = `add created time for auto produced go files`
|
||||
cGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
|
||||
cGenDaoBriefImportPrefix = `custom import prefix for generated go files`
|
||||
cGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
|
||||
cGenDaoBriefModelFile = `custom file name for storing generated model content`
|
||||
cGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
|
||||
cGenDaoBriefDescriptionTag = `add comment to description tag for each field`
|
||||
cGenDaoBriefNoJsonTag = `no json tag will be added for each field`
|
||||
cGenDaoBriefNoModelComment = `no model comment will be added for each field`
|
||||
cGenDaoBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
cGenDaoBriefJsonCase = `
|
||||
generated json tag case for model struct, cases are as follows:
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
tplVarTableName = `{TplTableName}`
|
||||
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
|
||||
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
|
||||
tplVarPackageImports = `{TplPackageImports}`
|
||||
tplVarImportPrefix = `{TplImportPrefix}`
|
||||
tplVarStructDefine = `{TplStructDefine}`
|
||||
tplVarColumnDefine = `{TplColumnDefine}`
|
||||
tplVarColumnNames = `{TplColumnNames}`
|
||||
tplVarGroupName = `{TplGroupName}`
|
||||
tplVarDatetimeStr = `{TplDatetimeStr}`
|
||||
)
|
||||
|
||||
var (
|
||||
createdAt = gtime.Now()
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenDaoConfig`: cGenDaoConfig,
|
||||
`cGenDaoUsage`: cGenDaoUsage,
|
||||
`cGenDaoBrief`: cGenDaoBrief,
|
||||
`cGenDaoEg`: cGenDaoEg,
|
||||
`cGenDaoAd`: cGenDaoAd,
|
||||
`cGenDaoBriefPath`: cGenDaoBriefPath,
|
||||
`cGenDaoBriefLink`: cGenDaoBriefLink,
|
||||
`cGenDaoBriefTables`: cGenDaoBriefTables,
|
||||
`cGenDaoBriefTablesEx`: cGenDaoBriefTablesEx,
|
||||
`cGenDaoBriefPrefix`: cGenDaoBriefPrefix,
|
||||
`cGenDaoBriefRemovePrefix`: cGenDaoBriefRemovePrefix,
|
||||
`cGenDaoBriefStdTime`: cGenDaoBriefStdTime,
|
||||
`cGenDaoBriefWithTime`: cGenDaoBriefWithTime,
|
||||
`cGenDaoBriefGJsonSupport`: cGenDaoBriefGJsonSupport,
|
||||
`cGenDaoBriefImportPrefix`: cGenDaoBriefImportPrefix,
|
||||
`cGenDaoBriefOverwriteDao`: cGenDaoBriefOverwriteDao,
|
||||
`cGenDaoBriefModelFile`: cGenDaoBriefModelFile,
|
||||
`cGenDaoBriefModelFileForDao`: cGenDaoBriefModelFileForDao,
|
||||
`cGenDaoBriefDescriptionTag`: cGenDaoBriefDescriptionTag,
|
||||
`cGenDaoBriefNoJsonTag`: cGenDaoBriefNoJsonTag,
|
||||
`cGenDaoBriefNoModelComment`: cGenDaoBriefNoModelComment,
|
||||
`cGenDaoBriefGroup`: cGenDaoBriefGroup,
|
||||
`cGenDaoBriefJsonCase`: cGenDaoBriefJsonCase,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
cGenDaoInput struct {
|
||||
g.Meta `name:"dao" config:"{cGenDaoConfig}" usage:"{cGenDaoUsage}" brief:"{cGenDaoBrief}" eg:"{cGenDaoEg}" ad:"{cGenDaoAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenDaoBriefPath}" d:"internal"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"e" brief:"{cGenDaoBriefTablesEx}"`
|
||||
Group string `name:"group" short:"g" brief:"{cGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenDaoBriefRemovePrefix}"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenDaoBriefJsonCase}" d:"CamelLower"`
|
||||
ImportPrefix string `name:"importPrefix" short:"i" brief:"{cGenDaoBriefImportPrefix}"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{cGenDaoBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"c" brief:"{cGenDaoBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{cGenDaoBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"o" brief:"{cGenDaoBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"d" brief:"{cGenDaoBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{cGenDaoBriefNoJsonTag" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{cGenDaoBriefNoModelComment}" orphan:"true"`
|
||||
}
|
||||
cGenDaoOutput struct{}
|
||||
|
||||
cGenDaoInternalInput struct {
|
||||
cGenDaoInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
ModName string // ModName specifies the module name of current golang project, which is used for import purpose.
|
||||
}
|
||||
cGenDao = gendao.CGenDao
|
||||
)
|
||||
|
||||
func (c cGen) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
|
||||
if g.Cfg().Available(ctx) {
|
||||
v := g.Cfg().MustGet(ctx, cGenDaoConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenDaoForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// doGenDaoForArray implements the "gen dao" command for configuration array.
|
||||
func doGenDaoForArray(ctx context.Context, index int, in cGenDaoInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
modName string // Go module name, eg: github.com/gogf/gf.
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" {
|
||||
mlog.Fatalf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
if in.ImportPrefix == "" {
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
modName = gstr.Trim(match[1])
|
||||
} else {
|
||||
mlog.Fatal("module name does not found in go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
if db, err = gdb.Instance(tempGroup); err != nil {
|
||||
mlog.Debugf(`database initialization failed: %+v`, err)
|
||||
}
|
||||
} else {
|
||||
mlog.Fatalf(`invalid database configuration: %s`, in.Link)
|
||||
}
|
||||
} else {
|
||||
db = g.DB(in.Group)
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal(`database initialization failed, may be invalid database configuration`)
|
||||
}
|
||||
|
||||
var tableNames []string
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: %+v", err)
|
||||
}
|
||||
}
|
||||
// Table excluding.
|
||||
if in.TablesEx != "" {
|
||||
array := garray.NewStrArrayFrom(tableNames)
|
||||
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
|
||||
array.RemoveValue(v)
|
||||
}
|
||||
tableNames = array.Slice()
|
||||
}
|
||||
|
||||
// Generating dao & model go files one by one according to given table name.
|
||||
newTableNames := make([]string, len(tableNames))
|
||||
for i, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
newTableName = in.Prefix + newTableName
|
||||
newTableNames[i] = newTableName
|
||||
// Dao.
|
||||
generateDao(ctx, db, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
// Do.
|
||||
generateDo(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
ModName: modName,
|
||||
})
|
||||
// Entity.
|
||||
generateEntity(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
|
||||
cGenDaoInput: in,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
|
||||
// generateDaoContentFile generates the dao and model content of given table.
|
||||
func generateDao(ctx context.Context, db gdb.DB, in cGenDaoInternalInput) {
|
||||
// Generating table data preparing.
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
|
||||
}
|
||||
var (
|
||||
dirRealPath = gfile.RealPath(in.Path)
|
||||
dirPathDao = gfile.Join(in.Path, defaultDaoPath)
|
||||
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
|
||||
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
|
||||
importPrefix = in.ImportPrefix
|
||||
)
|
||||
if importPrefix == "" {
|
||||
if dirRealPath == "" {
|
||||
dirRealPath = in.Path
|
||||
importPrefix = dirRealPath
|
||||
importPrefix = gstr.Trim(dirRealPath, "./")
|
||||
} else {
|
||||
importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "")
|
||||
}
|
||||
importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/")
|
||||
importPrefix = gstr.Join(g.SliceStr{in.ModName, importPrefix, defaultDaoPath}, "/")
|
||||
importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/"))
|
||||
}
|
||||
|
||||
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
|
||||
// Add suffix to avoid the table name which contains "_test",
|
||||
// which would make the go file a testing file.
|
||||
fileName += "_table"
|
||||
}
|
||||
|
||||
// dao - index
|
||||
generateDaoIndex(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName)
|
||||
|
||||
// dao - internal
|
||||
generateDaoInternal(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap)
|
||||
}
|
||||
|
||||
func generateDo(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
|
||||
var (
|
||||
doDirPath = gfile.Join(in.Path, defaultDoPath)
|
||||
)
|
||||
in.NoJsonTag = true
|
||||
in.DescriptionTag = false
|
||||
in.NoModelComment = false
|
||||
// Model content.
|
||||
for i, tableName := range tableNames {
|
||||
in.TableName = tableName
|
||||
fieldMap, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = newTableNames[i]
|
||||
doFilePath = gfile.Join(doDirPath, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition = generateStructDefinition(generateStructDefinitionInput{
|
||||
cGenDaoInternalInput: in,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: true,
|
||||
})
|
||||
)
|
||||
// replace all types to interface{}.
|
||||
structDefinition, _ = gregex.ReplaceStringFuncMatch(
|
||||
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
|
||||
structDefinition,
|
||||
func(match []string) string {
|
||||
// If the type is already a pointer/slice/map, it does nothing.
|
||||
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
|
||||
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
|
||||
}
|
||||
return match[0]
|
||||
},
|
||||
)
|
||||
modelContent := generateDoContent(
|
||||
in,
|
||||
tableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
structDefinition,
|
||||
)
|
||||
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(doFilePath)
|
||||
mlog.Print("generated:", doFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateEntity(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
|
||||
var (
|
||||
entityDirPath = gfile.Join(in.Path, defaultEntityPath)
|
||||
)
|
||||
|
||||
// Model content.
|
||||
for i, tableName := range tableNames {
|
||||
fieldMap, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = newTableNames[i]
|
||||
entityFilePath = gfile.Join(entityDirPath, gstr.CaseSnake(newTableName)+".go")
|
||||
entityContent = generateEntityContent(
|
||||
in,
|
||||
newTableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
generateStructDefinition(generateStructDefinitionInput{
|
||||
cGenDaoInternalInput: in,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: false,
|
||||
}),
|
||||
)
|
||||
)
|
||||
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(entityFilePath)
|
||||
mlog.Print("generated:", entityFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getImportPartContent(source string, isDo bool) string {
|
||||
var (
|
||||
packageImportsArray = garray.NewStrArray()
|
||||
)
|
||||
|
||||
if isDo {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
|
||||
}
|
||||
|
||||
// Time package recognition.
|
||||
if strings.Contains(source, "gtime.Time") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`)
|
||||
} else if strings.Contains(source, "time.Time") {
|
||||
packageImportsArray.Append(`"time"`)
|
||||
}
|
||||
|
||||
// Json type.
|
||||
if strings.Contains(source, "gjson.Json") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
|
||||
}
|
||||
|
||||
// Generate and write content to golang file.
|
||||
packageImportsStr := ""
|
||||
if packageImportsArray.Len() > 0 {
|
||||
packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n"))
|
||||
}
|
||||
return packageImportsStr
|
||||
}
|
||||
|
||||
func generateEntityContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
entityContent := gstr.ReplaceByMap(consts.TemplateGenDaoEntityContent, g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, false),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
entityContent = replaceDefaultVar(in, entityContent)
|
||||
return entityContent
|
||||
}
|
||||
|
||||
func generateDoContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
doContent := gstr.ReplaceByMap(consts.TemplateGenDaoDoContent, g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, true),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
doContent = replaceDefaultVar(in, doContent)
|
||||
return doContent
|
||||
}
|
||||
|
||||
func generateDaoIndex(in cGenDaoInternalInput, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string) {
|
||||
path := gfile.Join(dirPathDao, fileName+".go")
|
||||
if in.OverwriteDao || !gfile.Exists(path) {
|
||||
indexContent := gstr.ReplaceByMap(getTplDaoIndexContent(""), g.MapStrStr{
|
||||
tplVarImportPrefix: importPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
})
|
||||
indexContent = replaceDefaultVar(in, indexContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateDaoInternal(
|
||||
in cGenDaoInternalInput,
|
||||
tableNameCamelCase, tableNameCamelLowerCase, importPrefix string,
|
||||
dirPathDao, fileName string,
|
||||
fieldMap map[string]*gdb.TableField,
|
||||
) {
|
||||
path := gfile.Join(dirPathDao, "internal", fileName+".go")
|
||||
modelContent := gstr.ReplaceByMap(getTplDaoInternalContent(""), g.MapStrStr{
|
||||
tplVarImportPrefix: importPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarGroupName: in.Group,
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(fieldMap)),
|
||||
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(fieldMap)),
|
||||
})
|
||||
modelContent = replaceDefaultVar(in, modelContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
func replaceDefaultVar(in cGenDaoInternalInput, origin string) string {
|
||||
var tplDatetimeStr string
|
||||
if in.WithTime {
|
||||
tplDatetimeStr = fmt.Sprintf(`Created at %s`, createdAt.String())
|
||||
}
|
||||
return gstr.ReplaceByMap(origin, g.MapStrStr{
|
||||
tplVarDatetimeStr: tplDatetimeStr,
|
||||
})
|
||||
}
|
||||
|
||||
type generateStructDefinitionInput struct {
|
||||
cGenDaoInternalInput
|
||||
StructName string // Struct name.
|
||||
FieldMap map[string]*gdb.TableField // Table field map.
|
||||
IsDo bool // Is generating DTO struct.
|
||||
}
|
||||
|
||||
func generateStructDefinition(in generateStructDefinitionInput) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
array := make([][]string, len(in.FieldMap))
|
||||
names := sortFieldKeyForDao(in.FieldMap)
|
||||
for index, name := range names {
|
||||
field := in.FieldMap[name]
|
||||
array[index] = generateStructFieldDefinition(field, in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
stContent = gstr.Replace(stContent, "` ", "`")
|
||||
stContent = gstr.Replace(stContent, "``", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
|
||||
if in.IsDo {
|
||||
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
|
||||
}
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateStructFieldForModel generates and returns the attribute definition for specified field.
|
||||
func generateStructFieldDefinition(field *gdb.TableField, in generateStructDefinitionInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "[]byte"
|
||||
|
||||
case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint"
|
||||
} else {
|
||||
typeName = "int"
|
||||
}
|
||||
|
||||
case "int4", "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float32"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney", "numeric":
|
||||
typeName = "float64"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
if in.StdTime {
|
||||
typeName = "time.Time"
|
||||
} else {
|
||||
typeName = "*gtime.Time"
|
||||
}
|
||||
case "json", "jsonb":
|
||||
if in.GJsonSupport {
|
||||
typeName = "*gjson.Json"
|
||||
} else {
|
||||
typeName = "string"
|
||||
}
|
||||
default:
|
||||
// Automatically detect its data type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "float64"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "[]byte"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
if in.StdTime {
|
||||
typeName = "time.Time"
|
||||
} else {
|
||||
typeName = "*gtime.Time"
|
||||
}
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
tagKey = "`"
|
||||
result = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" #" + typeName,
|
||||
}
|
||||
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
|
||||
)
|
||||
|
||||
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
|
||||
|
||||
for k, v := range result {
|
||||
if in.NoJsonTag {
|
||||
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
|
||||
}
|
||||
if !in.DescriptionTag {
|
||||
v, _ = gregex.ReplaceString(`description:".*"`, ``, v)
|
||||
}
|
||||
if in.NoModelComment {
|
||||
v, _ = gregex.ReplaceString(`//.+`, ``, v)
|
||||
}
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// formatComment formats the comment string to fit the golang code without any lines.
|
||||
func formatComment(comment string) string {
|
||||
comment = gstr.ReplaceByArray(comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment = gstr.Trim(comment)
|
||||
return comment
|
||||
}
|
||||
|
||||
// generateColumnDefinitionForDao generates and returns the column names definition for specified table.
|
||||
func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
var (
|
||||
field = fieldMap[name]
|
||||
comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
}))
|
||||
)
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" # " + "string",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
defineContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
defineContent = gstr.Replace(defineContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(defineContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateColumnNamesForDao generates and returns the column names assignment content of column struct
|
||||
// for specified table.
|
||||
func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
field := fieldMap[name]
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name) + ":",
|
||||
fmt.Sprintf(` #"%s",`, field.Name),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
namesContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
namesContent = gstr.Replace(namesContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(namesContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func getTplDaoIndexContent(tplDaoIndexPath string) string {
|
||||
if tplDaoIndexPath != "" {
|
||||
return gfile.GetContents(tplDaoIndexPath)
|
||||
}
|
||||
return consts.TemplateDaoDaoIndexContent
|
||||
}
|
||||
|
||||
func getTplDaoInternalContent(tplDaoInternalPath string) string {
|
||||
if tplDaoInternalPath != "" {
|
||||
return gfile.GetContents(tplDaoInternalPath)
|
||||
}
|
||||
return consts.TemplateDaoDaoInternalContent
|
||||
}
|
||||
|
||||
// getJsonTagFromCase call gstr.Case* function to convert the s to specified case.
|
||||
func getJsonTagFromCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
i = 0
|
||||
j = 0
|
||||
result = make([]string, len(names))
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
15
cmd/gf/internal/cmd/cmd_gen_enums.go
Normal file
15
cmd/gf/internal/cmd/cmd_gen_enums.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/genenums"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenEnums = genenums.CGenEnums
|
||||
)
|
||||
@ -1,78 +1,13 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
)
|
||||
import "github.com/gogf/gf/cmd/gf/v2/internal/cmd/genpb"
|
||||
|
||||
type (
|
||||
cGenPbInput struct {
|
||||
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
|
||||
}
|
||||
cGenPbOutput struct{}
|
||||
cGenPb = genpb.CGenPb
|
||||
)
|
||||
|
||||
func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("protoc") == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
protoFolder := "protocol"
|
||||
if !gfile.Exists(protoFolder) {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder)
|
||||
}
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoFolder, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder)
|
||||
}
|
||||
dirSet := gset.NewStrSet()
|
||||
for _, file := range files {
|
||||
dirSet.Add(gfile.Dir(file))
|
||||
}
|
||||
var (
|
||||
servicePath = gfile.RealPath(".")
|
||||
goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH").String(), "src"))
|
||||
)
|
||||
dirSet.Iterator(func(protoDirPath string) bool {
|
||||
parsingCommand := fmt.Sprintf(
|
||||
"protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s",
|
||||
protoDirPath,
|
||||
servicePath,
|
||||
)
|
||||
if goPathSrc != "" {
|
||||
parsingCommand += " -I" + goPathSrc
|
||||
}
|
||||
mlog.Print(parsingCommand)
|
||||
if output, err := gproc.ShellExec(parsingCommand); err != nil {
|
||||
mlog.Print(output)
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
// Custom replacement.
|
||||
//pbFolder := "protobuf"
|
||||
//_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string {
|
||||
// content := gfile.GetContents(path)
|
||||
// content = gstr.ReplaceByArray(content, g.SliceStr{
|
||||
// `gtime "gtime"`, `gtime "github.com/gogf/gf/v2/os/gtime"`,
|
||||
// })
|
||||
// _ = gfile.PutContents(path, content)
|
||||
// utils.GoFmt(path)
|
||||
// return path
|
||||
//})
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,409 +1,13 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const (
|
||||
cGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
cGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
cGenPbEntityEg = `
|
||||
gf gen pbentity
|
||||
gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen pbentity -p ./protocol/demos/entity -t user,user_detail,user_login
|
||||
gf gen pbentity -r user_
|
||||
`
|
||||
|
||||
cGenPbEntityAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "protocol/demos/entity"
|
||||
tables: "order,products"
|
||||
package: "demos"
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "protocol/demos/entity"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
package: "demos"
|
||||
option: |
|
||||
option go_package = "protobuf/demos";
|
||||
option java_package = "protobuf/demos";
|
||||
option php_namespace = "protobuf/demos";
|
||||
`
|
||||
cGenPbEntityBriefPath = `directory path for generated files`
|
||||
cGenPbEntityBriefPackage = `package name for all entity proto files`
|
||||
cGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
|
||||
cGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenPbEntityBriefOption = `extra protobuf options`
|
||||
cGenPbEntityBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
|
||||
cGenPbEntityBriefNameCase = `
|
||||
case for message attribute names, default is "Camel":
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
cGenPbEntityBriefJsonCase = `
|
||||
case for message json tag, cases are the same as "nameCase", default "CamelLower".
|
||||
set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
import "github.com/gogf/gf/cmd/gf/v2/internal/cmd/genpbentity"
|
||||
|
||||
type (
|
||||
cGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
|
||||
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
|
||||
}
|
||||
cGenPbEntityOutput struct{}
|
||||
|
||||
cGenPbEntityInternalInput struct {
|
||||
cGenPbEntityInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
cGenPbEntity = genpbentity.CGenPbEntity
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenPbEntityConfig`: cGenPbEntityConfig,
|
||||
`cGenPbEntityBrief`: cGenPbEntityBrief,
|
||||
`cGenPbEntityEg`: cGenPbEntityEg,
|
||||
`cGenPbEntityAd`: cGenPbEntityAd,
|
||||
`cGenPbEntityBriefPath`: cGenPbEntityBriefPath,
|
||||
`cGenPbEntityBriefPackage`: cGenPbEntityBriefPackage,
|
||||
`cGenPbEntityBriefLink`: cGenPbEntityBriefLink,
|
||||
`cGenPbEntityBriefTables`: cGenPbEntityBriefTables,
|
||||
`cGenPbEntityBriefPrefix`: cGenPbEntityBriefPrefix,
|
||||
`cGenPbEntityBriefRemovePrefix`: cGenPbEntityBriefRemovePrefix,
|
||||
`cGenPbEntityBriefGroup`: cGenPbEntityBriefGroup,
|
||||
`cGenPbEntityBriefNameCase`: cGenPbEntityBriefNameCase,
|
||||
`cGenPbEntityBriefJsonCase`: cGenPbEntityBriefJsonCase,
|
||||
`cGenPbEntityBriefOption`: cGenPbEntityBriefOption,
|
||||
})
|
||||
}
|
||||
|
||||
func (c cGen) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
if config.Available(ctx) {
|
||||
v := config.MustGet(ctx, cGenPbEntityConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenPbEntityForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
func doGenPbEntityForArray(ctx context.Context, index int, in cGenPbEntityInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if in.Package == "" {
|
||||
mlog.Fatal("package name should not be empty")
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
} else {
|
||||
db = g.DB()
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal("database initialization failed")
|
||||
}
|
||||
|
||||
tableNames := ([]string)(nil)
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: \n %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
generatePbEntityContentFile(ctx, db, cGenPbEntityInternalInput{
|
||||
cGenPbEntityInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generatePbEntityContentFile generates the protobuf files for given table.
|
||||
func generatePbEntityContentFile(ctx context.Context, db gdb.DB, in cGenPbEntityInternalInput) {
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
// Change the `newTableName` if `Prefix` is given.
|
||||
newTableName := "Entity_" + in.Prefix + in.NewTableName
|
||||
var (
|
||||
tableNameCamelCase = gstr.CaseCamel(newTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = gfile.Join(in.Path, fileName+".proto")
|
||||
)
|
||||
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
|
||||
"{PackageName}": in.Package,
|
||||
"{OptionContent}": in.Option,
|
||||
"{EntityMessage}": entityMessageDefine,
|
||||
})
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateEntityMessageDefinition generates and returns the message definition for specified table.
|
||||
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in cGenPbEntityInternalInput) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForPbEntity(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
|
||||
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in cGenPbEntityInternalInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
comment string
|
||||
jsonTagStr string
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "bytes"
|
||||
|
||||
case "bit", "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint32"
|
||||
} else {
|
||||
typeName = "int32"
|
||||
}
|
||||
|
||||
case "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney":
|
||||
typeName = "double"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
typeName = "int64"
|
||||
|
||||
default:
|
||||
// Auto detecting type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "double"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "bytes"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
typeName = "int64"
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Trim(comment)
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment)
|
||||
if jsonTagName := formatCase(field.Name, in.JsonCase); jsonTagName != "" {
|
||||
jsonTagStr = fmt.Sprintf(`[(gogoproto.jsontag) = "%s"]`, jsonTagName)
|
||||
// beautiful indent.
|
||||
if index < 10 {
|
||||
// 3 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else if index < 100 {
|
||||
// 2 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else {
|
||||
// 1 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
" #" + typeName,
|
||||
" #" + formatCase(field.Name, in.NameCase),
|
||||
" #= " + gconv.String(index) + jsonTagStr + ";",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
|
||||
func getTplPbEntityContent(tplEntityPath string) string {
|
||||
if tplEntityPath != "" {
|
||||
return gfile.GetContents(tplEntityPath)
|
||||
}
|
||||
return consts.TemplatePbEntityMessageContent
|
||||
}
|
||||
|
||||
// formatCase call gstr.Case* function to convert the s to specified case.
|
||||
func formatCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
|
||||
case "none":
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
result = make([]string, len(names))
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
15
cmd/gf/internal/cmd/cmd_gen_service.go
Normal file
15
cmd/gf/internal/cmd/cmd_gen_service.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/genservice"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenService = genservice.CGenService
|
||||
)
|
||||
@ -1,17 +1,27 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -35,6 +45,10 @@ gf init my-mono-repo -m
|
||||
name for the project. It will create a folder with NAME in current directory.
|
||||
The NAME will also be the module name for the project.
|
||||
`
|
||||
// cInitGitDir the git directory
|
||||
cInitGitDir = ".git"
|
||||
// cInitGitignore the gitignore file
|
||||
cInitGitignore = ".gitignore"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -48,22 +62,29 @@ func init() {
|
||||
type cInitInput struct {
|
||||
g.Meta `name:"init"`
|
||||
Name string `name:"NAME" arg:"true" v:"required" brief:"{cInitNameBrief}"`
|
||||
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
|
||||
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
|
||||
Update bool `name:"update" short:"u" brief:"update to the latest goframe version" orphan:"true"`
|
||||
}
|
||||
|
||||
type cInitOutput struct{}
|
||||
|
||||
func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err error) {
|
||||
var (
|
||||
overwrote = false
|
||||
)
|
||||
if !gfile.IsEmpty(in.Name) && !allyes.Check() {
|
||||
s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, in.Name)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
overwrote = true
|
||||
}
|
||||
mlog.Print("initializing...")
|
||||
|
||||
// Create project folder and files.
|
||||
var (
|
||||
templateRepoName string
|
||||
gitignoreFile = in.Name + "/" + cInitGitignore
|
||||
)
|
||||
if in.Mono {
|
||||
templateRepoName = cInitMonoRepo
|
||||
@ -77,18 +98,60 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
|
||||
return
|
||||
}
|
||||
|
||||
// build ignoreFiles from the .gitignore file
|
||||
ignoreFiles := make([]string, 0, 10)
|
||||
ignoreFiles = append(ignoreFiles, cInitGitDir)
|
||||
if overwrote {
|
||||
err = gfile.ReadLines(gitignoreFile, func(line string) error {
|
||||
// Add only hidden files or directories
|
||||
// If other directories are added, it may cause the entire directory to be ignored
|
||||
// such as 'main' in the .gitignore file, but the path is 'D:\main\my-project'
|
||||
if line != "" && strings.HasPrefix(line, ".") {
|
||||
ignoreFiles = append(ignoreFiles, line)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// if not found the .gitignore file will skip os.ErrNotExist error
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Replace template name to project name.
|
||||
err = gfile.ReplaceDir(
|
||||
cInitRepoPrefix+templateRepoName,
|
||||
gfile.Basename(gfile.RealPath(in.Name)),
|
||||
in.Name,
|
||||
"*",
|
||||
true,
|
||||
)
|
||||
err = gfile.ReplaceDirFunc(func(path, content string) string {
|
||||
for _, ignoreFile := range ignoreFiles {
|
||||
if strings.Contains(path, ignoreFile) {
|
||||
return content
|
||||
}
|
||||
}
|
||||
return gstr.Replace(gfile.GetContents(path), cInitRepoPrefix+templateRepoName, gfile.Basename(gfile.RealPath(in.Name)))
|
||||
}, in.Name, "*", true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Update the GoFrame version.
|
||||
if in.Update {
|
||||
mlog.Print("update goframe...")
|
||||
// go get -u github.com/gogf/gf/v2@latest
|
||||
updateCommand := `go get -u github.com/gogf/gf/v2@latest`
|
||||
if in.Name != "." {
|
||||
updateCommand = fmt.Sprintf(`cd %s && %s`, in.Name, updateCommand)
|
||||
}
|
||||
if err = gproc.ShellRun(ctx, updateCommand); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
// go mod tidy
|
||||
gomModTidyCommand := `go mod tidy`
|
||||
if in.Name != "." {
|
||||
gomModTidyCommand = fmt.Sprintf(`cd %s && %s`, in.Name, gomModTidyCommand)
|
||||
}
|
||||
if err = gproc.ShellRun(ctx, gomModTidyCommand); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Print("initialization done! ")
|
||||
if !in.Mono {
|
||||
enjoyCommand := `gf run main.go`
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/service"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -18,6 +25,7 @@ type cInstall struct {
|
||||
type cInstallInput struct {
|
||||
g.Meta `name:"install"`
|
||||
}
|
||||
|
||||
type cInstallOutput struct{}
|
||||
|
||||
func (c cInstall) Index(ctx context.Context, in cInstallInput) (out *cInstallOutput, err error) {
|
||||
|
||||
@ -1,16 +1,23 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -37,29 +44,33 @@ gf pack /var/www/public packed/data.go -n=packed
|
||||
destination file path for packed file. if extension of the filename is ".go" and "-n" option is given,
|
||||
it enables packing SRC to go file, or else it packs SRC into a binary file.
|
||||
`
|
||||
cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed`
|
||||
cPackPrefixBrief = `prefix for each file packed into the resource file`
|
||||
cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed`
|
||||
cPackPrefixBrief = `prefix for each file packed into the resource file`
|
||||
cPackKeepPathBrief = `keep the source path from system to resource file, usually for relative path`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cPackUsage`: cPackUsage,
|
||||
`cPackBrief`: cPackBrief,
|
||||
`cPackEg`: cPackEg,
|
||||
`cPackSrcBrief`: cPackSrcBrief,
|
||||
`cPackDstBrief`: cPackDstBrief,
|
||||
`cPackNameBrief`: cPackNameBrief,
|
||||
`cPackPrefixBrief`: cPackPrefixBrief,
|
||||
`cPackUsage`: cPackUsage,
|
||||
`cPackBrief`: cPackBrief,
|
||||
`cPackEg`: cPackEg,
|
||||
`cPackSrcBrief`: cPackSrcBrief,
|
||||
`cPackDstBrief`: cPackDstBrief,
|
||||
`cPackNameBrief`: cPackNameBrief,
|
||||
`cPackPrefixBrief`: cPackPrefixBrief,
|
||||
`cPackKeepPathBrief`: cPackKeepPathBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type cPackInput struct {
|
||||
g.Meta `name:"pack"`
|
||||
Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"`
|
||||
Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"`
|
||||
Name string `name:"name" short:"n" brief:"{cPackNameBrief}"`
|
||||
Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"`
|
||||
g.Meta `name:"pack"`
|
||||
Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"`
|
||||
Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"`
|
||||
Name string `name:"name" short:"n" brief:"{cPackNameBrief}"`
|
||||
Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"`
|
||||
KeepPath bool `name:"keepPath" short:"k" brief:"{cPackKeepPathBrief}" orphan:"true"`
|
||||
}
|
||||
|
||||
type cPackOutput struct{}
|
||||
|
||||
func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err error) {
|
||||
@ -75,12 +86,16 @@ func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err
|
||||
if in.Name == "" && gfile.ExtName(in.Dst) == "go" {
|
||||
in.Name = gfile.Basename(gfile.Dir(in.Dst))
|
||||
}
|
||||
var option = gres.Option{
|
||||
Prefix: in.Prefix,
|
||||
KeepPath: in.KeepPath,
|
||||
}
|
||||
if in.Name != "" {
|
||||
if err = gres.PackToGoFile(in.Src, in.Dst, in.Name, in.Prefix); err != nil {
|
||||
if err = gres.PackToGoFileWithOption(in.Src, in.Dst, in.Name, option); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err = gres.PackToFile(in.Src, in.Dst, in.Prefix); err != nil {
|
||||
if err = gres.PackToFileWithOption(in.Src, in.Dst, option); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@ -14,6 +21,8 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -25,10 +34,11 @@ type cRun struct {
|
||||
}
|
||||
|
||||
type cRunApp struct {
|
||||
File string // Go run file name.
|
||||
Path string // Directory storing built binary.
|
||||
Options string // Extra "go run" options.
|
||||
Args string // Custom arguments.
|
||||
File string // Go run file name.
|
||||
Path string // Directory storing built binary.
|
||||
Options string // Extra "go run" options.
|
||||
Args string // Custom arguments.
|
||||
WatchPaths []string // Watch paths for live reload.
|
||||
}
|
||||
|
||||
const (
|
||||
@ -38,14 +48,17 @@ const (
|
||||
gf run main.go
|
||||
gf run main.go --args "server -p 8080"
|
||||
gf run main.go -mod=vendor
|
||||
gf run main.go -w "manifest/config/*.yaml"
|
||||
`
|
||||
cRunDc = `
|
||||
The "run" command is used for running go codes with hot-compiled-like feature,
|
||||
which compiles and runs the go codes asynchronously when codes change.
|
||||
`
|
||||
cRunFileBrief = `building file path.`
|
||||
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
|
||||
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
|
||||
cRunFileBrief = `building file path.`
|
||||
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
|
||||
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
|
||||
cRunArgsBrief = `custom arguments for your process`
|
||||
cRunWatchPathsBrief = `watch additional paths for live reload, separated by ",". i.e. "manifest/config/*.yaml"`
|
||||
)
|
||||
|
||||
var (
|
||||
@ -54,22 +67,26 @@ var (
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cRunUsage`: cRunUsage,
|
||||
`cRunBrief`: cRunBrief,
|
||||
`cRunEg`: cRunEg,
|
||||
`cRunDc`: cRunDc,
|
||||
`cRunFileBrief`: cRunFileBrief,
|
||||
`cRunPathBrief`: cRunPathBrief,
|
||||
`cRunExtraBrief`: cRunExtraBrief,
|
||||
`cRunUsage`: cRunUsage,
|
||||
`cRunBrief`: cRunBrief,
|
||||
`cRunEg`: cRunEg,
|
||||
`cRunDc`: cRunDc,
|
||||
`cRunFileBrief`: cRunFileBrief,
|
||||
`cRunPathBrief`: cRunPathBrief,
|
||||
`cRunExtraBrief`: cRunExtraBrief,
|
||||
`cRunArgsBrief`: cRunArgsBrief,
|
||||
`cRunWatchPathsBrief`: cRunWatchPathsBrief,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
cRunInput struct {
|
||||
g.Meta `name:"run"`
|
||||
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
|
||||
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
|
||||
g.Meta `name:"run"`
|
||||
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
|
||||
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
|
||||
Args string `name:"args" short:"a" brief:"{cRunArgsBrief}"`
|
||||
WatchPaths string `name:"watchPaths" short:"w" brief:"{cRunWatchPathsBrief}"`
|
||||
}
|
||||
cRunOutput struct{}
|
||||
)
|
||||
@ -81,34 +98,38 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
|
||||
}
|
||||
|
||||
app := &cRunApp{
|
||||
File: in.File,
|
||||
Path: in.Path,
|
||||
Options: in.Extra,
|
||||
File: in.File,
|
||||
Path: in.Path,
|
||||
Options: in.Extra,
|
||||
Args: in.Args,
|
||||
WatchPaths: strings.Split(in.WatchPaths, ","),
|
||||
}
|
||||
dirty := gtype.NewBool()
|
||||
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
|
||||
if gfile.ExtName(event.Path) != "go" {
|
||||
if gfile.ExtName(event.Path) != "go" && !matchWatchPaths(app.WatchPaths, event.Path) {
|
||||
return
|
||||
}
|
||||
|
||||
// Variable `dirty` is used for running the changes only one in one second.
|
||||
if !dirty.Cas(false, true) {
|
||||
return
|
||||
}
|
||||
|
||||
// With some delay in case of multiple code changes in very short interval.
|
||||
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
|
||||
defer dirty.Set(false)
|
||||
mlog.Printf(`go file changes: %s`, event.String())
|
||||
app.Run()
|
||||
mlog.Printf(`watched file changes: %s`, event.String())
|
||||
app.Run(ctx)
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
go app.Run()
|
||||
go app.Run(ctx)
|
||||
select {}
|
||||
}
|
||||
|
||||
func (app *cRunApp) Run() {
|
||||
func (app *cRunApp) Run(ctx context.Context) {
|
||||
// Rebuild and run the codes.
|
||||
renamePath := ""
|
||||
mlog.Printf("build: %s", app.File)
|
||||
@ -131,7 +152,7 @@ func (app *cRunApp) Run() {
|
||||
app.File,
|
||||
)
|
||||
mlog.Print(buildCommand)
|
||||
result, err := gproc.ShellExec(buildCommand)
|
||||
result, err := gproc.ShellExec(ctx, buildCommand)
|
||||
if err != nil {
|
||||
mlog.Printf("build error: \n%s%s", result, err.Error())
|
||||
return
|
||||
@ -140,7 +161,6 @@ func (app *cRunApp) Run() {
|
||||
if process != nil {
|
||||
if err := process.Kill(); err != nil {
|
||||
mlog.Debugf("kill process error: %s", err.Error())
|
||||
//return
|
||||
}
|
||||
}
|
||||
// Run the binary file.
|
||||
@ -149,13 +169,32 @@ func (app *cRunApp) Run() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Special handling for windows platform.
|
||||
// DO NOT USE "cmd /c" command.
|
||||
process = gproc.NewProcess(runCommand, nil)
|
||||
process = gproc.NewProcess(outputPath, strings.Fields(app.Args))
|
||||
} else {
|
||||
process = gproc.NewProcessCmd(runCommand, nil)
|
||||
}
|
||||
if pid, err := process.Start(); err != nil {
|
||||
if pid, err := process.Start(ctx); err != nil {
|
||||
mlog.Printf("build running error: %s", err.Error())
|
||||
} else {
|
||||
mlog.Printf("build running pid: %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
func matchWatchPaths(watchPaths []string, eventPath string) bool {
|
||||
for _, path := range watchPaths {
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
mlog.Printf("match watchPath '%s' error: %s", path, err.Error())
|
||||
continue
|
||||
}
|
||||
matched, err := filepath.Match(absPath, eventPath)
|
||||
if err != nil {
|
||||
mlog.Printf("match watchPath '%s' error: %s", path, err.Error())
|
||||
continue
|
||||
}
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@ -11,6 +16,8 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -32,7 +39,7 @@ like json/xml/yaml/toml/ini.
|
||||
cTplParseEg = `
|
||||
gf tpl parse -p ./template -v values.json -r
|
||||
gf tpl parse -p ./template -v values.json -n *.tpl -r
|
||||
gf tpl parse -p ./template -v values.json -d '${,}}' -r
|
||||
gf tpl parse -p ./template -v values.json -d '${{,}}' -r
|
||||
gf tpl parse -p ./template -v values.json -o ./template.parsed
|
||||
`
|
||||
cTplSupportValuesFilePattern = `*.json,*.xml,*.yaml,*.yml,*.toml,*.ini`
|
||||
@ -62,7 +69,7 @@ func init() {
|
||||
}
|
||||
|
||||
func (c *cTpl) Parse(ctx context.Context, in cTplParseInput) (out *cTplParseOutput, err error) {
|
||||
if in.Output == "" && in.Replace == false {
|
||||
if in.Output == "" && !in.Replace {
|
||||
return nil, gerror.New(`parameter output and replace should not be both empty`)
|
||||
}
|
||||
delimiters := gstr.SplitAndTrim(in.Delimiters, ",")
|
||||
|
||||
219
cmd/gf/internal/cmd/cmd_up.go
Normal file
219
cmd/gf/internal/cmd/cmd_up.go
Normal file
@ -0,0 +1,219 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/minio/selfupdate"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Up = cUp{}
|
||||
)
|
||||
|
||||
type cUp struct {
|
||||
g.Meta `name:"up" brief:"upgrade GoFrame version/tool to latest one in current project" eg:"{cUpEg}" `
|
||||
}
|
||||
|
||||
const (
|
||||
gfPackage = `github.com/gogf/gf/`
|
||||
cUpEg = `
|
||||
gf up
|
||||
gf up -a
|
||||
gf up -c
|
||||
gf up -cf
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cUpEg`: cUpEg,
|
||||
})
|
||||
}
|
||||
|
||||
type cUpInput struct {
|
||||
g.Meta `name:"up" config:"gfcli.up"`
|
||||
All bool `name:"all" short:"a" brief:"upgrade both version and cli, auto fix codes" orphan:"true"`
|
||||
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool" orphan:"true"`
|
||||
Fix bool `name:"fix" short:"f" brief:"auto fix codes(it only make sense if cli is to be upgraded)" orphan:"true"`
|
||||
}
|
||||
|
||||
type cUpOutput struct{}
|
||||
|
||||
func (c cUp) Index(ctx context.Context, in cUpInput) (out *cUpOutput, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
mlog.Print()
|
||||
mlog.Print(`👏congratulations! you've upgraded to the latest version of GoFrame! enjoy it!👏`)
|
||||
mlog.Print()
|
||||
}
|
||||
}()
|
||||
|
||||
var doUpgradeVersionOut *doUpgradeVersionOutput
|
||||
if in.All {
|
||||
in.Cli = true
|
||||
in.Fix = true
|
||||
}
|
||||
if doUpgradeVersionOut, err = c.doUpgradeVersion(ctx, in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in.Cli {
|
||||
if err = c.doUpgradeCLI(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if in.Cli && in.Fix {
|
||||
if doUpgradeVersionOut != nil && len(doUpgradeVersionOut.Items) > 0 {
|
||||
upgradedPathSet := gset.NewStrSet()
|
||||
for _, item := range doUpgradeVersionOut.Items {
|
||||
if !upgradedPathSet.AddIfNotExist(item.DirPath) {
|
||||
continue
|
||||
}
|
||||
if err = c.doAutoFixing(ctx, item.DirPath, item.Version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type doUpgradeVersionOutput struct {
|
||||
Items []doUpgradeVersionOutputItem
|
||||
}
|
||||
|
||||
type doUpgradeVersionOutputItem struct {
|
||||
DirPath string
|
||||
Version string
|
||||
}
|
||||
|
||||
func (c cUp) doUpgradeVersion(ctx context.Context, in cUpInput) (out *doUpgradeVersionOutput, err error) {
|
||||
mlog.Print(`start upgrading version...`)
|
||||
out = &doUpgradeVersionOutput{
|
||||
Items: make([]doUpgradeVersionOutputItem, 0),
|
||||
}
|
||||
type Package struct {
|
||||
Name string
|
||||
Version string
|
||||
}
|
||||
|
||||
var (
|
||||
temp string
|
||||
dirPath = gfile.Pwd()
|
||||
goModPath = gfile.Join(dirPath, "go.mod")
|
||||
)
|
||||
// It recursively upgrades the go.mod from sub folder to its parent folders.
|
||||
for {
|
||||
if gfile.Exists(goModPath) {
|
||||
var packages []Package
|
||||
err = gfile.ReadLines(goModPath, func(line string) error {
|
||||
line = gstr.Trim(line)
|
||||
line = gstr.TrimLeftStr(line, "require ")
|
||||
line = gstr.Trim(line)
|
||||
if gstr.HasPrefix(line, gfPackage) {
|
||||
array := gstr.SplitAndTrim(line, " ")
|
||||
packages = append(packages, Package{
|
||||
Name: array[0],
|
||||
Version: array[1],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, pkg := range packages {
|
||||
mlog.Printf(`upgrading "%s" from "%s" to "latest"`, pkg.Name, pkg.Version)
|
||||
// go get -u
|
||||
command := fmt.Sprintf(`cd %s && go get -u %s@latest`, dirPath, pkg.Name)
|
||||
if err = gproc.ShellRun(ctx, command); err != nil {
|
||||
return
|
||||
}
|
||||
// go mod tidy
|
||||
if err = utils.GoModTidy(ctx, dirPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.Items = append(out.Items, doUpgradeVersionOutputItem{
|
||||
DirPath: dirPath,
|
||||
Version: pkg.Version,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
temp = gfile.Dir(dirPath)
|
||||
if temp == "" || temp == dirPath {
|
||||
return
|
||||
}
|
||||
dirPath = temp
|
||||
goModPath = gfile.Join(dirPath, "go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
// doUpgradeCLI downloads the new version binary with process.
|
||||
func (c cUp) doUpgradeCLI(ctx context.Context) (err error) {
|
||||
mlog.Print(`start upgrading cli...`)
|
||||
var (
|
||||
downloadUrl = fmt.Sprintf(
|
||||
`https://github.com/gogf/gf/releases/latest/download/gf_%s_%s`,
|
||||
runtime.GOOS, runtime.GOARCH,
|
||||
)
|
||||
localSaveFilePath = gfile.SelfPath() + "~"
|
||||
)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
downloadUrl += ".exe"
|
||||
}
|
||||
|
||||
mlog.Printf(`start downloading "%s" to "%s", it may take some time`, downloadUrl, localSaveFilePath)
|
||||
err = utils.HTTPDownloadFileWithPercent(downloadUrl, localSaveFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
mlog.Printf(`new version cli binary is successfully installed to "%s"`, gfile.SelfPath())
|
||||
mlog.Printf(`remove temporary buffer file "%s"`, localSaveFilePath)
|
||||
_ = gfile.Remove(localSaveFilePath)
|
||||
}()
|
||||
|
||||
// It fails if file not exist or its size is less than 1MB.
|
||||
if !gfile.Exists(localSaveFilePath) || gfile.Size(localSaveFilePath) < 1024*1024 {
|
||||
mlog.Fatalf(`download "%s" to "%s" failed`, downloadUrl, localSaveFilePath)
|
||||
}
|
||||
|
||||
newFile, err := gfile.Open(localSaveFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// selfupdate
|
||||
err = selfupdate.Apply(newFile, selfupdate.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cUp) doAutoFixing(ctx context.Context, dirPath string, version string) (err error) {
|
||||
mlog.Printf(`auto fixing directory path "%s" from version "%s" ...`, dirPath, version)
|
||||
command := fmt.Sprintf(`gf fix -p %s`, dirPath)
|
||||
_ = gproc.ShellRun(ctx, command)
|
||||
return
|
||||
}
|
||||
@ -1,23 +1,39 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
Version = cVersion{}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultIndent = "{{indent}}"
|
||||
)
|
||||
|
||||
type cVersion struct {
|
||||
g.Meta `name:"version" brief:"show version information of current binary"`
|
||||
}
|
||||
@ -25,43 +41,102 @@ type cVersion struct {
|
||||
type cVersionInput struct {
|
||||
g.Meta `name:"version"`
|
||||
}
|
||||
|
||||
type cVersionOutput struct{}
|
||||
|
||||
func (c cVersion) Index(ctx context.Context, in cVersionInput) (*cVersionOutput, error) {
|
||||
info := gbuild.Info()
|
||||
if info.Git == "" {
|
||||
info.Git = "none"
|
||||
}
|
||||
mlog.Printf(`GoFrame CLI Tool %s, https://goframe.org`, gf.VERSION)
|
||||
gfVersion, err := c.getGFVersionOfCurrentProject()
|
||||
if err != nil {
|
||||
gfVersion = err.Error()
|
||||
detailBuffer := &detailBuffer{}
|
||||
detailBuffer.WriteString(fmt.Sprintf("%s", gf.VERSION))
|
||||
|
||||
detailBuffer.appendLine(0, "Welcome to GoFrame!")
|
||||
|
||||
detailBuffer.appendLine(0, "Env Detail:")
|
||||
goVersion, ok := getGoVersion()
|
||||
if ok {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Go Version: %s", goVersion))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("GF Version(go.mod): %s", getGoFrameVersion(2)))
|
||||
} else {
|
||||
gfVersion = gfVersion + " in current go.mod"
|
||||
}
|
||||
mlog.Printf(`GoFrame Version: %s`, gfVersion)
|
||||
mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath())
|
||||
if info.GoFrame == "" {
|
||||
mlog.Print(`Current is a custom installed version, no installation information.`)
|
||||
return nil, nil
|
||||
v, err := c.getGFVersionOfCurrentProject()
|
||||
if err == nil {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("GF Version(go.mod): %s", v))
|
||||
} else {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("GF Version(go.mod): %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Print(gstr.Trim(fmt.Sprintf(`
|
||||
CLI Built Detail:
|
||||
Go Version: %s
|
||||
GF Version: %s
|
||||
Git Commit: %s
|
||||
Build Time: %s
|
||||
`, info.Golang, info.GoFrame, info.Git, info.Time)))
|
||||
detailBuffer.appendLine(0, "CLI Detail:")
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Installed At: %s", gfile.SelfPath()))
|
||||
info := gbuild.Info()
|
||||
if info.GoFrame == "" {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built Go Version: %s", runtime.Version()))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built GF Version: %s", gf.VERSION))
|
||||
} else {
|
||||
if info.Git == "" {
|
||||
info.Git = "none"
|
||||
}
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built Go Version: %s", info.Golang))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built GF Version: %s", info.GoFrame))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Git Commit: %s", info.Git))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built Time: %s", info.Time))
|
||||
}
|
||||
|
||||
detailBuffer.appendLine(0, "Others Detail:")
|
||||
detailBuffer.appendLine(1, "Docs: https://goframe.org")
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Now : %s", time.Now().Format(time.RFC3339)))
|
||||
|
||||
mlog.Print(detailBuffer.replaceAllIndent(" "))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// detailBuffer is a buffer for detail information.
|
||||
type detailBuffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
// appendLine appends a line to the buffer with given indent level.
|
||||
func (d *detailBuffer) appendLine(indentLevel int, line string) {
|
||||
d.WriteString(fmt.Sprintf("\n%s%s", strings.Repeat(defaultIndent, indentLevel), line))
|
||||
}
|
||||
|
||||
// replaceAllIndent replaces the tab with given indent string and prints the buffer content.
|
||||
func (d *detailBuffer) replaceAllIndent(indentStr string) string {
|
||||
return strings.ReplaceAll(d.String(), defaultIndent, indentStr)
|
||||
}
|
||||
|
||||
// getGoFrameVersion returns the goframe version of current project using.
|
||||
func getGoFrameVersion(indentLevel int) (gfVersion string) {
|
||||
pkgInfo, err := gproc.ShellExec(context.Background(), `go list -f "{{if (not .Main)}}{{.Path}}@{{.Version}}{{end}}" -m all`)
|
||||
if err != nil {
|
||||
return "cannot find go.mod"
|
||||
}
|
||||
pkgList := gstr.Split(pkgInfo, "\n")
|
||||
for _, v := range pkgList {
|
||||
if strings.HasPrefix(v, "github.com/gogf/gf") {
|
||||
gfVersion += fmt.Sprintf("\n%s%s", strings.Repeat(defaultIndent, indentLevel), v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getGoVersion returns the go version
|
||||
func getGoVersion() (goVersion string, ok bool) {
|
||||
goVersion, err := gproc.ShellExec(context.Background(), "go version")
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
goVersion = gstr.TrimLeftStr(goVersion, "go version ")
|
||||
goVersion = gstr.TrimRightStr(goVersion, "\n")
|
||||
return goVersion, true
|
||||
}
|
||||
|
||||
// getGFVersionOfCurrentProject checks and returns the GoFrame version current project using.
|
||||
func (c cVersion) getGFVersionOfCurrentProject() (string, error) {
|
||||
goModPath := gfile.Join(gfile.Pwd(), "go.mod")
|
||||
if gfile.Exists(goModPath) {
|
||||
lines := gstr.SplitAndTrim(gfile.GetContents(goModPath), "\n")
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
line = gstr.TrimLeftStr(line, "require ")
|
||||
line = gstr.Trim(line)
|
||||
// Version 1.
|
||||
match, err := gregex.MatchString(`^github\.com/gogf/gf\s+(.+)$`, line)
|
||||
|
||||
11
cmd/gf/internal/cmd/cmd_z_init_test.go
Normal file
11
cmd/gf/internal/cmd/cmd_z_init_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import "context"
|
||||
|
||||
var ctx = context.Background()
|
||||
24
cmd/gf/internal/cmd/cmd_z_unit_fix_test.go
Normal file
24
cmd/gf/internal/cmd/cmd_z_unit_fix_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Fix_doFixV25Content(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
content = gtest.DataContent(`fix`, `fix25_content.go`)
|
||||
f = cFix{}
|
||||
)
|
||||
_, err := f.doFixV25Content(content)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
85
cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go
Normal file
85
cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/genctrl"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Gen_Ctrl_Default(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
path = gfile.Temp(guid.S())
|
||||
apiFolder = gtest.DataPath("genctrl", "api")
|
||||
in = genctrl.CGenCtrlInput{
|
||||
SrcFolder: apiFolder,
|
||||
DstFolder: path,
|
||||
WatchFile: "",
|
||||
SdkPath: "",
|
||||
SdkStdVersion: false,
|
||||
SdkNoV1: false,
|
||||
Clear: false,
|
||||
Merge: false,
|
||||
}
|
||||
)
|
||||
err := gutil.FillStructWithDefault(&in)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = gfile.Mkdir(path)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// apiInterface file
|
||||
var (
|
||||
genApi = apiFolder + filepath.FromSlash("/article/article.go")
|
||||
genApiExpect = apiFolder + filepath.FromSlash("/article/article_expect.go")
|
||||
)
|
||||
defer gfile.Remove(genApi)
|
||||
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
|
||||
|
||||
// files
|
||||
files, err := gfile.ScanDir(path, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
path + filepath.FromSlash("/article/article.go"),
|
||||
path + filepath.FromSlash("/article/article_new.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_create.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_get_list.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_get_one.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_update.go"),
|
||||
path + filepath.FromSlash("/article/article_v2_create.go"),
|
||||
path + filepath.FromSlash("/article/article_v2_update.go"),
|
||||
})
|
||||
|
||||
// content
|
||||
testPath := gtest.DataPath("genctrl", "controller")
|
||||
expectFiles := []string{
|
||||
testPath + filepath.FromSlash("/article/article.go"),
|
||||
testPath + filepath.FromSlash("/article/article_new.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_create.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_get_list.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_get_one.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_update.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v2_create.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v2_update.go"),
|
||||
}
|
||||
for i := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
}
|
||||
})
|
||||
}
|
||||
227
cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go
Normal file
227
cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/gendao"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
func dropTableWithDb(db gdb.DB, table string) {
|
||||
dropTableStmt := fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)
|
||||
if _, err := db.Exec(ctx, dropTableStmt); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Gen_Dao_Default(t *testing.T) {
|
||||
link := "mysql:root:12345678@tcp(127.0.0.1:3306)/test?loc=Local&parseTime=true"
|
||||
db, err := gdb.New(gdb.ConfigNode{
|
||||
Link: link,
|
||||
})
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
table = "table_user"
|
||||
sqlContent = fmt.Sprintf(
|
||||
gtest.DataContent(`gendao`, `user.tpl.sql`),
|
||||
table,
|
||||
)
|
||||
)
|
||||
dropTableWithDb(db, table)
|
||||
array := gstr.SplitAndTrim(sqlContent, ";")
|
||||
for _, v := range array {
|
||||
if _, err = db.Exec(ctx, v); err != nil {
|
||||
t.AssertNil(err)
|
||||
}
|
||||
}
|
||||
defer dropTableWithDb(db, table)
|
||||
|
||||
var (
|
||||
path = gfile.Temp(guid.S())
|
||||
group = "test"
|
||||
in = gendao.CGenDaoInput{
|
||||
Path: path,
|
||||
Link: link,
|
||||
Tables: "",
|
||||
TablesEx: "",
|
||||
Group: group,
|
||||
Prefix: "",
|
||||
RemovePrefix: "",
|
||||
JsonCase: "SnakeScreaming",
|
||||
ImportPrefix: "",
|
||||
DaoPath: "",
|
||||
DoPath: "",
|
||||
EntityPath: "",
|
||||
TplDaoIndexPath: "",
|
||||
TplDaoInternalPath: "",
|
||||
TplDaoDoPath: "",
|
||||
TplDaoEntityPath: "",
|
||||
StdTime: false,
|
||||
WithTime: false,
|
||||
GJsonSupport: false,
|
||||
OverwriteDao: false,
|
||||
DescriptionTag: false,
|
||||
NoJsonTag: false,
|
||||
NoModelComment: false,
|
||||
Clear: false,
|
||||
TypeMapping: nil,
|
||||
}
|
||||
)
|
||||
err = gutil.FillStructWithDefault(&in)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = gfile.Mkdir(path)
|
||||
t.AssertNil(err)
|
||||
|
||||
// for go mod import path auto retrieve.
|
||||
err = gfile.Copy(
|
||||
gtest.DataPath("gendao", "go.mod.txt"),
|
||||
gfile.Join(path, "go.mod"),
|
||||
)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = gendao.CGenDao{}.Dao(ctx, in)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
// files
|
||||
files, err := gfile.ScanDir(path, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
filepath.FromSlash(path + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(path + "/dao/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/entity/table_user.go"),
|
||||
})
|
||||
// content
|
||||
testPath := gtest.DataPath("gendao", "generated_user")
|
||||
expectFiles := []string{
|
||||
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/dao/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
|
||||
}
|
||||
for i, _ := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Gen_Dao_TypeMapping(t *testing.T) {
|
||||
link := "mysql:root:12345678@tcp(127.0.0.1:3306)/test?loc=Local&parseTime=true"
|
||||
db, err := gdb.New(gdb.ConfigNode{
|
||||
Link: link,
|
||||
})
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
table = "table_user"
|
||||
sqlContent = fmt.Sprintf(
|
||||
gtest.DataContent(`gendao`, `user.tpl.sql`),
|
||||
table,
|
||||
)
|
||||
)
|
||||
defer dropTableWithDb(db, table)
|
||||
array := gstr.SplitAndTrim(sqlContent, ";")
|
||||
for _, v := range array {
|
||||
if _, err = db.Exec(ctx, v); err != nil {
|
||||
t.AssertNil(err)
|
||||
}
|
||||
}
|
||||
defer dropTableWithDb(db, table)
|
||||
|
||||
var (
|
||||
path = gfile.Temp(guid.S())
|
||||
group = "test"
|
||||
in = gendao.CGenDaoInput{
|
||||
Path: path,
|
||||
Link: link,
|
||||
Tables: "",
|
||||
TablesEx: "",
|
||||
Group: group,
|
||||
Prefix: "",
|
||||
RemovePrefix: "",
|
||||
JsonCase: "",
|
||||
ImportPrefix: "",
|
||||
DaoPath: "",
|
||||
DoPath: "",
|
||||
EntityPath: "",
|
||||
TplDaoIndexPath: "",
|
||||
TplDaoInternalPath: "",
|
||||
TplDaoDoPath: "",
|
||||
TplDaoEntityPath: "",
|
||||
StdTime: false,
|
||||
WithTime: false,
|
||||
GJsonSupport: false,
|
||||
OverwriteDao: false,
|
||||
DescriptionTag: false,
|
||||
NoJsonTag: false,
|
||||
NoModelComment: false,
|
||||
Clear: false,
|
||||
TypeMapping: map[gendao.DBFieldTypeName]gendao.CustomAttributeType{
|
||||
"int": {
|
||||
Type: "int64",
|
||||
Import: "",
|
||||
},
|
||||
"decimal": {
|
||||
Type: "decimal.Decimal",
|
||||
Import: "github.com/shopspring/decimal",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
err = gutil.FillStructWithDefault(&in)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = gfile.Mkdir(path)
|
||||
t.AssertNil(err)
|
||||
|
||||
// for go mod import path auto retrieve.
|
||||
err = gfile.Copy(
|
||||
gtest.DataPath("gendao", "go.mod.txt"),
|
||||
gfile.Join(path, "go.mod"),
|
||||
)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = gendao.CGenDao{}.Dao(ctx, in)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
// files
|
||||
files, err := gfile.ScanDir(path, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
filepath.FromSlash(path + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(path + "/dao/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/entity/table_user.go"),
|
||||
})
|
||||
// content
|
||||
testPath := gtest.DataPath("gendao", "generated_user_type_mapping")
|
||||
expectFiles := []string{
|
||||
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/dao/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
|
||||
}
|
||||
for i, _ := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
}
|
||||
})
|
||||
}
|
||||
72
cmd/gf/internal/cmd/cmd_z_unit_gen_service_test.go
Normal file
72
cmd/gf/internal/cmd/cmd_z_unit_gen_service_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/genservice"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Gen_Service_Default(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
path = gfile.Temp(guid.S())
|
||||
dstFolder = path + filepath.FromSlash("/service")
|
||||
apiFolder = gtest.DataPath("genservice", "logic")
|
||||
in = genservice.CGenServiceInput{
|
||||
SrcFolder: apiFolder,
|
||||
DstFolder: dstFolder,
|
||||
DstFileNameCase: "Snake",
|
||||
WatchFile: "",
|
||||
StPattern: "",
|
||||
Packages: nil,
|
||||
ImportPrefix: "",
|
||||
Clear: false,
|
||||
}
|
||||
)
|
||||
err := gutil.FillStructWithDefault(&in)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = gfile.Mkdir(path)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
_, err = genservice.CGenService{}.Service(ctx, in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// logic file
|
||||
var (
|
||||
genApi = apiFolder + filepath.FromSlash("/logic.go")
|
||||
genApiExpect = apiFolder + filepath.FromSlash("/logic_expect.go")
|
||||
)
|
||||
defer gfile.Remove(genApi)
|
||||
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
|
||||
|
||||
// files
|
||||
files, err := gfile.ScanDir(dstFolder, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
dstFolder + filepath.FromSlash("/article.go"),
|
||||
})
|
||||
|
||||
// contents
|
||||
testPath := gtest.DataPath("genservice", "service")
|
||||
expectFiles := []string{
|
||||
testPath + filepath.FromSlash("/article.go"),
|
||||
}
|
||||
for i := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
}
|
||||
})
|
||||
}
|
||||
236
cmd/gf/internal/cmd/genctrl/genctrl.go
Normal file
236
cmd/gf/internal/cmd/genctrl/genctrl.go
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
CGenCtrlConfig = `gfcli.gen.ctrl`
|
||||
CGenCtrlUsage = `gf gen ctrl [OPTION]`
|
||||
CGenCtrlBrief = `parse api definitions to generate controller/sdk go files`
|
||||
CGenCtrlEg = `
|
||||
gf gen ctrl
|
||||
`
|
||||
CGenCtrlBriefSrcFolder = `source folder path to be parsed. default: api`
|
||||
CGenCtrlBriefDstFolder = `destination folder path storing automatically generated go files. default: internal/controller`
|
||||
CGenCtrlBriefWatchFile = `used in file watcher, it re-generates go files only if given file is under srcFolder`
|
||||
CGenCtrlBriefSdkPath = `also generate SDK go files for api definitions to specified directory`
|
||||
CGenCtrlBriefSdkStdVersion = `use standard version prefix for generated sdk request path`
|
||||
CGenCtrlBriefSdkNoV1 = `do not add version suffix for interface module name if version is v1`
|
||||
CGenCtrlBriefClear = `auto delete generated and unimplemented controller go files if api definitions are missing`
|
||||
CGenCtrlControllerMerge = `generate all controller files into one go file by name of api definition source go file`
|
||||
)
|
||||
|
||||
const (
|
||||
PatternApiDefinition = `type[\s\(]+(\w+)Req\s+struct\s+{([\s\S]+?)}`
|
||||
PatternCtrlDefinition = `func\s+\(.+?\)\s+\w+\(.+?\*(\w+)\.(\w+)Req\)\s+\(.+?\*(\w+)\.(\w+)Res,\s+\w+\s+error\)\s+{`
|
||||
)
|
||||
|
||||
const (
|
||||
genCtrlFileLockSeconds = 10
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenCtrlConfig`: CGenCtrlConfig,
|
||||
`CGenCtrlUsage`: CGenCtrlUsage,
|
||||
`CGenCtrlBrief`: CGenCtrlBrief,
|
||||
`CGenCtrlEg`: CGenCtrlEg,
|
||||
`CGenCtrlBriefSrcFolder`: CGenCtrlBriefSrcFolder,
|
||||
`CGenCtrlBriefDstFolder`: CGenCtrlBriefDstFolder,
|
||||
`CGenCtrlBriefWatchFile`: CGenCtrlBriefWatchFile,
|
||||
`CGenCtrlBriefSdkPath`: CGenCtrlBriefSdkPath,
|
||||
`CGenCtrlBriefSdkStdVersion`: CGenCtrlBriefSdkStdVersion,
|
||||
`CGenCtrlBriefSdkNoV1`: CGenCtrlBriefSdkNoV1,
|
||||
`CGenCtrlBriefClear`: CGenCtrlBriefClear,
|
||||
`CGenCtrlControllerMerge`: CGenCtrlControllerMerge,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
CGenCtrl struct{}
|
||||
CGenCtrlInput struct {
|
||||
g.Meta `name:"ctrl" config:"{CGenCtrlConfig}" usage:"{CGenCtrlUsage}" brief:"{CGenCtrlBrief}" eg:"{CGenCtrlEg}"`
|
||||
SrcFolder string `short:"s" name:"srcFolder" brief:"{CGenCtrlBriefSrcFolder}" d:"api"`
|
||||
DstFolder string `short:"d" name:"dstFolder" brief:"{CGenCtrlBriefDstFolder}" d:"internal/controller"`
|
||||
WatchFile string `short:"w" name:"watchFile" brief:"{CGenCtrlBriefWatchFile}"`
|
||||
SdkPath string `short:"k" name:"sdkPath" brief:"{CGenCtrlBriefSdkPath}"`
|
||||
SdkStdVersion bool `short:"v" name:"sdkStdVersion" brief:"{CGenCtrlBriefSdkStdVersion}" orphan:"true"`
|
||||
SdkNoV1 bool `short:"n" name:"sdkNoV1" brief:"{CGenCtrlBriefSdkNoV1}" orphan:"true"`
|
||||
Clear bool `short:"c" name:"clear" brief:"{CGenCtrlBriefClear}" orphan:"true"`
|
||||
Merge bool `short:"m" name:"merge" brief:"{CGenCtrlControllerMerge}" orphan:"true"`
|
||||
}
|
||||
CGenCtrlOutput struct{}
|
||||
)
|
||||
|
||||
func (c CGenCtrl) Ctrl(ctx context.Context, in CGenCtrlInput) (out *CGenCtrlOutput, err error) {
|
||||
if in.WatchFile != "" {
|
||||
err = c.generateByWatchFile(
|
||||
in.WatchFile, in.SdkPath, in.SdkStdVersion, in.SdkNoV1, in.Clear, in.Merge,
|
||||
)
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
if !gfile.Exists(in.SrcFolder) {
|
||||
mlog.Fatalf(`source folder path "%s" does not exist`, in.SrcFolder)
|
||||
}
|
||||
// retrieve all api modules.
|
||||
apiModuleFolderPaths, err := gfile.ScanDir(in.SrcFolder, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, apiModuleFolderPath := range apiModuleFolderPaths {
|
||||
if !gfile.IsDir(apiModuleFolderPath) {
|
||||
continue
|
||||
}
|
||||
// generate go files by api module.
|
||||
var (
|
||||
module = gfile.Basename(apiModuleFolderPath)
|
||||
dstModuleFolderPath = gfile.Join(in.DstFolder, module)
|
||||
)
|
||||
err = c.generateByModule(
|
||||
apiModuleFolderPath, dstModuleFolderPath, in.SdkPath,
|
||||
in.SdkStdVersion, in.SdkNoV1, in.Clear, in.Merge,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenCtrl) generateByWatchFile(watchFile, sdkPath string, sdkStdVersion, sdkNoV1, clear, merge bool) (err error) {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
flockContent = gfile.GetContents(flockFilePath)
|
||||
)
|
||||
if flockContent != "" {
|
||||
if gtime.Timestamp()-gconv.Int64(flockContent) < genCtrlFileLockSeconds {
|
||||
// If another generating process is running, it just exits.
|
||||
mlog.Debug(`another "gen service" process is running, exit`)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer gfile.Remove(flockFilePath)
|
||||
_ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
|
||||
|
||||
// check this updated file is an api file.
|
||||
// watch file should be in standard goframe project structure.
|
||||
var (
|
||||
apiVersionPath = gfile.Dir(watchFile)
|
||||
apiModuleFolderPath = gfile.Dir(apiVersionPath)
|
||||
shouldBeNameOfAPi = gfile.Basename(gfile.Dir(apiModuleFolderPath))
|
||||
)
|
||||
if shouldBeNameOfAPi != "api" {
|
||||
return nil
|
||||
}
|
||||
// watch file should have api definitions.
|
||||
if gfile.Exists(watchFile) {
|
||||
if !gregex.IsMatchString(PatternApiDefinition, gfile.GetContents(watchFile)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
projectRootPath = gfile.Dir(gfile.Dir(apiModuleFolderPath))
|
||||
module = gfile.Basename(apiModuleFolderPath)
|
||||
dstModuleFolderPath = gfile.Join(projectRootPath, "internal", "controller", module)
|
||||
)
|
||||
return c.generateByModule(
|
||||
apiModuleFolderPath, dstModuleFolderPath, sdkPath, sdkStdVersion, sdkNoV1, clear, merge,
|
||||
)
|
||||
}
|
||||
|
||||
// parseApiModule parses certain api and generate associated go files by certain module, not all api modules.
|
||||
func (c CGenCtrl) generateByModule(
|
||||
apiModuleFolderPath, dstModuleFolderPath, sdkPath string,
|
||||
sdkStdVersion, sdkNoV1, clear, merge bool,
|
||||
) (err error) {
|
||||
// parse src and dst folder go files.
|
||||
apiItemsInSrc, err := c.getApiItemsInSrc(apiModuleFolderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiItemsInDst, err := c.getApiItemsInDst(dstModuleFolderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// generate api interface go files.
|
||||
if err = newApiInterfaceGenerator().Generate(apiModuleFolderPath, apiItemsInSrc); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// generate controller go files.
|
||||
// api filtering for already implemented api controllers.
|
||||
var (
|
||||
alreadyImplementedCtrlSet = gset.NewStrSet()
|
||||
toBeImplementedApiItems = make([]apiItem, 0)
|
||||
)
|
||||
for _, item := range apiItemsInDst {
|
||||
alreadyImplementedCtrlSet.Add(item.String())
|
||||
}
|
||||
for _, item := range apiItemsInSrc {
|
||||
if alreadyImplementedCtrlSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
toBeImplementedApiItems = append(toBeImplementedApiItems, item)
|
||||
}
|
||||
if len(toBeImplementedApiItems) > 0 {
|
||||
err = newControllerGenerator().Generate(dstModuleFolderPath, toBeImplementedApiItems, merge)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// delete unimplemented controllers if api definitions are missing.
|
||||
if clear {
|
||||
var (
|
||||
apiDefinitionSet = gset.NewStrSet()
|
||||
extraApiItemsInCtrl = make([]apiItem, 0)
|
||||
)
|
||||
for _, item := range apiItemsInSrc {
|
||||
apiDefinitionSet.Add(item.String())
|
||||
}
|
||||
for _, item := range apiItemsInDst {
|
||||
if apiDefinitionSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
extraApiItemsInCtrl = append(extraApiItemsInCtrl, item)
|
||||
}
|
||||
if len(extraApiItemsInCtrl) > 0 {
|
||||
err = newControllerClearer().Clear(dstModuleFolderPath, extraApiItemsInCtrl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate sdk go files.
|
||||
if sdkPath != "" {
|
||||
if err = newApiSdkGenerator().Generate(apiItemsInSrc, sdkPath, sdkStdVersion, sdkNoV1); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
23
cmd/gf/internal/cmd/genctrl/genctrl_api_item.go
Normal file
23
cmd/gf/internal/cmd/genctrl/genctrl_api_item.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import "github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
type apiItem struct {
|
||||
Import string `eg:"demo.com/api/user/v1"`
|
||||
FileName string `eg:"user"`
|
||||
Module string `eg:"user"`
|
||||
Version string `eg:"v1"`
|
||||
MethodName string `eg:"GetList"`
|
||||
}
|
||||
|
||||
func (a apiItem) String() string {
|
||||
return gstr.Join([]string{
|
||||
a.Import, a.Module, a.Version, a.MethodName,
|
||||
}, ",")
|
||||
}
|
||||
145
cmd/gf/internal/cmd/genctrl/genctrl_calculate.go
Normal file
145
cmd/gf/internal/cmd/genctrl/genctrl_calculate.go
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func (c CGenCtrl) getApiItemsInSrc(apiModuleFolderPath string) (items []apiItem, err error) {
|
||||
var (
|
||||
fileContent string
|
||||
importPath string
|
||||
)
|
||||
// The second level folders: versions.
|
||||
apiVersionFolderPaths, err := gfile.ScanDir(apiModuleFolderPath, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, apiVersionFolderPath := range apiVersionFolderPaths {
|
||||
if !gfile.IsDir(apiVersionFolderPath) {
|
||||
continue
|
||||
}
|
||||
// The second level folders: versions.
|
||||
apiFileFolderPaths, err := gfile.ScanDir(apiVersionFolderPath, "*.go", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importPath = utils.GetImportPath(apiVersionFolderPath)
|
||||
for _, apiFileFolderPath := range apiFileFolderPaths {
|
||||
if gfile.IsDir(apiFileFolderPath) {
|
||||
continue
|
||||
}
|
||||
fileContent = gfile.GetContents(apiFileFolderPath)
|
||||
matches, err := gregex.MatchAllString(PatternApiDefinition, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
methodName = match[1]
|
||||
structBody = match[2]
|
||||
)
|
||||
// ignore struct name that match a request, but has no g.Meta in its body.
|
||||
if !gstr.Contains(structBody, `g.Meta`) {
|
||||
continue
|
||||
}
|
||||
item := apiItem{
|
||||
Import: gstr.Trim(importPath, `"`),
|
||||
FileName: gfile.Name(apiFileFolderPath),
|
||||
Module: gfile.Basename(apiModuleFolderPath),
|
||||
Version: gfile.Basename(apiVersionFolderPath),
|
||||
MethodName: methodName,
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenCtrl) getApiItemsInDst(dstFolder string) (items []apiItem, err error) {
|
||||
if !gfile.Exists(dstFolder) {
|
||||
return nil, nil
|
||||
}
|
||||
type importItem struct {
|
||||
Path string
|
||||
Alias string
|
||||
}
|
||||
var fileContent string
|
||||
filePaths, err := gfile.ScanDir(dstFolder, "*.go", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, filePath := range filePaths {
|
||||
fileContent = gfile.GetContents(filePath)
|
||||
match, err := gregex.MatchString(`import\s+\(([\s\S]+?)\)`, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(match) < 2 {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
array []string
|
||||
importItems []importItem
|
||||
importLines = gstr.SplitAndTrim(match[1], "\n")
|
||||
module = gfile.Basename(gfile.Dir(filePath))
|
||||
)
|
||||
// retrieve all imports.
|
||||
for _, importLine := range importLines {
|
||||
array = gstr.SplitAndTrim(importLine, " ")
|
||||
if len(array) == 2 {
|
||||
importItems = append(importItems, importItem{
|
||||
Path: gstr.Trim(array[1], `"`),
|
||||
Alias: array[0],
|
||||
})
|
||||
} else {
|
||||
importItems = append(importItems, importItem{
|
||||
Path: gstr.Trim(array[0], `"`),
|
||||
})
|
||||
}
|
||||
}
|
||||
// retrieve all api usages.
|
||||
matches, err := gregex.MatchAllString(PatternCtrlDefinition, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, match = range matches {
|
||||
// try to find the import path of the api.
|
||||
var (
|
||||
importPath string
|
||||
version = match[1]
|
||||
methodName = match[2] // not the function name, but the method name in api definition.
|
||||
)
|
||||
for _, item := range importItems {
|
||||
if item.Alias != "" {
|
||||
if item.Alias == version {
|
||||
importPath = item.Path
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if gfile.Basename(item.Path) == version {
|
||||
importPath = item.Path
|
||||
break
|
||||
}
|
||||
}
|
||||
item := apiItem{
|
||||
Import: gstr.Trim(importPath, `"`),
|
||||
Module: module,
|
||||
Version: gfile.Basename(importPath),
|
||||
MethodName: methodName,
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
164
cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl.go
Normal file
164
cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl.go
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type controllerGenerator struct{}
|
||||
|
||||
func newControllerGenerator() *controllerGenerator {
|
||||
return &controllerGenerator{}
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) Generate(dstModuleFolderPath string, apiModuleApiItems []apiItem, merge bool) (err error) {
|
||||
var (
|
||||
doneApiItemSet = gset.NewStrSet()
|
||||
)
|
||||
for _, item := range apiModuleApiItems {
|
||||
if doneApiItemSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
// retrieve all api items of the same module.
|
||||
var (
|
||||
subItems = c.getSubItemsByModuleAndVersion(apiModuleApiItems, item.Module, item.Version)
|
||||
importPath = gstr.Replace(gfile.Dir(item.Import), "\\", "/", -1)
|
||||
)
|
||||
if err = c.doGenerateCtrlNewByModuleAndVersion(
|
||||
dstModuleFolderPath, item.Module, item.Version, importPath,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
for _, subItem := range subItems {
|
||||
if err = c.doGenerateCtrlItem(dstModuleFolderPath, subItem, merge); err != nil {
|
||||
return
|
||||
}
|
||||
doneApiItemSet.Add(subItem.String())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) getSubItemsByModuleAndVersion(items []apiItem, module, version string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module && item.Version == version {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) doGenerateCtrlNewByModuleAndVersion(
|
||||
dstModuleFolderPath, module, version, importPath string,
|
||||
) (err error) {
|
||||
var (
|
||||
moduleFilePath = filepath.FromSlash(gfile.Join(dstModuleFolderPath, module+".go"))
|
||||
moduleFilePathNew = filepath.FromSlash(gfile.Join(dstModuleFolderPath, module+"_new.go"))
|
||||
ctrlName = fmt.Sprintf(`Controller%s`, gstr.UcFirst(version))
|
||||
interfaceName = fmt.Sprintf(`%s.I%s%s`, module, gstr.CaseCamel(module), gstr.UcFirst(version))
|
||||
newFuncName = fmt.Sprintf(`New%s`, gstr.UcFirst(version))
|
||||
newFuncNameDefinition = fmt.Sprintf(`func %s()`, newFuncName)
|
||||
alreadyCreated bool
|
||||
)
|
||||
if !gfile.Exists(moduleFilePath) {
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerEmpty, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
})
|
||||
if err = gfile.PutContents(moduleFilePath, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Printf(`generated: %s`, moduleFilePath)
|
||||
}
|
||||
if !gfile.Exists(moduleFilePathNew) {
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerNewEmpty, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
"{ImportPath}": fmt.Sprintf(`"%s"`, importPath),
|
||||
})
|
||||
if err = gfile.PutContents(moduleFilePathNew, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Printf(`generated: %s`, moduleFilePathNew)
|
||||
}
|
||||
filePaths, err := gfile.ScanDir(dstModuleFolderPath, "*.go", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, filePath := range filePaths {
|
||||
if gstr.Contains(gfile.GetContents(filePath), newFuncNameDefinition) {
|
||||
alreadyCreated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !alreadyCreated {
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerNewFunc, g.MapStrStr{
|
||||
"{CtrlName}": ctrlName,
|
||||
"{NewFuncName}": newFuncName,
|
||||
"{InterfaceName}": interfaceName,
|
||||
})
|
||||
err = gfile.PutContentsAppend(moduleFilePathNew, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) doGenerateCtrlItem(dstModuleFolderPath string, item apiItem, merge bool) (err error) {
|
||||
var (
|
||||
methodNameSnake = gstr.CaseSnake(item.MethodName)
|
||||
ctrlName = fmt.Sprintf(`Controller%s`, gstr.UcFirst(item.Version))
|
||||
methodFilePath = filepath.FromSlash(gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, item.Module, item.Version, methodNameSnake,
|
||||
)))
|
||||
)
|
||||
var content string
|
||||
|
||||
if merge {
|
||||
methodFilePath = gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, item.Module, item.Version, item.FileName,
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
if gfile.Exists(methodFilePath) {
|
||||
content = gstr.ReplaceByMap(consts.TemplateGenCtrlControllerMethodFuncMerge, g.MapStrStr{
|
||||
"{Module}": item.Module,
|
||||
"{CtrlName}": ctrlName,
|
||||
"{Version}": item.Version,
|
||||
"{MethodName}": item.MethodName,
|
||||
})
|
||||
|
||||
if gstr.Contains(gfile.GetContents(methodFilePath), fmt.Sprintf(`func (c *%v) %v`, ctrlName, item.MethodName)) {
|
||||
return
|
||||
}
|
||||
if err = gfile.PutContentsAppend(methodFilePath, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
content = gstr.ReplaceByMap(consts.TemplateGenCtrlControllerMethodFunc, g.MapStrStr{
|
||||
"{Module}": item.Module,
|
||||
"{ImportPath}": item.Import,
|
||||
"{CtrlName}": ctrlName,
|
||||
"{Version}": item.Version,
|
||||
"{MethodName}": item.MethodName,
|
||||
})
|
||||
if err = gfile.PutContents(methodFilePath, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mlog.Printf(`generated: %s`, methodFilePath)
|
||||
return
|
||||
}
|
||||
57
cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go
Normal file
57
cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type controllerClearer struct{}
|
||||
|
||||
func newControllerClearer() *controllerClearer {
|
||||
return &controllerClearer{}
|
||||
}
|
||||
|
||||
func (c *controllerClearer) Clear(dstModuleFolderPath string, extraApiItemsInCtrl []apiItem) (err error) {
|
||||
for _, item := range extraApiItemsInCtrl {
|
||||
if err = c.doClear(dstModuleFolderPath, item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerClearer) doClear(dstModuleFolderPath string, item apiItem) (err error) {
|
||||
var (
|
||||
methodNameSnake = gstr.CaseSnake(item.MethodName)
|
||||
methodFilePath = gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, item.Module, item.Version, methodNameSnake,
|
||||
))
|
||||
fileContent = gstr.Trim(gfile.GetContents(methodFilePath))
|
||||
)
|
||||
match, err := gregex.MatchString(`.+?Req.+?Res.+?{([\s\S]+?)}`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(match) > 1 {
|
||||
implements := gstr.Trim(match[1])
|
||||
// One line.
|
||||
if !gstr.Contains(implements, "\n") && gstr.Contains(implements, `CodeNotImplemented`) {
|
||||
mlog.Printf(
|
||||
`remove unimplemented and of no api definitions controller file: %s`,
|
||||
methodFilePath,
|
||||
)
|
||||
err = gfile.Remove(methodFilePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
117
cmd/gf/internal/cmd/genctrl/genctrl_generate_interface.go
Normal file
117
cmd/gf/internal/cmd/genctrl/genctrl_generate_interface.go
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type apiInterfaceGenerator struct{}
|
||||
|
||||
func newApiInterfaceGenerator() *apiInterfaceGenerator {
|
||||
return &apiInterfaceGenerator{}
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) Generate(apiModuleFolderPath string, apiModuleApiItems []apiItem) (err error) {
|
||||
if len(apiModuleApiItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
var firstApiItem = apiModuleApiItems[0]
|
||||
if err = c.doGenerate(apiModuleFolderPath, firstApiItem.Module, apiModuleApiItems); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) doGenerate(apiModuleFolderPath string, module string, items []apiItem) (err error) {
|
||||
var (
|
||||
moduleFilePath = filepath.FromSlash(gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.go`, module)))
|
||||
importPathMap = gmap.NewListMap()
|
||||
importPaths []string
|
||||
)
|
||||
// if there's already exist file that with the same but not auto generated go file,
|
||||
// it uses another file name.
|
||||
if !utils.IsFileDoNotEdit(moduleFilePath) {
|
||||
moduleFilePath = filepath.FromSlash(gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.if.go`, module)))
|
||||
}
|
||||
// all import paths.
|
||||
importPathMap.Set("\t"+`"context"`+"\n", 1)
|
||||
for _, item := range items {
|
||||
importPathMap.Set(fmt.Sprintf("\t"+`"%s"`, item.Import), 1)
|
||||
}
|
||||
importPaths = gconv.Strings(importPathMap.Keys())
|
||||
// interface definitions.
|
||||
var (
|
||||
doneApiItemSet = gset.NewStrSet()
|
||||
interfaceDefinition string
|
||||
interfaceContent = gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlApiInterface, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
"{ImportPaths}": gstr.Join(importPaths, "\n"),
|
||||
}))
|
||||
)
|
||||
for _, item := range items {
|
||||
if doneApiItemSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
// retrieve all api items of the same module.
|
||||
subItems := c.getSubItemsByModuleAndVersion(items, item.Module, item.Version)
|
||||
var (
|
||||
method string
|
||||
methods = make([]string, 0)
|
||||
interfaceName = fmt.Sprintf(`I%s%s`, gstr.CaseCamel(item.Module), gstr.UcFirst(item.Version))
|
||||
)
|
||||
for _, subItem := range subItems {
|
||||
method = fmt.Sprintf(
|
||||
"\t%s(ctx context.Context, req *%s.%sReq) (res *%s.%sRes, err error)",
|
||||
subItem.MethodName, subItem.Version, subItem.MethodName, subItem.Version, subItem.MethodName,
|
||||
)
|
||||
methods = append(methods, method)
|
||||
doneApiItemSet.Add(subItem.String())
|
||||
}
|
||||
interfaceDefinition += fmt.Sprintf("type %s interface {", interfaceName)
|
||||
interfaceDefinition += "\n"
|
||||
interfaceDefinition += gstr.Join(methods, "\n")
|
||||
interfaceDefinition += "\n"
|
||||
interfaceDefinition += fmt.Sprintf("}")
|
||||
interfaceDefinition += "\n\n"
|
||||
}
|
||||
interfaceContent = gstr.TrimLeft(gstr.ReplaceByMap(interfaceContent, g.MapStrStr{
|
||||
"{Interfaces}": gstr.TrimRightStr(interfaceDefinition, "\n", 2),
|
||||
}))
|
||||
err = gfile.PutContents(moduleFilePath, interfaceContent)
|
||||
mlog.Printf(`generated: %s`, moduleFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) getSubItemsByModule(items []apiItem, module string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) getSubItemsByModuleAndVersion(items []apiItem, module, version string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module && item.Version == version {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
195
cmd/gf/internal/cmd/genctrl/genctrl_generate_sdk.go
Normal file
195
cmd/gf/internal/cmd/genctrl/genctrl_generate_sdk.go
Normal file
@ -0,0 +1,195 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type apiSdkGenerator struct{}
|
||||
|
||||
func newApiSdkGenerator() *apiSdkGenerator {
|
||||
return &apiSdkGenerator{}
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) Generate(apiModuleApiItems []apiItem, sdkFolderPath string, sdkStdVersion, sdkNoV1 bool) (err error) {
|
||||
if err = c.doGenerateSdkPkgFile(sdkFolderPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var doneApiItemSet = gset.NewStrSet()
|
||||
for _, item := range apiModuleApiItems {
|
||||
if doneApiItemSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
// retrieve all api items of the same module.
|
||||
subItems := c.getSubItemsByModuleAndVersion(apiModuleApiItems, item.Module, item.Version)
|
||||
if err = c.doGenerateSdkIClient(sdkFolderPath, item.Import, item.Module, item.Version, sdkNoV1); err != nil {
|
||||
return
|
||||
}
|
||||
if err = c.doGenerateSdkImplementer(
|
||||
subItems, sdkFolderPath, item.Import, item.Module, item.Version, sdkStdVersion, sdkNoV1,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
for _, subItem := range subItems {
|
||||
doneApiItemSet.Add(subItem.String())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) doGenerateSdkPkgFile(sdkFolderPath string) (err error) {
|
||||
var (
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
pkgFilePath = filepath.FromSlash(gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.go`, pkgName)))
|
||||
fileContent string
|
||||
)
|
||||
if gfile.Exists(pkgFilePath) {
|
||||
return nil
|
||||
}
|
||||
fileContent = gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkPkgNew, g.MapStrStr{
|
||||
"{PkgName}": pkgName,
|
||||
}))
|
||||
err = gfile.PutContents(pkgFilePath, fileContent)
|
||||
mlog.Printf(`generated: %s`, pkgFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) doGenerateSdkIClient(
|
||||
sdkFolderPath, versionImportPath, module, version string, sdkNoV1 bool,
|
||||
) (err error) {
|
||||
var (
|
||||
fileContent string
|
||||
isDirty bool
|
||||
isExist bool
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
funcName = gstr.CaseCamel(module) + gstr.UcFirst(version)
|
||||
interfaceName = fmt.Sprintf(`I%s`, funcName)
|
||||
moduleImportPath = gstr.Replace(fmt.Sprintf(`"%s"`, gfile.Dir(versionImportPath)), "\\", "/", -1)
|
||||
iClientFilePath = filepath.FromSlash(gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.iclient.go`, pkgName)))
|
||||
interfaceFuncDefinition = fmt.Sprintf(
|
||||
`%s() %s.%s`,
|
||||
gstr.CaseCamel(module)+gstr.UcFirst(version), module, interfaceName,
|
||||
)
|
||||
)
|
||||
if sdkNoV1 && version == "v1" {
|
||||
interfaceFuncDefinition = fmt.Sprintf(
|
||||
`%s() %s.%s`,
|
||||
gstr.CaseCamel(module), module, interfaceName,
|
||||
)
|
||||
}
|
||||
if isExist = gfile.Exists(iClientFilePath); isExist {
|
||||
fileContent = gfile.GetContents(iClientFilePath)
|
||||
} else {
|
||||
fileContent = gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkIClient, g.MapStrStr{
|
||||
"{PkgName}": pkgName,
|
||||
}))
|
||||
}
|
||||
|
||||
// append the import path to current import paths.
|
||||
if !gstr.Contains(fileContent, moduleImportPath) {
|
||||
isDirty = true
|
||||
fileContent, err = gregex.ReplaceString(
|
||||
`(import \([\s\S]*?)\)`,
|
||||
fmt.Sprintf("$1\t%s\n)", moduleImportPath),
|
||||
fileContent,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// append the function definition to interface definition.
|
||||
if !gstr.Contains(fileContent, interfaceFuncDefinition) {
|
||||
isDirty = true
|
||||
fileContent, err = gregex.ReplaceString(
|
||||
`(type IClient interface {[\s\S]*?)}`,
|
||||
fmt.Sprintf("$1\t%s\n}", interfaceFuncDefinition),
|
||||
fileContent,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if isDirty {
|
||||
err = gfile.PutContents(iClientFilePath, fileContent)
|
||||
if isExist {
|
||||
mlog.Printf(`updated: %s`, iClientFilePath)
|
||||
} else {
|
||||
mlog.Printf(`generated: %s`, iClientFilePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) doGenerateSdkImplementer(
|
||||
items []apiItem, sdkFolderPath, versionImportPath, module, version string, sdkStdVersion, sdkNoV1 bool,
|
||||
) (err error) {
|
||||
var (
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
moduleNameCamel = gstr.CaseCamel(module)
|
||||
moduleNameSnake = gstr.CaseSnake(module)
|
||||
moduleImportPath = gstr.Replace(gfile.Dir(versionImportPath), "\\", "/", -1)
|
||||
versionPrefix = ""
|
||||
implementerName = moduleNameCamel + gstr.UcFirst(version)
|
||||
implementerFilePath = filepath.FromSlash(gfile.Join(sdkFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, pkgName, moduleNameSnake, version,
|
||||
)))
|
||||
)
|
||||
if sdkNoV1 && version == "v1" {
|
||||
implementerName = moduleNameCamel
|
||||
}
|
||||
// implementer file template.
|
||||
var importPaths = make([]string, 0)
|
||||
importPaths = append(importPaths, fmt.Sprintf("\t\"%s\"", moduleImportPath))
|
||||
importPaths = append(importPaths, fmt.Sprintf("\t\"%s\"", versionImportPath))
|
||||
implementerFileContent := gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkImplementer, g.MapStrStr{
|
||||
"{PkgName}": pkgName,
|
||||
"{ImportPaths}": gstr.Join(importPaths, "\n"),
|
||||
"{ImplementerName}": implementerName,
|
||||
}))
|
||||
// implementer new function definition.
|
||||
if sdkStdVersion {
|
||||
versionPrefix = fmt.Sprintf(`/api/%s`, version)
|
||||
}
|
||||
implementerFileContent += gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkImplementerNew, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
"{VersionPrefix}": versionPrefix,
|
||||
"{ImplementerName}": implementerName,
|
||||
}))
|
||||
// implementer functions definitions.
|
||||
for _, item := range items {
|
||||
implementerFileContent += gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkImplementerFunc, g.MapStrStr{
|
||||
"{Version}": item.Version,
|
||||
"{MethodName}": item.MethodName,
|
||||
"{ImplementerName}": implementerName,
|
||||
}))
|
||||
implementerFileContent += "\n"
|
||||
}
|
||||
err = gfile.PutContents(implementerFilePath, implementerFileContent)
|
||||
mlog.Printf(`generated: %s`, implementerFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) getSubItemsByModuleAndVersion(items []apiItem, module, version string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module && item.Version == version {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
430
cmd/gf/internal/cmd/gendao/gendao.go
Normal file
430
cmd/gf/internal/cmd/gendao/gendao.go
Normal file
@ -0,0 +1,430 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
CGenDaoConfig = `gfcli.gen.dao`
|
||||
CGenDaoUsage = `gf gen dao [OPTION]`
|
||||
CGenDaoBrief = `automatically generate go files for dao/do/entity`
|
||||
CGenDaoEg = `
|
||||
gf gen dao
|
||||
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen dao -p ./model -g user-center -t user,user_detail,user_login
|
||||
gf gen dao -r user_
|
||||
`
|
||||
|
||||
CGenDaoAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
dao:
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
tables: "order,products"
|
||||
jsonCase: "CamelLower"
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "./my-app"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
typeMapping:
|
||||
decimal:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
numeric:
|
||||
type: string
|
||||
`
|
||||
CGenDaoBriefPath = `directory path for generated files`
|
||||
CGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
CGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
CGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
|
||||
CGenDaoBriefPrefix = `add prefix for all table of specified link/database tables`
|
||||
CGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
CGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
|
||||
CGenDaoBriefWithTime = `add created time for auto produced go files`
|
||||
CGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
|
||||
CGenDaoBriefImportPrefix = `custom import prefix for generated go files`
|
||||
CGenDaoBriefDaoPath = `directory path for storing generated dao files under path`
|
||||
CGenDaoBriefDoPath = `directory path for storing generated do files under path`
|
||||
CGenDaoBriefEntityPath = `directory path for storing generated entity files under path`
|
||||
CGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
|
||||
CGenDaoBriefModelFile = `custom file name for storing generated model content`
|
||||
CGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
|
||||
CGenDaoBriefDescriptionTag = `add comment to description tag for each field`
|
||||
CGenDaoBriefNoJsonTag = `no json tag will be added for each field`
|
||||
CGenDaoBriefNoModelComment = `no model comment will be added for each field`
|
||||
CGenDaoBriefClear = `delete all generated go files that do not exist in database`
|
||||
CGenDaoBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table`
|
||||
CGenDaoBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
CGenDaoBriefJsonCase = `
|
||||
generated json tag case for model struct, cases are as follows:
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
CGenDaoBriefTplDaoIndexPath = `template file path for dao index file`
|
||||
CGenDaoBriefTplDaoInternalPath = `template file path for dao internal file`
|
||||
CGenDaoBriefTplDaoDoPathPath = `template file path for dao do file`
|
||||
CGenDaoBriefTplDaoEntityPath = `template file path for dao entity file`
|
||||
|
||||
tplVarTableName = `{TplTableName}`
|
||||
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
|
||||
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
|
||||
tplVarPackageImports = `{TplPackageImports}`
|
||||
tplVarImportPrefix = `{TplImportPrefix}`
|
||||
tplVarStructDefine = `{TplStructDefine}`
|
||||
tplVarColumnDefine = `{TplColumnDefine}`
|
||||
tplVarColumnNames = `{TplColumnNames}`
|
||||
tplVarGroupName = `{TplGroupName}`
|
||||
tplVarDatetimeStr = `{TplDatetimeStr}`
|
||||
tplVarCreatedAtDatetimeStr = `{TplCreatedAtDatetimeStr}`
|
||||
)
|
||||
|
||||
var (
|
||||
createdAt = gtime.Now()
|
||||
defaultTypeMapping = map[DBFieldTypeName]CustomAttributeType{
|
||||
"decimal": {
|
||||
Type: "float64",
|
||||
},
|
||||
"money": {
|
||||
Type: "float64",
|
||||
},
|
||||
"numeric": {
|
||||
Type: "float64",
|
||||
},
|
||||
"smallmoney": {
|
||||
Type: "float64",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenDaoConfig`: CGenDaoConfig,
|
||||
`CGenDaoUsage`: CGenDaoUsage,
|
||||
`CGenDaoBrief`: CGenDaoBrief,
|
||||
`CGenDaoEg`: CGenDaoEg,
|
||||
`CGenDaoAd`: CGenDaoAd,
|
||||
`CGenDaoBriefPath`: CGenDaoBriefPath,
|
||||
`CGenDaoBriefLink`: CGenDaoBriefLink,
|
||||
`CGenDaoBriefTables`: CGenDaoBriefTables,
|
||||
`CGenDaoBriefTablesEx`: CGenDaoBriefTablesEx,
|
||||
`CGenDaoBriefPrefix`: CGenDaoBriefPrefix,
|
||||
`CGenDaoBriefRemovePrefix`: CGenDaoBriefRemovePrefix,
|
||||
`CGenDaoBriefStdTime`: CGenDaoBriefStdTime,
|
||||
`CGenDaoBriefWithTime`: CGenDaoBriefWithTime,
|
||||
`CGenDaoBriefDaoPath`: CGenDaoBriefDaoPath,
|
||||
`CGenDaoBriefDoPath`: CGenDaoBriefDoPath,
|
||||
`CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath,
|
||||
`CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport,
|
||||
`CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix,
|
||||
`CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao,
|
||||
`CGenDaoBriefModelFile`: CGenDaoBriefModelFile,
|
||||
`CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao,
|
||||
`CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag,
|
||||
`CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag,
|
||||
`CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment,
|
||||
`CGenDaoBriefClear`: CGenDaoBriefClear,
|
||||
`CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping,
|
||||
`CGenDaoBriefGroup`: CGenDaoBriefGroup,
|
||||
`CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase,
|
||||
`CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath,
|
||||
`CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath,
|
||||
`CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath,
|
||||
`CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
CGenDao struct{}
|
||||
CGenDaoInput struct {
|
||||
g.Meta `name:"dao" config:"{CGenDaoConfig}" usage:"{CGenDaoUsage}" brief:"{CGenDaoBrief}" eg:"{CGenDaoEg}" ad:"{CGenDaoAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{CGenDaoBriefPath}" d:"internal"`
|
||||
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
|
||||
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"`
|
||||
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"`
|
||||
DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"`
|
||||
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
|
||||
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
|
||||
TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"`
|
||||
TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"`
|
||||
TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"`
|
||||
TplDaoEntityPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenDaoBriefTplDaoEntityPath}"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{CGenDaoBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"w" brief:"{CGenDaoBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenDaoBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenDaoBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenDaoBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
|
||||
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`
|
||||
|
||||
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
|
||||
}
|
||||
CGenDaoOutput struct{}
|
||||
|
||||
CGenDaoInternalInput struct {
|
||||
CGenDaoInput
|
||||
DB gdb.DB
|
||||
TableNames []string
|
||||
NewTableNames []string
|
||||
}
|
||||
|
||||
DBFieldTypeName = string
|
||||
CustomAttributeType struct {
|
||||
Type string `brief:"custom attribute type name"`
|
||||
Import string `brief:"custom import for this type"`
|
||||
}
|
||||
)
|
||||
|
||||
func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput, err error) {
|
||||
if g.Cfg().Available(ctx) {
|
||||
v := g.Cfg().MustGet(ctx, CGenDaoConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenDaoForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// doGenDaoForArray implements the "gen dao" command for configuration array.
|
||||
func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, CGenDaoConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, CGenDaoConfig, err)
|
||||
}
|
||||
}
|
||||
if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" {
|
||||
mlog.Fatalf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var tempGroup = gtime.TimestampNanoStr()
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Link: in.Link,
|
||||
})
|
||||
if db, err = gdb.Instance(tempGroup); err != nil {
|
||||
mlog.Fatalf(`database initialization failed: %+v`, err)
|
||||
}
|
||||
} else {
|
||||
db = g.DB(in.Group)
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal(`database initialization failed, may be invalid database configuration`)
|
||||
}
|
||||
|
||||
var tableNames []string
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: %+v", err)
|
||||
}
|
||||
}
|
||||
// Table excluding.
|
||||
if in.TablesEx != "" {
|
||||
array := garray.NewStrArrayFrom(tableNames)
|
||||
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
|
||||
array.RemoveValue(v)
|
||||
}
|
||||
tableNames = array.Slice()
|
||||
}
|
||||
|
||||
// merge default typeMapping to input typeMapping.
|
||||
if in.TypeMapping == nil {
|
||||
in.TypeMapping = defaultTypeMapping
|
||||
} else {
|
||||
for key, typeMapping := range defaultTypeMapping {
|
||||
if _, ok := in.TypeMapping[key]; !ok {
|
||||
in.TypeMapping[key] = typeMapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generating dao & model go files one by one according to given table name.
|
||||
newTableNames := make([]string, len(tableNames))
|
||||
for i, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
newTableName = in.Prefix + newTableName
|
||||
newTableNames[i] = newTableName
|
||||
}
|
||||
|
||||
// Dao: index and internal.
|
||||
generateDao(ctx, CGenDaoInternalInput{
|
||||
CGenDaoInput: in,
|
||||
DB: db,
|
||||
TableNames: tableNames,
|
||||
NewTableNames: newTableNames,
|
||||
})
|
||||
// Do.
|
||||
generateDo(ctx, CGenDaoInternalInput{
|
||||
CGenDaoInput: in,
|
||||
DB: db,
|
||||
TableNames: tableNames,
|
||||
NewTableNames: newTableNames,
|
||||
})
|
||||
// Entity.
|
||||
generateEntity(ctx, CGenDaoInternalInput{
|
||||
CGenDaoInput: in,
|
||||
DB: db,
|
||||
TableNames: tableNames,
|
||||
NewTableNames: newTableNames,
|
||||
})
|
||||
}
|
||||
|
||||
func getImportPartContent(ctx context.Context, source string, isDo bool, appendImports []string) string {
|
||||
var packageImportsArray = garray.NewStrArray()
|
||||
if isDo {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
|
||||
}
|
||||
|
||||
// Time package recognition.
|
||||
if strings.Contains(source, "gtime.Time") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`)
|
||||
} else if strings.Contains(source, "time.Time") {
|
||||
packageImportsArray.Append(`"time"`)
|
||||
}
|
||||
|
||||
// Json type.
|
||||
if strings.Contains(source, "gjson.Json") {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
|
||||
}
|
||||
|
||||
// Check and update imports in go.mod
|
||||
if appendImports != nil && len(appendImports) > 0 {
|
||||
goModPath := utils.GetModPath()
|
||||
if goModPath == "" {
|
||||
mlog.Fatal("go.mod not found in current project")
|
||||
}
|
||||
mod, err := modfile.Parse(goModPath, gfile.GetBytes(goModPath), nil)
|
||||
if err != nil {
|
||||
mlog.Fatalf("parse go.mod failed: %+v", err)
|
||||
}
|
||||
for _, appendImport := range appendImports {
|
||||
found := false
|
||||
for _, require := range mod.Require {
|
||||
if gstr.Contains(appendImport, require.Mod.Path) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
err = gproc.ShellRun(ctx, `go get `+appendImport)
|
||||
mlog.Fatalf(`%+v`, err)
|
||||
}
|
||||
packageImportsArray.Append(fmt.Sprintf(`"%s"`, appendImport))
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and write content to golang file.
|
||||
packageImportsStr := ""
|
||||
if packageImportsArray.Len() > 0 {
|
||||
packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n"))
|
||||
}
|
||||
return packageImportsStr
|
||||
}
|
||||
|
||||
func replaceDefaultVar(in CGenDaoInternalInput, origin string) string {
|
||||
var tplCreatedAtDatetimeStr string
|
||||
var tplDatetimeStr string = createdAt.String()
|
||||
if in.WithTime {
|
||||
tplCreatedAtDatetimeStr = fmt.Sprintf(`Created at %s`, tplDatetimeStr)
|
||||
}
|
||||
return gstr.ReplaceByMap(origin, g.MapStrStr{
|
||||
tplVarDatetimeStr: tplDatetimeStr,
|
||||
tplVarCreatedAtDatetimeStr: tplCreatedAtDatetimeStr,
|
||||
})
|
||||
}
|
||||
|
||||
func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
i = 0
|
||||
j = 0
|
||||
result = make([]string, len(names))
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getTemplateFromPathOrDefault(filePath string, def string) string {
|
||||
if filePath != "" {
|
||||
if contents := gfile.GetContents(filePath); contents != "" {
|
||||
return contents
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
30
cmd/gf/internal/cmd/gendao/gendao_clear.go
Normal file
30
cmd/gf/internal/cmd/gendao/gendao_clear.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
func doClear(ctx context.Context, dirPath string, force bool) {
|
||||
files, err := gfile.ScanDirFile(dirPath, "*.go", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if force || utils.IsFileDoNotEdit(file) {
|
||||
if err = gfile.Remove(file); err != nil {
|
||||
mlog.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
225
cmd/gf/internal/cmd/gendao/gendao_dao.go
Normal file
225
cmd/gf/internal/cmd/gendao/gendao_dao.go
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
func generateDao(ctx context.Context, in CGenDaoInternalInput) {
|
||||
var (
|
||||
dirPathDao = gfile.Join(in.Path, in.DaoPath)
|
||||
dirPathDaoInternal = gfile.Join(dirPathDao, "internal")
|
||||
)
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathDao, true)
|
||||
}
|
||||
for i := 0; i < len(in.TableNames); i++ {
|
||||
generateDaoSingle(ctx, generateDaoSingleInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: in.TableNames[i],
|
||||
NewTableName: in.NewTableNames[i],
|
||||
DirPathDao: dirPathDao,
|
||||
DirPathDaoInternal: dirPathDaoInternal,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type generateDaoSingleInput struct {
|
||||
CGenDaoInternalInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
DirPathDao string
|
||||
DirPathDaoInternal string
|
||||
}
|
||||
|
||||
// generateDaoSingle generates the dao and model content of given table.
|
||||
func generateDaoSingle(ctx context.Context, in generateDaoSingleInput) {
|
||||
// Generating table data preparing.
|
||||
fieldMap, err := in.DB.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
|
||||
}
|
||||
var (
|
||||
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
|
||||
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
|
||||
importPrefix = in.ImportPrefix
|
||||
)
|
||||
if importPrefix == "" {
|
||||
importPrefix = utils.GetImportPath(gfile.Join(in.Path, in.DaoPath))
|
||||
} else {
|
||||
importPrefix = gstr.Join(g.SliceStr{importPrefix, in.DaoPath}, "/")
|
||||
}
|
||||
|
||||
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
|
||||
// Add suffix to avoid the table name which contains "_test",
|
||||
// which would make the go file a testing file.
|
||||
fileName += "_table"
|
||||
}
|
||||
|
||||
// dao - index
|
||||
generateDaoIndex(generateDaoIndexInput{
|
||||
generateDaoSingleInput: in,
|
||||
TableNameCamelCase: tableNameCamelCase,
|
||||
TableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
ImportPrefix: importPrefix,
|
||||
FileName: fileName,
|
||||
})
|
||||
|
||||
// dao - internal
|
||||
generateDaoInternal(generateDaoInternalInput{
|
||||
generateDaoSingleInput: in,
|
||||
TableNameCamelCase: tableNameCamelCase,
|
||||
TableNameCamelLowerCase: tableNameCamelLowerCase,
|
||||
ImportPrefix: importPrefix,
|
||||
FileName: fileName,
|
||||
FieldMap: fieldMap,
|
||||
})
|
||||
}
|
||||
|
||||
type generateDaoIndexInput struct {
|
||||
generateDaoSingleInput
|
||||
TableNameCamelCase string
|
||||
TableNameCamelLowerCase string
|
||||
ImportPrefix string
|
||||
FileName string
|
||||
}
|
||||
|
||||
func generateDaoIndex(in generateDaoIndexInput) {
|
||||
path := filepath.FromSlash(gfile.Join(in.DirPathDao, in.FileName+".go"))
|
||||
if in.OverwriteDao || !gfile.Exists(path) {
|
||||
indexContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent),
|
||||
g.MapStrStr{
|
||||
tplVarImportPrefix: in.ImportPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarTableNameCamelCase: in.TableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase,
|
||||
})
|
||||
indexContent = replaceDefaultVar(in.CGenDaoInternalInput, indexContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type generateDaoInternalInput struct {
|
||||
generateDaoSingleInput
|
||||
TableNameCamelCase string
|
||||
TableNameCamelLowerCase string
|
||||
ImportPrefix string
|
||||
FileName string
|
||||
FieldMap map[string]*gdb.TableField
|
||||
}
|
||||
|
||||
func generateDaoInternal(in generateDaoInternalInput) {
|
||||
path := filepath.FromSlash(gfile.Join(in.DirPathDaoInternal, in.FileName+".go"))
|
||||
modelContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent),
|
||||
g.MapStrStr{
|
||||
tplVarImportPrefix: in.ImportPrefix,
|
||||
tplVarTableName: in.TableName,
|
||||
tplVarGroupName: in.Group,
|
||||
tplVarTableNameCamelCase: in.TableNameCamelCase,
|
||||
tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase,
|
||||
tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(in.FieldMap)),
|
||||
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(in.FieldMap)),
|
||||
})
|
||||
modelContent = replaceDefaultVar(in.CGenDaoInternalInput, modelContent)
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
utils.GoFmt(path)
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateColumnNamesForDao generates and returns the column names assignment content of column struct
|
||||
// for specified table.
|
||||
func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
field := fieldMap[name]
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name) + ":",
|
||||
fmt.Sprintf(` #"%s",`, field.Name),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
namesContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
namesContent = gstr.Replace(namesContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(namesContent)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateColumnDefinitionForDao generates and returns the column names definition for specified table.
|
||||
func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForDao(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
var (
|
||||
field = fieldMap[name]
|
||||
comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
}))
|
||||
)
|
||||
array[index] = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" # " + "string",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
defineContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
defineContent = gstr.Replace(defineContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(defineContent)
|
||||
return buffer.String()
|
||||
}
|
||||
93
cmd/gf/internal/cmd/gendao/gendao_do.go
Normal file
93
cmd/gf/internal/cmd/gendao/gendao_do.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
var dirPathDo = filepath.FromSlash(gfile.Join(in.Path, in.DoPath))
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathDo, false)
|
||||
}
|
||||
in.NoJsonTag = true
|
||||
in.DescriptionTag = false
|
||||
in.NoModelComment = false
|
||||
// Model content.
|
||||
for i, tableName := range in.TableNames {
|
||||
fieldMap, err := in.DB.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", tableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = in.NewTableNames[i]
|
||||
doFilePath = gfile.Join(dirPathDo, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition, _ = generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: tableName,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: true,
|
||||
})
|
||||
)
|
||||
// replace all types to interface{}.
|
||||
structDefinition, _ = gregex.ReplaceStringFuncMatch(
|
||||
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
|
||||
structDefinition,
|
||||
func(match []string) string {
|
||||
// If the type is already a pointer/slice/map, it does nothing.
|
||||
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
|
||||
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
|
||||
}
|
||||
return match[0]
|
||||
},
|
||||
)
|
||||
modelContent := generateDoContent(
|
||||
ctx,
|
||||
in,
|
||||
tableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
structDefinition,
|
||||
)
|
||||
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(doFilePath)
|
||||
mlog.Print("generated:", doFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateDoContent(
|
||||
ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string,
|
||||
) string {
|
||||
doContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoDoPath, consts.TemplateGenDaoDoContent),
|
||||
g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(ctx, structDefine, true, nil),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
},
|
||||
)
|
||||
doContent = replaceDefaultVar(in, doContent)
|
||||
return doContent
|
||||
}
|
||||
79
cmd/gf/internal/cmd/gendao/gendao_entity.go
Normal file
79
cmd/gf/internal/cmd/gendao/gendao_entity.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
|
||||
var dirPathEntity = gfile.Join(in.Path, in.EntityPath)
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathEntity, false)
|
||||
}
|
||||
// Model content.
|
||||
for i, tableName := range in.TableNames {
|
||||
fieldMap, err := in.DB.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", tableName, err)
|
||||
}
|
||||
|
||||
var (
|
||||
newTableName = in.NewTableNames[i]
|
||||
entityFilePath = filepath.FromSlash(gfile.Join(dirPathEntity, gstr.CaseSnake(newTableName)+".go"))
|
||||
structDefinition, appendImports = generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: tableName,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: false,
|
||||
})
|
||||
entityContent = generateEntityContent(
|
||||
ctx,
|
||||
in,
|
||||
newTableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
structDefinition,
|
||||
appendImports,
|
||||
)
|
||||
)
|
||||
|
||||
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
|
||||
} else {
|
||||
utils.GoFmt(entityFilePath)
|
||||
mlog.Print("generated:", entityFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateEntityContent(
|
||||
ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, appendImports []string,
|
||||
) string {
|
||||
entityContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent),
|
||||
g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(ctx, structDefine, false, appendImports),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
},
|
||||
)
|
||||
entityContent = replaceDefaultVar(in, entityContent)
|
||||
return entityContent
|
||||
}
|
||||
161
cmd/gf/internal/cmd/gendao/gendao_structure.go
Normal file
161
cmd/gf/internal/cmd/gendao/gendao_structure.go
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type generateStructDefinitionInput struct {
|
||||
CGenDaoInternalInput
|
||||
TableName string // Table name.
|
||||
StructName string // Struct name.
|
||||
FieldMap map[string]*gdb.TableField // Table field map.
|
||||
IsDo bool // Is generating DTO struct.
|
||||
}
|
||||
|
||||
func generateStructDefinition(ctx context.Context, in generateStructDefinitionInput) (string, []string) {
|
||||
var appendImports []string
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
array := make([][]string, len(in.FieldMap))
|
||||
names := sortFieldKeyForDao(in.FieldMap)
|
||||
for index, name := range names {
|
||||
var imports string
|
||||
field := in.FieldMap[name]
|
||||
array[index], imports = generateStructFieldDefinition(ctx, field, in)
|
||||
if imports != "" {
|
||||
appendImports = append(appendImports, imports)
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
stContent = gstr.Replace(stContent, "` ", "`")
|
||||
stContent = gstr.Replace(stContent, "``", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
|
||||
if in.IsDo {
|
||||
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
|
||||
}
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String(), appendImports
|
||||
}
|
||||
|
||||
// generateStructFieldDefinition generates and returns the attribute definition for specified field.
|
||||
func generateStructFieldDefinition(
|
||||
ctx context.Context, field *gdb.TableField, in generateStructDefinitionInput,
|
||||
) (attrLines []string, appendImport string) {
|
||||
var (
|
||||
err error
|
||||
localTypeName gdb.LocalType
|
||||
localTypeNameStr string
|
||||
jsonTag = gstr.CaseConvert(field.Name, gstr.CaseTypeMatch(in.JsonCase))
|
||||
)
|
||||
|
||||
if in.TypeMapping != nil && len(in.TypeMapping) > 0 {
|
||||
var (
|
||||
tryTypeName string
|
||||
)
|
||||
tryTypeMatch, _ := gregex.MatchString(`(.+?)\((.+)\)`, field.Type)
|
||||
if len(tryTypeMatch) == 3 {
|
||||
tryTypeName = gstr.Trim(tryTypeMatch[1])
|
||||
} else {
|
||||
tryTypeName = gstr.Split(field.Type, " ")[0]
|
||||
}
|
||||
if tryTypeName != "" {
|
||||
if typeMapping, ok := in.TypeMapping[strings.ToLower(tryTypeName)]; ok {
|
||||
localTypeNameStr = typeMapping.Type
|
||||
appendImport = typeMapping.Import
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if localTypeNameStr == "" {
|
||||
localTypeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
localTypeNameStr = string(localTypeName)
|
||||
switch localTypeName {
|
||||
case gdb.LocalTypeDate, gdb.LocalTypeDatetime:
|
||||
if in.StdTime {
|
||||
localTypeNameStr = "time.Time"
|
||||
} else {
|
||||
localTypeNameStr = "*gtime.Time"
|
||||
}
|
||||
|
||||
case gdb.LocalTypeInt64Bytes:
|
||||
localTypeNameStr = "int64"
|
||||
|
||||
case gdb.LocalTypeUint64Bytes:
|
||||
localTypeNameStr = "uint64"
|
||||
|
||||
// Special type handle.
|
||||
case gdb.LocalTypeJson, gdb.LocalTypeJsonb:
|
||||
if in.GJsonSupport {
|
||||
localTypeNameStr = "*gjson.Json"
|
||||
} else {
|
||||
localTypeNameStr = "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
tagKey = "`"
|
||||
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
|
||||
)
|
||||
attrLines = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" #" + localTypeNameStr,
|
||||
}
|
||||
attrLines = append(attrLines, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
|
||||
attrLines = append(attrLines, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
|
||||
attrLines = append(attrLines, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
|
||||
|
||||
for k, v := range attrLines {
|
||||
if in.NoJsonTag {
|
||||
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
|
||||
}
|
||||
if !in.DescriptionTag {
|
||||
v, _ = gregex.ReplaceString(`description:".*"`, ``, v)
|
||||
}
|
||||
if in.NoModelComment {
|
||||
v, _ = gregex.ReplaceString(`//.+`, ``, v)
|
||||
}
|
||||
attrLines[k] = v
|
||||
}
|
||||
return attrLines, appendImport
|
||||
}
|
||||
|
||||
// formatComment formats the comment string to fit the golang code without any lines.
|
||||
func formatComment(comment string) string {
|
||||
comment = gstr.ReplaceByArray(comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment = gstr.Trim(comment)
|
||||
return comment
|
||||
}
|
||||
83
cmd/gf/internal/cmd/genenums/genenums.go
Normal file
83
cmd/gf/internal/cmd/genenums/genenums.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genenums
|
||||
|
||||
import (
|
||||
"context"
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
type (
|
||||
CGenEnums struct{}
|
||||
CGenEnumsInput struct {
|
||||
g.Meta `name:"enums" config:"{CGenEnumsConfig}" brief:"{CGenEnumsBrief}" eg:"{CGenEnumsEg}"`
|
||||
Src string `name:"src" short:"s" dc:"source folder path to be parsed" d:"."`
|
||||
Path string `name:"path" short:"p" dc:"output go file path storing enums content" d:"internal/boot/boot_enums.go"`
|
||||
Prefixes []string `name:"prefixes" short:"x" dc:"only exports packages that starts with specified prefixes"`
|
||||
}
|
||||
CGenEnumsOutput struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
CGenEnumsConfig = `gfcli.gen.enums`
|
||||
CGenEnumsBrief = `parse go files in current project and generate enums go file`
|
||||
CGenEnumsEg = `
|
||||
gf gen enums
|
||||
gf gen enums -p internal/boot/boot_enums.go
|
||||
gf gen enums -p internal/boot/boot_enums.go -s .
|
||||
gf gen enums -x github.com/gogf
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenEnumsEg`: CGenEnumsEg,
|
||||
`CGenEnumsBrief`: CGenEnumsBrief,
|
||||
`CGenEnumsConfig`: CGenEnumsConfig,
|
||||
})
|
||||
}
|
||||
|
||||
func (c CGenEnums) Enums(ctx context.Context, in CGenEnumsInput) (out *CGenEnumsOutput, err error) {
|
||||
realPath := gfile.RealPath(in.Src)
|
||||
if realPath == "" {
|
||||
mlog.Fatalf(`source folder path "%s" does not exist`, in.Src)
|
||||
}
|
||||
err = gfile.Chdir(realPath)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
mlog.Printf(`scanning for enums: %s`, realPath)
|
||||
cfg := &packages.Config{
|
||||
Dir: realPath,
|
||||
Mode: pkgLoadMode,
|
||||
Tests: false,
|
||||
}
|
||||
pkgs, err := packages.Load(cfg)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
p := NewEnumsParser(in.Prefixes)
|
||||
p.ParsePackages(pkgs)
|
||||
var enumsContent = gstr.ReplaceByMap(consts.TemplateGenEnums, g.MapStrStr{
|
||||
"{PackageName}": gfile.Basename(gfile.Dir(in.Path)),
|
||||
"{EnumsJson}": "`" + p.Export() + "`",
|
||||
})
|
||||
enumsContent = gstr.Trim(enumsContent)
|
||||
if err = gfile.PutContents(in.Path, enumsContent); err != nil {
|
||||
return
|
||||
}
|
||||
mlog.Printf(`generated enums go file: %s`, in.Path)
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
148
cmd/gf/internal/cmd/genenums/genenums_parser.go
Normal file
148
cmd/gf/internal/cmd/genenums/genenums_parser.go
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genenums
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
const pkgLoadMode = 0xffffff
|
||||
|
||||
type EnumsParser struct {
|
||||
enums []EnumItem
|
||||
parsedPkg map[string]struct{}
|
||||
prefixes []string
|
||||
standardPackages map[string]struct{}
|
||||
}
|
||||
|
||||
type EnumItem struct {
|
||||
Name string
|
||||
Value string
|
||||
Kind constant.Kind // String/Int/Bool/Float/Complex/Unknown
|
||||
Type string // Pkg.ID + TypeName
|
||||
}
|
||||
|
||||
func NewEnumsParser(prefixes []string) *EnumsParser {
|
||||
return &EnumsParser{
|
||||
enums: make([]EnumItem, 0),
|
||||
parsedPkg: make(map[string]struct{}),
|
||||
prefixes: prefixes,
|
||||
standardPackages: getStandardPackages(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EnumsParser) ParsePackages(pkgs []*packages.Package) {
|
||||
for _, pkg := range pkgs {
|
||||
p.ParsePackage(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EnumsParser) ParsePackage(pkg *packages.Package) {
|
||||
// Ignore std packages.
|
||||
if _, ok := p.standardPackages[pkg.ID]; ok {
|
||||
return
|
||||
}
|
||||
// Ignore pared packages.
|
||||
if _, ok := p.parsedPkg[pkg.ID]; ok {
|
||||
return
|
||||
}
|
||||
p.parsedPkg[pkg.ID] = struct{}{}
|
||||
|
||||
// Only parse specified prefixes.
|
||||
if len(p.prefixes) > 0 {
|
||||
var hasPrefix bool
|
||||
for _, prefix := range p.prefixes {
|
||||
if hasPrefix = gstr.HasPrefix(pkg.ID, prefix); hasPrefix {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasPrefix {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
scope = pkg.Types.Scope()
|
||||
names = scope.Names()
|
||||
)
|
||||
for _, name := range names {
|
||||
con, ok := scope.Lookup(name).(*types.Const)
|
||||
if !ok {
|
||||
// Only constants can be enums.
|
||||
continue
|
||||
}
|
||||
if !con.Exported() {
|
||||
// Ignore unexported values.
|
||||
continue
|
||||
}
|
||||
|
||||
var enumType = con.Type().String()
|
||||
if !gstr.Contains(enumType, "/") {
|
||||
// Ignore std types.
|
||||
continue
|
||||
}
|
||||
var (
|
||||
enumName = con.Name()
|
||||
enumValue = con.Val().ExactString()
|
||||
enumKind = con.Val().Kind()
|
||||
)
|
||||
if con.Val().Kind() == constant.String {
|
||||
enumValue = constant.StringVal(con.Val())
|
||||
}
|
||||
p.enums = append(p.enums, EnumItem{
|
||||
Name: enumName,
|
||||
Value: enumValue,
|
||||
Type: enumType,
|
||||
Kind: enumKind,
|
||||
})
|
||||
}
|
||||
for _, im := range pkg.Imports {
|
||||
p.ParsePackage(im)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EnumsParser) Export() string {
|
||||
var typeEnumMap = make(map[string][]interface{})
|
||||
for _, enum := range p.enums {
|
||||
if typeEnumMap[enum.Type] == nil {
|
||||
typeEnumMap[enum.Type] = make([]interface{}, 0)
|
||||
}
|
||||
var value interface{}
|
||||
switch enum.Kind {
|
||||
case constant.Int:
|
||||
value = gconv.Int64(enum.Value)
|
||||
case constant.String:
|
||||
value = enum.Value
|
||||
case constant.Float:
|
||||
value = gconv.Float64(enum.Value)
|
||||
case constant.Bool:
|
||||
value = gconv.Bool(enum.Value)
|
||||
default:
|
||||
value = enum.Value
|
||||
}
|
||||
typeEnumMap[enum.Type] = append(typeEnumMap[enum.Type], value)
|
||||
}
|
||||
return gjson.MustEncodeString(typeEnumMap)
|
||||
}
|
||||
|
||||
func getStandardPackages() map[string]struct{} {
|
||||
standardPackages := make(map[string]struct{})
|
||||
stdPackages, err := packages.Load(nil, "std")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, p := range stdPackages {
|
||||
standardPackages[p.ID] = struct{}{}
|
||||
}
|
||||
return standardPackages
|
||||
}
|
||||
128
cmd/gf/internal/cmd/genpb/genpb.go
Normal file
128
cmd/gf/internal/cmd/genpb/genpb.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
type (
|
||||
CGenPb struct{}
|
||||
CGenPbInput struct {
|
||||
g.Meta `name:"pb" config:"{CGenPbConfig}" brief:"{CGenPbBrief}" eg:"{CGenPbEg}"`
|
||||
Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"`
|
||||
OutputApi string `name:"api" short:"a" dc:"output folder path storing generated go files of api" d:"api"`
|
||||
OutputCtrl string `name:"ctrl" short:"c" dc:"output folder path storing generated go files of controller" d:"internal/controller"`
|
||||
}
|
||||
CGenPbOutput struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
CGenPbConfig = `gfcli.gen.pb`
|
||||
CGenPbBrief = `parse proto files and generate protobuf go files`
|
||||
CGenPbEg = `
|
||||
gf gen pb
|
||||
gf gen pb -p . -a . -p .
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenPbEg`: CGenPbEg,
|
||||
`CGenPbBrief`: CGenPbBrief,
|
||||
`CGenPbConfig`: CGenPbConfig,
|
||||
})
|
||||
}
|
||||
|
||||
func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
protoc := gproc.SearchBinary("protoc")
|
||||
if protoc == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first: https://grpc.io/docs/languages/go/quickstart/`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
var (
|
||||
protoPath = gfile.RealPath(in.Path)
|
||||
isParsingPWD bool
|
||||
)
|
||||
if protoPath == "" {
|
||||
// Use current working directory as protoPath if there are proto files under.
|
||||
currentPath := gfile.Pwd()
|
||||
currentFiles, _ := gfile.ScanDirFile(currentPath, "*.proto")
|
||||
if len(currentFiles) > 0 {
|
||||
protoPath = currentPath
|
||||
isParsingPWD = true
|
||||
} else {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, in.Path)
|
||||
}
|
||||
}
|
||||
// output path checks.
|
||||
outputApiPath := gfile.RealPath(in.OutputApi)
|
||||
if outputApiPath == "" {
|
||||
if isParsingPWD {
|
||||
outputApiPath = protoPath
|
||||
} else {
|
||||
mlog.Fatalf(`output api folder "%s" does not exist`, in.OutputApi)
|
||||
}
|
||||
}
|
||||
outputCtrlPath := gfile.RealPath(in.OutputCtrl)
|
||||
if outputCtrlPath == "" {
|
||||
if isParsingPWD {
|
||||
outputCtrlPath = ""
|
||||
} else {
|
||||
mlog.Fatalf(`output controller folder "%s" does not exist`, in.OutputCtrl)
|
||||
}
|
||||
}
|
||||
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoPath, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, in.Path)
|
||||
}
|
||||
|
||||
if err = gfile.Chdir(protoPath); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
var command = gproc.NewProcess(protoc, nil)
|
||||
command.Args = append(command.Args, "--proto_path="+gfile.Pwd())
|
||||
command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputApiPath)
|
||||
command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputApiPath)
|
||||
command.Args = append(command.Args, file)
|
||||
mlog.Print(command.String())
|
||||
if err = command.Run(ctx); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
// Generate struct tag according comment rules.
|
||||
err = c.generateStructTag(ctx, generateStructTagInput{OutputApiPath: outputApiPath})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Generate controllers according comment rules.
|
||||
if outputCtrlPath != "" {
|
||||
err = c.generateController(ctx, generateControllerInput{
|
||||
OutputApiPath: outputApiPath,
|
||||
OutputCtrlPath: outputCtrlPath,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
196
cmd/gf/internal/cmd/genpb/genpb_controller.go
Normal file
196
cmd/gf/internal/cmd/genpb/genpb_controller.go
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type generateControllerInput struct {
|
||||
OutputApiPath string
|
||||
OutputCtrlPath string
|
||||
}
|
||||
|
||||
type generateCtrl struct {
|
||||
Name string
|
||||
Package string
|
||||
Version string
|
||||
Methods []generateCtrlMethod
|
||||
}
|
||||
|
||||
type generateCtrlMethod struct {
|
||||
Name string
|
||||
Definition string
|
||||
}
|
||||
|
||||
const (
|
||||
controllerTemplate = `
|
||||
package {Package}
|
||||
|
||||
type Controller struct {
|
||||
{Version}.Unimplemented{Name}Server
|
||||
}
|
||||
|
||||
func Register(s *grpcx.GrpcServer) {
|
||||
{Version}.Register{Name}Server(s.Server, &Controller{})
|
||||
}
|
||||
`
|
||||
controllerMethodTemplate = `
|
||||
func (*Controller) {Definition} {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
func (c CGenPb) generateController(ctx context.Context, in generateControllerInput) (err error) {
|
||||
files, err := gfile.ScanDirFile(in.OutputApiPath, "*_grpc.pb.go", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var controllers []generateCtrl
|
||||
for _, file := range files {
|
||||
fileControllers, err := c.parseControllers(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
controllers = append(controllers, fileControllers...)
|
||||
}
|
||||
if len(controllers) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Generate controller files.
|
||||
err = c.doGenerateControllers(in, controllers)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenPb) parseControllers(filePath string) ([]generateCtrl, error) {
|
||||
var (
|
||||
controllers []generateCtrl
|
||||
content = gfile.GetContents(filePath)
|
||||
)
|
||||
_, err := gregex.ReplaceStringFuncMatch(
|
||||
`type (\w+)Server interface {([\s\S]+?)}`,
|
||||
content,
|
||||
func(match []string) string {
|
||||
ctrl := generateCtrl{
|
||||
Name: match[1],
|
||||
Package: strings.ReplaceAll(gfile.Basename(gfile.Dir(gfile.Dir(filePath))), "-", "_"),
|
||||
Version: gfile.Basename(gfile.Dir(filePath)),
|
||||
Methods: make([]generateCtrlMethod, 0),
|
||||
}
|
||||
lines := gstr.Split(match[2], "\n")
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
if line == "" || !gstr.IsLetterUpper(line[0]) {
|
||||
continue
|
||||
}
|
||||
// Comment.
|
||||
if gregex.IsMatchString(`^//.+`, line) {
|
||||
continue
|
||||
}
|
||||
line, _ = gregex.ReplaceStringFuncMatch(
|
||||
`^(\w+)\(context\.Context, \*(\w+)\) \(\*(\w+), error\)$`,
|
||||
line,
|
||||
func(match []string) string {
|
||||
return fmt.Sprintf(
|
||||
`%s(ctx context.Context, req *%s.%s) (res *%s.%s, err error)`,
|
||||
match[1], ctrl.Version, match[2], ctrl.Version, match[3],
|
||||
)
|
||||
},
|
||||
)
|
||||
ctrl.Methods = append(ctrl.Methods, generateCtrlMethod{
|
||||
Name: gstr.Split(line, "(")[0],
|
||||
Definition: line,
|
||||
})
|
||||
}
|
||||
if len(ctrl.Methods) > 0 {
|
||||
controllers = append(controllers, ctrl)
|
||||
}
|
||||
return match[0]
|
||||
},
|
||||
)
|
||||
return controllers, err
|
||||
}
|
||||
|
||||
func (c CGenPb) doGenerateControllers(in generateControllerInput, controllers []generateCtrl) (err error) {
|
||||
for _, controller := range controllers {
|
||||
err = c.doGenerateController(in, controller)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = utils.ReplaceGeneratedContentGFV2(in.OutputCtrlPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c CGenPb) doGenerateController(in generateControllerInput, controller generateCtrl) (err error) {
|
||||
var (
|
||||
folderPath = gfile.Join(in.OutputCtrlPath, controller.Package)
|
||||
filePath = gfile.Join(folderPath, controller.Package+".go")
|
||||
isDirty bool
|
||||
)
|
||||
if !gfile.Exists(folderPath) {
|
||||
if err = gfile.Mkdir(folderPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !gfile.Exists(filePath) {
|
||||
templateContent := gstr.ReplaceByMap(controllerTemplate, g.MapStrStr{
|
||||
"{Name}": controller.Name,
|
||||
"{Version}": controller.Version,
|
||||
"{Package}": controller.Package,
|
||||
})
|
||||
if err = gfile.PutContents(filePath, templateContent); err != nil {
|
||||
return err
|
||||
}
|
||||
isDirty = true
|
||||
}
|
||||
// Exist controller content.
|
||||
var ctrlContent string
|
||||
files, err := gfile.ScanDirFile(folderPath, "*.go", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
if ctrlContent != "" {
|
||||
ctrlContent += "\n"
|
||||
}
|
||||
ctrlContent += gfile.GetContents(file)
|
||||
}
|
||||
// Generate method content.
|
||||
var generatedContent string
|
||||
for _, method := range controller.Methods {
|
||||
if gstr.Contains(ctrlContent, fmt.Sprintf(`%s(`, method.Name)) {
|
||||
continue
|
||||
}
|
||||
if generatedContent != "" {
|
||||
generatedContent += "\n"
|
||||
}
|
||||
generatedContent += gstr.ReplaceByMap(controllerMethodTemplate, g.MapStrStr{
|
||||
"{Definition}": method.Definition,
|
||||
})
|
||||
}
|
||||
if generatedContent != "" {
|
||||
err = gfile.PutContentsAppend(filePath, generatedContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isDirty = true
|
||||
}
|
||||
if isDirty {
|
||||
utils.GoFmt(filePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
113
cmd/gf/internal/cmd/genpb/genpb_tag.go
Normal file
113
cmd/gf/internal/cmd/genpb/genpb_tag.go
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type generateStructTagInput struct {
|
||||
OutputApiPath string
|
||||
}
|
||||
|
||||
func (c CGenPb) generateStructTag(ctx context.Context, in generateStructTagInput) (err error) {
|
||||
files, err := gfile.ScanDirFile(in.OutputApiPath, "*.pb.go", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var content string
|
||||
for _, file := range files {
|
||||
content = gfile.GetContents(file)
|
||||
content, err = c.doTagReplacement(ctx, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = gfile.PutContents(file, content); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.GoFmt(file)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenPb) doTagReplacement(ctx context.Context, content string) (string, error) {
|
||||
content, err := gregex.ReplaceStringFuncMatch(`type (\w+) struct {([\s\S]+?)}`, content, func(match []string) string {
|
||||
var (
|
||||
topCommentMatch []string
|
||||
tailCommentMatch []string
|
||||
lines = gstr.Split(match[2], "\n")
|
||||
lineTagMap = gmap.NewListMap()
|
||||
)
|
||||
for index, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// Top comment.
|
||||
topCommentMatch, _ = gregex.MatchString(`^/[/|\*](.+)`, line)
|
||||
if len(topCommentMatch) > 1 {
|
||||
c.tagCommentIntoListMap(gstr.Trim(topCommentMatch[1]), lineTagMap)
|
||||
continue
|
||||
}
|
||||
// Tail comment.
|
||||
tailCommentMatch, _ = gregex.MatchString(".+?`.+?`.+?//(.+)", line)
|
||||
if len(tailCommentMatch) > 1 {
|
||||
c.tagCommentIntoListMap(gstr.Trim(tailCommentMatch[1]), lineTagMap)
|
||||
}
|
||||
// Tag injection.
|
||||
if !lineTagMap.IsEmpty() {
|
||||
tagContent := c.listMapToStructTag(lineTagMap)
|
||||
lineTagMap.Clear()
|
||||
line, _ = gregex.ReplaceString("`(.+)`", fmt.Sprintf("`$1 %s`", tagContent), line)
|
||||
}
|
||||
lines[index] = line
|
||||
}
|
||||
match[2] = gstr.Join(lines, "\n")
|
||||
return fmt.Sprintf("type %s struct {%s}", match[1], match[2])
|
||||
})
|
||||
return content, err
|
||||
}
|
||||
|
||||
func (c CGenPb) tagCommentIntoListMap(comment string, lineTagMap *gmap.ListMap) {
|
||||
tagCommentMatch, _ := gregex.MatchString(`^(\w+):(.+)`, comment)
|
||||
if len(tagCommentMatch) > 1 {
|
||||
var (
|
||||
tagName = gstr.Trim(tagCommentMatch[1])
|
||||
tagContent = gstr.Trim(tagCommentMatch[2])
|
||||
)
|
||||
lineTagMap.Set(tagName, lineTagMap.GetVar(tagName).String()+tagContent)
|
||||
} else {
|
||||
var (
|
||||
tagName = "dc"
|
||||
tagContent = comment
|
||||
)
|
||||
lineTagMap.Set(tagName, lineTagMap.GetVar(tagName).String()+tagContent)
|
||||
}
|
||||
}
|
||||
|
||||
func (c CGenPb) listMapToStructTag(lineTagMap *gmap.ListMap) string {
|
||||
var tag string
|
||||
lineTagMap.Iterator(func(key, value interface{}) bool {
|
||||
if tag != "" {
|
||||
tag += " "
|
||||
}
|
||||
tag += fmt.Sprintf(
|
||||
`%s:"%s"`,
|
||||
key, gstr.Replace(gconv.String(value), `"`, `\"`),
|
||||
)
|
||||
return true
|
||||
})
|
||||
return tag
|
||||
}
|
||||
421
cmd/gf/internal/cmd/genpbentity/genpbentity.go
Normal file
421
cmd/gf/internal/cmd/genpbentity/genpbentity.go
Normal file
@ -0,0 +1,421 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpbentity
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
type (
|
||||
CGenPbEntity struct{}
|
||||
CGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{CGenPbEntityConfig}" brief:"{CGenPbEntityBrief}" eg:"{CGenPbEntityEg}" ad:"{CGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{CGenPbEntityBriefPath}" d:"manifest/protobuf/pbentity"`
|
||||
Package string `name:"package" short:"k" brief:"{CGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{CGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{CGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{CGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{CGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{CGenPbEntityBriefOption}"`
|
||||
}
|
||||
CGenPbEntityOutput struct{}
|
||||
|
||||
CGenPbEntityInternalInput struct {
|
||||
CGenPbEntityInput
|
||||
DB gdb.DB
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPackageSuffix = `api/pbentity`
|
||||
CGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
CGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
CGenPbEntityEg = `
|
||||
gf gen pbentity
|
||||
gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen pbentity -p ./protocol/demos/entity -t user,user_detail,user_login
|
||||
gf gen pbentity -r user_ -k github.com/gogf/gf/example/protobuf
|
||||
gf gen pbentity -r user_
|
||||
`
|
||||
|
||||
CGenPbEntityAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "protocol/demos/entity"
|
||||
tables: "order,products"
|
||||
package: "demos"
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "protocol/demos/entity"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
package: "demos"
|
||||
option: |
|
||||
option go_package = "protobuf/demos";
|
||||
option java_package = "protobuf/demos";
|
||||
option php_namespace = "protobuf/demos";
|
||||
`
|
||||
CGenPbEntityBriefPath = `directory path for generated files storing`
|
||||
CGenPbEntityBriefPackage = `package path for all entity proto files`
|
||||
CGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
CGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
CGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
|
||||
CGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
CGenPbEntityBriefOption = `extra protobuf options`
|
||||
CGenPbEntityBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
|
||||
CGenPbEntityBriefNameCase = `
|
||||
case for message attribute names, default is "Camel":
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
CGenPbEntityBriefJsonCase = `
|
||||
case for message json tag, cases are the same as "nameCase", default "CamelLower".
|
||||
set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenPbEntityConfig`: CGenPbEntityConfig,
|
||||
`CGenPbEntityBrief`: CGenPbEntityBrief,
|
||||
`CGenPbEntityEg`: CGenPbEntityEg,
|
||||
`CGenPbEntityAd`: CGenPbEntityAd,
|
||||
`CGenPbEntityBriefPath`: CGenPbEntityBriefPath,
|
||||
`CGenPbEntityBriefPackage`: CGenPbEntityBriefPackage,
|
||||
`CGenPbEntityBriefLink`: CGenPbEntityBriefLink,
|
||||
`CGenPbEntityBriefTables`: CGenPbEntityBriefTables,
|
||||
`CGenPbEntityBriefPrefix`: CGenPbEntityBriefPrefix,
|
||||
`CGenPbEntityBriefRemovePrefix`: CGenPbEntityBriefRemovePrefix,
|
||||
`CGenPbEntityBriefGroup`: CGenPbEntityBriefGroup,
|
||||
`CGenPbEntityBriefNameCase`: CGenPbEntityBriefNameCase,
|
||||
`CGenPbEntityBriefJsonCase`: CGenPbEntityBriefJsonCase,
|
||||
`CGenPbEntityBriefOption`: CGenPbEntityBriefOption,
|
||||
})
|
||||
}
|
||||
|
||||
func (c CGenPbEntity) PbEntity(ctx context.Context, in CGenPbEntityInput) (out *CGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
if config.Available(ctx) {
|
||||
v := config.MustGet(ctx, CGenPbEntityConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenPbEntityForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, CGenPbEntityConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, CGenPbEntityConfig, err)
|
||||
}
|
||||
}
|
||||
if in.Package == "" {
|
||||
mlog.Debug(`package parameter is empty, trying calculating the package path using go.mod`)
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
modName string
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
modName = gstr.Trim(match[1])
|
||||
in.Package = modName + "/" + defaultPackageSuffix
|
||||
} else {
|
||||
mlog.Fatal("module name does not found in go.mod")
|
||||
}
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: in.Link,
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
} else {
|
||||
db = g.DB()
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal("database initialization failed")
|
||||
}
|
||||
|
||||
tableNames := ([]string)(nil)
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: \n %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
generatePbEntityContentFile(ctx, CGenPbEntityInternalInput{
|
||||
CGenPbEntityInput: in,
|
||||
DB: db,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generatePbEntityContentFile generates the protobuf files for given table.
|
||||
func generatePbEntityContentFile(ctx context.Context, in CGenPbEntityInternalInput) {
|
||||
fieldMap, err := in.DB.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
// Change the `newTableName` if `Prefix` is given.
|
||||
newTableName := in.Prefix + in.NewTableName
|
||||
var (
|
||||
imports string
|
||||
tableNameCamelCase = gstr.CaseCamel(newTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = filepath.FromSlash(gfile.Join(in.Path, fileName+".proto"))
|
||||
)
|
||||
if gstr.Contains(entityMessageDefine, "google.protobuf.Timestamp") {
|
||||
imports = `import "google/protobuf/timestamp.proto";`
|
||||
}
|
||||
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
|
||||
"{Imports}": imports,
|
||||
"{PackageName}": gfile.Basename(in.Package),
|
||||
"{GoPackage}": in.Package,
|
||||
"{OptionContent}": in.Option,
|
||||
"{EntityMessage}": entityMessageDefine,
|
||||
})
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateEntityMessageDefinition generates and returns the message definition for specified table.
|
||||
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in CGenPbEntityInternalInput) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForPbEntity(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
|
||||
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in CGenPbEntityInternalInput) []string {
|
||||
var (
|
||||
localTypeName gdb.LocalType
|
||||
comment string
|
||||
jsonTagStr string
|
||||
err error
|
||||
ctx = gctx.GetInitCtx()
|
||||
)
|
||||
localTypeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var typeMapping = map[gdb.LocalType]string{
|
||||
gdb.LocalTypeString: "string",
|
||||
gdb.LocalTypeDate: "google.protobuf.Timestamp",
|
||||
gdb.LocalTypeDatetime: "google.protobuf.Timestamp",
|
||||
gdb.LocalTypeInt: "int32",
|
||||
gdb.LocalTypeUint: "uint32",
|
||||
gdb.LocalTypeInt64: "int64",
|
||||
gdb.LocalTypeUint64: "uint64",
|
||||
gdb.LocalTypeIntSlice: "repeated int32",
|
||||
gdb.LocalTypeInt64Slice: "repeated int64",
|
||||
gdb.LocalTypeUint64Slice: "repeated uint64",
|
||||
gdb.LocalTypeInt64Bytes: "repeated int64",
|
||||
gdb.LocalTypeUint64Bytes: "repeated uint64",
|
||||
gdb.LocalTypeFloat32: "float",
|
||||
gdb.LocalTypeFloat64: "double",
|
||||
gdb.LocalTypeBytes: "bytes",
|
||||
gdb.LocalTypeBool: "bool",
|
||||
gdb.LocalTypeJson: "string",
|
||||
gdb.LocalTypeJsonb: "string",
|
||||
}
|
||||
localTypeNameStr := typeMapping[localTypeName]
|
||||
if localTypeNameStr == "" {
|
||||
localTypeNameStr = "string"
|
||||
}
|
||||
|
||||
comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Trim(comment)
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment)
|
||||
if jsonTagName := formatCase(field.Name, in.JsonCase); jsonTagName != "" {
|
||||
// beautiful indent.
|
||||
if index < 10 {
|
||||
// 3 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else if index < 100 {
|
||||
// 2 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else {
|
||||
// 1 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
" #" + localTypeNameStr,
|
||||
" #" + formatCase(field.Name, in.NameCase),
|
||||
" #= " + gconv.String(index) + jsonTagStr + ";",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
|
||||
func getTplPbEntityContent(tplEntityPath string) string {
|
||||
if tplEntityPath != "" {
|
||||
return gfile.GetContents(tplEntityPath)
|
||||
}
|
||||
return consts.TemplatePbEntityMessageContent
|
||||
}
|
||||
|
||||
// formatCase call gstr.Case* function to convert the s to specified case.
|
||||
func formatCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
|
||||
case "none":
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
result = make([]string, len(names))
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
408
cmd/gf/internal/cmd/genservice/genservice.go
Normal file
408
cmd/gf/internal/cmd/genservice/genservice.go
Normal file
@ -0,0 +1,408 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
CGenServiceConfig = `gfcli.gen.service`
|
||||
CGenServiceUsage = `gf gen service [OPTION]`
|
||||
CGenServiceBrief = `parse struct and associated functions from packages to generate service go file`
|
||||
CGenServiceEg = `
|
||||
gf gen service
|
||||
gf gen service -f Snake
|
||||
`
|
||||
CGenServiceBriefSrcFolder = `source folder path to be parsed. default: internal/logic`
|
||||
CGenServiceBriefDstFolder = `destination folder path storing automatically generated go files. default: internal/service`
|
||||
CGenServiceBriefFileNameCase = `
|
||||
destination file name storing automatically generated go files, cases are as follows:
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Lower | anykindofstring |
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString |
|
||||
| Snake | any_kind_of_string | default
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
CGenServiceBriefWatchFile = `used in file watcher, it re-generates all service go files only if given file is under srcFolder`
|
||||
CGenServiceBriefStPattern = `regular expression matching struct name for generating service. default: ^s([A-Z]\\\\w+)$`
|
||||
CGenServiceBriefPackages = `produce go files only for given source packages(source folders)`
|
||||
CGenServiceBriefImportPrefix = `custom import prefix to calculate import path for generated importing go file of logic`
|
||||
CGenServiceBriefClear = `delete all generated go files that are not used any further`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenServiceConfig`: CGenServiceConfig,
|
||||
`CGenServiceUsage`: CGenServiceUsage,
|
||||
`CGenServiceBrief`: CGenServiceBrief,
|
||||
`CGenServiceEg`: CGenServiceEg,
|
||||
`CGenServiceBriefSrcFolder`: CGenServiceBriefSrcFolder,
|
||||
`CGenServiceBriefDstFolder`: CGenServiceBriefDstFolder,
|
||||
`CGenServiceBriefFileNameCase`: CGenServiceBriefFileNameCase,
|
||||
`CGenServiceBriefWatchFile`: CGenServiceBriefWatchFile,
|
||||
`CGenServiceBriefStPattern`: CGenServiceBriefStPattern,
|
||||
`CGenServiceBriefPackages`: CGenServiceBriefPackages,
|
||||
`CGenServiceBriefImportPrefix`: CGenServiceBriefImportPrefix,
|
||||
`CGenServiceBriefClear`: CGenServiceBriefClear,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
CGenService struct{}
|
||||
CGenServiceInput struct {
|
||||
g.Meta `name:"service" config:"{CGenServiceConfig}" usage:"{CGenServiceUsage}" brief:"{CGenServiceBrief}" eg:"{CGenServiceEg}"`
|
||||
SrcFolder string `short:"s" name:"srcFolder" brief:"{CGenServiceBriefSrcFolder}" d:"internal/logic"`
|
||||
DstFolder string `short:"d" name:"dstFolder" brief:"{CGenServiceBriefDstFolder}" d:"internal/service"`
|
||||
DstFileNameCase string `short:"f" name:"dstFileNameCase" brief:"{CGenServiceBriefFileNameCase}" d:"Snake"`
|
||||
WatchFile string `short:"w" name:"watchFile" brief:"{CGenServiceBriefWatchFile}"`
|
||||
StPattern string `short:"a" name:"stPattern" brief:"{CGenServiceBriefStPattern}" d:"^s([A-Z]\\w+)$"`
|
||||
Packages []string `short:"p" name:"packages" brief:"{CGenServiceBriefPackages}"`
|
||||
ImportPrefix string `short:"i" name:"importPrefix" brief:"{CGenServiceBriefImportPrefix}"`
|
||||
Clear bool `short:"l" name:"clear" brief:"{CGenServiceBriefClear}" orphan:"true"`
|
||||
}
|
||||
CGenServiceOutput struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
genServiceFileLockSeconds = 10
|
||||
)
|
||||
|
||||
func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGenServiceOutput, err error) {
|
||||
in.SrcFolder = filepath.ToSlash(in.SrcFolder)
|
||||
in.SrcFolder = gstr.TrimRight(in.SrcFolder, `/`)
|
||||
in.WatchFile = filepath.ToSlash(in.WatchFile)
|
||||
in.WatchFile = gstr.TrimRight(in.WatchFile, `/`)
|
||||
|
||||
// Watch file handling.
|
||||
if in.WatchFile != "" {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
flockContent = gfile.GetContents(flockFilePath)
|
||||
)
|
||||
if flockContent != "" {
|
||||
if gtime.Timestamp()-gconv.Int64(flockContent) < genServiceFileLockSeconds {
|
||||
// If another "gen service" process is running, it just exits.
|
||||
mlog.Debug(`another "gen service" process is running, exit`)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer gfile.Remove(flockFilePath)
|
||||
_ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
|
||||
|
||||
// It works only if given WatchFile is in SrcFolder.
|
||||
var (
|
||||
watchFileDir = gfile.Dir(in.WatchFile)
|
||||
srcFolderDir = gfile.Dir(watchFileDir)
|
||||
)
|
||||
mlog.Debug("watchFileDir:", watchFileDir)
|
||||
mlog.Debug("logicFolderDir:", srcFolderDir)
|
||||
if !gstr.HasSuffix(gstr.Replace(srcFolderDir, `\`, `/`), in.SrcFolder) {
|
||||
mlog.Printf(`ignore watch file "%s", not in source path "%s"`, in.WatchFile, in.SrcFolder)
|
||||
return
|
||||
}
|
||||
var newWorkingDir = gfile.Dir(gfile.Dir(srcFolderDir))
|
||||
if err = gfile.Chdir(newWorkingDir); err != nil {
|
||||
mlog.Fatalf(`%+v`, err)
|
||||
}
|
||||
mlog.Debug("Chdir:", newWorkingDir)
|
||||
|
||||
in.WatchFile = ""
|
||||
in.Packages = []string{gfile.Basename(watchFileDir)}
|
||||
return c.Service(ctx, in)
|
||||
}
|
||||
|
||||
if !gfile.Exists(in.SrcFolder) {
|
||||
mlog.Fatalf(`source folder path "%s" does not exist`, in.SrcFolder)
|
||||
}
|
||||
|
||||
if in.ImportPrefix == "" {
|
||||
in.ImportPrefix = utils.GetImportPath(in.SrcFolder)
|
||||
}
|
||||
|
||||
var (
|
||||
isDirty bool // Temp boolean.
|
||||
files []string // Temp file array.
|
||||
fileContent string // Temp file content for handling go file.
|
||||
initImportSrcPackages []string // Used for generating logic.go.
|
||||
inputPackages = in.Packages // Custom packages.
|
||||
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder)) // Package name for generated go files.
|
||||
generatedDstFilePathSet = gset.NewStrSet() // All generated file path set.
|
||||
)
|
||||
// The first level folders.
|
||||
srcFolderPaths, err := gfile.ScanDir(in.SrcFolder, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, srcFolderPath := range srcFolderPaths {
|
||||
if !gfile.IsDir(srcFolderPath) {
|
||||
continue
|
||||
}
|
||||
// Only retrieve sub files, no recursively.
|
||||
if files, err = gfile.ScanDir(srcFolderPath, "*.go", false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
continue
|
||||
}
|
||||
// Parse single logic package folder.
|
||||
var (
|
||||
// StructName => FunctionDefinitions
|
||||
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
|
||||
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
|
||||
importAliasToPathMap = gmap.NewStrStrMap() // for conflict imports check. alias => import path(with `"`)
|
||||
importPathToAliasMap = gmap.NewStrStrMap() // for conflict imports check. import path(with `"`) => alias
|
||||
srcPackageName = gfile.Basename(srcFolderPath)
|
||||
ok bool
|
||||
dstFilePath = gfile.Join(in.DstFolder,
|
||||
c.getDstFileNameCase(srcPackageName, in.DstFileNameCase)+".go",
|
||||
)
|
||||
srcCodeCommentedMap = make(map[string]string)
|
||||
)
|
||||
generatedDstFilePathSet.Add(dstFilePath)
|
||||
for _, file := range files {
|
||||
var packageItems []packageItem
|
||||
fileContent = gfile.GetContents(file)
|
||||
// Calculate code comments in source Go files.
|
||||
err = c.calculateCodeCommented(in, fileContent, srcCodeCommentedMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// remove all comments.
|
||||
fileContent, err = gregex.ReplaceString(`(//.*)|((?s)/\*.*?\*/)`, "", fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate imported packages of source go files.
|
||||
packageItems, err = c.calculateImportedPackages(fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// try finding the conflicts imports between files.
|
||||
for _, item := range packageItems {
|
||||
var alias = item.Alias
|
||||
if alias == "" {
|
||||
alias = gfile.Basename(gstr.Trim(item.Path, `"`))
|
||||
}
|
||||
|
||||
// ignore unused import paths, which do not exist in function definitions.
|
||||
if !gregex.IsMatchString(fmt.Sprintf(`func .+?([^\w])%s(\.\w+).+?{`, alias), fileContent) {
|
||||
mlog.Debugf(`ignore unused package: %s`, item.RawImport)
|
||||
continue
|
||||
}
|
||||
|
||||
// find the exist alias with the same import path.
|
||||
var existAlias = importPathToAliasMap.Get(item.Path)
|
||||
if existAlias != "" {
|
||||
fileContent, err = gregex.ReplaceStringFuncMatch(
|
||||
fmt.Sprintf(`([^\w])%s(\.\w+)`, alias), fileContent,
|
||||
func(match []string) string {
|
||||
return match[1] + existAlias + match[2]
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// resolve alias conflicts.
|
||||
var importPath = importAliasToPathMap.Get(alias)
|
||||
if importPath == "" {
|
||||
importAliasToPathMap.Set(alias, item.Path)
|
||||
importPathToAliasMap.Set(item.Path, alias)
|
||||
srcImportedPackages.Add(item.RawImport)
|
||||
continue
|
||||
}
|
||||
if importPath != item.Path {
|
||||
// update the conflicted alias for import path with suffix.
|
||||
// eg:
|
||||
// v1 -> v10
|
||||
// v11 -> v110
|
||||
for aliasIndex := 0; ; aliasIndex++ {
|
||||
item.Alias = fmt.Sprintf(`%s%d`, alias, aliasIndex)
|
||||
var existPathForAlias = importAliasToPathMap.Get(item.Alias)
|
||||
if existPathForAlias != "" {
|
||||
if existPathForAlias == item.Path {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
importPathToAliasMap.Set(item.Path, item.Alias)
|
||||
importAliasToPathMap.Set(item.Alias, item.Path)
|
||||
// reformat the import path with alias.
|
||||
item.RawImport = fmt.Sprintf(`%s %s`, item.Alias, item.Path)
|
||||
|
||||
// update the file content with new alias import.
|
||||
fileContent, err = gregex.ReplaceStringFuncMatch(
|
||||
fmt.Sprintf(`([^\w])%s(\.\w+)`, alias), fileContent,
|
||||
func(match []string) string {
|
||||
return match[1] + item.Alias + match[2]
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcImportedPackages.Add(item.RawImport)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate functions and interfaces for service generating.
|
||||
err = c.calculateInterfaceFunctions(in, fileContent, srcPkgInterfaceMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
initImportSrcPackages = append(
|
||||
initImportSrcPackages,
|
||||
fmt.Sprintf(`%s/%s`, in.ImportPrefix, srcPackageName),
|
||||
)
|
||||
// Ignore source packages if input packages given.
|
||||
if len(inputPackages) > 0 && !gstr.InArray(inputPackages, srcPackageName) {
|
||||
mlog.Debugf(
|
||||
`ignore source package "%s" as it is not in desired packages: %+v`,
|
||||
srcPackageName, inputPackages,
|
||||
)
|
||||
continue
|
||||
}
|
||||
// Generating service go file for single logic package.
|
||||
if ok, err = c.generateServiceFile(generateServiceFilesInput{
|
||||
CGenServiceInput: in,
|
||||
SrcStructFunctions: srcPkgInterfaceMap,
|
||||
SrcImportedPackages: srcImportedPackages.Slice(),
|
||||
SrcPackageName: srcPackageName,
|
||||
DstPackageName: dstPackageName,
|
||||
DstFilePath: dstFilePath,
|
||||
SrcCodeCommentedMap: srcCodeCommentedMap,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
isDirty = true
|
||||
}
|
||||
}
|
||||
|
||||
if in.Clear {
|
||||
files, err = gfile.ScanDirFile(in.DstFolder, "*.go", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var relativeFilePath string
|
||||
for _, file := range files {
|
||||
relativeFilePath = gstr.SubStrFromR(file, in.DstFolder)
|
||||
if !generatedDstFilePathSet.Contains(relativeFilePath) && utils.IsFileDoNotEdit(relativeFilePath) {
|
||||
mlog.Printf(`remove no longer used service file: %s`, relativeFilePath)
|
||||
if err = gfile.Remove(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isDirty {
|
||||
// Generate initialization go file.
|
||||
if len(initImportSrcPackages) > 0 {
|
||||
if err = c.generateInitializationFile(in, initImportSrcPackages); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Replace v1 to v2 for GoFrame.
|
||||
if err = utils.ReplaceGeneratedContentGFV2(in.DstFolder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mlog.Printf(`gofmt go files in "%s"`, in.DstFolder)
|
||||
utils.GoFmt(in.DstFolder)
|
||||
}
|
||||
|
||||
// auto update main.go.
|
||||
if err = c.checkAndUpdateMain(in.SrcFolder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenService) checkAndUpdateMain(srcFolder string) (err error) {
|
||||
var (
|
||||
logicPackageName = gstr.ToLower(gfile.Basename(srcFolder))
|
||||
logicFilePath = gfile.Join(srcFolder, logicPackageName+".go")
|
||||
importPath = utils.GetImportPath(logicFilePath)
|
||||
importStr = fmt.Sprintf(`_ "%s"`, importPath)
|
||||
mainFilePath = gfile.Join(gfile.Dir(gfile.Dir(gfile.Dir(logicFilePath))), "main.go")
|
||||
mainFileContent = gfile.GetContents(mainFilePath)
|
||||
)
|
||||
// No main content found.
|
||||
if mainFileContent == "" {
|
||||
return nil
|
||||
}
|
||||
if gstr.Contains(mainFileContent, importStr) {
|
||||
return nil
|
||||
}
|
||||
match, err := gregex.MatchString(`import \(([\s\S]+?)\)`, mainFileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// No match.
|
||||
if len(match) < 2 {
|
||||
return nil
|
||||
}
|
||||
lines := garray.NewStrArrayFrom(gstr.Split(match[1], "\n"))
|
||||
for i, line := range lines.Slice() {
|
||||
line = gstr.Trim(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if line[0] == '_' {
|
||||
continue
|
||||
}
|
||||
// Insert the logic import into imports.
|
||||
if err = lines.InsertBefore(i, fmt.Sprintf("\t%s\n\n", importStr)); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
mainFileContent, err = gregex.ReplaceString(
|
||||
`import \(([\s\S]+?)\)`,
|
||||
fmt.Sprintf(`import (%s)`, lines.Join("\n")),
|
||||
mainFileContent,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Print(`update main.go`)
|
||||
err = gfile.PutContents(mainFilePath, mainFileContent)
|
||||
utils.GoFmt(mainFilePath)
|
||||
return
|
||||
}
|
||||
173
cmd/gf/internal/cmd/genservice/genservice_calculate.go
Normal file
173
cmd/gf/internal/cmd/genservice/genservice_calculate.go
Normal file
@ -0,0 +1,173 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type packageItem struct {
|
||||
Alias string
|
||||
Path string
|
||||
RawImport string
|
||||
}
|
||||
|
||||
func (c CGenService) calculateImportedPackages(fileContent string) (packages []packageItem, err error) {
|
||||
f, err := parser.ParseFile(token.NewFileSet(), "", fileContent, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packages = make([]packageItem, 0)
|
||||
for _, s := range f.Imports {
|
||||
if s.Path != nil {
|
||||
if s.Name != nil {
|
||||
// If it has alias, and it is not `_`.
|
||||
if pkgAlias := s.Name.String(); pkgAlias != "_" {
|
||||
packages = append(packages, packageItem{
|
||||
Alias: pkgAlias,
|
||||
Path: s.Path.Value,
|
||||
RawImport: pkgAlias + " " + s.Path.Value,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// no alias
|
||||
packages = append(packages, packageItem{
|
||||
Alias: "",
|
||||
Path: s.Path.Value,
|
||||
RawImport: s.Path.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
func (c CGenService) calculateCodeCommented(in CGenServiceInput, fileContent string, srcCodeCommentedMap map[string]string) error {
|
||||
matches, err := gregex.MatchAllString(`((((//.*)|(/\*[\s\S]*?\*/))\s)+)func \((.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structName string
|
||||
structMatch []string
|
||||
funcReceiver = gstr.Trim(match[1+5])
|
||||
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
|
||||
functionHead = gstr.Trim(gstr.Replace(match[2+5], "\n", ""))
|
||||
commentedInfo = ""
|
||||
)
|
||||
if len(receiverArray) > 1 {
|
||||
structName = receiverArray[1]
|
||||
} else if len(receiverArray) == 1 {
|
||||
structName = receiverArray[0]
|
||||
}
|
||||
structName = gstr.Trim(structName, "*")
|
||||
|
||||
// Case of:
|
||||
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
|
||||
functionHead = gstr.Replace(functionHead, `,)`, `)`)
|
||||
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
|
||||
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
|
||||
if !gstr.IsLetterUpper(functionHead[0]) {
|
||||
continue
|
||||
}
|
||||
// Match and pick the struct name from receiver.
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
|
||||
commentedInfo = match[1]
|
||||
if len(commentedInfo) > 0 {
|
||||
srcCodeCommentedMap[fmt.Sprintf("%s-%s", structName, functionHead)] = commentedInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c CGenService) calculateInterfaceFunctions(
|
||||
in CGenServiceInput, fileContent string, srcPkgInterfaceMap map[string]*garray.StrArray,
|
||||
) (err error) {
|
||||
var (
|
||||
ok bool
|
||||
matches [][]string
|
||||
srcPkgInterfaceFuncArray *garray.StrArray
|
||||
)
|
||||
// calculate struct name and its functions according function definitions.
|
||||
matches, err = gregex.MatchAllString(`func \((.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structName string
|
||||
structMatch []string
|
||||
funcReceiver = gstr.Trim(match[1])
|
||||
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
|
||||
functionHead = gstr.Trim(gstr.Replace(match[2], "\n", ""))
|
||||
)
|
||||
if len(receiverArray) > 1 {
|
||||
structName = receiverArray[1]
|
||||
} else if len(receiverArray) == 1 {
|
||||
structName = receiverArray[0]
|
||||
}
|
||||
structName = gstr.Trim(structName, "*")
|
||||
|
||||
// Case of:
|
||||
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
|
||||
functionHead = gstr.Replace(functionHead, `,)`, `)`)
|
||||
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
|
||||
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
|
||||
if !gstr.IsLetterUpper(functionHead[0]) {
|
||||
continue
|
||||
}
|
||||
// Match and pick the struct name from receiver.
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
|
||||
srcPkgInterfaceMap[structName] = garray.NewStrArray()
|
||||
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
|
||||
}
|
||||
srcPkgInterfaceFuncArray.Append(functionHead)
|
||||
}
|
||||
// calculate struct name according type definitions.
|
||||
matches, err = gregex.MatchAllString(`type (.+) struct\s*{`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structName string
|
||||
structMatch []string
|
||||
)
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, match[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
|
||||
srcPkgInterfaceMap[structName] = garray.NewStrArray()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
202
cmd/gf/internal/cmd/genservice/genservice_generate.go
Normal file
202
cmd/gf/internal/cmd/genservice/genservice_generate.go
Normal file
@ -0,0 +1,202 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
type generateServiceFilesInput struct {
|
||||
CGenServiceInput
|
||||
DstFilePath string // Absolute file path for generated service go file.
|
||||
SrcStructFunctions map[string]*garray.StrArray
|
||||
SrcImportedPackages []string
|
||||
SrcPackageName string
|
||||
DstPackageName string
|
||||
SrcCodeCommentedMap map[string]string
|
||||
}
|
||||
|
||||
func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool, err error) {
|
||||
var (
|
||||
generatedContent string
|
||||
allFuncArray = garray.NewStrArray() // Used for check whether interface dirty, going to change file content.
|
||||
importedPackagesContent = fmt.Sprintf(
|
||||
"import (\n%s\n)", gstr.Join(in.SrcImportedPackages, "\n"),
|
||||
)
|
||||
)
|
||||
generatedContent += gstr.ReplaceByMap(consts.TemplateGenServiceContentHead, g.MapStrStr{
|
||||
"{Imports}": importedPackagesContent,
|
||||
"{PackageName}": in.DstPackageName,
|
||||
})
|
||||
|
||||
// Type definitions.
|
||||
generatedContent += "type("
|
||||
generatedContent += "\n"
|
||||
for structName, funcArray := range in.SrcStructFunctions {
|
||||
allFuncArray.Append(funcArray.Slice()...)
|
||||
// Add comments to a method.
|
||||
for index, funcName := range funcArray.Slice() {
|
||||
if commentedInfo, exist := in.SrcCodeCommentedMap[fmt.Sprintf("%s-%s", structName, funcName)]; exist {
|
||||
funcName = commentedInfo + funcName
|
||||
_ = funcArray.Set(index, funcName)
|
||||
}
|
||||
}
|
||||
generatedContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentInterface, g.MapStrStr{
|
||||
"{InterfaceName}": "I" + structName,
|
||||
"{FuncDefinition}": funcArray.Join("\n\t"),
|
||||
}))
|
||||
generatedContent += "\n"
|
||||
}
|
||||
generatedContent += ")"
|
||||
generatedContent += "\n"
|
||||
|
||||
// Generating variable and register definitions.
|
||||
var (
|
||||
variableContent string
|
||||
generatingInterfaceCheck string
|
||||
)
|
||||
// Variable definitions.
|
||||
for structName := range in.SrcStructFunctions {
|
||||
generatingInterfaceCheck = fmt.Sprintf(`[^\w\d]+%s.I%s[^\w\d]`, in.DstPackageName, structName)
|
||||
if gregex.IsMatchString(generatingInterfaceCheck, generatedContent) {
|
||||
continue
|
||||
}
|
||||
variableContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentVariable, g.MapStrStr{
|
||||
"{StructName}": structName,
|
||||
"{InterfaceName}": "I" + structName,
|
||||
}))
|
||||
variableContent += "\n"
|
||||
}
|
||||
if variableContent != "" {
|
||||
generatedContent += "var("
|
||||
generatedContent += "\n"
|
||||
generatedContent += variableContent
|
||||
generatedContent += ")"
|
||||
generatedContent += "\n"
|
||||
}
|
||||
// Variable register function definitions.
|
||||
for structName := range in.SrcStructFunctions {
|
||||
generatingInterfaceCheck = fmt.Sprintf(`[^\w\d]+%s.I%s[^\w\d]`, in.DstPackageName, structName)
|
||||
if gregex.IsMatchString(generatingInterfaceCheck, generatedContent) {
|
||||
continue
|
||||
}
|
||||
generatedContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentRegister, g.MapStrStr{
|
||||
"{StructName}": structName,
|
||||
"{InterfaceName}": "I" + structName,
|
||||
}))
|
||||
generatedContent += "\n\n"
|
||||
}
|
||||
|
||||
// Replace empty braces that have new line.
|
||||
generatedContent, _ = gregex.ReplaceString(`{[\s\t]+}`, `{}`, generatedContent)
|
||||
|
||||
// Remove package name calls of `dstPackageName` in produced codes.
|
||||
generatedContent, _ = gregex.ReplaceString(fmt.Sprintf(`\*{0,1}%s\.`, in.DstPackageName), ``, generatedContent)
|
||||
|
||||
// Write file content to disk.
|
||||
if gfile.Exists(in.DstFilePath) {
|
||||
if !utils.IsFileDoNotEdit(in.DstFilePath) {
|
||||
mlog.Printf(`ignore file as it is manually maintained: %s`, in.DstFilePath)
|
||||
return false, nil
|
||||
}
|
||||
if !c.isToGenerateServiceGoFile(in.DstPackageName, in.DstFilePath, allFuncArray) {
|
||||
mlog.Printf(`not dirty, ignore generating service go file: %s`, in.DstFilePath)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
mlog.Printf(`generating service go file: %s`, in.DstFilePath)
|
||||
if err = gfile.PutContents(in.DstFilePath, generatedContent); err != nil {
|
||||
return true, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// isToGenerateServiceGoFile checks and returns whether the service content dirty.
|
||||
func (c CGenService) isToGenerateServiceGoFile(dstPackageName, filePath string, funcArray *garray.StrArray) bool {
|
||||
var (
|
||||
err error
|
||||
fileContent = gfile.GetContents(filePath)
|
||||
generatedFuncArray = garray.NewSortedStrArrayFrom(funcArray.Slice())
|
||||
contentFuncArray = garray.NewSortedStrArray()
|
||||
)
|
||||
if fileContent == "" {
|
||||
return true
|
||||
}
|
||||
// remove all comments.
|
||||
fileContent, err = gregex.ReplaceString(`(//.*)|((?s)/\*.*?\*/)`, "", fileContent)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return false
|
||||
}
|
||||
matches, _ := gregex.MatchAllString(`\s+interface\s+{([\s\S]+?)}`, fileContent)
|
||||
for _, match := range matches {
|
||||
contentFuncArray.Append(gstr.SplitAndTrim(match[1], "\n")...)
|
||||
}
|
||||
if generatedFuncArray.Len() != contentFuncArray.Len() {
|
||||
mlog.Debugf(
|
||||
`dirty, generatedFuncArray.Len()[%d] != contentFuncArray.Len()[%d]`,
|
||||
generatedFuncArray.Len(), contentFuncArray.Len(),
|
||||
)
|
||||
return true
|
||||
}
|
||||
var funcDefinition string
|
||||
for i := 0; i < generatedFuncArray.Len(); i++ {
|
||||
funcDefinition, _ = gregex.ReplaceString(
|
||||
fmt.Sprintf(`\*{0,1}%s\.`, dstPackageName), ``, generatedFuncArray.At(i),
|
||||
)
|
||||
if funcDefinition != contentFuncArray.At(i) {
|
||||
mlog.Debugf(`dirty, %s != %s`, funcDefinition, contentFuncArray.At(i))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// generateInitializationFile generates `logic.go`.
|
||||
func (c CGenService) generateInitializationFile(in CGenServiceInput, importSrcPackages []string) (err error) {
|
||||
var (
|
||||
logicPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder))
|
||||
logicFilePath = gfile.Join(in.SrcFolder, logicPackageName+".go")
|
||||
logicImports string
|
||||
generatedContent string
|
||||
)
|
||||
if !utils.IsFileDoNotEdit(logicFilePath) {
|
||||
mlog.Debugf(`ignore file as it is manually maintained: %s`, logicFilePath)
|
||||
return nil
|
||||
}
|
||||
for _, importSrcPackage := range importSrcPackages {
|
||||
logicImports += fmt.Sprintf(`%s_ "%s"%s`, "\t", importSrcPackage, "\n")
|
||||
}
|
||||
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceLogicContent, g.MapStrStr{
|
||||
"{PackageName}": logicPackageName,
|
||||
"{Imports}": logicImports,
|
||||
})
|
||||
mlog.Printf(`generating init go file: %s`, logicFilePath)
|
||||
if err = gfile.PutContents(logicFilePath, generatedContent); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.GoFmt(logicFilePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDstFileNameCase call gstr.Case* function to convert the s to specified case.
|
||||
func (c CGenService) getDstFileNameCase(str, caseStr string) (newStr string) {
|
||||
if newStr := gstr.CaseConvert(str, gstr.CaseTypeMatch(caseStr)); newStr != str {
|
||||
return newStr
|
||||
}
|
||||
return gstr.CaseSnake(str)
|
||||
}
|
||||
30
cmd/gf/internal/cmd/testdata/fix/fix25_content.go
vendored
Normal file
30
cmd/gf/internal/cmd/testdata/fix/fix25_content.go
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func Test_Router_Hook_Multi(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/multi-hook", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/multi-hook/{id}", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
}
|
||||
24
cmd/gf/internal/cmd/testdata/genctrl/api/article/article_expect.go
vendored
Normal file
24
cmd/gf/internal/cmd/testdata/genctrl/api/article/article_expect.go
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v2"
|
||||
)
|
||||
|
||||
type IArticleV1 interface {
|
||||
Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error)
|
||||
Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error)
|
||||
GetList(ctx context.Context, req *v1.GetListReq) (res *v1.GetListRes, err error)
|
||||
GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error)
|
||||
}
|
||||
|
||||
type IArticleV2 interface {
|
||||
Create(ctx context.Context, req *v2.CreateReq) (res *v2.CreateRes, err error)
|
||||
Update(ctx context.Context, req *v2.UpdateReq) (res *v2.UpdateRes, err error)
|
||||
}
|
||||
27
cmd/gf/internal/cmd/testdata/genctrl/api/article/v1/edit.go
vendored
Normal file
27
cmd/gf/internal/cmd/testdata/genctrl/api/article/v1/edit.go
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type (
|
||||
CreateReq struct {
|
||||
g.Meta `path:"/article/create" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
CreateRes struct{}
|
||||
)
|
||||
|
||||
type (
|
||||
UpdateReq struct {
|
||||
g.Meta `path:"/article/update" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
UpdateRes struct{}
|
||||
)
|
||||
25
cmd/gf/internal/cmd/testdata/genctrl/api/article/v1/get.go
vendored
Normal file
25
cmd/gf/internal/cmd/testdata/genctrl/api/article/v1/get.go
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type GetListReq struct {
|
||||
g.Meta `path:"/article/list" method:"get" tags:"ArticleService"`
|
||||
}
|
||||
|
||||
type GetListRes struct {
|
||||
list []struct{}
|
||||
}
|
||||
|
||||
type GetOneReq struct {
|
||||
g.Meta `path:"/article/one" method:"get" tags:"ArticleService"`
|
||||
}
|
||||
|
||||
type GetOneRes struct {
|
||||
one struct{}
|
||||
}
|
||||
23
cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go
vendored
Normal file
23
cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package v2
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type CreateReq struct {
|
||||
g.Meta `path:"/article/create" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
type CreateRes struct{}
|
||||
|
||||
type UpdateReq struct {
|
||||
g.Meta `path:"/article/update" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
type UpdateRes struct{}
|
||||
5
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article.go
vendored
Normal file
5
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article.go
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package article
|
||||
21
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_new.go
vendored
Normal file
21
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_new.go
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article"
|
||||
)
|
||||
|
||||
type ControllerV1 struct{}
|
||||
|
||||
func NewV1() article.IArticleV1 {
|
||||
return &ControllerV1{}
|
||||
}
|
||||
|
||||
type ControllerV2 struct{}
|
||||
|
||||
func NewV2() article.IArticleV2 {
|
||||
return &ControllerV2{}
|
||||
}
|
||||
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_create.go
vendored
Normal file
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_create.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_get_list.go
vendored
Normal file
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_get_list.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetList(ctx context.Context, req *v1.GetListReq) (res *v1.GetListRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_get_one.go
vendored
Normal file
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_get_one.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_update.go
vendored
Normal file
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v1_update.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v2_create.go
vendored
Normal file
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v2_create.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v2"
|
||||
)
|
||||
|
||||
func (c *ControllerV2) Create(ctx context.Context, req *v2.CreateReq) (res *v2.CreateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v2_update.go
vendored
Normal file
14
cmd/gf/internal/cmd/testdata/genctrl/controller/article/article_v2_update.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/genctrl/api/article/v2"
|
||||
)
|
||||
|
||||
func (c *ControllerV2) Update(ctx context.Context, req *v2.UpdateReq) (res *v2.UpdateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
85
cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/internal/table_user.go
vendored
Normal file
85
cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/internal/table_user.go
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// TableUserDao is the data access object for table table_user.
|
||||
type TableUserDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of current DAO.
|
||||
columns TableUserColumns // columns contains all the column names of Table for convenient usage.
|
||||
}
|
||||
|
||||
// TableUserColumns defines and stores column names for table table_user.
|
||||
type TableUserColumns struct {
|
||||
Id string // User ID
|
||||
Passport string // User Passport
|
||||
Password string // User Password
|
||||
Nickname string // User Nickname
|
||||
Score string // Total score amount.
|
||||
CreateAt string // Created Time
|
||||
UpdateAt string // Updated Time
|
||||
}
|
||||
|
||||
// tableUserColumns holds the columns for table table_user.
|
||||
var tableUserColumns = TableUserColumns{
|
||||
Id: "id",
|
||||
Passport: "passport",
|
||||
Password: "password",
|
||||
Nickname: "nickname",
|
||||
Score: "score",
|
||||
CreateAt: "create_at",
|
||||
UpdateAt: "update_at",
|
||||
}
|
||||
|
||||
// NewTableUserDao creates and returns a new DAO object for table data access.
|
||||
func NewTableUserDao() *TableUserDao {
|
||||
return &TableUserDao{
|
||||
group: "test",
|
||||
table: "table_user",
|
||||
columns: tableUserColumns,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of current DAO.
|
||||
func (dao *TableUserDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of current dao.
|
||||
func (dao *TableUserDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of current dao.
|
||||
func (dao *TableUserDao) Columns() TableUserColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the configuration group name of database of current dao.
|
||||
func (dao *TableUserDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
|
||||
func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function f
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *TableUserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
27
cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/table_user.go
vendored
Normal file
27
cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/table_user.go
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"for-gendao-test/pkg/dao/internal"
|
||||
)
|
||||
|
||||
// internalTableUserDao is internal type for wrapping internal DAO implements.
|
||||
type internalTableUserDao = *internal.TableUserDao
|
||||
|
||||
// tableUserDao is the data access object for table table_user.
|
||||
// You can define custom methods on it to extend its functionality as you wish.
|
||||
type tableUserDao struct {
|
||||
internalTableUserDao
|
||||
}
|
||||
|
||||
var (
|
||||
// TableUser is globally public accessible object for table table_user operations.
|
||||
TableUser = tableUserDao{
|
||||
internal.NewTableUserDao(),
|
||||
}
|
||||
)
|
||||
|
||||
// Fill with you ideas below.
|
||||
22
cmd/gf/internal/cmd/testdata/gendao/generated_user/model/do/table_user.go
vendored
Normal file
22
cmd/gf/internal/cmd/testdata/gendao/generated_user/model/do/table_user.go
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// TableUser is the golang structure of table table_user for DAO operations like Where/Data.
|
||||
type TableUser struct {
|
||||
g.Meta `orm:"table:table_user, do:true"`
|
||||
Id interface{} // User ID
|
||||
Passport interface{} // User Passport
|
||||
Password interface{} // User Password
|
||||
Nickname interface{} // User Nickname
|
||||
Score interface{} // Total score amount.
|
||||
CreateAt *gtime.Time // Created Time
|
||||
UpdateAt *gtime.Time // Updated Time
|
||||
}
|
||||
20
cmd/gf/internal/cmd/testdata/gendao/generated_user/model/entity/table_user.go
vendored
Normal file
20
cmd/gf/internal/cmd/testdata/gendao/generated_user/model/entity/table_user.go
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// TableUser is the golang structure for table table_user.
|
||||
type TableUser struct {
|
||||
Id uint `json:"ID" ` // User ID
|
||||
Passport string `json:"PASSPORT" ` // User Passport
|
||||
Password string `json:"PASSWORD" ` // User Password
|
||||
Nickname string `json:"NICKNAME" ` // User Nickname
|
||||
Score float64 `json:"SCORE" ` // Total score amount.
|
||||
CreateAt *gtime.Time `json:"CREATE_AT" ` // Created Time
|
||||
UpdateAt *gtime.Time `json:"UPDATE_AT" ` // Updated Time
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user