diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 935237d..0000000 --- a/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM ubuntu:latest -RUN mkdir /scripts -COPY ./src/scripts/run.sh /scripts -RUN apt-get update -RUN apt-get install -y curl gcc g++ software-properties-common python3 default-jre default-jdk golang r-base php-cli ruby-full npm --no-install-recommends -# RUN apt-get install -y gcc -# RUN apt-get install -y g++ -# RUN apt-get install -y software-properties-common -# RUN apt-get install -y python3 -# RUN apt-get install -y default-jre -# RUN apt-get install -y default-jdk -# RUN apt-get install -y golang -# RUN apt-get install -y r-base -# RUN apt-get install -y php-cli -# RUN apt-get install -y ruby-full -# RUN apt-get install -y npm -RUN npm install -g underscore shelljs sys lodash async body-parser -RUN curl https://sh.rustup.rs -sSf | bash -s -- -y diff --git a/Pipfile b/Pipfile index ce948cd..eec611e 100644 --- a/Pipfile +++ b/Pipfile @@ -13,8 +13,10 @@ tox = "*" [packages] quart = "*" quart-cors = "*" -aiodocker = "*" python-dotenv = "*" +docker = "*" +inquirer = "*" +halo = "*" [requires] python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock index 595f2c6..695a1b2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "cf05fe856c5cbcbb5be4fa0a8d081d804e25c69ce649660ebe2aad776b9772fb" + "sha256": "fbd19e03b725d06f9a5ca647acfef87b49953ca362c49c4f4308654cc7091f40" }, "pipfile-spec": 6, "requires": { @@ -16,14 +16,6 @@ ] }, "default": { - "aiodocker": { - "hashes": [ - "sha256:59dfae91b5acbfa953baf4a3553b7c5ff375346b0f3bbfd8cae11c3b93adce04", - "sha256:bfbb44dbee185dbc8943be68d1f51358af3ec473c463bdee68a25e33d70ae3ad" - ], - "index": "pypi", - "version": "==0.19.1" - }, "aiofiles": { "hashes": [ "sha256:377fdf7815cc611870c59cbd07b68b180841d2a2b79812d8c218be02448c2acb", @@ -31,39 +23,12 @@ ], "version": "==0.5.0" }, - "aiohttp": { - "hashes": [ - "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e", - "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326", - "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a", - "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654", - "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a", - "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4", - "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17", - "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec", - "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd", - "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48", - "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59", - "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965" - ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.6.2" - }, - "async-timeout": { - "hashes": [ - "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", - "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" - ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.0.1" - }, - "attrs": { + "blessed": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:8371d69ac55558e4b1591964873d6721136e9ea17a730aeb3add7d27761b134b", + "sha256:a9a774fc6eda05248735b0d86e866d640ca2fef26038878f7e4d23f7749a1e40" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==19.3.0" + "version": "==1.17.6" }, "blinker": { "hashes": [ @@ -71,6 +36,13 @@ ], "version": "==1.4" }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "version": "==2020.6.20" + }, "chardet": { "hashes": [ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", @@ -86,6 +58,22 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==7.1.2" }, + "colorama": { + "hashes": [ + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.4.3" + }, + "docker": { + "hashes": [ + "sha256:03a46400c4080cb6f7aa997f881ddd84fef855499ece219d75fbdb53289c17ab", + "sha256:26eebadce7e298f55b76a88c4f8802476c5eaddbdbe38dbc6cce8781c47c9b54" + ], + "index": "pypi", + "version": "==4.2.2" + }, "h11": { "hashes": [ "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1", @@ -100,6 +88,14 @@ ], "version": "==3.2.0" }, + "halo": { + "hashes": [ + "sha256:78750d4778488b79b12104f3a966259579df6b42eec0d7736b3125bfc4c01b10", + "sha256:ad1fb4d7e29fe846d323bf4a8bf9e65fddcb24c10258c13e964cab13278f9568" + ], + "index": "pypi", + "version": "==0.0.30" + }, "hpack": { "hashes": [ "sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89", @@ -130,6 +126,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.10" }, + "inquirer": { + "hashes": [ + "sha256:d15e15de1ad5696f1967e7a23d8e2fce69d2e41a70b008948d676881ed94c3a5", + "sha256:e819188de0ca7985a99c282176c6f50fb08b0d33867fd1965d3f3e97d6c8f83f" + ], + "index": "pypi", + "version": "==2.7.0" + }, "itsdangerous": { "hashes": [ "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", @@ -146,6 +150,13 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==2.11.2" }, + "log-symbols": { + "hashes": [ + "sha256:4952106ff8b605ab7d5081dd2c7e6ca7374584eff7086f499c06edd1ce56dcca", + "sha256:cf0bbc6fe1a8e53f0d174a716bc625c4f87043cc21eb55dd8a740cfe22680556" + ], + "version": "==0.0.14" + }, "markupsafe": { "hashes": [ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", @@ -185,29 +196,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.1" }, - "multidict": { - "hashes": [ - "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", - "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", - "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", - "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", - "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", - "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", - "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", - "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", - "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", - "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", - "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", - "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", - "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", - "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", - "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", - "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", - "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" - ], - "markers": "python_version >= '3.5'", - "version": "==4.7.6" - }, "priority": { "hashes": [ "sha256:6bc1961a6d7fcacbfc337769f1a382c8e746566aaa365e78047abe9f66b2ffbe", @@ -223,6 +211,16 @@ "index": "pypi", "version": "==0.14.0" }, + "python-editor": { + "hashes": [ + "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", + "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", + "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8", + "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77", + "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522" + ], + "version": "==1.0.4" + }, "quart": { "hashes": [ "sha256:b0b5148ab57b775ed173ed9e9897f3b13d2f54e39cfadf4a396f442ec96bb57f", @@ -239,6 +237,42 @@ "index": "pypi", "version": "==0.3.0" }, + "readchar": { + "hashes": [ + "sha256:3ac34aab28563bc895f73233d5c08b28f951ca190d5850b8d4bec973132a8dca", + "sha256:ed00b7a49bb12f345319d9fa393f289f03670310ada2beb55e8c3f017c648f1e" + ], + "version": "==2.0.1" + }, + "requests": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==2.24.0" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.15.0" + }, + "spinners": { + "hashes": [ + "sha256:1eb6aeb4781d72ab42ed8a01dcf20f3002bf50740d7154d12fb8c9769bf9e27f", + "sha256:2fa30d0b72c9650ad12bbe031c9943b8d441e41b4f5602b0ec977a19f3290e98" + ], + "version": "==0.0.24" + }, + "termcolor": { + "hashes": [ + "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b" + ], + "version": "==1.1.0" + }, "toml": { "hashes": [ "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", @@ -252,8 +286,31 @@ "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae", "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392" ], + "markers": "python_version < '3.8'", "version": "==3.7.4.2" }, + "urllib3": { + "hashes": [ + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.25.10" + }, + "wcwidth": { + "hashes": [ + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" + ], + "version": "==0.2.5" + }, + "websocket-client": { + "hashes": [ + "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549", + "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010" + ], + "version": "==0.57.0" + }, "werkzeug": { "hashes": [ "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", @@ -269,29 +326,6 @@ ], "markers": "python_full_version >= '3.6.1'", "version": "==0.15.0" - }, - "yarl": { - "hashes": [ - "sha256:1707230e1ea48ea06a3e20acb4ce05a38d2465bd9566c21f48f6212a88e47536", - "sha256:1f269e8e6676193a94635399a77c9059e1826fb6265c9204c9e5a8ccd36006e1", - "sha256:2657716c1fc998f5f2675c0ee6ce91282e0da0ea9e4a94b584bb1917e11c1559", - "sha256:431faa6858f0ea323714d8b7b4a7da1db2eeb9403607f0eaa3800ab2c5a4b627", - "sha256:5bbcb195da7de57f4508b7508c33f7593e9516e27732d08b9aad8586c7b8c384", - "sha256:5c82f5b1499342339f22c83b97dbe2b8a09e47163fab86cd934a8dd46620e0fb", - "sha256:5d410f69b4f92c5e1e2a8ffb73337cd8a274388c6975091735795588a538e605", - "sha256:66b4f345e9573e004b1af184bc00431145cf5e089a4dcc1351505c1f5750192c", - "sha256:875b2a741ce0208f3b818008a859ab5d0f461e98a32bbdc6af82231a9e761c55", - "sha256:9a3266b047d15e78bba38c8455bf68b391c040231ca5965ef867f7cbbc60bde5", - "sha256:9a592c4aa642249e9bdaf76897d90feeb08118626b363a6be8788a9b300274b5", - "sha256:a1772068401d425e803999dada29a6babf041786e08be5e79ef63c9ecc4c9575", - "sha256:b065a5c3e050395ae563019253cc6c769a50fd82d7fa92d07476273521d56b7c", - "sha256:b325fefd574ebef50e391a1072d1712a60348ca29c183e1d546c9d87fec2cd32", - "sha256:cf5eb664910d759bbae0b76d060d6e21f8af5098242d66c448bbebaf2a7bfa70", - "sha256:f058b6541477022c7b54db37229f87dacf3b565de4f901ff5a0a78556a174fea", - "sha256:f5cfed0766837303f688196aa7002730d62c5cc802d98c6395ea1feb87252727" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.0" } }, "develop": { @@ -312,11 +346,11 @@ }, "cfgv": { "hashes": [ - "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53", - "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513" + "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", + "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" ], "markers": "python_full_version >= '3.6.1'", - "version": "==3.1.0" + "version": "==3.2.0" }, "coverage": { "hashes": [ @@ -388,6 +422,13 @@ "markers": "python_version < '3.8'", "version": "==1.7.0" }, + "iniconfig": { + "hashes": [ + "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437", + "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69" + ], + "version": "==1.0.1" + }, "more-itertools": { "hashes": [ "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5", @@ -444,11 +485,11 @@ }, "pytest": { "hashes": [ - "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1", - "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8" + "sha256:85228d75db9f45e06e57ef9bf4429267f81ac7c0d742cc9ed63d09886a9fe6f4", + "sha256:8b6007800c53fdacd5a5c192203f4e531eb2a1540ad9c752e052ec0f7143dbad" ], "index": "pypi", - "version": "==5.4.3" + "version": "==6.0.1" }, "pytest-asyncio": { "hashes": [ @@ -507,18 +548,11 @@ }, "virtualenv": { "hashes": [ - "sha256:688a61d7976d82b92f7906c367e83bb4b3f0af96f8f75bfcd3da95608fe8ac6c", - "sha256:8f582a030156282a9ee9d319984b759a232b07f86048c1d6a9e394afa44e78c8" + "sha256:7b54fd606a1b85f83de49ad8d80dbec08e983a2d2f96685045b262ebc7481ee5", + "sha256:8cd7b2a4850b003a11be2fc213e206419efab41115cc14bca20e69654f2ac08e" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.0.28" - }, - "wcwidth": { - "hashes": [ - "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", - "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" - ], - "version": "==0.2.5" + "version": "==20.0.30" }, "zipp": { "hashes": [ diff --git a/cli.py b/cli.py new file mode 100644 index 0000000..908ae41 --- /dev/null +++ b/cli.py @@ -0,0 +1,71 @@ +import os + +import inquirer +from inquirer.themes import GreenPassion + +from src.utils.image_builder import Colorize, ImageBuilder + + +def get_languages() -> list: + """Return a list a list of buildable languages + Assumes the following directory structure + docker/ + lang1/ + Dockerfile + lang2/ + Dockerfile + lang3/ + Dockerfile + """ + x = next(os.walk("docker/"))[1] # get only directories + x.remove("script") # remove script directory + return x + + +def ignore(answer: dict) -> bool: + if "all" in answer.get("action"): + return True + return False + + +def main(questions: list, languages: list): + option = inquirer.prompt(questions, theme=GreenPassion()) + build_type = option.get("action") # Build type: all or selected + build_option = "rebuild" in build_type # build or rebuild + ib = ImageBuilder() + if "all" in build_type: + ib.create_images(languages, build_option) + else: + ib.create_images(option.get("partial_build"), build_option) + + +if __name__ == "__main__": + txt = ( + "This script can build or rebuild the docker images specified in the docker folder." + "It will also generate the language_list file in src/ folder." + ) + summary = Colorize.colorize(text=txt, colour="purple",) + print(summary) + languages = get_languages() + questions = [ + inquirer.List( + name="action", + message="Select the appropriate action", + choices=[ + "build all images", + "build selected images", + "rebuild all images", + "rebuild selected images", + ], + ), + inquirer.Checkbox( + name="partial_build", + message=( + "Select the language you want to install:" + "(press to select, to finalize)" + ), + choices=languages, + ignore=ignore, + ), + ] + main(questions, languages) diff --git a/.dockerignore b/docker/.dockerignore similarity index 89% rename from .dockerignore rename to docker/.dockerignore index ab5417e..f3321fe 100644 --- a/.dockerignore +++ b/docker/.dockerignore @@ -4,5 +4,7 @@ .gitignore .github + .venv temp/ +demo/ diff --git a/docker/gcc/Dockerfile b/docker/gcc/Dockerfile new file mode 100644 index 0000000..e878a75 --- /dev/null +++ b/docker/gcc/Dockerfile @@ -0,0 +1,5 @@ +FROM mridul303/gcc-alpine:10.2.0 +RUN mkdir /script && \ + apk update \ + rm -rf /var/cache/apk/* +COPY ./script /scripe diff --git a/docker/gcc/buld_from_source/Dockerfile b/docker/gcc/buld_from_source/Dockerfile new file mode 100644 index 0000000..e3eae05 --- /dev/null +++ b/docker/gcc/buld_from_source/Dockerfile @@ -0,0 +1,79 @@ +FROM alpine as alpine + +ARG GCC_VERSION +ENV GCC_VERSION=${GCC_VERSION} + + +FROM alpine as builder + +RUN apk add --quiet --no-cache \ + build-base \ + dejagnu \ + isl-dev \ + make \ + mpc1-dev \ + mpfr-dev \ + texinfo \ + zlib-dev +RUN wget -q https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/gcc-${GCC_VERSION}.tar.gz && \ + tar -xzf gcc-${GCC_VERSION}.tar.gz && \ + rm -f gcc-${GCC_VERSION}.tar.gz + +WORKDIR /gcc-${GCC_VERSION} + +RUN ./configure \ + --prefix=/usr/local \ + --build=$(uname -m)-alpine-linux-musl \ + --host=$(uname -m)-alpine-linux-musl \ + --target=$(uname -m)-alpine-linux-musl \ + --with-pkgversion="Alpine ${GCC_VERSION}" \ + --enable-checking=release \ + --disable-fixed-point \ + --disable-libmpx \ + --disable-libmudflap \ + --disable-libsanitizer \ + --disable-libssp \ + --disable-libstdcxx-pch \ + --disable-multilib \ + --disable-nls \ + --disable-symvers \ + --disable-werror \ + --enable-__cxa_atexit \ + --enable-default-pie \ + --enable-languages=c,c++ \ + --enable-shared \ + --enable-threads \ + --enable-tls \ + --with-linker-hash-style=gnu \ + --with-system-zlib +RUN make --silent -j $(nproc) +RUN make --silent -j $(nproc) install-strip + +RUN ln -s /usr/bin/gcc /usr/local/bin/cc + +RUN gcc -v + + +FROM alpine:3.12 + +RUN apk update; \ + apk add --quiet --no-cache \ + autoconf \ + automake \ + binutils \ + cmake \ + file \ + git \ + gmp \ + isl \ + libc-dev \ + libtool \ + make \ + mpc1 \ + mpfr4 \ + musl-dev \ + pkgconf \ + zlib-dev; \ + rm -rf /var/cache/apk/* + +COPY --from=builder /usr/local/ /usr/ diff --git a/docker/python/Dockerfile b/docker/python/Dockerfile new file mode 100644 index 0000000..c2d416b --- /dev/null +++ b/docker/python/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7-alpine3.12 + +RUN mkdir /script && \ + apk update;\ + apk add --no-cache bash; \ + rm -rf /var/cache/apk/* +COPY ./script /script diff --git a/src/scripts/run.sh b/docker/script/run.sh similarity index 100% rename from src/scripts/run.sh rename to docker/script/run.sh diff --git a/src/config.py b/src/config.py index ae84e3e..503a19b 100644 --- a/src/config.py +++ b/src/config.py @@ -5,31 +5,46 @@ class DockerConfig: - def __init__(self, cmd, dest): - timeout = 20 + def __init__(self, cmd: str, dest: str): + tout = 20 self.cmd = cmd self.dest = dest env = os.getenv("QUART_ENV", "production") if env == "development": - timeout = 10 - self.timeout = timeout + tout = 10 + self.tout = tout + def _image_name(self, name: str): + name = name.lower() + if "python" in name: + return "python-compiler" + if "c++" == name or "c" == name: + return "gcc-compiler" + + return f"{name}-compiler" + + @property def data(self): data = { - "config": { - "Image": "compiler:v1", - "Cmd": ["/bin/bash", "-c"], - "HostConfig": {"Binds": []}, - }, - "name": "CompilerDock", - "timeout": 0, + "name": None, + "volumes": {}, + "detach": True, + "image": None, + "command": ["/bin/bash", "-c"], + "auto_remove": True, } - data["timeout"] = self.timeout - data["config"]["Cmd"].append(self.cmd) - data["config"]["HostConfig"]["Binds"].append(f"{self.dest}:/compile") + img_name = self._image_name(self.cmd.split()[1]) + data["image"] = img_name + data["name"] = f"{img_name}-container" + data["command"].append(self.cmd) + data["volumes"].update({self.dest: {"bind": "/compile", "mode": "rw"}}) return data + @property + def timeout(self): + return self.tout + class FileNames(enum.Enum): error = "errors" @@ -38,7 +53,7 @@ class FileNames(enum.Enum): output = "output" -exe = {"c": " ./a.out", "c++": " ./a.out", "rust": " ./file"} +exe = {"c": " && ./a.out", "c++": " && ./a.out", "rust": " && ./file"} lang_cmd = { "python3": "python3", "c": "gcc", diff --git a/src/utils/image_builder.py b/src/utils/image_builder.py new file mode 100644 index 0000000..db14366 --- /dev/null +++ b/src/utils/image_builder.py @@ -0,0 +1,106 @@ +import docker +from docker.errors import APIError, BuildError, ImageNotFound +from halo import Halo + + +class Colorize: + _colours = { + "PURPLE": "\033[95m", + "BLUE": "\033[94m", + "GREEN": "\033[92m", + "ORANGE": "\033[93m", + "RED": "\033[91m", + "ENDC": "\033[0m", + } + + @classmethod + def colorize(cls, text: str, colour: str) -> str: + clr = cls._colours.get(colour.upper(), "") + end = cls._colours.get("ENDC") + return "".join([clr, text, end]) + + +class ImageBuilder: + def __init__(self): + self.client = docker.from_env() + self.spinner = Halo(spinner="dots") + self.colorize = Colorize.colorize + + def __del__(self): + if self.spinner: + self.spinner.stop() + + def image_exists(self, img: str) -> bool: + try: + self.client.images.get(img) + return True + except ImageNotFound: + return False + + def build_images(self, languages): + failed, built = 0, 0 + for lang in languages: + try: + text_build = f"Building image for {lang}" + img_name = f"{lang}-compiler" + self.spinner.start(self.colorize(text=text_build, colour="orange")) + if self.image_exists(img_name): + self.spinner.succeed( + self.colorize( + text=f"Image {img_name} already exists, skipping build...", + colour="green", + ) + ) + else: + _, _ = self.client.images.build( + path="docker/", + dockerfile=f"{lang}/Dockerfile", + tag=img_name, + forcerm=True, + ) + self.spinner.succeed(self.colorize(text=text_build, colour="green")) + built += 1 + + except APIError as apierr: + failed += 1 + self.spinner.fail(self.colorize(text=apierr.explanation, colour="red")) + except BuildError as be: + failed += 1 + self.spinner.fail(self.colorize(text=be.msg, colour="red")) + + print(f"Built: {built}") + print(f"Failed {failed}") + + def rebuild_images(self, languages): + rebuilt, failed = 0, 0 + for lang in languages: + try: + text_build = f"Reuilding image for {lang}" + img_name = f"{lang}-compiler" + self.spinner.start(self.colorize(text=text_build, colour="orange")) + if self.image_exists(img_name): + self.client.images.remove(image=img_name, force=True) + + _, _ = self.client.images.build( + path="docker/", + dockerfile=f"{lang}/Dockerfile", + tag=img_name, + forcerm=True, + ) + self.spinner.succeed(self.colorize(text=text_build, colour="green")) + rebuilt += 1 + except APIError as apierr: + failed += 1 + self.spinner.fail(self.colorize(text=apierr.explanation, colour="red")) + except BuildError as be: + failed += 1 + self.spinner.fail(self.colorize(text=be.msg, colour="red")) + + print(f"Reuilt: {rebuilt}") + print(f"Failed {failed}") + + def create_images(self, languages: list, rebuild: bool = False): + if rebuild: + self.rebuild_images(languages) + else: + self.build_images(languages) diff --git a/src/utils/run.py b/src/utils/run.py index 12f0e88..f192fb5 100644 --- a/src/utils/run.py +++ b/src/utils/run.py @@ -2,7 +2,7 @@ import os import tempfile -import aiodocker +import docker from quart.exceptions import HTTPException from src.config import BASE_PATH, DockerConfig, FileNames, exe, filenames, lang_cmd @@ -15,25 +15,21 @@ async def run_container(dest, command): - docker_config = DockerConfig(command, dest).data() - docker = aiodocker.Docker() + docker_config = DockerConfig(command, dest) + client = docker.from_env() sleep_count = 0 - finish_run = False + finish_run = None try: - container = await docker.containers.create_or_replace( - name=docker_config["name"], config=docker_config["config"] - ) - await container.start() - while sleep_count != docker_config["timeout"] and not finish_run: + client.containers.run(**docker_config.data) + while sleep_count != docker_config.timeout and not finish_run: if os.path.exists(f"{dest}/{FileNames.completed.value}"): finish_run = True break else: await asyncio.sleep(1) sleep_count += 1 - await container.delete(force=True) + except Exception: - await docker.close() raise HTTPException(status_code=500, description="Docker error", name=None) else: return finish_run @@ -56,7 +52,7 @@ async def run_code(lang, code, stdin): create_files(stdin, FileNames.input.value, dest) command = "".join( [ - "/scripts/run.sh ", + "/script/run.sh ", lang_cmd[lang], " ", filenames[lang], @@ -65,7 +61,6 @@ async def run_code(lang, code, stdin): ) finish_run = await run_container(dest, command) - print("GOT HERE??") error, fail = error_check(dest, FileNames.error.value) if finish_run: output = read_ouput(dest, FileNames.completed.value) diff --git a/tox.ini b/tox.ini index 5359a9f..84fd443 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ known_third_party = docker,load_dotenv,pytest,quart,quart_cors [flake8] -max-line-length = 88 +max-line-length = 92 max-complexity = 18 select = B,C,E,F,W,T4,B9 ignore = E203, W503