diff --git a/.github/workflows/deno_on_release.yml b/.github/workflows/deno_on_release.yml new file mode 100644 index 0000000000..f194b96567 --- /dev/null +++ b/.github/workflows/deno_on_release.yml @@ -0,0 +1,49 @@ +name: Publish deno-client +on: + push: + tags: + - "v*" +env: + repo: windmill-deno-client + +jobs: + build_deno_and_push_to_repo: + runs-on: ubuntu-latest + container: openapitools/openapi-generator-cli:v6.0.0-beta + steps: + - uses: actions/checkout@v3 + - name: generate_deno + run: | + cd deno-client + rm .gitignore + ./generate.sh + - name: Pushes to another repository + id: push_directory + uses: cpina/github-action-push-to-another-repository@devel + env: + API_TOKEN_GITHUB: ${{ secrets.DENO_PAT }} + with: + source-directory: deno-client/ + destination-github-username: ${{ github.repository_owner }} + destination-repository-name: ${{ env.repo }} + user-email: ruben@windmill.dev + commit-message: See ORIGIN_COMMIT from $GITHUB_REF + target-branch: main + + tag_repo: + needs: [build_deno_and_push_to_repo] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + repository: ${{ github.repository_owner }}/${{ env.repo }} + token: ${{ secrets.DENO_PAT }} + path: ./client + + - name: Push client + run: | + cd ./client + git config --global user.email "ruben@windmill.dev" + git config --global user.name "rubenfiszel[bot]" + git tag -a ${{ github.ref_name }} -m "${{ github.ref_name }}" + git push --tags diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 50a7d6db2f..a15f3d6e30 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "adler" version = "1.0.2" @@ -87,6 +97,20 @@ dependencies = [ "term", ] +[[package]] +name = "ast_node" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc4c00309ed1c8104732df4a5fa9acc3b796b6f8531dfbd5ce0078c86f997244" +dependencies = [ + "darling", + "pmutil", + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "async-lock" version = "2.5.0" @@ -255,6 +279,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" +[[package]] +name = "better_scoped_tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73e8ecdec39e98aa3b19e8cd0b8ed8f77ccb86a6b0b2dc7cd86d105438a2123" +dependencies = [ + "scoped-tls", +] + [[package]] name = "bit-set" version = "0.5.2" @@ -606,6 +639,41 @@ dependencies = [ "subtle 1.0.0", ] +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "data-encoding" version = "2.3.2" @@ -618,6 +686,15 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" +[[package]] +name = "debug_unreachable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3" +dependencies = [ + "unreachable", +] + [[package]] name = "deno_core" version = "0.110.0" @@ -725,7 +802,7 @@ dependencies = [ "lazy_static", "regex", "serde", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -782,6 +859,18 @@ dependencies = [ "syn", ] +[[package]] +name = "enum_kind" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b940da354ae81ef0926c5eaa428207b8f4f091d3956c891dfbd124162bed99" +dependencies = [ + "pmutil", + "proc-macro2", + "swc_macros_common", + "syn", +] + [[package]] name = "event-listener" version = "2.5.2" @@ -880,6 +969,18 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "from_variant" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0951635027ca477be98f8774abd6f0345233439d63f307e47101acb40c7cc63d" +dependencies = [ + "pmutil", + "proc-macro2", + "swc_macros_common", + "syn", +] + [[package]] name = "fslock" version = "0.1.8" @@ -1402,6 +1503,12 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.3" @@ -1472,6 +1579,19 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" +[[package]] +name = "is-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b2c46692aee0d1b3aad44e781ac0f0e7db42ef27adaa0a877b627040019813" +dependencies = [ + "Inflector", + "pmutil", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "itertools" version = "0.8.2" @@ -1550,7 +1670,7 @@ dependencies = [ "serde", "serde_derive", "sha2 0.8.2", - "string_cache", + "string_cache 0.7.5", "term", "unicode-xid 0.1.0", ] @@ -1593,6 +1713,79 @@ dependencies = [ "webpki-roots 0.22.3", ] +[[package]] +name = "lexical" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd3e434c16f0164124ade12dcdee324fcc3dafb1cad0c7f1d8c2451a1aa6886" +dependencies = [ + "lexical-core", +] + +[[package]] +name = "lexical-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92912c4af2e7d9075be3e5e3122c4d7263855fa6cce34fbece4dd08e5884624d" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f518eed87c3be6debe6d26b855c97358d8a11bf05acec137e5f53080f5ad2dd8" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc852ec67c6538bbb2b9911116a385b24510e879a69ab516e6a151b15a79168" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c72a9d52c5c4e62fa2cdc2cb6c694a39ae1382d9c2a17a466f18e272a0930eb1" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a89ec1d062e481210c309b672f73a0567b7855f21e7d2fae636df44d12e97f9" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "094060bd2a7c2ff3a16d5304a6ae82727cb3cc9d1c70f813cc73f744c319337e" +dependencies = [ + "lexical-util", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.123" @@ -1853,6 +2046,18 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -2055,17 +2260,36 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" dependencies = [ - "phf_shared", + "phf_shared 0.7.24", "rand 0.6.5", ] +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + [[package]] name = "phf_shared" version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ - "siphasher", + "siphasher 0.2.3", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.10", ] [[package]] @@ -2112,6 +2336,17 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "pmutil" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -2569,6 +2804,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustls" version = "0.19.1" @@ -2612,7 +2853,7 @@ dependencies = [ "lalrpop", "lalrpop-util", "log", - "num-bigint", + "num-bigint 0.2.6", "num-traits", "unic-emoji-char", "unic-ucd-ident", @@ -2644,6 +2885,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scopeguard" version = "1.1.0" @@ -2837,6 +3084,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.6" @@ -3008,6 +3261,12 @@ dependencies = [ "tokio-rustls 0.22.0", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string_cache" version = "0.7.5" @@ -3016,32 +3275,71 @@ checksum = "89c058a82f9fd69b1becf8c274f412281038877c553182f1d02eb027045a2d67" dependencies = [ "lazy_static", "new_debug_unreachable", - "phf_shared", + "phf_shared 0.7.24", "precomputed-hash", "serde", - "string_cache_codegen", + "string_cache_codegen 0.4.4", "string_cache_shared", ] +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot 0.12.0", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + [[package]] name = "string_cache_codegen" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.7.24", + "phf_shared 0.7.24", "proc-macro2", "quote", "string_cache_shared", ] +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + [[package]] name = "string_cache_shared" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" +[[package]] +name = "string_enum" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f584cc881e9e5f1fd6bf827b0444aa94c30d8fe6378cf241071b5f5700b2871f" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "stringprep" version = "0.1.2" @@ -3052,6 +3350,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "strsim" version = "0.10.0" @@ -3070,6 +3374,125 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "swc_atoms" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8735ce37e421749498e038955abc1135eec6a4af0b54a173e55d2e5542d472" +dependencies = [ + "string_cache 0.8.4", + "string_cache_codegen 0.5.2", +] + +[[package]] +name = "swc_common" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5988fd245a86176271951c089f9144b7b48014bc1212872f9bd194f5f0f9dfd" +dependencies = [ + "ahash", + "ast_node", + "better_scoped_tls", + "cfg-if 1.0.0", + "debug_unreachable", + "either", + "from_variant", + "num-bigint 0.4.3", + "once_cell", + "rustc-hash", + "serde", + "siphasher 0.3.10", + "string_cache 0.8.4", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_ecma_ast" +version = "0.78.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed68ad13e4489f309ffed9d302337d7c8bde11d00b8b275b7aa7fda4da035bf" +dependencies = [ + "is-macro", + "num-bigint 0.4.3", + "serde", + "string_enum", + "swc_atoms", + "swc_common", + "unicode-id", +] + +[[package]] +name = "swc_ecma_parser" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb97dc6efc95313dedc5158055cc811da77395ef7b54be61948b5ad097a3671" +dependencies = [ + "either", + "enum_kind", + "lexical", + "num-bigint 0.4.3", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", + "unicode-id", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c8f200a2eaed938e7c1a685faaa66e6d42fa9e17da5f62572d3cbc335898f5e" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_macros_common" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5dca3f08d02da4684c3373150f7c045128f81ea00f0c434b1b012bc65a6cce3" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_visit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c639379dd2a8a0221fa1e12fafbdd594ba53a0cace6560054da52409dfcc1a" +dependencies = [ + "either", + "swc_visit_macros", +] + +[[package]] +name = "swc_visit_macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b9b72892df873972549838bf84d6c56234c7502148a7e23b5a3da6e0fedfb8" +dependencies = [ + "Inflector", + "pmutil", + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "syn" version = "1.0.91" @@ -3607,6 +4030,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typed-arena" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" + [[package]] name = "typenum" version = "1.15.0" @@ -3691,6 +4120,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-id" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fe8d9274f490a36442acb4edfd0c4e473fdfc6a8b5cd32f28a0235761aedbe" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -3706,6 +4141,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -3730,6 +4171,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" +[[package]] +name = "unreachable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" +dependencies = [ + "void", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -3796,6 +4246,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "walkdir" version = "2.3.2" @@ -4066,6 +4522,9 @@ dependencies = [ "slack-http-verifier", "sql-builder", "sqlx", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", "tempfile", "thiserror", "time 0.3.9", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index c1aebc4154..c7526aec41 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -55,6 +55,9 @@ regex = "^1" deno_core = "^0" indexmap = "~1.6.2" async-recursion = "^1" +swc_common = "^0" +swc_ecma_parser = "^0" +swc_ecma_ast = "^0" sqlx = { version = "^0", features = ["macros", "offline", "migrate", "uuid", "json", "chrono", "postgres", "runtime-tokio-rustls"]} dotenv = "^0" diff --git a/backend/migrations/20220504193929_typescript_support.down.sql b/backend/migrations/20220504193929_typescript_support.down.sql new file mode 100644 index 0000000000..ffee7499ec --- /dev/null +++ b/backend/migrations/20220504193929_typescript_support.down.sql @@ -0,0 +1,12 @@ +-- Add down migration script here +DROP TYPE SCRIPT_LANG; + +ALTER TABLE script +DROP COLUMN language SCRIPT_LANG; + +ALTER TABLE queue +DROP COLUMN language SCRIPT_LANG; + +ALTER TABLE completed_job +DROP COLUMN language SCRIPT_LANG; + diff --git a/backend/migrations/20220504193929_typescript_support.up.sql b/backend/migrations/20220504193929_typescript_support.up.sql new file mode 100644 index 0000000000..7be9d83390 --- /dev/null +++ b/backend/migrations/20220504193929_typescript_support.up.sql @@ -0,0 +1,11 @@ +-- Add up migration script here +CREATE TYPE SCRIPT_LANG AS ENUM ('python3', 'deno'); + +ALTER TABLE script +ADD COLUMN language SCRIPT_LANG NOT NULL DEFAULT 'python3'; + +ALTER TABLE queue +ADD COLUMN language SCRIPT_LANG NOT NULL DEFAULT 'python3'; + +ALTER TABLE completed_job +ADD COLUMN language SCRIPT_LANG NOT NULL DEFAULT 'python3'; diff --git a/backend/openapi.yaml b/backend/openapi.yaml index f137653e52..0b4e737f19 100644 --- a/backend/openapi.yaml +++ b/backend/openapi.yaml @@ -1317,11 +1317,16 @@ paths: type: array items: type: string + language: + type: string + enum: [python3, deno] + required: - path - summary - description - content + - language responses: "201": description: script created @@ -1330,14 +1335,35 @@ paths: schema: type: string - /scripts/tojsonschema: + /scripts/python/tojsonschema: post: - summary: inspect code to infer jsonschema of arguments - operationId: toJsonschema + summary: inspect python code to infer jsonschema of arguments + operationId: pythonToJsonschema tags: - script requestBody: - description: code with the main function + description: python code with the main function + required: true + content: + application/json: + schema: + type: string + responses: + "200": + description: parsed args + content: + application/json: + schema: + $ref: "#/components/schemas/MainArgSignature" + + /scripts/deno/tojsonschema: + post: + summary: inspect deno code to infer jsonschema of arguments + operationId: denoToJsonschema + tags: + - script + requestBody: + description: deno code with the main function required: true content: application/json: @@ -2654,6 +2680,9 @@ components: type: string lock_error_logs: type: string + language: + type: string + enum: [python3, deno] required: - hash - path @@ -2665,10 +2694,11 @@ components: - deleted - is_template - extra_perms + - language ScriptArgs: type: object - additionalProperties: true + additionalProperties: {} QueuedJob: type: object @@ -2729,6 +2759,9 @@ components: $ref: "#/components/schemas/FlowValue" is_flow_step: type: boolean + language: + type: string + enum: [python3, deno] required: - id - running @@ -2796,6 +2829,9 @@ components: $ref: "#/components/schemas/FlowValue" is_flow_step: type: boolean + language: + type: string + enum: [python3, deno] required: - id - created_by @@ -3057,10 +3093,14 @@ components: type: string args: $ref: "#/components/schemas/ScriptArgs" + language: + type: string + enum: [python3, deno] required: - content - args + - language CreateResource: type: object diff --git a/backend/sqlx-data.json b/backend/sqlx-data.json index 658d76db61..8bbb702e08 100644 --- a/backend/sqlx-data.json +++ b/backend/sqlx-data.json @@ -436,6 +436,64 @@ ] } }, + "2420cb110a116dfbc6b8658a6d2d35db60c6aadd157488cb72eb9116ae7e9f54": { + "query": "INSERT INTO queue\n (workspace_id, id, parent_job, created_by, permissioned_as, scheduled_for, \n script_hash, script_path, raw_code, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step, language)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Uuid", + "Uuid", + "Varchar", + "Varchar", + "Timestamptz", + "Int8", + "Varchar", + "Text", + "Jsonb", + { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Bool", + { + "Custom": { + "name": "script_lang", + "kind": { + "Enum": [ + "python3", + "deno" + ] + } + } + } + ] + }, + "nullable": [ + false + ] + } + }, "255aafff962738317f3227ae4eb871830d89b4c12c73d8dbabe6836da124e54d": { "query": "select path from script where hash = $1 AND (workspace_id = $2 OR workspace_id = 'starter')", "describe": { @@ -1902,53 +1960,6 @@ "nullable": [] } }, - "a151ceeddfd4a2825d4528542d3adc5c0a6947573558a2fd62429ba5da369617": { - "query": "INSERT INTO queue\n (workspace_id, id, parent_job, created_by, permissioned_as, scheduled_for, \n script_hash, script_path, raw_code, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Uuid", - "Varchar", - "Varchar", - "Timestamptz", - "Int8", - "Varchar", - "Text", - "Jsonb", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview" - ] - } - } - }, - "Varchar", - "Jsonb", - "Jsonb", - "Bool" - ] - }, - "nullable": [ - false - ] - } - }, "a1d46b44718a63d6ce5a9054d493dadbffb205500dc8fb55e9816bcdb613e0d5": { "query": "DELETE FROM queue WHERE schedule_path = $1", "describe": { @@ -2300,6 +2311,40 @@ ] } }, + "be33c6eb702c149044650d49b3c50493d7538d590be3f4ff6242fea85c57c667": { + "query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Int8", + "Varchar", + "Int8Array", + "Text", + "Text", + "Text", + "Varchar", + "Text", + "Bool", + "Jsonb", + "Text", + { + "Custom": { + "name": "script_lang", + "kind": { + "Enum": [ + "python3", + "deno" + ] + } + } + } + ] + }, + "nullable": [] + } + }, "bf1d8e043338867e1da1ed236ff6c85a566d5fd58d4b0d5c3a10454513811ba3": { "query": "UPDATE workspace_settings\n SET slack_team_id = null, slack_name = null WHERE workspace_id = $1", "describe": { @@ -2802,29 +2847,6 @@ ] } }, - "f12e710a0c2b2e13b98fd522028b6af8be74a9126a8207aa2974b47fd36e1845": { - "query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Int8", - "Varchar", - "Int8Array", - "Text", - "Text", - "Text", - "Varchar", - "Text", - "Bool", - "Jsonb", - "Text" - ] - }, - "nullable": [] - } - }, "f325a1262084bd3468e12dc8bcc289a96536f172b679af54dd0fbc82d4d7c987": { "query": "DELETE FROM usr_to_group WHERE usr = $1 AND group_ = $2 AND workspace_id = $3", "describe": { diff --git a/backend/src/jobs.rs b/backend/src/jobs.rs index e3902a5716..524ca2274c 100644 --- a/backend/src/jobs.rs +++ b/backend/src/jobs.rs @@ -12,6 +12,7 @@ use sqlx::{query_scalar, Postgres, Transaction}; use std::collections::HashMap; use crate::js_eval::eval_timeout; +use crate::scripts::ScriptLang; use crate::users::create_token_for_owner; use crate::{ audit::{audit_log, ActionKind}, @@ -83,6 +84,7 @@ pub struct QueuedJob { pub flow_status: Option, pub raw_flow: Option, pub is_flow_step: bool, + pub language: ScriptLang, } #[derive(Debug, sqlx::FromRow, Serialize)] @@ -111,6 +113,7 @@ struct CompletedJob { flow_status: Option, raw_flow: Option, is_flow_step: bool, + language: ScriptLang, } #[derive(Deserialize, Clone, Copy)] @@ -264,6 +267,7 @@ async fn run_preview_job( JobPayload::Code(RawCode { content: preview.content, path: preview.path, + language: preview.language, }), preview.args, &authed.username, @@ -417,6 +421,7 @@ async fn list_jobs( "permissioned_as", "flow_status", "is_flow_step", + "language", ], ); let sqlc = list_completed_jobs_query( @@ -450,6 +455,7 @@ async fn list_jobs( "permissioned_as", "flow_status", "is_flow_step", + "language", ], ); let sql = format!( @@ -564,6 +570,7 @@ async fn list_completed_jobs( "null as flow_status", "null as raw_flow", "is_flow_step", + "language", ], ) .sql()?; @@ -857,6 +864,7 @@ struct UnifiedJob { permissioned_as: String, flow_status: Option, is_flow_step: bool, + language: ScriptLang, } impl From for Job { @@ -887,6 +895,7 @@ impl From for Job { flow_status: uj.flow_status, raw_flow: None, is_flow_step: uj.is_flow_step, + language: uj.language, }), "QueuedJob" => Job::QueuedJob(QueuedJob { workspace_id: uj.workspace_id, @@ -912,6 +921,7 @@ impl From for Job { flow_status: uj.flow_status, raw_flow: None, is_flow_step: uj.is_flow_step, + language: uj.language, }), t => panic!("job type {} not valid", t), } @@ -925,6 +935,7 @@ struct CancelJob { pub struct RawCode { content: String, path: Option, + language: ScriptLang, } #[derive(Deserialize)] @@ -932,6 +943,7 @@ struct Preview { content: String, path: Option, args: Option>, + language: ScriptLang, } #[derive(Deserialize)] @@ -1004,22 +1016,32 @@ pub async fn push<'c>( } } - let (script_hash, script_path, raw_code, job_kind, raw_flow) = match job_payload { + let (script_hash, script_path, raw_code, job_kind, raw_flow, language) = match job_payload { JobPayload::ScriptHash { hash, path } => { - (Some(hash.0), Some(path), None, JobKind::Script, None) - } - JobPayload::Code(RawCode { content, path }) => { - (None, path, Some(content), JobKind::Preview, None) + (Some(hash.0), Some(path), None, JobKind::Script, None, None) } + JobPayload::Code(RawCode { + content, + path, + language, + }) => ( + None, + path, + Some(content), + JobKind::Preview, + None, + Some(language), + ), JobPayload::Dependencies { hash, dependencies } => ( Some(hash.0), None, Some(dependencies.join("\n")), JobKind::Dependencies, None, + Some(ScriptLang::Python3), ), JobPayload::RawFlow { value, path } => { - (None, path, None, JobKind::FlowPreview, Some(value)) + (None, path, None, JobKind::FlowPreview, Some(value), None) } JobPayload::Flow(flow) => { let value_json = sqlx::query_scalar!("SELECT value FROM flow WHERE path = $1 AND (workspace_id = $2 OR workspace_id = 'starter')", @@ -1032,7 +1054,7 @@ pub async fn push<'c>( "could not convert json to flow for {flow}: {err:?}" )) })?; - (None, Some(flow), None, JobKind::Flow, Some(value)) + (None, Some(flow), None, JobKind::Flow, Some(value), None) } }; @@ -1046,8 +1068,8 @@ pub async fn push<'c>( let uuid = sqlx::query_scalar!( "INSERT INTO queue (workspace_id, id, parent_job, created_by, permissioned_as, scheduled_for, - script_hash, script_path, raw_code, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING id", + script_hash, script_path, raw_code, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step, language) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING id", workspace_id, job_id, parent_job, @@ -1062,7 +1084,8 @@ pub async fn push<'c>( schedule_path, raw_flow.map(|f| serde_json::json!(f)), flow_status.map(|f| serde_json::json!(f)), - is_flow_step + is_flow_step, + language: ScriptLang ) .fetch_one(&mut tx) .await?; diff --git a/backend/src/parser.rs b/backend/src/parser.rs index 3b5ef04a3e..a2118832c5 100644 --- a/backend/src/parser.rs +++ b/backend/src/parser.rs @@ -25,7 +25,7 @@ pub struct MainArgSignature { pub args: Vec, } -#[derive(Serialize)] +#[derive(Serialize, Clone)] #[serde(rename_all(serialize = "lowercase"))] pub enum Typ { Str, @@ -39,7 +39,7 @@ pub enum Typ { Unknown, } -#[derive(Serialize)] +#[derive(Serialize, Clone)] pub struct Arg { pub name: String, pub typ: Typ, @@ -47,7 +47,7 @@ pub struct Arg { pub has_default: bool, } -pub fn parse_signature(code: &str) -> error::Result { +pub fn parse_python_signature(code: &str) -> error::Result { let ast = parser::parse_program(code) .map_err(|e| error::Error::ExecutionErr(format!("Error parsing code: {}", e.to_string())))? .statements; @@ -115,6 +115,136 @@ pub fn parse_signature(code: &str) -> error::Result { } } +use swc_common::sync::Lrc; +use swc_common::{FileName, SourceMap}; +use swc_ecma_ast::{ + AssignPat, BindingIdent, Decl, ExportDecl, FnDecl, Ident, ModuleDecl, ModuleItem, Pat, Stmt, + TsKeywordTypeKind, TsType, +}; +use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig}; + +pub fn parse_deno_signature(code: &str) -> error::Result { + let cm: Lrc = Default::default(); + let fm = cm.new_source_file(FileName::Custom("test.ts".into()), code.into()); + let lexer = Lexer::new( + // We want to parse ecmascript + Syntax::Typescript(TsConfig::default()), + // EsVersion defaults to es5 + Default::default(), + StringInput::from(&*fm), + None, + ); + + let mut parser = Parser::new_from(lexer); + + let mut err_s = "".to_string(); + for e in parser.take_errors() { + err_s += &e.into_kind().msg().to_string(); + } + + let ast = parser + .parse_module() + .map_err(|e| { + error::Error::ExecutionErr(format!("impossible to parse module: {err_s}\n{e:?}")) + })? + .body; + + // println!("{ast:?}"); + let params = ast.into_iter().find_map(|x| match x { + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { + decl: + Decl::Fn(FnDecl { + ident: + Ident { + span: _, + sym, + optional: _, + }, + declare: _, + function, + }), + span: _, + })) if &sym.to_string() == "main" => Some(function.params), + _ => None, + }); + if let Some(params) = params { + Ok(MainArgSignature { + star_args: false, + star_kwargs: false, + args: params + .into_iter() + .map(|x| match x.pat { + Pat::Ident(ident) => { + let (name, typ) = binding_ident_to_arg(&ident)?; + Ok(Arg { + name, + typ, + default: None, + has_default: false, + }) + } + Pat::Assign(AssignPat { + span: _, + left, + right, + type_ann: _, + }) => { + let (name, typ) = + left.as_ident().map(binding_ident_to_arg).ok_or_else(|| { + error::Error::ExecutionErr(format!( + "Arg {left:?} has unexepected syntax" + )) + })??; + Ok(Arg { + name, + typ, + default: serde_json::to_value(right) + .map_err(|e| error::Error::ExecutionErr(e.to_string()))? + .as_object() + .and_then(|x| x.get("value").to_owned()) + .cloned(), + + has_default: true, + }) + } + _ => Err(error::Error::ExecutionErr(format!( + "Arg {x:?} has unexepected syntax" + ))), + }) + .collect::, error::Error>>()?, + }) + } else { + Err(error::Error::ExecutionErr( + "main function was not findable (expected to find 'export main function(...)'" + .to_string(), + )) + } +} + +fn binding_ident_to_arg( + BindingIdent { id, type_ann }: &BindingIdent, +) -> anyhow::Result<(String, Typ)> { + Ok(( + id.sym.to_string(), + type_ann + .as_ref() + .map(|x| match &*x.type_ann { + TsType::TsKeywordType(t) => match t.kind { + TsKeywordTypeKind::TsObjectKeyword => Typ::Dict, + TsKeywordTypeKind::TsBooleanKeyword => Typ::Bool, + TsKeywordTypeKind::TsBigIntKeyword => Typ::Int, + TsKeywordTypeKind::TsNumberKeyword => Typ::Float, + TsKeywordTypeKind::TsStringKeyword => Typ::Str, + _ => Typ::Unknown, + }, + // TODO: we can do better here and extract the inner type of array + TsType::TsArrayType(_) => Typ::List, + _ => Typ::Unknown, + }) + .unwrap_or(Typ::Unknown), + )) +} + const STDIMPORTS: [&str; 301] = [ "__future__", "_abc", @@ -468,7 +598,7 @@ fn to_value(et: &ExpressionType) -> Option { } } -pub fn parse_imports(code: &str) -> error::Result> { +pub fn parse_python_imports(code: &str) -> error::Result> { let find_requirements = code .lines() .find_position(|x| x.starts_with("#requirements:")); @@ -527,7 +657,7 @@ mod tests { use super::*; #[test] - fn test_parse_sig() -> anyhow::Result<()> { + fn test_parse_python_sig() -> anyhow::Result<()> { //let code = "print(2 + 3, fd=sys.stderr)"; let code = " @@ -540,13 +670,13 @@ def main(test1: str, name: datetime.datetime = datetime.now(), byte: bytes = byt return {\"len\": len(name), \"splitted\": name.split() } "; - println!("{}", serde_json::to_string(&parse_signature(code)?)?); + println!("{}", serde_json::to_string(&parse_python_signature(code)?)?); Ok(()) } #[test] - fn test_parse_imports() -> anyhow::Result<()> { + fn test_parse_python_imports() -> anyhow::Result<()> { //let code = "print(2 + 3, fd=sys.stderr)"; let code = " @@ -559,14 +689,14 @@ def main(): pass "; - let r = parse_imports(code)?; + let r = parse_python_imports(code)?; println!("{}", serde_json::to_string(&r)?); assert_eq!(r, vec!["wmill", "zanzibar", "matplotlib"]); Ok(()) } #[test] - fn test_parse_imports2() -> anyhow::Result<()> { + fn test_parse_python_imports2() -> anyhow::Result<()> { //let code = "print(2 + 3, fd=sys.stderr)"; let code = " #requirements: @@ -583,10 +713,24 @@ def main(): pass "; - let r = parse_imports(code)?; + let r = parse_python_imports(code)?; println!("{}", serde_json::to_string(&r)?); assert_eq!(r, vec!["burkina=0.4", "nigeria"]); Ok(()) } + + #[test] + fn test_parse_deno_sig() -> anyhow::Result<()> { + let code = " + +export function main(test1: string, test2: string = \"burkina\") { + console.log(42) +} + +"; + println!("{}", serde_json::to_string(&parse_deno_signature(code)?)?); + + Ok(()) + } } diff --git a/backend/src/scripts.rs b/backend/src/scripts.rs index fb5e34a915..068ddbdc30 100644 --- a/backend/src/scripts.rs +++ b/backend/src/scripts.rs @@ -35,7 +35,12 @@ use std::{ const MAX_HASH_HISTORY_LENGTH_STORED: usize = 20; pub fn global_service() -> Router { - Router::new().route("/tojsonschema", post(parse_code_to_jsonschema)) + Router::new() + .route( + "/python/tojsonschema", + post(parse_python_code_to_jsonschema), + ) + .route("/deno/tojsonschema", post(parse_deno_code_to_jsonschema)) } pub fn workspaced_service() -> Router { @@ -50,6 +55,13 @@ pub fn workspaced_service() -> Router { .route("/deployment_status/h/:hash", get(get_deployment_status)) } +#[derive(sqlx::Type, Serialize, Deserialize, Debug, PartialEq, Clone, Hash)] +#[sqlx(type_name = "SCRIPT_LANG", rename_all = "lowercase")] +#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))] +pub enum ScriptLang { + Deno, + Python3, +} #[derive(sqlx::Type, PartialEq, Debug, Hash, Clone, Copy)] #[sqlx(transparent)] pub struct ScriptHash(pub i64); @@ -113,6 +125,7 @@ pub struct Script { pub extra_perms: serde_json::Value, pub lock: Option, pub lock_error_logs: Option, + pub language: ScriptLang, } #[derive(Serialize, Deserialize, sqlx::Type, Debug)] @@ -138,6 +151,7 @@ pub struct NewScript { pub schema: Option, pub is_template: Option, pub lock: Option>, + pub language: ScriptLang, } #[derive(Deserialize)] @@ -181,6 +195,7 @@ async fn list_scripts( "extra_perms", "null as lock", "CASE WHEN lock_error_logs IS NOT NULL THEN 'error' ELSE null END as lock_error_logs", + "language", ]) .order_by("created_at", lq.order_desc.unwrap_or(true)) .and_where("workspace_id = ? OR workspace_id = 'starter'".bind(&w_id)) @@ -344,10 +359,16 @@ async fn create_script( .map(|v| v.1.clone()) .unwrap_or(json!({})); + let lock = if ns.language == ScriptLang::Deno { + Some("".to_string()) + } else { + ns.lock.as_ref().map(|x| x.join("\n")) + }; //::text::json is to ensure we use serde_json with preserve order sqlx::query!( "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, \ - created_by, schema, is_template, extra_perms, lock) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12)", + created_by, schema, is_template, extra_perms, lock, language) VALUES \ + ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13)", &w_id, &hash.0, ns.path, @@ -359,13 +380,14 @@ async fn create_script( ns.schema.and_then(|x| serde_json::to_string(&x.0).ok()), ns.is_template.unwrap_or(false), extra_perms, - ns.lock.as_ref().map(|x| x.join("\n")) + lock, + ns.language: ScriptLang ) .execute(&mut tx) .await?; - let mut tx = if ns.lock.is_none() { - let dependencies = parser::parse_imports(&ns.content)?; + let mut tx = if ns.lock.is_none() && ns.language == ScriptLang::Python3 { + let dependencies = parser::parse_python_imports(&ns.content)?; let (_, tx) = jobs::push( tx, &w_id, @@ -593,10 +615,16 @@ async fn delete_script_by_hash( Ok(Json(script)) } -async fn parse_code_to_jsonschema( +async fn parse_python_code_to_jsonschema( Json(code): Json, ) -> JsonResult { - parser::parse_signature(&code).map(Json) + parser::parse_python_signature(&code).map(Json) +} + +async fn parse_deno_code_to_jsonschema( + Json(code): Json, +) -> JsonResult { + parser::parse_deno_signature(&code).map(Json) } pub fn to_i64(s: &str) -> Result { diff --git a/backend/src/worker.rs b/backend/src/worker.rs index a47d981f22..65d117637a 100644 --- a/backend/src/worker.rs +++ b/backend/src/worker.rs @@ -24,7 +24,7 @@ use crate::{ QueuedJob, }, parser::{self, Typ}, - scripts::ScriptHash, + scripts::{ScriptHash, ScriptLang}, users::{create_token_for_owner, get_email_from_username}, variables, }; @@ -44,11 +44,14 @@ use tokio::sync::mpsc; const TMP_DIR: &str = "/tmp/windmill"; const PIP_CACHE_DIR: &str = "/tmp/windmill/cache/pip"; +const DENO_CACHE_DIR: &str = "/tmp/windmill/cache/deno"; const NUM_SECS_ENV_CHECK: u64 = 15; const INCLUDE_DEPS_SH_CONTENT: &str = include_str!("../../nsjail/download_deps.sh"); const NSJAIL_CONFIG_DOWNLOAD_CONTENT: &str = include_str!("../../nsjail/download.config.proto"); -const NSJAIL_CONFIG_RUN_CONTENT: &str = include_str!("../../nsjail/run.config.proto"); +const NSJAIL_CONFIG_RUN_PYTHON3_CONTENT: &str = + include_str!("../../nsjail/run.python3.config.proto"); +const NSJAIL_CONFIG_RUN_DENO_CONTENT: &str = include_str!("../../nsjail/run.deno.config.proto"); pub async fn run_worker( db: &DB, @@ -66,17 +69,13 @@ pub async fn run_worker( let worker_dir = format!("{TMP_DIR}/{worker_name}"); tracing::debug!(worker_dir = %worker_dir, worker_name = %worker_name, "Creating worker dir"); - DirBuilder::new() - .recursive(true) - .create(&worker_dir) - .await - .expect("could not create initial worker dir"); - - DirBuilder::new() - .recursive(true) - .create(&PIP_CACHE_DIR) - .await - .expect("could not create initial worker dir"); + for x in [&worker_dir, PIP_CACHE_DIR, DENO_CACHE_DIR] { + DirBuilder::new() + .recursive(true) + .create(x) + .await + .expect("could not create initial worker dir"); + } let _ = write_file(&worker_dir, "download_deps.sh", INCLUDE_DEPS_SH_CONTENT).await; @@ -289,6 +288,7 @@ async fn handle_job( .expect("could not create initial job dir"); let mut status: Result; + if matches!(job.job_kind, JobKind::Dependencies) { let requirements = job .raw_code @@ -344,12 +344,17 @@ async fn handle_job( .await?; } } else { - let (inner_content, requirements_o) = if matches!(job.job_kind, JobKind::Preview) { + let (inner_content, requirements_o, language) = if matches!(job.job_kind, JobKind::Preview) + { let code = (job.raw_code.as_ref().unwrap_or(&"no raw code".to_owned())).to_owned(); - let reqs = parser::parse_imports(&code)?.join("\n"); - (code, Some(reqs)) + let reqs = if job.language == ScriptLang::Python3 { + Some(parser::parse_python_imports(&code)?.join("\n")) + } else { + None + }; + (code, reqs, job.language.to_owned()) } else { - sqlx::query_as::<_, (String, Option)>("SELECT content, lock FROM script WHERE hash = $1 AND (workspace_id = $2 OR workspace_id = 'starter')") + sqlx::query_as::<_, (String, Option, ScriptLang)>("SELECT content, lock, language FROM script WHERE hash = $1 AND (workspace_id = $2 OR workspace_id = 'starter')") .bind(&job.script_hash.unwrap_or(ScriptHash(0)).0) .bind(&job.workspace_id) .fetch_optional(db) @@ -357,69 +362,76 @@ async fn handle_job( .ok_or_else(|| Error::InternalErr(format!("expected content and lock")))? }; - let requirements = - requirements_o.ok_or_else(|| Error::InternalErr(format!("lockfile missing")))?; + match language { + ScriptLang::Python3 => { + let requirements = requirements_o + .ok_or_else(|| Error::InternalErr(format!("lockfile missing")))?; - let _ = write_file( - &job_dir, - "download.config.proto", - &NSJAIL_CONFIG_DOWNLOAD_CONTENT - .replace("{JOB_DIR}", &job_dir) - .replace("{WORKER_DIR}", &worker_dir) - .replace("{CACHE_DIR}", PIP_CACHE_DIR), - ) - .await?; - let _ = write_file(&job_dir, "requirements.txt", &requirements).await?; + let _ = write_file( + &job_dir, + "download.config.proto", + &NSJAIL_CONFIG_DOWNLOAD_CONTENT + .replace("{JOB_DIR}", &job_dir) + .replace("{WORKER_DIR}", &worker_dir) + .replace("{CACHE_DIR}", PIP_CACHE_DIR), + ) + .await?; + let _ = write_file(&job_dir, "requirements.txt", &requirements).await?; - let child = Command::new("nsjail") - .current_dir(&job_dir) - .args(vec!["--config", "download.config.proto"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; + let child = Command::new("nsjail") + .current_dir(&job_dir) + .args(vec!["--config", "download.config.proto"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; - logs.push_str("\n--- DEPENDENCIES INSTALL ---\n"); - status = handle_child(job, db, &mut logs, &mut last_line, timeout, child).await; - if status.is_ok() { - logs.push_str("\n\n--- CODE EXECUTION ---\n"); + logs.push_str("\n--- PIP DEPENDENCIES INSTALL ---\n"); + status = handle_child(job, db, &mut logs, &mut last_line, timeout, child).await; - set_logs(logs, job.id, db).await; + if status.is_ok() { + logs.push_str("\n\n--- PTHON CODE EXECUTION ---\n"); - let _ = write_file(&job_dir, "inner.py", &inner_content).await?; + set_logs(logs, job.id, db).await; - let sig = crate::parser::parse_signature(&inner_content)?; - let transforms = sig.args.into_iter().map(|x| match x.typ { + let _ = write_file(&job_dir, "inner.py", &inner_content).await?; + + let sig = crate::parser::parse_python_signature(&inner_content)?; + let transforms = sig.args.into_iter().map(|x| match x.typ { Typ::Bytes => format!("if \"{}\" in kwargs and kwargs[\"{}\"] is not None:\n kwargs[\"{}\"] = base64.b64decode(kwargs[\"{}\"])\n", x.name, x.name, x.name, x.name), Typ::Datetime => format!("if \"{}\" in kwargs and kwargs[\"{}\"] is not None:\n kwargs[\"{}\"] = datetime.strptime(kwargs[\"{}\"], '%Y-%m-%dT%H:%M')\n", x.name, x.name, x.name, x.name), _ => "".to_string() }).collect::>().join(""); - let tx = db.begin().await?; + let tx = db.begin().await?; - let token = create_token_for_owner( - &db, - &job.workspace_id, - &job.permissioned_as, - crate::users::NewToken { - label: Some("ephemeral-script".to_string()), - expiration: Some( - chrono::Utc::now() + chrono::Duration::seconds((timeout * 2).into()), - ), - }, - &job.created_by, - ) - .await?; + let token = create_token_for_owner( + &db, + &job.workspace_id, + &job.permissioned_as, + crate::users::NewToken { + label: Some("ephemeral-script".to_string()), + expiration: Some( + chrono::Utc::now() + + chrono::Duration::seconds((timeout * 2).into()), + ), + }, + &job.created_by, + ) + .await?; - let args = if let Some(args) = &job.args { - Some(transform_json_value(&token, &job.workspace_id, base_url, args.clone()).await) - } else { - None - }; - let ser_args = serde_json::to_string(&args) - .map_err(|e| Error::ExecutionErr(e.to_string()))? - .replace("\\\"", "\\\\\""); - let wrapper_content: String = format!( - r#" + let args = if let Some(args) = &job.args { + Some( + transform_json_value(&token, &job.workspace_id, base_url, args.clone()) + .await, + ) + } else { + None + }; + let ser_args = serde_json::to_string(&args) + .map_err(|e| Error::ExecutionErr(e.to_string()))? + .replace("\\\"", "\\\\\""); + let wrapper_content: String = format!( + r#" import json import base64 from datetime import datetime @@ -443,44 +455,150 @@ print() print("result:") print(res_json) "#, - ); - write_file(&job_dir, "main.py", &wrapper_content).await?; + ); + write_file(&job_dir, "main.py", &wrapper_content).await?; - tx.commit().await?; - let reserved_variables = variables::get_reserved_variables( - &job.workspace_id, - &token, - &get_email_from_username(&job.created_by, db) - .await? - .unwrap_or_else(|| "nosuitable@email.xyz".to_string()), - &job.created_by, - &job.id.to_string(), - ) - .into_iter() - .map(|rv| (rv.name, rv.value)); + tx.commit().await?; + let reserved_variables = variables::get_reserved_variables( + &job.workspace_id, + &token, + &get_email_from_username(&job.created_by, db) + .await? + .unwrap_or_else(|| "nosuitable@email.xyz".to_string()), + &job.created_by, + &job.id.to_string(), + ) + .into_iter() + .map(|rv| (rv.name, rv.value)); + let _ = write_file( + &job_dir, + "run.config.proto", + &NSJAIL_CONFIG_RUN_PYTHON3_CONTENT.replace("{JOB_DIR}", &job_dir), + ) + .await?; - let _ = write_file( - &job_dir, - "run.config.proto", - &NSJAIL_CONFIG_RUN_CONTENT.replace("{JOB_DIR}", &job_dir), - ) - .await?; + let child = Command::new("nsjail") + .current_dir(&job_dir) + .envs(reserved_variables) + .args(vec![ + "--config", + "run.config.proto", + "--", + "/usr/local/bin/python3", + "-u", + "/tmp/main.py", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + status = handle_child(job, db, &mut logs, &mut last_line, timeout, child).await; + } + } + ScriptLang::Deno => { + logs.push_str("\n\n--- DENO CODE EXECUTION ---\n"); - let child = Command::new("nsjail") - .current_dir(&job_dir) - .envs(reserved_variables) - .args(vec![ - "--config", + set_logs(logs, job.id, db).await; + + let _ = write_file(&job_dir, "inner.ts", &inner_content).await?; + + let sig = crate::parser::parse_deno_signature(&inner_content)?; + // let transforms = sig.args.clone().into_iter().map(|x| match x.typ { + // Typ::Bytes => format!("if \"{}\" in kwargs and kwargs[\"{}\"] is not None:\n kwargs[\"{}\"] = base64.b64decode(kwargs[\"{}\"])\n", x.name, x.name, x.name, x.name), + // Typ::Datetime => format!("if \"{}\" in kwargs and kwargs[\"{}\"] is not None:\n kwargs[\"{}\"] = datetime.strptime(kwargs[\"{}\"], '%Y-%m-%dT%H:%M')\n", x.name, x.name, x.name, x.name), + // _ => "".to_string() + // }).collect::>().join(""); + + let tx = db.begin().await?; + + let token = create_token_for_owner( + &db, + &job.workspace_id, + &job.permissioned_as, + crate::users::NewToken { + label: Some("ephemeral-script".to_string()), + expiration: Some( + chrono::Utc::now() + chrono::Duration::seconds((timeout * 2).into()), + ), + }, + &job.created_by, + ) + .await?; + + let args = if let Some(args) = &job.args { + Some( + transform_json_value(&token, &job.workspace_id, base_url, args.clone()) + .await, + ) + } else { + None + }; + let ser_args = serde_json::to_string(&args) + .map_err(|e| Error::ExecutionErr(e.to_string()))? + .replace("\\\"", "\\\\\""); + let spread = sig.args.into_iter().map(|x| x.name).join(","); + let wrapper_content: String = format!( + r#" +import {{ main }} from "./inner.ts"; +const {{{spread}}}= JSON.parse(`{ser_args}`); + +async function run() {{ + let res: any = await main({spread}); + if (res == undefined) {{ + res = {{}} + }} + if (typeof res !== 'object') {{ + res = {{ res1: res }} + }} + + const res_json = JSON.stringify(res); + console.log(); + console.log("result:"); + console.log(res_json); +}} +run(); +"#, + ); + write_file(&job_dir, "main.ts", &wrapper_content).await?; + + tx.commit().await?; + let reserved_variables = variables::get_reserved_variables( + &job.workspace_id, + &token, + &get_email_from_username(&job.created_by, db) + .await? + .unwrap_or_else(|| "nosuitable@email.xyz".to_string()), + &job.created_by, + &job.id.to_string(), + ) + .into_iter() + .map(|rv| (rv.name, rv.value)); + let _ = write_file( + &job_dir, "run.config.proto", - "--", - "/usr/local/bin/python3", - "-u", - "/tmp/main.py", - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; - status = handle_child(job, db, &mut logs, &mut last_line, timeout, child).await; + &NSJAIL_CONFIG_RUN_DENO_CONTENT + .replace("{JOB_DIR}", &job_dir) + .replace("{CACHE_DIR}", DENO_CACHE_DIR), + ) + .await?; + + let child = Command::new("nsjail") + .current_dir(&job_dir) + .envs(reserved_variables) + .args(vec![ + "--config", + "run.config.proto", + "--", + "/usr/bin/deno", + "run", + "--v8-flags=--max-heap-size=2048", + "-A", + "/tmp/main.ts", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + status = handle_child(job, db, &mut logs, &mut last_line, timeout, child).await; + } } } tokio::fs::remove_dir_all(job_dir).await?; diff --git a/backend/v8.snap b/backend/v8.snap index d623db64b8..703b15cc5e 100644 Binary files a/backend/v8.snap and b/backend/v8.snap differ diff --git a/deno-client/.gitignore b/deno-client/.gitignore new file mode 100644 index 0000000000..87ca018220 --- /dev/null +++ b/deno-client/.gitignore @@ -0,0 +1 @@ +windmill-api diff --git a/deno-client/.vscode/settings.json b/deno-client/.vscode/settings.json new file mode 100644 index 0000000000..52ca21d5c4 --- /dev/null +++ b/deno-client/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "deno.enable": true, + "deno.lint": true, + "deno.unstable": true +} diff --git a/deno-client/generate.sh b/deno-client/generate.sh new file mode 100755 index 0000000000..882ad1bad7 --- /dev/null +++ b/deno-client/generate.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e + +/usr/local/bin/docker-entrypoint.sh generate -i ../backend/openapi.yaml -g typescript --additional-properties platform=deno -o windmill-api +sed -i 's/this\.type = "Job";//' windmill-api/models/Job.ts diff --git a/deno-client/index.ts b/deno-client/index.ts new file mode 100644 index 0000000000..15643c5c60 --- /dev/null +++ b/deno-client/index.ts @@ -0,0 +1,46 @@ +import { ResourceApi, VariableApi, ServerConfiguration } from './windmill-api/index.ts' +import { createConfiguration, type Configuration as Configuration } from './windmill-api/configuration.ts' + +export * from './windmill-api/index.ts' + +export function createConf(): Configuration & { workspace_id: string } { + const token = Deno.env.get("WM_TOKEN") ?? 'no_token' + return { + ...createConfiguration({ + baseServer: new ServerConfiguration(Deno.env.get("BASE_URL") ?? 'http://localhost:8000/api', {}), + authMethods: { bearerAuth: { tokenProvider: { getToken() { return token } } } }, + }), workspace_id: Deno.env.get("WM_WORKSPACE") ?? 'no_workspace' + } +} + +export async function getResource(path: string): Promise { + const conf = createConf() + const resource = await new ResourceApi(conf).getResource(conf.workspace_id, path) + return await transformLeaves(resource.value) +} + +export async function getVariable(path: string): Promise { + const conf = createConf() + const variable = await new VariableApi(conf).getVariable(conf.workspace_id, path) + return variable.value +} + +export async function transformLeaves(d: { [key: string]: any }): Promise<{ [key: string]: any }> { + for (const k in d) { + d[k] = await _transformLeaf(d[k]) + } + return d +} + +const VAR_RESOURCE_PREFIX = "$var:" +async function _transformLeaf(v: any): Promise { + if (typeof v === 'object') { + return transformLeaves(v) + } + else if (typeof v === 'string' && v.startsWith(VAR_RESOURCE_PREFIX)) { + const varName = v.substring(VAR_RESOURCE_PREFIX.length) + return await getVariable(varName) + } else { + return v + } +} diff --git a/deno-client/openapitools.json b/deno-client/openapitools.json new file mode 100644 index 0000000000..4a3fac0798 --- /dev/null +++ b/deno-client/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.0.0-beta" + } +} diff --git a/frontend/Caddyfile b/frontend/Caddyfile index dd015fd2c2..7495ec5d5a 100644 --- a/frontend/Caddyfile +++ b/frontend/Caddyfile @@ -5,4 +5,8 @@ bind {$ADDRESS} reverse_proxy /api/* http://localhost:8000 reverse_proxy /* http://localhost:3000 + reverse_proxy /ws/* http://localhost:3001 { + lb_policy header "Authorization" + } + } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3d43985ec5..04f062d5ca 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill", - "version": "1.6.0", + "version": "1.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "windmill", - "version": "1.6.0", + "version": "1.6.1", "dependencies": { "@codingame/monaco-jsonrpc": "^0.3.1", "@codingame/monaco-languageclient": "^0.17.0", diff --git a/frontend/src/infer.ts b/frontend/src/infer.ts index eeaf6f353e..bff07ff73d 100644 --- a/frontend/src/infer.ts +++ b/frontend/src/infer.ts @@ -1,12 +1,22 @@ import type { Schema, SchemaProperty } from './common' -import { ScriptService } from './gen' +import { ScriptService, type MainArgSignature } from './gen' import { sendUserToast } from './utils' -export async function inferArgs(code: string, schema: Schema): Promise { +export async function inferArgs(language: "python3" | "deno", code: string, schema: Schema): Promise { try { - const inferedSchema = await ScriptService.toJsonschema({ - requestBody: code - }) + let inferedSchema: MainArgSignature + if (language == "python3") { + inferedSchema = await ScriptService.pythonToJsonschema({ + requestBody: code + }) + } else if (language == "deno") { + inferedSchema = await ScriptService.denoToJsonschema({ + requestBody: code + }) + } else { + return + } + schema.required = [] const oldProperties = Object.assign({}, schema.properties) schema.properties = {} @@ -30,12 +40,6 @@ export async function inferArgs(code: string, schema: Schema): Promise { } } -function array_move(arr: T[], fromIndex: number, toIndex: number) { - var element = arr[fromIndex] - arr.splice(fromIndex, 1) - arr.splice(toIndex, 0, element) -} - function pythonToJsonSchemaType(t: string, s: SchemaProperty): void { if (t === 'int') { s.type = 'integer' diff --git a/frontend/src/routes/components/Editor.svelte b/frontend/src/routes/components/Editor.svelte index aeae558dce..c4a5814434 100644 --- a/frontend/src/routes/components/Editor.svelte +++ b/frontend/src/routes/components/Editor.svelte @@ -12,7 +12,9 @@ let divEl: HTMLDivElement | null = null let editor: monaco.editor.IStandaloneCodeEditor let monaco - export let lang = 'python' + + export let deno = false + export let lang = deno ? 'typescript' : 'python' export let code: string export let readOnly = false export let hash: string = (Math.random() + 1).toString(36).substring(2) @@ -76,7 +78,7 @@ export async function reloadWebsocket() { closeWebsockets() - if (lang == 'python') { + if (lang == 'python' || deno) { // install Monaco language client services const { MonacoLanguageClient, CloseAction, ErrorAction, createConnection } = await import( '@codingame/monaco-languageclient' @@ -86,7 +88,7 @@ return new MonacoLanguageClient({ name: name, clientOptions: { - documentSelector: ['python'], + documentSelector: deno ? ['typescript'] : ['python'], errorHandler: { error: () => ErrorAction.Shutdown, closed: () => CloseAction.Restart @@ -95,7 +97,18 @@ isTrusted: true }, // workspaceFolder: { uri: Uri.parse(`/tmp/${name}`), name: 'tmp', index: 0 }, - initializationOptions + initializationOptions, + middleware: { + workspace: { + configuration: (params, token, configuration) => { + return [ + { + enable: true + } + ] + } + } + } }, connectionProvider: { get: (errorHandler, closeHandler) => { @@ -132,28 +145,38 @@ console.error(`connection to ${name} language server failed`) } } - connectToLanguageServer(`wss://${$page.url.host}/ws/pyright`, 'pyright', { - executionEnvironments: [ - { - root: '/tmp/pyright', - pythonVersion: '3.7', - pythonPlatform: 'platform', - extraPaths: [] - } - ] - }) - connectToLanguageServer(`wss://${$page.url.host}/ws/black`, 'black', { - formatters: { - black: { - command: 'black', - args: ['--quiet', '-'] + if (deno) { + connectToLanguageServer(`ws://${$page.url.host}/ws/deno`, 'deno', { + deno: { + enable: true, + lint: true } - }, - formatFiletypes: { - python: 'black' - } - }) + }) + } else { + connectToLanguageServer(`wss://${$page.url.host}/ws/pyright`, 'pyright', { + executionEnvironments: [ + { + root: '/tmp/pyright', + pythonVersion: '3.7', + pythonPlatform: 'platform', + extraPaths: [] + } + ] + }) + + connectToLanguageServer(`wss://${$page.url.host}/ws/black`, 'black', { + formatters: { + black: { + command: 'black', + args: ['--quiet', '-'] + } + }, + formatFiletypes: { + python: 'black' + } + }) + } } } @@ -232,15 +255,20 @@ } if (lang == 'typescript') { - // compiler options - monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ - target: monaco.languages.typescript.ScriptTarget.ES6, - allowNonTsExtensions: true, - noLib: true - }) + if (deno) { + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + diagnosticCodesToIgnore: [2691] + }) + } else { + // compiler options + monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ + target: monaco.languages.typescript.ScriptTarget.ES6, + allowNonTsExtensions: true, + noLib: true + }) - monaco.languages.typescript.typescriptDefaults.addExtraLib( - ` + monaco.languages.typescript.typescriptDefaults.addExtraLib( + ` /** * get variable (including secret) at path * @param {string} path - path of the variable (e.g: g/all/pretty_secret) @@ -276,11 +304,11 @@ export const previous_result: any; */ export const params: any; `, - 'file:///node_modules/@types/windmill/index.d.ts' - ) + 'file:///node_modules/@types/windmill/index.d.ts' + ) + } } - - if (lang == 'python') { + if (lang == 'python' || deno) { const { MonacoServices } = await import('@codingame/monaco-languageclient') MonacoServices.install(monaco) diff --git a/frontend/src/routes/components/GroupModal.svelte b/frontend/src/routes/components/GroupModal.svelte index 0c86b85117..0e65396c86 100644 --- a/frontend/src/routes/components/GroupModal.svelte +++ b/frontend/src/routes/components/GroupModal.svelte @@ -1,10 +1,8 @@ + - +{#if script} + +{/if} diff --git a/frontend/src/routes/scripts/get/[...hash].svelte b/frontend/src/routes/scripts/get/[...hash].svelte index 46b986c029..3c6ab60203 100644 --- a/frontend/src/routes/scripts/get/[...hash].svelte +++ b/frontend/src/routes/scripts/get/[...hash].svelte @@ -14,7 +14,7 @@ faSpinner } from '@fortawesome/free-solid-svg-icons' import Highlight from 'svelte-highlight' - import python from 'svelte-highlight/src/languages/python' + import { typescript, python } from 'svelte-highlight/src/languages' import github from 'svelte-highlight/src/styles/github' import Tooltip from '../../components/Tooltip.svelte' @@ -327,7 +327,11 @@

Code

- + {#if script.language == 'python3'} + + {:else if script.language == 'deno'} + + {/if}

Dependencies lock file

diff --git a/frontend/src/routes/scripts/run/[...hash].svelte b/frontend/src/routes/scripts/run/[...hash].svelte index 0c826451bb..044cb96584 100644 --- a/frontend/src/routes/scripts/run/[...hash].svelte +++ b/frontend/src/routes/scripts/run/[...hash].svelte @@ -18,7 +18,7 @@ script = await ScriptService.getScriptByHash({ workspace: $workspaceStore!, hash }) if (script.schema == undefined) { script.schema = emptySchema() - inferArgs(script.content, script.schema) + inferArgs(script.language, script.content, script.schema) script = script } } else { diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 1757a28dd1..3d4f4c0ca3 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -2,6 +2,15 @@ import preprocess from 'svelte-preprocess' import adapter from '@sveltejs/adapter-static' const ppath = process.env.PREVIEW_PATH +import { readFileSync } from 'fs'; +import { fileURLToPath } from 'url'; + +const file = fileURLToPath(new URL('package.json', import.meta.url)); +const json = readFileSync(file, 'utf8'); +const version = JSON.parse(json) + + + /** @type {import('@sveltejs/kit').Config} */ const config = { // Consult https://github.com/sveltejs/svelte-preprocess @@ -17,6 +26,9 @@ const config = { base: ppath ? ppath : '' }, vite: { + define: { + '__pkg__': version + }, ssr: { noExternal: ['dayjs'] }, diff --git a/lsp/pyls_launcher.py b/lsp/pyls_launcher.py index 021cf279f9..44f7d13875 100644 --- a/lsp/pyls_launcher.py +++ b/lsp/pyls_launcher.py @@ -71,10 +71,15 @@ class DiagnosticLS(LanguageServerWebSocketHandler): procargs = ["diagnostic-languageserver", "--stdio"] +class DenoLS(LanguageServerWebSocketHandler): + procargs = ["deno", "lsp"] + + if __name__ == "__main__": app = web.Application([ (r"/ws/pyright", PyrightLS), (r"/ws/black", DiagnosticLS), + (r"/ws/deno", DenoLS), ]) app.listen(3001, address="0.0.0.0") ioloop.IOLoop.current().start() diff --git a/nsjail/run.deno.config.proto b/nsjail/run.deno.config.proto new file mode 100644 index 0000000000..09f778a69d --- /dev/null +++ b/nsjail/run.deno.config.proto @@ -0,0 +1,108 @@ +name: "deno run script" + +mode: ONCE +hostname: "deno" +log_level: ERROR +time_limit: 300 + +rlimit_as: 16000 +rlimit_cpu: 1000 +rlimit_fsize: 1024 +rlimit_nofile: 64 + +cwd: "/tmp" + + +clone_newnet: false +keep_caps: false +keep_env: true +mount_proc: true + +mount { + src: "/bin" + dst: "/bin" + is_bind: true +} + +mount { + src: "/lib" + dst: "/lib" + is_bind: true +} + + +mount { + src: "/lib64" + dst: "/lib64" + is_bind: true +} + + +mount { + src: "/usr" + dst: "/usr" + is_bind: true +} + +mount { + src: "/dev/null" + dst: "/dev/null" + is_bind: true + rw: true +} + +mount { + dst: "/tmp" + fstype: "tmpfs" + rw: true + options: "size=500000000" +} + + +mount { + src: "{JOB_DIR}/inner.ts" + dst: "/tmp/inner.ts" + is_bind: true +} + +mount { + src: "{JOB_DIR}/main.ts" + dst: "/tmp/main.ts" + is_bind: true +} + + +mount { + src: "/etc/ssl" + dst: "/etc/ssl" + is_bind: true +} + +mount { + src: "/etc/resolv.conf" + dst: "/etc/resolv.conf" + is_bind: true +} + +mount { + src: "/dev/random" + dst: "/dev/random" + is_bind: true +} + +mount { + src: "{CACHE_DIR}" + dst: "/tmp/.cache/deno" + is_bind: true + rw: true + mandatory: false +} + + +iface_no_lo: true + +envar: "DENO_DIR=/tmp/.cache/deno" +envar: "NO_COLOR=true" + + + diff --git a/nsjail/run.config.proto b/nsjail/run.python3.config.proto similarity index 100% rename from nsjail/run.config.proto rename to nsjail/run.python3.config.proto