diff --git a/.gitignore b/.gitignore index 294865e..fd7d59b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ terraform.* .DS_Store venv/ .venv/ -.idea/ \ No newline at end of file +.idea/ +node_modules/ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..00d4f01 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,998 @@ +{ + "name": "wbld", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "tailwindcss": "^3.3.1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", + "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", + "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", + "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz", + "integrity": "sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==", + "dev": true, + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.17.2", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.0.9", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1", + "sucrase": "^3.29.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..984217d --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "tailwindcss": "^3.3.1" + } +} diff --git a/requirements.in b/requirements.in index aedeed1..c13ef3f 100644 --- a/requirements.in +++ b/requirements.in @@ -12,5 +12,6 @@ pylint pytest python-dotenv sentry-sdk +sh shortuuid watchdog[watchmedo] diff --git a/requirements.txt b/requirements.txt index 900c18c..dd28b1c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,40 +2,42 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile +# pip-compile --resolver=backtracking # aiofiles==22.1.0 # via platformio -aiohttp==3.7.3 +aiohttp==3.8.4 # via # -r requirements.in # aiohttp-jinja2 # discord-py -aiohttp-jinja2==1.4.2 +aiohttp-jinja2==1.5.1 # via -r requirements.in +aiosignal==1.3.1 + # via aiohttp ajsonrpc==1.2.0 # via platformio -anyio==3.5.0 +anyio==3.6.2 # via starlette -argh==0.26.2 - # via watchdog -astroid==2.4.2 +astroid==2.15.3 # via pylint -async-timeout==3.0.1 +async-timeout==4.0.2 # via aiohttp -attrs==20.3.0 - # via - # aiohttp - # pytest -black==22.10.0 +attrs==23.1.0 + # via aiohttp +black==23.3.0 # via -r requirements.in -bottle==0.12.19 +bottle==0.12.25 # via platformio -certifi==2020.12.5 +certifi==2022.12.7 # via # requests # sentry-sdk -chardet==3.0.4 +cffi==1.15.1 + # via + # cryptography + # pynacl +charset-normalizer==3.1.0 # via # aiohttp # requests @@ -44,134 +46,143 @@ click==8.1.3 # black # platformio # uvicorn -colorama==0.4.4 +colorama==0.4.6 # via platformio -deprecated==1.2.11 +cryptography==40.0.2 + # via pyjwt +deprecated==1.2.13 # via pygithub -discord-py==1.6.0 +dill==0.3.6 + # via pylint +discord-py==2.2.2 # via -r requirements.in -gitdb==4.0.5 +exceptiongroup==1.1.1 + # via pytest +frozenlist==1.3.3 + # via + # aiohttp + # aiosignal +gitdb==4.0.10 # via gitpython -gitpython==3.1.12 +gitpython==3.1.31 # via -r requirements.in -h11==0.12.0 +h11==0.14.0 # via # uvicorn # wsproto -humanize==3.2.0 +humanize==4.6.0 # via -r requirements.in -idna==2.10 +idna==3.4 # via # anyio # requests # yarl -ifaddr==0.1.7 - # via zeroconf -iniconfig==1.1.1 +iniconfig==2.0.0 # via pytest -isort==5.7.0 +isort==5.12.0 # via pylint -jinja2==2.11.2 +jinja2==3.1.2 # via aiohttp-jinja2 -lazy-object-proxy==1.4.3 +lazy-object-proxy==1.9.0 # via astroid -loguru==0.5.3 +loguru==0.7.0 # via -r requirements.in -markupsafe==1.1.1 +markupsafe==2.1.2 # via jinja2 -marshmallow==3.10.0 +marshmallow==3.19.0 # via platformio -mccabe==0.6.1 +mccabe==0.7.0 # via pylint -multidict==5.1.0 +multidict==6.0.4 # via # aiohttp # yarl -mypy-extensions==0.4.3 - # via black -packaging==20.8 - # via pytest -pathspec==0.10.2 +mypy-extensions==1.0.0 # via black -platformdirs==2.5.4 +packaging==23.1 + # via + # black + # marshmallow + # pytest +pathspec==0.11.1 # via black -platformio==6.1.5 +platformdirs==3.2.0 + # via + # black + # pylint +platformio==6.1.6 # via -r requirements.in -pluggy==0.13.1 +pluggy==1.0.0 # via pytest -py==1.10.0 - # via pytest -pydantic==1.9.0 +pycparser==2.21 + # via cffi +pydantic==1.10.7 # via -r requirements.in -pyelftools==0.27 +pyelftools==0.29 # via platformio -pygithub==1.54.1 +pygithub==1.58.1 # via -r requirements.in -pyjwt==1.7.1 +pyjwt[crypto]==2.6.0 # via pygithub -pylint==2.6.0 +pylint==2.17.2 # via -r requirements.in -pyparsing==2.4.7 - # via packaging +pynacl==1.5.0 + # via pygithub pyserial==3.5 # via platformio -pytest==6.2.1 +pytest==7.3.1 # via -r requirements.in -python-dotenv==0.15.0 +python-dotenv==1.0.0 # via -r requirements.in -pyyaml==5.4 +pyyaml==6.0 # via watchdog -requests==2.25.1 +requests==2.28.2 # via # platformio # pygithub semantic-version==2.10.0 # via platformio -sentry-sdk==0.19.5 +sentry-sdk==1.20.0 # via -r requirements.in -shortuuid==1.0.1 +sh==2.0.3 # via -r requirements.in -six==1.15.0 - # via astroid -smmap==3.0.4 +shortuuid==1.0.11 + # via -r requirements.in +smmap==5.0.0 # via gitdb -sniffio==1.2.0 +sniffio==1.3.0 # via anyio -starlette==0.21.0 +starlette==0.23.1 # via platformio tabulate==0.9.0 # via platformio -toml==0.10.2 +tomli==2.0.1 # via + # black # pylint # pytest -tomli==2.0.1 - # via black -typing-extensions==4.4.0 +tomlkit==0.11.7 + # via pylint +typing-extensions==4.5.0 # via - # aiohttp - # aiohttp-jinja2 + # astroid # black # pydantic + # pylint # starlette -urllib3==1.26.2 +urllib3==1.26.15 # via # requests # sentry-sdk -uvicorn==0.19.0 +uvicorn==0.20.0 # via platformio -watchdog[watchmedo]==1.0.2 +watchdog[watchmedo]==3.0.0 # via -r requirements.in -wrapt==1.12.1 +wrapt==1.15.0 # via # astroid # deprecated wsproto==1.2.0 # via platformio -yarl==1.6.3 +yarl==1.9.1 # via aiohttp -zeroconf==0.37.0 - # via platformio - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..904e13d --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,9 @@ +module.exports = { + content: [ + "./wbld/templates/*.html" + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/tests/conftest.py b/tests/conftest.py index d9800aa..540aaa1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import pytest + from wbld.build.storage import Storage diff --git a/tests/test_build.py b/tests/test_build.py index 9996621..1b20a98 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -1,14 +1,15 @@ from __future__ import annotations + import os +import pytest from platformio.project.config import ProjectConfig from pydantic.error_wrappers import ValidationError -import pytest +from wbld.build import Build, Builder, BuilderCustom from wbld.build.config import CustomConfig -from wbld.build.models import BuildModel from wbld.build.enums import Kind, State -from wbld.build import Build, Builder, BuilderCustom +from wbld.build.models import BuildModel from wbld.repository import Clone diff --git a/tricks.yaml b/tricks.yaml index 9b66a51..973c7ac 100644 --- a/tricks.yaml +++ b/tricks.yaml @@ -1,8 +1,12 @@ tricks: - - watchdog.tricks.LoggerTrick: - patterns: ["*.py", "*.js"] - watchdog.tricks.AutoRestartTrick: command: ["python", "-m", "wbld.bot"] patterns: - "*.py" + - watchdog.tricks.AutoRestartTrick: + command: ["python", "-m", "wbld.web"] + patterns: + - "*.py" - "*.html" + - "*.jinja2" + - "*.css" diff --git a/wbld/__init__.py b/wbld/__init__.py index 0497d05..9477e89 100644 --- a/wbld/__init__.py +++ b/wbld/__init__.py @@ -1,5 +1,5 @@ -from dotenv import load_dotenv import sentry_sdk +from dotenv import load_dotenv from sentry_sdk.integrations.aiohttp import AioHttpIntegration sentry_sdk.init(integrations=[AioHttpIntegration()]) diff --git a/wbld/bot.py b/wbld/bot.py index c975f37..d1b67d1 100644 --- a/wbld/bot.py +++ b/wbld/bot.py @@ -1,39 +1,78 @@ +import asyncio import os +from discord import Intents +from discord.errors import PrivilegedIntentsRequired from discord.ext import commands -from wbld.log import logger -from wbld.cogs.wbld import WbldCog from wbld.cogs.health import Health +from wbld.cogs.pio import Pio +from wbld.cogs.wbld import WbldCog +from wbld.log import logger BASE_URL = os.getenv("BASE_URL", "https://wbld.app") TOKEN = os.getenv("DISCORD_TOKEN") PING_URL = os.getenv("PING_URL") PREFIXES = [os.getenv("DISCORD_PREFIX", "./")] DEFAULT_BRANCH = os.getenv("DEFAULT_BRANCH", "main") +OWNER_ID = os.getenv("OWNER_ID", 206914075391688704) class Bot(commands.Bot): def __init__(self, **kwargs): - super(Bot, self).__init__(**kwargs) + intents = Intents.default() + intents.message_content = True + intents.messages = True + intents.reactions = True + intents.dm_messages = True + intents.members = False # Do we need this? + super(Bot, self).__init__(**kwargs, intents=intents, help_command=commands.DefaultHelpCommand()) async def on_ready(self): logger.info("{} has connected to Discord!", self.user) - logger.complete() + + async def register_cog(cog, *args, **kwargs): + logger.info("Registering cog: {}", cog.__name__, args, kwargs) + await self.add_cog(cog(self, *args, **kwargs)) + + if PING_URL: + await register_cog(Health, PING_URL) + + await register_cog(Pio) + + await register_cog(WbldCog, BASE_URL, DEFAULT_BRANCH) + await logger.complete() bot = Bot( command_prefix=PREFIXES, case_insensitive=True, description="A WLED firmware Discord bot.", + shard_id=0, + owner_id=OWNER_ID, ) +async def main(): + try: + if TOKEN: + if PING_URL: + await bot.add_cog(Health(bot, PING_URL)) + await bot.add_cog(WbldCog(bot, BASE_URL, DEFAULT_BRANCH)) + await bot.start(TOKEN) + else: + logger.error("Please set your DISCORD_TOKEN.") + except PrivilegedIntentsRequired as e: + logger.error("Please enable the privileged intents. Error: {}", e) + except Exception as e: + logger.exception("Error: {}", e) + finally: + await bot.close() + + if __name__ == "__main__": - if TOKEN: - if PING_URL: - bot.add_cog(Health(bot, PING_URL)) - bot.add_cog(WbldCog(bot, BASE_URL, DEFAULT_BRANCH)) - bot.run(TOKEN) - else: - logger.error("Please set your DISCORD_TOKEN.") + try: + asyncio.run(main()) + except KeyboardInterrupt: + logger.info("Shutting down...") + asyncio.run(bot.close()) diff --git a/wbld/build/__init__.py b/wbld/build/__init__.py index 6975a53..ac478ee 100644 --- a/wbld/build/__init__.py +++ b/wbld/build/__init__.py @@ -1,4 +1,5 @@ from contextlib import redirect_stderr, redirect_stdout +from itertools import islice import os import shutil from timeit import default_timer as timer @@ -23,21 +24,58 @@ def __new__(cls, build_id: str) -> BuildModel: class Manager: - @staticmethod - def list_builds(sort=True, reverse=True): + per_page = 10 + + @classmethod + def list_builds(cls, sort=True, reverse=True, page=1, per_page=None): def sorting(key): if sort: return key.stat().st_ctime return None - for path in sorted(Storage.base_path.iterdir(), key=sorting, reverse=reverse): - if path.joinpath(BuildModel.build_file).exists(): - yield BuildModel.parse_build_path(path) + if per_page is None: + per_page = cls.per_page + + start_index = (page - 1) * per_page + end_index = start_index + per_page + + builds = ( + BuildModel.parse_build_path(path) + for path in filter( + lambda p: p.joinpath(BuildModel.build_file).exists(), + sorted( + Storage.base_path.iterdir(), + key=sorting, + reverse=reverse + ) + ) + ) + + builds_slice = islice(builds, start_index, end_index) + + for build in builds_slice: + yield build @staticmethod def get_build(build_id) -> BuildModel: return BuildModel.parse_build_id(build_id) + @staticmethod + def last_page(per_page=10): + return len(list(Storage.base_path.iterdir())) // per_page + + @staticmethod + def total_build_count() -> int: + return len(list(Storage.base_path.iterdir())) + + @staticmethod + def get_all_pages(per_page=10): + return list(range(1, Manager.last_page(per_page=per_page) + 1)) + + @staticmethod + def get_per_page(): + return Manager.per_page + class BuilderError(Exception): pass diff --git a/wbld/build/config.py b/wbld/build/config.py index 64b2b80..109a60a 100644 --- a/wbld/build/config.py +++ b/wbld/build/config.py @@ -14,7 +14,7 @@ def __init__(self, cc, message="Too many sections in configuration"): class CustomConfig(ConfigParser): def __init__(self, snippet): super(CustomConfig, self).__init__() - self.snippet = snippet + self.snippet: str = snippet self.read_string(self.snippet) if len(self) > 1: @@ -29,15 +29,15 @@ def __str__(self): @staticmethod def remove_prefix(text, prefix): if text.startswith(prefix): - return text[len(prefix) :] + return text[len(prefix):] return text @property - def section(self): + def section(self) -> str: return self.sections()[0] @property - def env(self): + def env(self) -> str: return CustomConfig.remove_prefix(self.section, "env:") @property diff --git a/wbld/build/models.py b/wbld/build/models.py index 9914fa4..2d3dcd3 100644 --- a/wbld/build/models.py +++ b/wbld/build/models.py @@ -8,7 +8,6 @@ from wbld.build.enums import Kind, State from wbld.build.storage import Storage -from wbld.log import logger class Author: @@ -19,7 +18,7 @@ def __get_validators__(cls): @classmethod def validate(cls, author: Union[Member, User]): types = Union[Member, User] - fields = ["id", "name", "avatar_url", "discriminator"] + fields = ["id", "name", "avatar", "discriminator"] if isinstance(author, types.__args__): return dict([(name, str(getattr(author, name))) for name in fields]) @@ -30,6 +29,9 @@ def validate(cls, author: Union[Member, User]): raise TypeError("Invalid value") +sha1_regex = constr(regex=r"^[0-9a-f]{40}$") + + class BuildModel(BaseModel): author: Author = None build_file: ClassVar[str] = "build.json" @@ -37,7 +39,7 @@ class BuildModel(BaseModel): env: str kind: Kind path: DirectoryPath = Field(default_factory=Storage.generate_build_uuid_path) - sha1: constr(regex=r"^[0-9a-f]{40}$") + sha1: sha1_regex snippet: str = None state: State = State.PENDING version: str diff --git a/wbld/build/shbuilder.py b/wbld/build/shbuilder.py new file mode 100644 index 0000000..5e3e36b --- /dev/null +++ b/wbld/build/shbuilder.py @@ -0,0 +1,161 @@ +from abc import ABC, abstractmethod +import shutil +from timeit import default_timer as timer +from pathlib import Path +import json + + +from wbld.log import logger +from wbld.build.config import CustomConfig +from wbld.build.models import BuildModel +from wbld.build.enums import Kind, State +from wbld.build.storage import Storage +from wbld.repository import Clone +from wbld.pio import PioCommand + + +def is_platformio_project(path: Path) -> bool: + return path.joinpath("platformio.ini").exists() + + +class Build: + def __new__(cls, build_id: str) -> BuildModel: + return BuildModel.parse_build_id(build_id) + + +class Manager: + @staticmethod + def list_builds(sort=True, reverse=True): + def sorting(key): + if sort: + return key.stat().st_ctime + return None + + for path in sorted(Storage.base_path.iterdir(), key=sorting, reverse=reverse): + if path.joinpath(BuildModel.build_file).exists(): + logger.debug(path) + yield BuildModel.parse_build_path(path) + + @staticmethod + def get_build(build_id) -> BuildModel: + return BuildModel.parse_build_id(build_id) + + +class BuilderError(Exception): + pass + + +class Builder(ABC): + def __init__(self, version: str, env: str): + self.version = version + self.env = env + self._clone = None + + def __enter__(self): + self.setup() + logger.debug(f"Entering builder for build {self.build.build_id} at {self.path}") + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.cleanup() + + def platform_install(self, platform, skip_default_package=True, silent=True): + pkg = self.package_manager.install(spec=platform, skip_default_package=skip_default_package, silent=silent) + return pkg + + @property + def firmware_filename(self): + return f"{self._clone.path}/.pio/build/{self.build.env}/firmware.bin" + + @property + @abstractmethod + def kind(self): + return NotImplementedError + + # pylint: disable=too-many-arguments + def run(self, variables=None, targets=None, silent=False, verbose=False, jobs=2): + timer_start = timer() + log_combined = self.build.file_log.open("w") + + pio = PioCommand(return_command=True, out=log_combined) + + self.build.state = State.BUILDING + + run = pio.run(environment=self.build.env, project_dir=self.path, verbose=verbose, jobs=jobs) + + if run.exit_code == 0: + self.gather_files([open(self.firmware_filename, "rb")]) + self.build.state = State.SUCCESS + else: + self.build.state = State.FAILED + + timer_end = timer() + duration = float(timer_end - timer_start) + self.build.duration = duration + return self.build + + def get_list_of_envs(self): + pio = PioCommand(err_to_out=False) + project = pio.project.config(project_dir=self.path, json_output=True) + return [env[0] for env in json.loads(project) if env[0].startswith("env:")] + + def check_env(self, env: str) -> bool: + if f"env:{env}" in self.get_list_of_envs(): + return True + return False + + def setup(self): + self._clone = Clone(cleanup=False) + self._clone.clone_version(self.version) + + if not is_platformio_project(self._clone.path): + logger.error(f"Raising FileNotFoundError for path: {self._clone.path}") + raise FileNotFoundError(self._clone.path) + + self.build = BuildModel(kind=self.kind, env=self.env, version=self._clone.version, sha1=str(self._clone.sha1)) + self.path = self._clone.path + self.project_config = self.path.joinpath("platformio.ini") + + logger.debug(f"Builder setup for build {self.build.build_id} at {self.path}") + + if not self.check_env(self.build.env): + raise BuilderError(f"Environment doesn't exist: {self.build.env}") + + def cleanup(self): + self._clone.cleanup() + + def gather_files(self, files): + for file in files: + shutil.copy(file.name, self.build.path) + file.close() + logger.debug(f"Files gathered in {self.build.path}: {files}") + + +class BuilderBuiltin(Builder): + def __init__(self, version: str, env: str): + logger.debug(f"Built-in build for version {version} using env: {env}") + super(BuilderBuiltin, self).__init__(version, env) + + @property + def kind(self): + return Kind.BUILTIN + + +class BuilderCustom(Builder): + def __init__(self, version: str, clone: Clone, snippet): + custom_config = CustomConfig(snippet) + env = custom_config.env + + logger.debug(f"Custom build in {clone.path} using env: {env}") + logger.trace(f"Custom config: {custom_config.snippet}") + + with open(f"{clone.path}/platformio_override.ini", "w") as file: + logger.debug(f"Writing out custom config to: {file.name}") + custom_config.write(file) + + super(BuilderCustom, self).__init__(clone, custom_config.env) + self.build.snippet = snippet + + @property + def kind(self): + return Kind.CUSTOM diff --git a/wbld/cogs/health.py b/wbld/cogs/health.py index 614d692..d3497ea 100644 --- a/wbld/cogs/health.py +++ b/wbld/cogs/health.py @@ -1,5 +1,5 @@ -from aiohttp import ClientSession, ClientError -from discord.ext import tasks, commands +from aiohttp import ClientError, ClientSession +from discord.ext import commands, tasks from wbld.log import logger diff --git a/wbld/cogs/pio.py b/wbld/cogs/pio.py new file mode 100644 index 0000000..87b71aa --- /dev/null +++ b/wbld/cogs/pio.py @@ -0,0 +1,84 @@ +from discord.ext import commands + +from wbld.pio import PioCommand + + +class Pio(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.pio = PioCommand() + + @commands.group(description="PlatformIO commands.") + async def pio(self, ctx: commands.Context): + """ + PlatformIO debug commands. + + ./pio + """ + if ctx.invoked_subcommand is None: + subcommands = ", ".join([f"`{c.name}`" for c in ctx.command.commands]) + await ctx.send(f"Invalid `{ctx.command.name}` command passed. Available subcommands: {subcommands}") + + @pio.group(description="PlatformIO package commands.") + async def pkg(self, ctx: commands.Context): + """ + PlatformIO package commands. + + ./pio pkg + """ + if ctx.invoked_subcommand is None: + subcommands = ", ".join([f"`{c.name}`" for c in ctx.command.commands]) + await ctx.send(f"Invalid `{ctx.command.name}` command passed. Available subcommands: {subcommands}") + + @pkg.command(description="List globally installed packages.") + async def list(self, ctx: commands.Context, env: str = None): + """ + List globally installed packages. + + ./pio pkg list + """ + global_package_list = self.pio.package.list("--global") + + await ctx.send(f"```\n{global_package_list}\n```") + + @pio.group(description="PlatformIO system commands.") + async def system(self, ctx: commands.Context): + """ + PlatformIO system commands. + + System info: + + ./pio system info + + System prune: + + ./pio system prune + """ + if ctx.invoked_subcommand is None: + subcommands = ", ".join([f"`{c.name}`" for c in ctx.command.commands]) + await ctx.send(f"Invalid `{ctx.command.name}` command passed. Available subcommands: {subcommands}") + + @system.command(description="Info about PlatformIO system.") + async def info(self, ctx: commands.Context): + """ + Info about PlatformIO system. + + ./pio system info + """ + + system_info = self.pio.system.info() + + await ctx.send(f"```\n{system_info}\n```") + + @system.command(description="Prune PlatformIO system.") + @commands.is_owner() + async def prune(self, ctx: commands.Context): + """ + Prune PlatformIO system. + + ./pio system prune + """ + + system_prune = self.pio.system.prune(f=True) + + await ctx.send(f"```\n{system_prune}\n```") diff --git a/wbld/cogs/wbld.py b/wbld/cogs/wbld.py index 1d6db8c..8692e45 100644 --- a/wbld/cogs/wbld.py +++ b/wbld/cogs/wbld.py @@ -1,15 +1,17 @@ from asyncio.exceptions import TimeoutError from configparser import MissingSectionHeaderError, ParsingError +from typing import List, Union -from discord import File, Embed, Colour +from discord import Colour, Embed, File +from discord.app_commands import Command from discord.ext import commands -from wbld.build import Builder, BuilderCustom from wbld.build.config import CustomConfigException -from wbld.build.models import BuildModel from wbld.build.enums import State +from wbld.build.models import BuildModel +from wbld.build.shbuilder import BuilderBuiltin, BuilderCustom from wbld.log import logger -from wbld.repository import Reference, ReferenceException, Clone +from wbld.repository import Reference, ReferenceException class WbldEmbed(Embed): @@ -18,7 +20,7 @@ def __init__(self, ctx: commands.Context, build: BuildModel, base_url: str, **kw self.colour = Colour.blue() self.title = f"Build Started: {build.build_id}" self.url = f"{base_url}/build/{build.build_id}" - self.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) + self.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url) self.add_field(name="env", value=build.env) if build.state == State.SUCCESS: @@ -40,18 +42,24 @@ class WbldCog(commands.Cog, name="Builder"): Commands to build and work with WLED firmware. """ - def __init__(self, bot, base_url: str, default_branch: str): - self.bot = bot - self.base_url = base_url - self.default_branch = default_branch + def __init__(self, bot: commands.Bot, base_url: str, default_branch: str): + self.bot: commands.Bot = bot + self.base_url: str = base_url + self.default_branch: str = default_branch - async def _build_firmware(self, ctx: commands.Context, version, env_or_snippet, builder, clone=None): - try: - if not clone: - clone = Clone(version) - clone.clone_version() + async def _build_firmware( + self, + ctx: commands.Context, + version: str, + env_or_snippet: str, + builder: Union[BuilderBuiltin, BuilderCustom], + ): + await ctx.defer(ephemeral=False) - with builder(clone, env_or_snippet) as build: + config_exceptions = (CustomConfigException, MissingSectionHeaderError, ParsingError) + + try: + with builder(version, env_or_snippet) as build: await ctx.send( f"Sure thing. Building env `{build.build.env}` as `{build.build.build_id}`. This will take a moment.", embed=WbldEmbed(ctx, build.build, self.base_url), @@ -75,11 +83,7 @@ async def _build_firmware(self, ctx: commands.Context, version, env_or_snippet, logger.error(f"Error building firmware for `{build.build.env}` against `{version}`.") except ReferenceException as error: await ctx.send(f"{error}: {version}") - except ( - CustomConfigException, - MissingSectionHeaderError, - ParsingError, - ) as error: + except config_exceptions as error: await ctx.send( content=f"Config Errror:\n\n{error}\n\nCheck your configuration and see help using: `{ctx.prefix}help`" ) @@ -98,22 +102,22 @@ async def _send_ready(ctx, reference): logger.debug(ctx) logger.debug(reference) await logger.complete() + embed = Embed( title=reference.commit.sha, url=reference.commit.commit.html_url, description=reference.commit.commit.message, color=0x034EFC, ) + embed.set_author( name=reference.repository.full_name, url=reference.repository.html_url, - icon_url=reference.repository.owner.avatar_url, - ) - await ctx.send( - content="OK. Ready to build. Please paste your custom PlatformIO environment config.", - embed=embed, + icon_url=reference.repository.owner.avatar.url, ) + await ctx.send(content="OK. Ready to build. Please paste your custom PlatformIO environment config.", embed=embed) + @commands.Cog.listener() async def on_command_error(self, ctx, exception): if isinstance(exception, TimeoutError): @@ -126,19 +130,28 @@ async def on_command_error(self, ctx, exception): commands.errors.MissingRequiredArgument, commands.errors.MaxConcurrencyReached, commands.errors.CommandInvokeError, + FileNotFoundError, ), ): await ctx.send(exception) + + logger.warning(exception) await logger.complete() - raise exception @commands.Cog.listener() async def on_command(self, ctx): logger.debug(f"Command {ctx.command.qualified_name} called by {str(ctx.author)}") await logger.complete() - @commands.group(description="Firmware building commands.") - async def build(self, ctx): + @commands.Cog.listener() + async def on_ready(self): + my_guild = self.bot.get_guild(206915180062441475) + await self.bot.tree.sync(guild=my_guild) + logger.debug("WbldCog ready.") + await logger.complete() + + @commands.hybrid_group(description="Firmware building commands.") + async def build(self, ctx: commands.Context): """ See help for builtin firmware: @@ -155,9 +168,9 @@ async def build(self, ctx): @commands.max_concurrency(1, per=commands.BucketType.user) @build.command() - async def builtin(self, ctx, env, version=None): + async def builtin(self, ctx: commands.Context, env: str, version: str = None): """ - Builds and returns a firmware file for an environment which already exists in the WLED PlatformIO configuration. + Builds and returns a firmware file for an environment which already exists in the WLED PlatformIO. Example: @@ -166,11 +179,12 @@ async def builtin(self, ctx, env, version=None): if not version: version = self.default_branch - await self._build_firmware(ctx, version, env, Builder) + await self._build_firmware(ctx, version, env, BuilderBuiltin) + # await ctx.send("This command is currently disabled.") @commands.max_concurrency(1, per=commands.BucketType.user) @build.command() - async def custom(self, ctx, version=None): + async def custom(self, ctx: commands.Context, version: str = None, repository: str = None): """ Builds and returns firmware for a custom configuration snippet that you provide. @@ -199,24 +213,16 @@ def inner_check(message): return inner_check + await ctx.send(f"Ready to build `{version}`. Paste your custom PlatformIO environment config.") try: - clone = Clone(version) - commit = clone.clone_version() - except Exception as error: - raise error + msg = await self.bot.wait_for("message", check=check_author(ctx.author, ctx.channel), timeout=30) + except TimeoutError: + await ctx.send("Didn't receive configuraton within 30 seconds. Try again!") else: - await ctx.send( - f"Ready to build `{version}` (`{commit.hexsha}`). Paste your custom PlatformIO environment config." - ) - try: - msg = await self.bot.wait_for("message", check=check_author(ctx.author, ctx.channel), timeout=30) - except TimeoutError: - await ctx.send("Didn't receive configuraton within 30 seconds. Try again!") - else: - await self._build_firmware(ctx, version, msg.content, BuilderCustom, clone=clone) + await self._build_firmware(ctx, version, msg.content, BuilderCustom) @build.command() - async def log(self, ctx, build_id): + async def log(self, ctx: commands.Context, build_id: str): """ Returns the log file containing stdout and stderr of the PlatformIO build. """ @@ -231,3 +237,19 @@ async def log(self, ctx, build_id): else: file_send = File(build.file_log, filename=f"wled_build_{build_id}.log") await ctx.send(file=file_send, content=f"Log file for build: `{build_id}`") + + @commands.command(name="wbldsync", hidden=True) + @commands.is_owner() + async def sync(self, ctx: commands.Context, guild_id: int = 206915180062441475): + my_guild = self.bot.get_guild(guild_id) + + self.bot.tree.copy_global_to(guild=my_guild) + await self.bot.tree.sync(guild=my_guild) + logger.info("Synced application commands to guild: {}", my_guild) + + commands: List[Command] = self.bot.tree.get_commands(guild=my_guild) + + for command in commands: + await ctx.send(f"Synced command: {command.name}") + + await logger.complete() diff --git a/wbld/pio/__init__.py b/wbld/pio/__init__.py new file mode 100644 index 0000000..3d93cb6 --- /dev/null +++ b/wbld/pio/__init__.py @@ -0,0 +1,19 @@ +import os + +from sh import Command + +PIO_COMMAND_NAME = "pio" + +current_env = os.environ.copy() +current_env["PLATFORMIO_DISABLE_COLOR"] = "true" +current_env["CI"] = "true" + + +class PioCommand: + def __new__(cls, out=None, err_to_out=True, return_command=False): + return Command(PIO_COMMAND_NAME).bake( + _return_cmd=return_command, + _out=out, + _err_to_out=err_to_out, + _env=current_env, + ) diff --git a/wbld/repository.py b/wbld/repository.py index f60e250..0a7d8cb 100644 --- a/wbld/repository.py +++ b/wbld/repository.py @@ -1,9 +1,9 @@ -from tempfile import TemporaryDirectory import os from pathlib import Path +from tempfile import TemporaryDirectory -from github import Github, GithubException from git import Repo +from github import Github, GithubException class ReferenceException(Exception): @@ -52,27 +52,49 @@ def get_tag(self): class Clone: - def __init__(self, version, url="https://github.com/Aircoookie/WLED.git"): + def __init__(self, url: str = "https://github.com/Aircoookie/WLED.git", cleanup: bool = True): self.tempdir = TemporaryDirectory() - self.path = Path(self.tempdir.name) + self._path = Path(self.tempdir.name) self.url = url - self.version = version self.repo = Repo.init(str(self.path)) - self.sha1 = None + self._cleanup = cleanup + self._version = None def __enter__(self): - return self.path + return self def __exit__(self, exc_type, exc_value, traceback): self.cleanup() - def clone_version(self): + @property + def sha1(self): + try: + return self.repo.commit() + except ValueError: + return None + + @property + def version(self): + if not self._version: + raise Exception("No version set") + + return self._version + + @property + def path(self): + if self._path.exists(): + return self._path + else: + raise FileNotFoundError("Path does not exist. Did you clone the repository?") + + def clone_version(self, version: str = "main"): + self._version = version origin = self.repo.create_remote("origin", self.url) origin.fetch() - self.repo.git.checkout(self.version) + self.repo.git.checkout(version) - self.sha1 = self.repo.commit() return self.sha1 def cleanup(self): - self.tempdir.cleanup() + if self._cleanup: + self.tempdir.cleanup() diff --git a/wbld/static/css/output.css b/wbld/static/css/output.css new file mode 100644 index 0000000..6a93fdc --- /dev/null +++ b/wbld/static/css/output.css @@ -0,0 +1,1583 @@ +/* +! tailwindcss v3.3.1 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.pill-custom { + display: inline; + border-radius: 0.25rem; + --tw-bg-opacity: 1; + background-color: rgb(168 85 247 / var(--tw-bg-opacity)); + padding-left: 0.5rem; + padding-right: 0.5rem; + font-size: 0.75rem; + line-height: 1rem; + line-height: 1; + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + opacity: 0.5; +} + +.pill-builtin { + display: inline; + border-radius: 0.25rem; + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + padding-left: 0.5rem; + padding-right: 0.5rem; + font-size: 0.75rem; + line-height: 1rem; + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.inset-0 { + inset: 0px; +} + +.inset-x-0 { + left: 0px; + right: 0px; +} + +.inset-y-0 { + top: 0px; + bottom: 0px; +} + +.-top-px { + top: -1px; +} + +.bottom-0 { + bottom: 0px; +} + +.left-0 { + left: 0px; +} + +.isolate { + isolation: isolate; +} + +.z-10 { + z-index: 10; +} + +.-m-1 { + margin: -0.25rem; +} + +.-m-1\.5 { + margin: -0.375rem; +} + +.-m-2 { + margin: -0.5rem; +} + +.-m-2\.5 { + margin: -0.625rem; +} + +.mx-2 { + margin-left: 0.5rem; + margin-right: 0.5rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.-mx-3 { + margin-left: -0.75rem; + margin-right: -0.75rem; +} + +.-mr-2 { + margin-right: -0.5rem; +} + +.-mt-px { + margin-top: -1px; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.mr-1 { + margin-right: 0.25rem; +} + +.mr-1\.5 { + margin-right: 0.375rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.mr-8 { + margin-right: 2rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.hidden { + display: none; +} + +.h-16 { + height: 4rem; +} + +.h-3 { + height: 0.75rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-full { + height: 100%; +} + +.h-1 { + height: 0.25rem; +} + +.h-1\.5 { + height: 0.375rem; +} + +.h-12 { + height: 3rem; +} + +.min-h-full { + min-height: 100%; +} + +.w-0 { + width: 0px; +} + +.w-3 { + width: 0.75rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-8 { + width: 2rem; +} + +.w-auto { + width: auto; +} + +.w-1 { + width: 0.25rem; +} + +.w-1\.5 { + width: 0.375rem; +} + +.w-12 { + width: 3rem; +} + +.w-full { + width: 100%; +} + +.min-w-0 { + min-width: 0px; +} + +.max-w-7xl { + max-width: 80rem; +} + +.flex-1 { + flex: 1 1 0%; +} + +.flex-initial { + flex: 0 1 auto; +} + +.flex-none { + flex: none; +} + +.flex-auto { + flex: 1 1 auto; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.list-disc { + list-style-type: disc; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-start { + align-items: flex-start; +} + +.items-center { + align-items: center; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-x-1 { + -moz-column-gap: 0.25rem; + column-gap: 0.25rem; +} + +.gap-x-1\.5 { + -moz-column-gap: 0.375rem; + column-gap: 0.375rem; +} + +.gap-x-4 { + -moz-column-gap: 1rem; + column-gap: 1rem; +} + +.gap-x-6 { + -moz-column-gap: 1.5rem; + column-gap: 1.5rem; +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.-space-x-px > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(-1px * var(--tw-space-x-reverse)); + margin-left: calc(-1px * calc(1 - var(--tw-space-x-reverse))); +} + +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-divide-opacity)); +} + +.divide-gray-100 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(243 244 246 / var(--tw-divide-opacity)); +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-y-auto { + overflow-y: auto; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.whitespace-pre { + white-space: pre; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded-l-md { + border-top-left-radius: 0.375rem; + border-bottom-left-radius: 0.375rem; +} + +.rounded-r-md { + border-top-right-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; +} + +.border { + border-width: 1px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-b-2 { + border-bottom-width: 2px; +} + +.border-l-4 { + border-left-width: 4px; +} + +.border-t { + border-top-width: 1px; +} + +.border-t-2 { + border-top-width: 2px; +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); +} + +.border-indigo-500 { + --tw-border-opacity: 1; + border-color: rgb(99 102 241 / var(--tw-border-opacity)); +} + +.border-transparent { + border-color: transparent; +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); +} + +.bg-indigo-50 { + --tw-bg-opacity: 1; + background-color: rgb(238 242 255 / var(--tw-bg-opacity)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-yellow-300 { + --tw-bg-opacity: 1; + background-color: rgb(253 224 71 / var(--tw-bg-opacity)); +} + +.bg-emerald-500 { + --tw-bg-opacity: 1; + background-color: rgb(16 185 129 / var(--tw-bg-opacity)); +} + +.bg-emerald-500\/20 { + background-color: rgb(16 185 129 / 0.2); +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.bg-indigo-600 { + --tw-bg-opacity: 1; + background-color: rgb(79 70 229 / var(--tw-bg-opacity)); +} + +.p-2 { + padding: 0.5rem; +} + +.p-1 { + padding: 0.25rem; +} + +.p-1\.5 { + padding: 0.375rem; +} + +.p-2\.5 { + padding: 0.625rem; +} + +.p-6 { + padding: 1.5rem; +} + +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.py-10 { + padding-top: 2.5rem; + padding-bottom: 2.5rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + +.pb-6 { + padding-bottom: 1.5rem; +} + +.pl-1 { + padding-left: 0.25rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + +.pl-3 { + padding-left: 0.75rem; +} + +.pr-1 { + padding-right: 0.25rem; +} + +.pr-4 { + padding-right: 1rem; +} + +.pt-1 { + padding-top: 0.25rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pt-4 { + padding-top: 1rem; +} + +.font-mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-bold { + font-weight: 700; +} + +.font-extralight { + font-weight: 200; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.italic { + font-style: italic; +} + +.leading-6 { + line-height: 1.5rem; +} + +.leading-normal { + line-height: 1.5; +} + +.leading-tight { + line-height: 1.25; +} + +.leading-5 { + line-height: 1.25rem; +} + +.leading-7 { + line-height: 1.75rem; +} + +.tracking-tight { + letter-spacing: -0.025em; +} + +.text-blue-500 { + --tw-text-opacity: 1; + color: rgb(59 130 246 / var(--tw-text-opacity)); +} + +.text-gray-100 { + --tw-text-opacity: 1; + color: rgb(243 244 246 / var(--tw-text-opacity)); +} + +.text-gray-300 { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); +} + +.text-indigo-600 { + --tw-text-opacity: 1; + color: rgb(79 70 229 / var(--tw-text-opacity)); +} + +.text-indigo-700 { + --tw-text-opacity: 1; + color: rgb(67 56 202 / var(--tw-text-opacity)); +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.text-yellow-500 { + --tw-text-opacity: 1; + color: rgb(234 179 8 / var(--tw-text-opacity)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.subpixel-antialiased { + -webkit-font-smoothing: auto; + -moz-osx-font-smoothing: auto; +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.ring-inset { + --tw-ring-inset: inset; +} + +.ring-gray-900\/5 { + --tw-ring-color: rgb(17 24 39 / 0.05); +} + +.ring-gray-300 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); +} + +.hover\:border-gray-300:hover { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.hover\:text-blue-700:hover { + --tw-text-opacity: 1; + color: rgb(29 78 216 / var(--tw-text-opacity)); +} + +.hover\:text-gray-500:hover { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.hover\:text-gray-700:hover { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.hover\:text-gray-800:hover { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.hover\:underline:hover { + text-decoration-line: underline; +} + +.focus\:z-20:focus { + z-index: 20; +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:outline-offset-0:focus { + outline-offset: 0px; +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-indigo-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity)); +} + +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} + +.focus-visible\:outline:focus-visible { + outline-style: solid; +} + +.focus-visible\:outline-2:focus-visible { + outline-width: 2px; +} + +.focus-visible\:outline-offset-2:focus-visible { + outline-offset: 2px; +} + +.focus-visible\:outline-offset-0:focus-visible { + outline-offset: 0px; +} + +.focus-visible\:outline-indigo-600:focus-visible { + outline-color: #4f46e5; +} + +@media (min-width: 640px) { + .sm\:-my-px { + margin-top: -1px; + margin-bottom: -1px; + } + + .sm\:ml-6 { + margin-left: 1.5rem; + } + + .sm\:block { + display: block; + } + + .sm\:flex { + display: flex; + } + + .sm\:hidden { + display: none; + } + + .sm\:h-6 { + height: 1.5rem; + } + + .sm\:flex-1 { + flex: 1 1 0%; + } + + .sm\:flex-col { + flex-direction: column; + } + + .sm\:items-end { + align-items: flex-end; + } + + .sm\:items-center { + align-items: center; + } + + .sm\:justify-end { + justify-content: flex-end; + } + + .sm\:justify-between { + justify-content: space-between; + } + + .sm\:space-x-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2rem * var(--tw-space-x-reverse)); + margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); + } + + .sm\:rounded-md { + border-radius: 0.375rem; + } + + .sm\:rounded-xl { + border-radius: 0.75rem; + } + + .sm\:px-0 { + padding-left: 0px; + padding-right: 0px; + } + + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +@media (min-width: 768px) { + .md\:visible { + visibility: visible; + } + + .md\:-mt-px { + margin-top: -1px; + } + + .md\:block { + display: block; + } + + .md\:flex { + display: flex; + } + + .md\:inline-flex { + display: inline-flex; + } + + .md\:grid { + display: grid; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:gap-4 { + gap: 1rem; + } +} + +@media (min-width: 1024px) { + .lg\:block { + display: block; + } + + .lg\:flex { + display: flex; + } + + .lg\:hidden { + display: none; + } + + .lg\:gap-x-12 { + -moz-column-gap: 3rem; + column-gap: 3rem; + } + + .lg\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } +} diff --git a/wbld/static/images/favicon.ico b/wbld/static/images/favicon.ico new file mode 100644 index 0000000..fde031f Binary files /dev/null and b/wbld/static/images/favicon.ico differ diff --git a/wbld/static/src/css/input.css b/wbld/static/src/css/input.css new file mode 100644 index 0000000..832a334 --- /dev/null +++ b/wbld/static/src/css/input.css @@ -0,0 +1,13 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer components { + .pill-custom { + @apply inline text-xs px-2 bg-purple-500 text-white rounded leading-none opacity-50; + } + + .pill-builtin { + @apply inline text-xs px-2 bg-blue-500 text-white rounded; + } + } diff --git a/wbld/templates/base.html b/wbld/templates/base.html new file mode 100644 index 0000000..a4e6373 --- /dev/null +++ b/wbld/templates/base.html @@ -0,0 +1,29 @@ + + + +
+ + + + + + +{{ build.build_id }}
+ +{{ 'Builtin' if + build.kind|e == 'Kind.BUILTIN' else 'Custom' }}
+{{ build.env }}
++ firmware.bin +
++ combined.txt +
+Duration:
+{{ build.duration_human }}
+{{ build.snippet }}
+{{ 'Builtin' if build.kind|e == 'Kind.BUILTIN' else 'Custom' }}
-{{ build.env }}
-- firmware.bin -
-- combined.txt -
-Duration:
-{{ build.duration_human }}
-{{ build.snippet }}
-Co-Founder / CEO
+Last seen
+Co-Founder / CTO
+Last seen
+Business Relations
+Online
+Front-end Developer
+Last seen
+Designer
+Last seen
++ + + Tom Cook + +
+ +Director of Product
+Online
+{{ 'Builtin' if build.kind|e == 'Kind.BUILTIN' else 'Custom' }}
-{{ build.env }}
-No builds found.
-{{ build.build_id }}
++ +
++ {{ 'Builtin' if build.kind|e == 'Kind.BUILTIN' else 'Custom' }} +
++ Built as {{build.env }} +
+ +No builds found.
+