~repos /website

#astro#js#html#css

git clone https://pyrossh.dev/repos/website.git

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


f272160b pyrossh

1 year ago
add posts
package-lock.json CHANGED
@@ -9,7 +9,9 @@
9
9
  "version": "0.0.1",
10
10
  "dependencies": {
11
11
  "@sentry/sveltekit": "^7.102.0",
12
- "@unocss/reset": "^0.58.5"
12
+ "@unocss/reset": "^0.58.5",
13
+ "remark-github": "^12.0.0",
14
+ "shiki": "^1.1.7"
13
15
  },
14
16
  "devDependencies": {
15
17
  "@iconify/json": "^2.2.185",
@@ -23,6 +25,7 @@
23
25
  "eslint": "^8.56.0",
24
26
  "eslint-config-prettier": "^9.1.0",
25
27
  "eslint-plugin-svelte": "^2.36.0-next.4",
28
+ "mdsvex": "^0.11.0",
26
29
  "prettier": "^3.1.1",
27
30
  "prettier-plugin-svelte": "^3.1.2",
28
31
  "svelte": "4.2.11",
@@ -1939,6 +1942,11 @@
1939
1942
  "node": ">= 10"
1940
1943
  }
1941
1944
  },
1945
+ "node_modules/@shikijs/core": {
1946
+ "version": "1.1.7",
1947
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.1.7.tgz",
1948
+ "integrity": "sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg=="
1949
+ },
1942
1950
  "node_modules/@sinclair/typebox": {
1943
1951
  "version": "0.27.8",
1944
1952
  "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@@ -2062,12 +2070,25 @@
2062
2070
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
2063
2071
  "dev": true
2064
2072
  },
2073
+ "node_modules/@types/mdast": {
2074
+ "version": "4.0.3",
2075
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz",
2076
+ "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==",
2077
+ "dependencies": {
2078
+ "@types/unist": "*"
2079
+ }
2080
+ },
2065
2081
  "node_modules/@types/pug": {
2066
2082
  "version": "2.0.10",
2067
2083
  "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
2068
2084
  "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
2069
2085
  "dev": true
2070
2086
  },
2087
+ "node_modules/@types/unist": {
2088
+ "version": "2.0.10",
2089
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
2090
+ "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
2091
+ },
2071
2092
  "node_modules/@ungap/structured-clone": {
2072
2093
  "version": "1.2.0",
2073
2094
  "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -4383,11 +4404,64 @@
4383
4404
  "recast": "^0.23.2"
4384
4405
  }
4385
4406
  },
4407
+ "node_modules/mdast-util-find-and-replace": {
4408
+ "version": "3.0.1",
4409
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz",
4410
+ "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==",
4411
+ "dependencies": {
4412
+ "@types/mdast": "^4.0.0",
4413
+ "escape-string-regexp": "^5.0.0",
4414
+ "unist-util-is": "^6.0.0",
4415
+ "unist-util-visit-parents": "^6.0.0"
4416
+ },
4417
+ "funding": {
4418
+ "type": "opencollective",
4419
+ "url": "https://opencollective.com/unified"
4420
+ }
4421
+ },
4422
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
4423
+ "version": "5.0.0",
4424
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
4425
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
4426
+ "engines": {
4427
+ "node": ">=12"
4428
+ },
4429
+ "funding": {
4430
+ "url": "https://github.com/sponsors/sindresorhus"
4431
+ }
4432
+ },
4433
+ "node_modules/mdast-util-to-string": {
4434
+ "version": "4.0.0",
4435
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
4436
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
4437
+ "dependencies": {
4438
+ "@types/mdast": "^4.0.0"
4439
+ },
4440
+ "funding": {
4441
+ "type": "opencollective",
4442
+ "url": "https://opencollective.com/unified"
4443
+ }
4444
+ },
4386
4445
  "node_modules/mdn-data": {
4387
4446
  "version": "2.0.30",
4388
4447
  "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
4389
4448
  "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
4390
4449
  },
4450
+ "node_modules/mdsvex": {
4451
+ "version": "0.11.0",
4452
+ "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.11.0.tgz",
4453
+ "integrity": "sha512-gJF1s0N2nCmdxcKn8HDn0LKrN8poStqAicp6bBcsKFd/zkUBGLP5e7vnxu+g0pjBbDFOscUyI1mtHz+YK2TCDw==",
4454
+ "dev": true,
4455
+ "dependencies": {
4456
+ "@types/unist": "^2.0.3",
4457
+ "prism-svelte": "^0.4.7",
4458
+ "prismjs": "^1.17.1",
4459
+ "vfile-message": "^2.0.4"
4460
+ },
4461
+ "peerDependencies": {
4462
+ "svelte": ">=3 <5"
4463
+ }
4464
+ },
4391
4465
  "node_modules/merge-stream": {
4392
4466
  "version": "2.0.0",
4393
4467
  "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -5087,6 +5161,21 @@
5087
5161
  "url": "https://github.com/chalk/ansi-styles?sponsor=1"
5088
5162
  }
5089
5163
  },
5164
+ "node_modules/prism-svelte": {
5165
+ "version": "0.4.7",
5166
+ "resolved": "https://registry.npmjs.org/prism-svelte/-/prism-svelte-0.4.7.tgz",
5167
+ "integrity": "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==",
5168
+ "dev": true
5169
+ },
5170
+ "node_modules/prismjs": {
5171
+ "version": "1.29.0",
5172
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
5173
+ "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
5174
+ "dev": true,
5175
+ "engines": {
5176
+ "node": ">=6"
5177
+ }
5178
+ },
5090
5179
  "node_modules/progress": {
5091
5180
  "version": "2.0.3",
5092
5181
  "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -5161,6 +5250,23 @@
5161
5250
  "node": ">= 4"
5162
5251
  }
5163
5252
  },
5253
+ "node_modules/remark-github": {
5254
+ "version": "12.0.0",
5255
+ "resolved": "https://registry.npmjs.org/remark-github/-/remark-github-12.0.0.tgz",
5256
+ "integrity": "sha512-ByefQKFN184LeiGRCabfl7zUJsdlMYWEhiLX1gpmQ11yFg6xSuOTW7LVCv0oc1x+YvUMJW23NU36sJX2RWGgvg==",
5257
+ "dependencies": {
5258
+ "@types/mdast": "^4.0.0",
5259
+ "mdast-util-find-and-replace": "^3.0.0",
5260
+ "mdast-util-to-string": "^4.0.0",
5261
+ "to-vfile": "^8.0.0",
5262
+ "unist-util-visit": "^5.0.0",
5263
+ "vfile": "^6.0.0"
5264
+ },
5265
+ "funding": {
5266
+ "type": "opencollective",
5267
+ "url": "https://opencollective.com/unified"
5268
+ }
5269
+ },
5164
5270
  "node_modules/resolve-from": {
5165
5271
  "version": "4.0.0",
5166
5272
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -5432,6 +5538,14 @@
5432
5538
  "node": ">=8"
5433
5539
  }
5434
5540
  },
5541
+ "node_modules/shiki": {
5542
+ "version": "1.1.7",
5543
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.7.tgz",
5544
+ "integrity": "sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==",
5545
+ "dependencies": {
5546
+ "@shikijs/core": "1.1.7"
5547
+ }
5548
+ },
5435
5549
  "node_modules/siginfo": {
5436
5550
  "version": "2.0.0",
5437
5551
  "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -5759,6 +5873,18 @@
5759
5873
  "node": ">=8.0"
5760
5874
  }
5761
5875
  },
5876
+ "node_modules/to-vfile": {
5877
+ "version": "8.0.0",
5878
+ "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-8.0.0.tgz",
5879
+ "integrity": "sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==",
5880
+ "dependencies": {
5881
+ "vfile": "^6.0.0"
5882
+ },
5883
+ "funding": {
5884
+ "type": "opencollective",
5885
+ "url": "https://opencollective.com/unified"
5886
+ }
5887
+ },
5762
5888
  "node_modules/totalist": {
5763
5889
  "version": "3.0.1",
5764
5890
  "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
@@ -5844,6 +5970,73 @@
5844
5970
  "url": "https://github.com/sponsors/antfu"
5845
5971
  }
5846
5972
  },
5973
+ "node_modules/unist-util-is": {
5974
+ "version": "6.0.0",
5975
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
5976
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
5977
+ "dependencies": {
5978
+ "@types/unist": "^3.0.0"
5979
+ },
5980
+ "funding": {
5981
+ "type": "opencollective",
5982
+ "url": "https://opencollective.com/unified"
5983
+ }
5984
+ },
5985
+ "node_modules/unist-util-is/node_modules/@types/unist": {
5986
+ "version": "3.0.2",
5987
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
5988
+ "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="
5989
+ },
5990
+ "node_modules/unist-util-stringify-position": {
5991
+ "version": "2.0.3",
5992
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
5993
+ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
5994
+ "dev": true,
5995
+ "dependencies": {
5996
+ "@types/unist": "^2.0.2"
5997
+ },
5998
+ "funding": {
5999
+ "type": "opencollective",
6000
+ "url": "https://opencollective.com/unified"
6001
+ }
6002
+ },
6003
+ "node_modules/unist-util-visit": {
6004
+ "version": "5.0.0",
6005
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
6006
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
6007
+ "dependencies": {
6008
+ "@types/unist": "^3.0.0",
6009
+ "unist-util-is": "^6.0.0",
6010
+ "unist-util-visit-parents": "^6.0.0"
6011
+ },
6012
+ "funding": {
6013
+ "type": "opencollective",
6014
+ "url": "https://opencollective.com/unified"
6015
+ }
6016
+ },
6017
+ "node_modules/unist-util-visit-parents": {
6018
+ "version": "6.0.1",
6019
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
6020
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
6021
+ "dependencies": {
6022
+ "@types/unist": "^3.0.0",
6023
+ "unist-util-is": "^6.0.0"
6024
+ },
6025
+ "funding": {
6026
+ "type": "opencollective",
6027
+ "url": "https://opencollective.com/unified"
6028
+ }
6029
+ },
6030
+ "node_modules/unist-util-visit-parents/node_modules/@types/unist": {
6031
+ "version": "3.0.2",
6032
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
6033
+ "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="
6034
+ },
6035
+ "node_modules/unist-util-visit/node_modules/@types/unist": {
6036
+ "version": "3.0.2",
6037
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
6038
+ "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="
6039
+ },
5847
6040
  "node_modules/unocss": {
5848
6041
  "version": "0.58.5",
5849
6042
  "resolved": "https://registry.npmjs.org/unocss/-/unocss-0.58.5.tgz",
@@ -5958,6 +6151,64 @@
5958
6151
  "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
5959
6152
  "dev": true
5960
6153
  },
6154
+ "node_modules/vfile": {
6155
+ "version": "6.0.1",
6156
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
6157
+ "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
6158
+ "dependencies": {
6159
+ "@types/unist": "^3.0.0",
6160
+ "unist-util-stringify-position": "^4.0.0",
6161
+ "vfile-message": "^4.0.0"
6162
+ },
6163
+ "funding": {
6164
+ "type": "opencollective",
6165
+ "url": "https://opencollective.com/unified"
6166
+ }
6167
+ },
6168
+ "node_modules/vfile-message": {
6169
+ "version": "2.0.4",
6170
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
6171
+ "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
6172
+ "dev": true,
6173
+ "dependencies": {
6174
+ "@types/unist": "^2.0.0",
6175
+ "unist-util-stringify-position": "^2.0.0"
6176
+ },
6177
+ "funding": {
6178
+ "type": "opencollective",
6179
+ "url": "https://opencollective.com/unified"
6180
+ }
6181
+ },
6182
+ "node_modules/vfile/node_modules/@types/unist": {
6183
+ "version": "3.0.2",
6184
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
6185
+ "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="
6186
+ },
6187
+ "node_modules/vfile/node_modules/unist-util-stringify-position": {
6188
+ "version": "4.0.0",
6189
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
6190
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
6191
+ "dependencies": {
6192
+ "@types/unist": "^3.0.0"
6193
+ },
6194
+ "funding": {
6195
+ "type": "opencollective",
6196
+ "url": "https://opencollective.com/unified"
6197
+ }
6198
+ },
6199
+ "node_modules/vfile/node_modules/vfile-message": {
6200
+ "version": "4.0.2",
6201
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
6202
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
6203
+ "dependencies": {
6204
+ "@types/unist": "^3.0.0",
6205
+ "unist-util-stringify-position": "^4.0.0"
6206
+ },
6207
+ "funding": {
6208
+ "type": "opencollective",
6209
+ "url": "https://opencollective.com/unified"
6210
+ }
6211
+ },
5961
6212
  "node_modules/vite": {
5962
6213
  "version": "5.1.3",
5963
6214
  "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz",
package.json CHANGED
@@ -17,7 +17,9 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@sentry/sveltekit": "^7.102.0",
20
- "@unocss/reset": "^0.58.5"
20
+ "@unocss/reset": "^0.58.5",
21
+ "remark-github": "^12.0.0",
22
+ "shiki": "^1.1.7"
21
23
  },
22
24
  "devDependencies": {
23
25
  "@iconify/json": "^2.2.185",
@@ -31,6 +33,7 @@
31
33
  "eslint": "^8.56.0",
32
34
  "eslint-config-prettier": "^9.1.0",
33
35
  "eslint-plugin-svelte": "^2.36.0-next.4",
36
+ "mdsvex": "^0.11.0",
34
37
  "prettier": "^3.1.1",
35
38
  "prettier-plugin-svelte": "^3.1.2",
36
39
  "svelte": "4.2.11",
src/lib/assets/posts/gopibot-to-the-rescue.md DELETED
@@ -1,139 +0,0 @@
1
- ---
2
- layout: ../../../layouts/post.astro
3
- title: Gopibot to the rescue
4
- description: A slackbot for deploying your applications (chatops)
5
- image: ../../../assets/images/gopibot.png
6
- date: October 04, 2017
7
- tags:
8
- - nodejs
9
- - slack
10
- - bot
11
- - chatops
12
- ---
13
-
14
- High Ho Gopibot away!
15
-
16
- Everybody please meet Gopibot our chatops bot which I built at Numberz to help us deploy our countless microservices to QA.
17
-
18
- ![Gopibot 1](../../../assets/images/gopibot.png)
19
-
20
- So here is the backstory,
21
- I was one of the developers who had access to our QA and Prod servers and the other person was the Head of Engineering and he is generally a busy guy.
22
- So whenever there is a change that needs to be deployed everyone comes to me and tells me to deploy their microservice/frontend to the QA and blatantly
23
- interrupts my awesome coding cycle.
24
-
25
- Alright then, I break off from my flow, ssh into the server and start running the deploy command. And all of you jsdev wannabes who have worked with
26
- react and webpack will know the horrors about deploying frontend code right. It takes forever so I have to wait there looking at the console along with
27
- the dev who wanted me to deploy it (lets call him kokill for now). So kokill and I patiently wait for the webpack build to finish. 1m , 2m, 3m and WTH
28
- 15m. And then its built and the new frontend is deployed to QA. YES! Now I can continue with my work. But wait then some other dev comes likes call him
29
- (D-Ne0) and he asks to deploy something else and again the same process of ssh’ing the server and another wait. This got repetitive and irritating. Then
30
- I started searching for solutions to the problem and looked high and low and thought that CI/CD is the only thing that can solve this problem. But then
31
- I saw something new called ChatOps where developers have chatbots to talk to automate this manual work. Just like we have bots these days to help you
32
- out in your work like getting your laundry, grocery and making orders.
33
-
34
- So I decided to take a shot at this in my free time. And it seems it was simpler than I thought and decided to use Slack our primary team communication
35
- platform. We used it daily for everything and I thought why not have a specific channel just where the bot resides and people could talk to the bot.
36
-
37
- Since we are typically a nodejs shop I decided to find a way to send messages to a slack bot. And slack has this really great sdk for nodejs.
38
- https://github.com/slackapi/node-slack-sdk
39
- First I went and created the bot in my slack team settings. And then wrote a script which would allow it to read messages from the channel it was added.
40
-
41
- Here is the simple script,
42
-
43
- ```js
44
- const RtmClient = require("@slack/client").RtmClient;
45
- const RTM_EVENTS = require("@slack/client").RTM_EVENTS;
46
- const CLIENT_EVENTS = require("@slack/client").CLIENT_EVENTS;
47
- const bot_token = process.env.SLACK_BOT_TOKEN;
48
-
49
- const rtm = new RtmClient(bot_token);
50
- const COMMANDS = {
51
- web: "ssh -i qa.pem user@url docker pull image-name && docker rm -f container-id && docker run -d image-name",
52
- };
53
- let deploymentInProgress = false;
54
- let counter = 0;
55
-
56
- rtm.on(RTM_EVENTS.MESSAGE, (event) => {
57
- console.log("Got event", event);
58
- if (
59
- (event.subtype === "message_changed" || event.subtype === "message_deleted") &&
60
- event.text &&
61
- event.text.indexOf("$slackBotId") > -1
62
- ) {
63
- return rtm.sendMessage("Please dont change the message and expect me to correct your past mistakes", event.channel);
64
- }
65
- if (event.subtype) {
66
- return;
67
- }
68
- if (event.type === "message" && event.text && event.text.indexOf("$slackBotId") > -1) {
69
- if (deploymentInProgress === true) {
70
- counter = counter + 1;
71
- if (counter > 3) {
72
- counter = 0;
73
- return rtm.sendMessage("Stop bugging me noob or I'll tell to raise you bugs", event.channel);
74
- }
75
- return rtm.sendMessage("I am already processing a deploy request please wait", event.channel);
76
- }
77
- var input = event.text.trim().replace("$slackBotId ", "");
78
- console.log("Got input", input);
79
- const arr = input.split(" ");
80
- arr.forEach((word) => {
81
- word = word.replace(/\s/g, "");
82
- });
83
- const currCommand = arr[0];
84
- if (COMMANDS.indexOf(currCommand) > -1) {
85
- rtm.sendMessage("Starting to deploy ${currCommand}", event.channel);
86
- const ssh = spawn(COMMANDS[currCommand]);
87
- deploymentInProgress = true;
88
- ssh.stdout.on("data", (data) => {
89
- rtm.sendMessage(data, event.channel);
90
- });
91
- ssh.stderr.on("data", (data) => {
92
- rtm.sendMessage(data, event.channel);
93
- });
94
- ssh.on("close", (code) => {
95
- deploymentInProgress = false;
96
- if (code === 0) {
97
- console.log("Deployed Successfully", currCommand);
98
- rtm.sendMessage("Deployed Successfully " + currCommand, event.channel);
99
- } else {
100
- console.log("child process exited with code ", code);
101
- rtm.sendMessage("child process exited with code " + code);
102
- }
103
- });
104
- return;
105
- } else {
106
- counter = counter + 1;
107
- if (counter > 3) {
108
- counter = 0;
109
- return rtm.sendMessage("Stop bugging me noob or I'll tell <@U30TXGLS1|gopi> to raise you bugs", event.channel);
110
- }
111
- return rtm.sendMessage(
112
- `command '${event.text} ' not found.You need to specify one of these commands [${COMMANDS.map((v, k) => k).join(
113
- ","
114
- )} ]`,
115
- event.channel
116
- );
117
- }
118
- }
119
- });
120
- console.log("Starting deploybot");
121
- rtm.start();
122
- ```
123
-
124
- So what the bot does is when someone mentions the bot with a command to run. It first checks if the command is defined in our COMMANDS map and then if
125
- it is, it executes the corresponding shell command for it on our QA server and then gives back progress/error/finished messages back to the channel so
126
- that everyone will be notified that someone had done a deployment. This is how it looks like,
127
-
128
- ![Gopibot 2](../../../assets/images/gopibot.png)
129
-
130
- Anyways to just have a boring bot that just runs boring commands was kinda boring. I thought of spicing up the bot interaction by making it say weird
131
- things if you keep giving it invalid commands. Making it more of a life like bot.
132
-
133
- Initially the bot was called deploybot and had a rocket icon but then there was our QA/Bug creator/Hell Raiser/Injoker in our team so I thought creating
134
- a mini him would be better and give the bot a real person’s personality and it worked and people kind a started talking to bot some random stuff and
135
- all.
136
-
137
- Further on we can maybe introduce natural language processing and deep learning to make the bot learn from our messages and not just take a single
138
- command. Like instead of me saying @gopibot cfm I can say @gopibot please deploy our cashflow server or please revert the deployment to the previous
139
- version and things like that.
src/lib/dateUtils.js ADDED
@@ -0,0 +1,5 @@
1
+ export const formatDate = (d) =>
2
+ new Intl.DateTimeFormat('en-IN').format(new Date(d)).replaceAll('/', '-');
3
+
4
+ export const formatDateLong = (d) =>
5
+ new Intl.DateTimeFormat('en-IN', { dateStyle: 'long' }).format(new Date(d));
src/{lib/assets/posts → posts}/eyecandy-golang-error-reporting.md RENAMED
@@ -1,13 +1,13 @@
1
1
  ---
2
- layout: ../../../layouts/post.astro
3
2
  title: Eyecandy golang error reporting
4
3
  description: Better error messages logging in golang
5
- image: ../../../assets/images/terminal1.png
4
+ image: /images/terminal1.png
6
- date: September 17, 2016
5
+ date: 2016-09-17
7
6
  tags:
8
7
  - golang
9
8
  - error
10
9
  - formatting
10
+ published: true
11
11
  ---
12
12
 
13
13
  We at playlyfe wanted to get an email report as soon as an error occurred on our production servers. Since golang does not have
@@ -72,7 +72,7 @@ https://github.com/maruel/panicparse/issues/8
72
72
  with the developer and +1’s we got a proper api which I could use.
73
73
  And now I haz got a prettier stack traces like this,
74
74
 
75
- ![Terminal 1](../../../assets/images/terminal1.png)
75
+ ![Terminal 1](/images/terminal1.png)
76
76
 
77
77
  So great I got ANSI coloring setup and the errors look great in our console but what about our
78
78
  mails. Of course this wasn’t going to work since emails primarily render text and HTML only, and
@@ -88,4 +88,4 @@ https://github.com/aymerick/douceur
88
88
 
89
89
  Finally after messing around with so many libraries I got around to getting it to work and this is how it looks in my email,
90
90
 
91
- ![Email 1](../../../assets/images/email1.png)
91
+ ![Email 1](/images/email1.png)
src/posts/gopibot-to-the-rescue.md ADDED
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: Gopibot to the rescue
3
+ description: A slackbot for deploying your applications (chatops)
4
+ image: /images/gopibot.png
5
+ date: 2017-04-19
6
+ tags:
7
+ - nodejs
8
+ - slack
9
+ - bot
10
+ - chatops
11
+ published: true
12
+ ---
13
+
14
+ High Ho Gopibot away!
15
+
16
+ Everybody please meet Gopibot our chatops bot which I built at Numberz to help us deploy our countless microservices to QA.
17
+
18
+ ![Gopibot 1](/images/gopibot.png)
19
+
20
+ So here is the backstory,
21
+ I was one of the developers who had access to our QA and Prod servers and the other person was the Head of Engineering and he is generally a busy guy.
22
+ So whenever there is a change that needs to be deployed everyone comes to me and tells me to deploy their microservice/frontend to the QA and blatantly
23
+ interrupts my awesome coding cycle.
24
+
25
+ Alright then, I break off from my flow, ssh into the server and start running the deploy command. And all of you jsdev wannabes who have worked with react and webpack will know the horrors about deploying frontend code right. It takes forever so I have to wait there looking at the console along with the dev who wanted me to deploy it (lets call him @kokill for now). So @kokill and I patiently wait for the webpack build to finish. 1m , 2m, 3m and WTH 15m. And then its built and the new frontend is deployed to QA. YES! Now I can continue with my work. But wait then some other dev comes likes call him (@D-Ne0) and he asks to deploy something else and again the same process of ssh’ing the server and another wait. This got repetitive and irritating.
26
+
27
+ Then I started searching for solutions to the problem and looked high and low and thought that CI/CD is the only thing that can solve this problem. But then I saw something new called ChatOps where developers have chatbots to talk to automate this manual work. Just like we have bots these days to help you out in your work like getting your laundry, grocery and making orders.
28
+
29
+ So I decided to take a shot at this in my free time. And it seems it was simpler than I thought and decided to use Slack our primary team communication platform. We used it daily for everything and I thought why not have a specific channel just where the bot resides and people could talk to the bot.
30
+
31
+ Since we are typically a nodejs shop I decided to find a way to send messages to a slack bot. And slack has this really great sdk for nodejs.
32
+ https://github.com/slackapi/node-slack-sdk
33
+ First I went and created the bot in my slack team settings. And then wrote a script which would allow it to read messages from the channel it was added.
34
+
35
+ Here is the simple script,
36
+
37
+ ```js
38
+ const RtmClient = require('@slack/client').RtmClient;
39
+ const RTM_EVENTS = require('@slack/client').RTM_EVENTS;
40
+ const CLIENT_EVENTS = require('@slack/client').CLIENT_EVENTS;
41
+ const bot_token = process.env.SLACK_BOT_TOKEN;
42
+
43
+ const rtm = new RtmClient(bot_token);
44
+ const COMMANDS = {
45
+ web: 'ssh -i qa.pem user@url docker pull image-name && docker rm -f container-id && docker run -d image-name'
46
+ };
47
+ let deploymentInProgress = false;
48
+ let counter = 0;
49
+
50
+ rtm.on(RTM_EVENTS.MESSAGE, (event) => {
51
+ console.log('Got event', event);
52
+ if (
53
+ (event.subtype === 'message_changed' || event.subtype === 'message_deleted') &&
54
+ event.text &&
55
+ event.text.indexOf('$slackBotId') > -1
56
+ ) {
57
+ return rtm.sendMessage(
58
+ 'Please dont change the message and expect me to correct your past mistakes',
59
+ event.channel
60
+ );
61
+ }
62
+ if (event.subtype) {
63
+ return;
64
+ }
65
+ if (event.type === 'message' && event.text && event.text.indexOf('$slackBotId') > -1) {
66
+ if (deploymentInProgress === true) {
67
+ counter = counter + 1;
68
+ if (counter > 3) {
69
+ counter = 0;
70
+ return rtm.sendMessage(
71
+ "Stop bugging me noob or I'll tell to raise you bugs",
72
+ event.channel
73
+ );
74
+ }
75
+ return rtm.sendMessage('I am already processing a deploy request please wait', event.channel);
76
+ }
77
+ var input = event.text.trim().replace('$slackBotId ', '');
78
+ console.log('Got input', input);
79
+ const arr = input.split(' ');
80
+ arr.forEach((word) => {
81
+ word = word.replace(/\s/g, '');
82
+ });
83
+ const currCommand = arr[0];
84
+ if (COMMANDS.indexOf(currCommand) > -1) {
85
+ rtm.sendMessage('Starting to deploy ${currCommand}', event.channel);
86
+ const ssh = spawn(COMMANDS[currCommand]);
87
+ deploymentInProgress = true;
88
+ ssh.stdout.on('data', (data) => {
89
+ rtm.sendMessage(data, event.channel);
90
+ });
91
+ ssh.stderr.on('data', (data) => {
92
+ rtm.sendMessage(data, event.channel);
93
+ });
94
+ ssh.on('close', (code) => {
95
+ deploymentInProgress = false;
96
+ if (code === 0) {
97
+ console.log('Deployed Successfully', currCommand);
98
+ rtm.sendMessage('Deployed Successfully ' + currCommand, event.channel);
99
+ } else {
100
+ console.log('child process exited with code ', code);
101
+ rtm.sendMessage('child process exited with code ' + code);
102
+ }
103
+ });
104
+ return;
105
+ } else {
106
+ counter = counter + 1;
107
+ if (counter > 3) {
108
+ counter = 0;
109
+ return rtm.sendMessage(
110
+ "Stop bugging me noob or I'll tell <@U30TXGLS1|gopi> to raise you bugs",
111
+ event.channel
112
+ );
113
+ }
114
+ return rtm.sendMessage(
115
+ `command '${event.text} ' not found.You need to specify one of these commands [${COMMANDS.map(
116
+ (v, k) => k
117
+ ).join(',')} ]`,
118
+ event.channel
119
+ );
120
+ }
121
+ }
122
+ });
123
+ console.log('Starting deploybot');
124
+ rtm.start();
125
+ ```
126
+
127
+ So what the bot does is when someone mentions the bot with a command to run. It first checks if the command is defined in our COMMANDS map and then if
128
+ it is, it executes the corresponding shell command for it on our QA server and then gives back progress/error/finished messages back to the channel so
129
+ that everyone will be notified that someone had done a deployment. This is how it looks like,
130
+
131
+ Anyways to just have a boring bot that just runs boring commands was kinda boring. I thought of spicing up the bot interaction by making it say weird
132
+ things if you keep giving it invalid commands. Making it more of a life like bot.
133
+
134
+ Initially the bot was called deploybot and had a rocket icon but then there was our QA/Bug creator/Hell Raiser/Injoker in our team so I thought creating
135
+ a mini him would be better and give the bot a real person’s personality and it worked and people kind a started talking to bot some random stuff and
136
+ all.
137
+
138
+ Further on we can maybe introduce natural language processing and deep learning to make the bot learn from our messages and not just take a single
139
+ command. Like instead of me saying @gopibot cfm I can say @gopibot please deploy our cashflow server or please revert the deployment to the previous
140
+ version and things like that.
src/routes/+layout.svelte CHANGED
@@ -2,18 +2,10 @@
2
2
  import { getStores } from '$app/stores';
3
3
  import Footer from '$lib/components/Footer.svelte';
4
4
  import Header from '$lib/components/Header.svelte';
5
- import { onMount } from 'svelte';
6
5
  // let { children } = $props();
7
6
 
8
7
  const { page } = getStores();
9
8
  let url = `https://pyros.sh${$page.url.pathname}`;
10
-
11
- let isPosts = false;
12
- onMount(() => {
13
- page.subscribe(($page) => {
14
- isPosts = $page.url.pathname.includes('posts');
15
- });
16
- });
17
9
  </script>
18
10
 
19
11
  <svelte:head>
@@ -30,10 +22,7 @@
30
22
  data-code="fynEKPntFh9oUyUcIO76Tmcxzjkmwfjg"
31
23
  ></script>
32
24
  </svelte:head>
33
- <div
34
- class:h-screen={isPosts}
35
- class="leading-8 flex flex-1 flex-col font-normal text-lg sm:leading-7 m-0"
25
+ <div class="leading-8 flex flex-1 flex-col font-normal text-lg sm:leading-7 m-0">
36
- >
37
26
  <Header />
38
27
  <main class="w-full h-full block bg-white">
39
28
  <div class="flex w-full flex-1 flex-row justify-center">
src/routes/+page.svelte CHANGED
@@ -255,7 +255,7 @@
255
255
 
256
256
  <style>
257
257
  h2 {
258
- --at-apply: text-xl font-semibold middle;
258
+ --at-apply: text-xl font-semibold;
259
259
  }
260
260
 
261
261
  section {
src/routes/posts/+page.js ADDED
@@ -0,0 +1,7 @@
1
+ export const load = async ({ params }) => {
2
+ const paths = import.meta.glob('/src/posts/*.md', { eager: true });
3
+ const posts = Object.keys(paths)
4
+ .map((key) => ({ ...paths[key].metadata, slug: key.split('/').at(-1).replace('.md', '') }))
5
+ .sort((first, second) => new Date(second.date).getTime() - new Date(first.date).getTime());
6
+ return { posts };
7
+ };
src/routes/posts/+page.svelte CHANGED
@@ -1,198 +1,27 @@
1
+ <script>
2
+ import { formatDate } from '$lib/dateUtils';
3
+ export let data;
4
+ </script>
5
+
1
6
  <svelte:head>
2
7
  <title>pyros.sh | Posts</title>
3
8
  <meta name="description" content="Peter John's Posts" />
4
9
  </svelte:head>
5
10
  <div>
6
- <div class="px-4 py-40 mx-auto">TBD</div>
11
+ <h1 class="font-bold text-3xl">Posts</h1>
12
+ <ul class="flex flex-col">
13
+ {#each data.posts as { title, description, image, date, slug, tags }}
14
+ <li class="grid grid-cols-1 sm:grid-cols-2 gap-3 justify-start items-end mt-5 leading-6">
15
+ <div class="flex flex-col">
16
+ <a class="text-black underline underline-offset-4" href={`/posts/${slug}`}>
17
+ {title}
18
+ </a>
19
+ <span class="hover:cursor-default">{description}</span>
20
+ </div>
21
+ <span class="flex flex-1 justify-end sm:ml-4 text-md text-gray-900">{formatDate(date)}</span
22
+ >
23
+ </li>
24
+ {/each}
25
+ </ul>
26
+ <div class="h-[24rem]" />
7
27
  </div>
8
-
9
- <!--
10
-
11
-
12
- // {
13
- // /* <div slot="body" class="container">
14
- // {
15
- // posts.map((post) => (
16
- // <div class="row">
17
- // <span>{post.date.toISOString().split("T")[0]}</span>
18
- // <a href={`/blog/${post.date.getFullYear()}/${post.id}`}>
19
- // {post.title}
20
- // </a>
21
- // </div>
22
- // ))
23
- // }
24
- // </div> */
25
- // }
26
- // ---
27
- // const { title, description, image, date, tags } = Astro.props.frontmatter;
28
- // ---
29
-
30
- // <title slot="head">pyros.sh | {title}</title>
31
- // <meta slot="head" name="description" content={description} />
32
- // <meta slot="head" name="keywords" content={tags} />
33
- // <div slot="body" class="post-page">
34
- // <div class="title-container">
35
- // <div>
36
- // <h1>{title}</h1>
37
- // <h2>{description}</h2>
38
- // </div>
39
- // <div class="date">
40
- // <h3>{date}</h3>
41
- // </div>
42
- // </div>
43
- // <div class="tags-container">
44
- // {tags.map((text) => <Tag text={text} />)}
45
- // </div>
46
- // <div>
47
- // <slot />
48
- // </div>
49
- // </div>
50
-
51
- // const files = await Astro.glob("./**/*.{md,mdx}");
52
- // const posts = files.map(({ frontmatter: item }) => ({
53
- // id: item.title.toLowerCase().replaceAll(" ", "-"),
54
- // title: item.title,
55
- // date: new Date(item.date),
56
- // }));
57
- // ---
58
-
59
- // <style>
60
- // .container {
61
- // display: flex;
62
- // flex-direction: column;
63
- // min-height: calc(100vh - 120px);
64
- // }
65
-
66
- // .row {
67
- // display: flex;
68
- // flex-direction: row;
69
- // align-items: center;
70
- // margin-top: 1.5rem;
71
- // line-height: 1.5rem;
72
-
73
- // & span {
74
- // width: 9rem;
75
- // }
76
-
77
- // & a {
78
- // margin-left: 2rem;
79
- // text-decoration: none;
80
- // color: black;
81
- // border-bottom: 2px solid black;
82
-
83
- // &:hover,
84
- // &:visited {
85
- // text-decoration: none;
86
- // }
87
-
88
- // @media (--mobile) {
89
- // margin-left: 0rem;
90
- // }
91
- // }
92
- // }
93
- // </style>
94
-
95
-
96
- -->
97
-
98
- <style>
99
- .post-page {
100
- display: flex;
101
- flex-direction: column;
102
- width: 100%;
103
-
104
- & code {
105
- font-family:
106
- Menlo,
107
- Monaco,
108
- Courier New,
109
- monospace;
110
- font-size: 0.9em;
111
- }
112
-
113
- & p {
114
- margin-top: 1rem;
115
- margin-bottom: 1rem;
116
- }
117
-
118
- & pre {
119
- max-width: 64rem;
120
- font-family: monospace;
121
- font-size: 14px;
122
- border-radius: 16px;
123
- padding: 16px;
124
- margin: 8px;
125
- line-height: 20px;
126
- overflow-x: auto;
127
- }
128
-
129
- & img {
130
- width: auto;
131
- @media (--mobile) {
132
- width: 100%;
133
- }
134
- }
135
-
136
- & .title-container {
137
- display: flex;
138
- flex: 1;
139
- font-family: serif;
140
- flex-direction: column;
141
-
142
- & h1 {
143
- color: var(--black-light);
144
- margin: 0;
145
- line-height: 3rem;
146
- }
147
-
148
- & h2 {
149
- color: var(--yellow-dark);
150
- font-size: 1.5rem;
151
- font-weight: 500;
152
- margin-top: 20px;
153
- margin-bottom: 20px;
154
- }
155
-
156
- & h3 {
157
- color: var(--black-light);
158
- font-size: 1.5rem;
159
- font-weight: 500;
160
- margin: 0;
161
- }
162
-
163
- & .date {
164
- display: flex;
165
- flex: 1;
166
- flex-direction: row;
167
- justify-content: flex-end;
168
- margin-right: var(--space-10);
169
-
170
- @media (--mobile) {
171
- justify-content: flex-start;
172
- margin-top: 0.5rem;
173
- margin-bottom: 0.5rem;
174
- }
175
- }
176
- }
177
-
178
- & .tags-container {
179
- margin-top: var(--space-1);
180
- margin-bottom: var(--space-1);
181
-
182
- @media (--mobile) {
183
- margin-top: var(--space-4);
184
- margin-bottom: var(--space-4);
185
- }
186
- }
187
- }
188
-
189
- .tag {
190
- background-color: var(--black-light);
191
- color: white;
192
- display: inline-block;
193
- padding-left: 8px;
194
- padding-right: 8px;
195
- text-align: center;
196
- margin-right: 1rem;
197
- }
198
- </style>
src/routes/posts/[slug]/+page.js ADDED
@@ -0,0 +1,13 @@
1
+ import { error } from '@sveltejs/kit';
2
+
3
+ export async function load({ params }) {
4
+ try {
5
+ const post = await import(`../../../posts/${params.slug}.md`);
6
+ return {
7
+ content: post.default,
8
+ meta: post.metadata
9
+ };
10
+ } catch (e) {
11
+ error(404, `Could not find ${params.slug}`);
12
+ }
13
+ }
src/routes/posts/[slug]/+page.svelte ADDED
@@ -0,0 +1,125 @@
1
+ <script>
2
+ import { formatDateLong } from '$lib/dateUtils';
3
+ export let data;
4
+ </script>
5
+
6
+ <svelte:head>
7
+ <title>pyros.sh | {data.meta.title}</title>
8
+ <meta name="description" content={data.meta.description} />
9
+ <meta name="keywords" content={data.meta.tags} />
10
+ <meta property="og:type" content="article" />
11
+ <meta property="og:title" content={data.meta.title} />
12
+ </svelte:head>
13
+
14
+ <article class="post-page">
15
+ <hgroup class="flex flex-1 flex-col font-serif">
16
+ <h1 class="font-bold text-4xl">{data.meta.title}</h1>
17
+ <p class="text-md">
18
+ <span class="font-semibold">Published on: </span>{formatDateLong(data.meta.date)}
19
+ </p>
20
+ </hgroup>
21
+ <div class="mb-6">
22
+ <span class="font-semibold">Tags:</span>
23
+ {#each data.meta.tags as tag}
24
+ <span
25
+ class="inline-flex items-center rounded-md bg-gray-100 px-2 py-1 text-base font-medium text-gray-700 ring-1 ring-inset ring-gray-500/10 mr-3"
26
+ >{tag}</span
27
+ >
28
+ {/each}
29
+ </div>
30
+ <div class="prose">
31
+ <svelte:component this={data.content} />
32
+ </div>
33
+ </article>
34
+
35
+ <style>
36
+ .post-page {
37
+ display: flex;
38
+ flex-direction: column;
39
+ width: 100%;
40
+
41
+ & code {
42
+ font-family:
43
+ Menlo,
44
+ Monaco,
45
+ Courier New,
46
+ monospace;
47
+ font-size: 0.9em;
48
+ }
49
+
50
+ & p {
51
+ margin-top: 1rem;
52
+ margin-bottom: 1rem;
53
+ }
54
+
55
+ & pre {
56
+ max-width: 64rem;
57
+ font-family: monospace;
58
+ font-size: 14px;
59
+ border-radius: 16px;
60
+ padding: 16px;
61
+ margin: 8px;
62
+ line-height: 20px;
63
+ overflow-x: auto;
64
+ }
65
+
66
+ & img {
67
+ width: auto;
68
+ @media (--mobile) {
69
+ width: 100%;
70
+ }
71
+ }
72
+
73
+ & .title-container {
74
+ display: flex;
75
+ flex: 1;
76
+ font-family: serif;
77
+ flex-direction: column;
78
+
79
+ & h1 {
80
+ color: var(--black-light);
81
+ margin: 0;
82
+ line-height: 3rem;
83
+ }
84
+
85
+ & h2 {
86
+ color: var(--yellow-dark);
87
+ font-size: 1.5rem;
88
+ font-weight: 500;
89
+ margin-top: 20px;
90
+ margin-bottom: 20px;
91
+ }
92
+
93
+ & h3 {
94
+ color: var(--black-light);
95
+ font-size: 1.5rem;
96
+ font-weight: 500;
97
+ margin: 0;
98
+ }
99
+
100
+ & .date {
101
+ display: flex;
102
+ flex: 1;
103
+ flex-direction: row;
104
+ justify-content: flex-end;
105
+ margin-right: var(--space-10);
106
+
107
+ @media (--mobile) {
108
+ justify-content: flex-start;
109
+ margin-top: 0.5rem;
110
+ margin-bottom: 0.5rem;
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ .tag {
117
+ background-color: var(--black-light);
118
+ color: white;
119
+ display: inline-block;
120
+ padding-left: 8px;
121
+ padding-right: 8px;
122
+ text-align: center;
123
+ margin-right: 1rem;
124
+ }
125
+ </style>
{src/lib/assets → static}/images/desktop.png RENAMED
File without changes
{src/lib/assets → static}/images/email1.png RENAMED
File without changes
{src/lib/assets → static}/images/gdx-studio.png RENAMED
File without changes
{src/lib/assets → static}/images/gopibot.png RENAMED
File without changes
{src/lib/assets → static}/images/gromer.png RENAMED
File without changes
{src/lib/assets → static}/images/pine.png RENAMED
File without changes
{src/lib/assets → static}/images/rust-embed.png RENAMED
File without changes
{src/lib/assets → static}/images/terminal1.png RENAMED
File without changes
svelte.config.js CHANGED
@@ -1,7 +1,37 @@
1
1
  import adapter from '@sveltejs/adapter-static';
2
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3
+ import { mdsvex, escapeSvelte } from 'mdsvex';
4
+ import github from 'remark-github';
5
+ import { getHighlighter } from 'shiki';
6
+
7
+ /** @type {import('mdsvex').MdsvexOptions} */
8
+ const mdsvexOptions = {
9
+ extensions: ['.md'],
10
+ remarkPlugins: [
11
+ [
12
+ github,
13
+ {
14
+ repository: 'https://github.com/pyrossh/pyros.sh'
15
+ }
16
+ ]
17
+ ],
18
+ highlight: {
19
+ highlighter: async (code, lang = 'text') => {
20
+ const highlighter = await getHighlighter({
21
+ themes: ['dracula'],
22
+ langs: ['javascript', 'typescript', 'go']
23
+ });
24
+ await highlighter.loadLanguage('javascript', 'typescript', 'go');
25
+ const html = escapeSvelte(highlighter.codeToHtml(code, { lang, theme: 'dracula' }));
26
+ return `{@html \`${html}\` }`;
27
+ }
28
+ }
29
+ };
2
30
 
3
31
  /** @type {import('@sveltejs/kit').Config} */
4
32
  const config = {
33
+ extensions: ['.svelte', '.md'],
34
+ preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)],
5
35
  kit: {
6
36
  adapter: adapter({
7
37
  strict: true,