From 95a5906103ff971c5eaae0efed6848558c17540f Mon Sep 17 00:00:00 2001 From: andy-polhill Date: Tue, 25 Jul 2023 00:51:51 +0000 Subject: [PATCH] deploy: 0bc35cf82226a3314f1d0d7b0ccbc4551546f63a --- blog/gatsby-comments-github-discussions-part-1/index.html | 2 +- .../gatsby-comments-github-discussions-part-1/page-data.json | 2 +- sitemap/sitemap-0.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blog/gatsby-comments-github-discussions-part-1/index.html b/blog/gatsby-comments-github-discussions-part-1/index.html index e5979e607..35cff8d6b 100644 --- a/blog/gatsby-comments-github-discussions-part-1/index.html +++ b/blog/gatsby-comments-github-discussions-part-1/index.html @@ -2,7 +2,7 @@ window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }; Andy Polhill - Gatsby comments with GitHub Discussions (part 1)

Gatsby comments with GitHub Discussions (part 1)

Andy PolhillFebruary 18, 2022


This is the first of a two part series where I talk through a recent side project that uses GitHub Discussions as a datastore for blog comments.

Part One: Focuses on getting the data into GitHub from the browser

Part Two: Focuses on pulling the new data out at build time and rebuilding the site.

Why are you doing this?

The idea of generating html files on the fly, a process we often call static site rendering, has always appealed to me. There are some real practical benefits, hosting is simple and cheap, response times are super quick, search engine optimisation is straightforward. There are of course some downsides, one of those being when you have to deal with dynamic or user generated data. A prime example being comments hosted on a blog.

For this blog I use Gatsby. It would be overkill for many blogs, however this is also where I intend to play around with a few code projects. Under the hood Gatsby harnesses the power of React and GraphQL. This provides a good toolkit for progressing beyond straight forward blogging. I use Github Actions for the build process and host the output on Github Pages. The GitHub ecosystem integrates nicely together, I also like the pace of change with new features rolling out all the time.

I'm hoping to provide a slow but steady output of blog posts. I would love to hear what people think about what I have written. -There are a load of Gatsby plugins out there for dealing with comments. Lots of them are very comprehensive and I recommend checking them out before embarking on something like this. For me, none of the plugins quite ticked all of the boxes.

These were the main critera I was looking for.

  • No Cookies: This site doesn't have any cookies and it doesn't need any, I'd like it to stay that way.
  • No Auth: I don't want people to have to login to another provider or create an account just to write a comment.
  • Full html/css control: I want the comments to look like they are a first class part of the site.
  • No Cost: The only thing I pay for on this site is cookie free analytics via Plausible.io.
  • No Extra Providers: The Github Ecosystem does most of what I need from one interface.

What is the plan?

I'd heard about people using GitHub Issues as a datastore for comments. This sounds like a neat idea, only I think it makes more sense to use GitHub Discussions, after all semantically it's more correct. If I was doing this in the work place I would do lots of upfront planning and research. The beauty of a side project is you can put all of that to one side temporarily, and get creative. I have no qualms in going straight to code and taking a little coding adventure.

My initial idea was to create a flow that looks something like this.

HTTP Request triggers Github Action via workflow dispatch
Adds new comment via GitHub GraphQL mutation
Fetch all comments via GitHub GraphQL query
New build contains comments embedded in HTML
1. User Posts a new comment
2. Github Actions: Discussion Job
3. Github Actions: Build Job
4. Github Actions: Redeploy Site

Authentication, Tokens and cloud function later

This all seemed like a relatively solid plan, you may have spotted the first flaw already. My intention was to trigger GitHub Actions via a workflow dispatch (HTTP request). In order to do this you need a valid GitHub token. GitHub tokens do not have a huge amount of granularity, putting a token in the browser could expose more than is intended.

At this point I had to compromise slightly on my "No Extra Providers" criteria. The only way I could be confident was to put a cloud function in the middle. The cloud function would effectively act as a gate keeper. It would provide some simple validation logic. If the function is happy with the comment it will append the GitHub token to the request that triggers the workflow.

The flow now has an extra step, not one that makes me happy but one that I believe is necessary.

HTTP Request sends parameters to Cloud Function
Cloud function appends the token and sends HTTP Request to GitHub Actions
Adds new comment via GitHub GraphQL mutation
Fetch all comments via GitHub GraphQL query
New build contains comments embedded in HTML
1. User Posts a new comment
1. Cloud Function: Validates
2. Github Actions: Discussion Job
3. Github Actions: Build Job
4. Github Actions: Redeploy Site

The Code

With the approach laid out it's time to look at some code. I'll keep things at a relatively high level and assume you have a baseline knowledge of JavaScript. There is plenty of opportunity to iterate or modify this approach for your own needs. I'll run through the code in the same order that we set out in the flow above.

React component.

Gatsby uses React, so lets look at creating a simple form component. There are lots of libraries available to abstract away some of the form complexity. Given this may be the only form on my site I'm opting to just use standard React. The below code will work but I have pulled out the validation and user feedback code to keep it short.

+There are a load of Gatsby plugins out there for dealing with comments. Lots of them are very comprehensive and I recommend checking them out before embarking on something like this. For me, none of the plugins quite ticked all of the boxes.

These were the main critera I was looking for.

  • No Cookies: This site doesn't have any cookies and it doesn't need any, I'd like it to stay that way.
  • No Auth: I don't want people to have to login to another provider or create an account just to write a comment.
  • Full html/css control: I want the comments to look like they are a first class part of the site.
  • No Cost: The only thing I pay for on this site is cookie free analytics via Plausible.io.
  • No Extra Providers: The Github Ecosystem does most of what I need from one interface.

What is the plan?

I'd heard about people using GitHub Issues as a datastore for comments. This sounds like a neat idea, only I think it makes more sense to use GitHub Discussions, after all semantically it's more correct. If I was doing this in the work place I would do lots of upfront planning and research. The beauty of a side project is you can put all of that to one side temporarily, and get creative. I have no qualms in going straight to code and taking a little coding adventure.

My initial idea was to create a flow that looks something like this.

HTTP Request triggers Github Action via workflow dispatch
Adds new comment via GitHub GraphQL mutation
Fetch all comments via GitHub GraphQL query
New build contains comments embedded in HTML
1. User Posts a new comment
2. Github Actions: Discussion Job
3. Github Actions: Build Job
4. Github Actions: Redeploy Site

Authentication, Tokens and cloud function later

This all seemed like a relatively solid plan, you may have spotted the first flaw already. My intention was to trigger GitHub Actions via a workflow dispatch (HTTP request). In order to do this you need a valid GitHub token. GitHub tokens do not have a huge amount of granularity, putting a token in the browser could expose more than is intended.

At this point I had to compromise slightly on my "No Extra Providers" criteria. The only way I could be confident was to put a cloud function in the middle. The cloud function would effectively act as a gate keeper. It would provide some simple validation logic. If the function is happy with the comment it will append the GitHub token to the request that triggers the workflow.

The flow now has an extra step, not one that makes me happy but one that I believe is necessary.

HTTP Request sends parameters to Cloud Function
Cloud function appends the token and sends HTTP Request to GitHub Actions
Adds new comment via GitHub GraphQL mutation
Fetch all comments via GitHub GraphQL query
New build contains comments embedded in HTML
1. User Posts a new comment
1. Cloud Function: Validates
2. Github Actions: Discussion Job
3. Github Actions: Build Job
4. Github Actions: Redeploy Site

The Code

With the approach laid out it's time to look at some code. I'll keep things at a relatively high level and assume you have a baseline knowledge of JavaScript. There is plenty of opportunity to iterate or modify this approach for your own needs. I'll run through the code in the same order that we set out in the flow above.

React component.

Gatsby uses React, so lets look at creating a simple form component. There are lots of libraries available to abstract away some of the form complexity. Given this may be the only form on my site I'm opting to just use standard React. The below code will work but I have pulled out the validation and user feedback code to keep it short.

export default function CommentForm({ discussionId }) { diff --git a/page-data/blog/gatsby-comments-github-discussions-part-1/page-data.json b/page-data/blog/gatsby-comments-github-discussions-part-1/page-data.json index 0e9192c5e..64c8f0f60 100644 --- a/page-data/blog/gatsby-comments-github-discussions-part-1/page-data.json +++ b/page-data/blog/gatsby-comments-github-discussions-part-1/page-data.json @@ -1,5 +1,5 @@ { "componentChunkName": "component---src-pages-blog-mdx-slug-js", "path": "/blog/gatsby-comments-github-discussions-part-1/", - "result": {"data":{"mdx":{"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n \"title\": \"Gatsby comments with GitHub Discussions (part 1)\",\n \"slug\": \"blog/gatsby-comments-github-discussions-part-1/\",\n \"author\": \"Andy Polhill\",\n \"date\": \"2022-02-18T00:00:00.000Z\",\n \"discussionId\": \"D_kwDOEZ2jC84AOZk_\",\n \"description\": \"Part 1 of a 2 part guide to use GitHub Discussions as datastore for your Gatsby blog comments. In this episode we focus on getting the data into GitHub.\"\n};\nvar layoutProps = {\n _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n var components = _ref.components,\n props = _objectWithoutProperties(_ref, _excluded);\n\n return mdx(MDXLayout, _extends({}, layoutProps, props, {\n components: components,\n mdxType: \"MDXLayout\"\n }), mdx(\"p\", null, \"This is the first of a two part series where I talk through a recent side project that uses \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/discussions\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub Discussions\"), \" as a datastore for blog comments.\"), mdx(\"p\", null, mdx(\"strong\", {\n parentName: \"p\"\n }, \"Part One:\"), \" Focuses on getting the data into GitHub from the browser\"), mdx(\"p\", null, mdx(\"strong\", {\n parentName: \"p\"\n }, mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"/blog/gatsby-comments-github-discussions-part-2/\"\n }, \"Part Two\"), \":\"), \" Focuses on pulling the new data out at build time and rebuilding the site.\"), mdx(\"h2\", null, \"Why are you doing this?\"), mdx(\"p\", null, \"The idea of generating html files on the fly, a process we often call \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://www.smashingmagazine.com/2020/07/differences-static-generated-sites-server-side-rendered-apps/\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"static site rendering\"), \", has always appealed to me. There are some real practical benefits, hosting is simple and cheap, response times are super quick, search engine optimisation is straightforward. There are of course some downsides, one of those being when you have to deal with dynamic or user generated data. A prime example being comments hosted on a blog.\"), mdx(\"p\", null, \"For this blog I use Gatsby. It would be overkill for many blogs, however this is also where I intend to play around with a few code projects. Under the hood Gatsby harnesses the power of React and GraphQL. This provides a good toolkit for progressing beyond straight forward blogging. I use \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://github.com/features/actions\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Github Actions\"), \" for the build process and host the output on \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://pages.github.com/\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Github Pages\"), \". The GitHub ecosystem integrates nicely together, I also like the pace of change with new features rolling out all the time.\"), mdx(\"p\", null, \"I'm hoping to provide a slow but steady output of blog posts. I would love to hear what people think about what I have written.\\nThere are a load of \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-comments/\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Gatsby plugins\"), \" out there for dealing with comments. Lots of them are very comprehensive and I recommend checking them out before embarking on something like this. For me, none of the plugins quite ticked all of the boxes.\"), mdx(\"p\", null, \"These were the main critera I was looking for.\"), mdx(\"ul\", null, mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Cookies:\"), \" This site doesn't have any cookies and it doesn't need any, I'd like it to stay that way.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Auth:\"), \" I don't want people to have to login to another provider or create an account just to write a comment.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Full html/css control:\"), \" I want the comments to look like they are a first class part of the site.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Cost:\"), \" The only thing I pay for on this site is cookie free analytics via \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"Plausible.io\"\n }, \"Plausible.io\"), \".\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Extra Providers:\"), \" The Github Ecosystem does most of what I need from one interface.\")), mdx(\"h2\", null, \"What is the plan?\"), mdx(\"p\", null, \"I'd heard about people using GitHub Issues as a datastore for comments. This sounds like a neat idea, only I think it makes more sense to use GitHub Discussions, after all semantically it's more correct. If I was doing this in the work place I would do lots of upfront planning and research. The beauty of a side project is you can put all of that to one side temporarily, and get creative. I have no qualms in going straight to code and taking a little coding adventure.\"), mdx(\"p\", null, \"My initial idea was to create a flow that looks something like this.\"), mdx(\"div\", {\n \"className\": \"mermaid\",\n \"data-processed\": \"true\"\n }, mdx(\"svg\", {\n parentName: \"div\",\n \"id\": \"mermaid-1690161675037\",\n \"width\": \"100%\",\n \"xmlns\": \"http://www.w3.org/2000/svg\",\n \"xmlnsXLink\": \"http://www.w3.org/1999/xlink\",\n \"height\": \"372\",\n \"style\": {\n \"maxWidth\": \"726.328125px\"\n },\n \"viewBox\": \"0 0 726.328125 372\"\n }, mdx(\"style\", {\n parentName: \"svg\"\n }, \"#mermaid-1690161675037 {font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#mermaid-1690161675037 .error-icon{fill:#552222;}#mermaid-1690161675037 .error-text{fill:#552222;stroke:#552222;}#mermaid-1690161675037 .edge-thickness-normal{stroke-width:2px;}#mermaid-1690161675037 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1690161675037 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1690161675037 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1690161675037 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1690161675037 .marker{fill:#666;stroke:#666;}#mermaid-1690161675037 .marker.cross{stroke:#666;}#mermaid-1690161675037 svg{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;}#mermaid-1690161675037 .label{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;color:#000000;}#mermaid-1690161675037 .cluster-label text{fill:#333;}#mermaid-1690161675037 .cluster-label span{color:#333;}#mermaid-1690161675037 .label text,#mermaid-1690161675037 span{fill:#000000;color:#000000;}#mermaid-1690161675037 .node rect,#mermaid-1690161675037 .node circle,#mermaid-1690161675037 .node ellipse,#mermaid-1690161675037 .node polygon,#mermaid-1690161675037 .node path{fill:#eee;stroke:#999;stroke-width:1px;}#mermaid-1690161675037 .node .label{text-align:center;}#mermaid-1690161675037 .node.clickable{cursor:pointer;}#mermaid-1690161675037 .arrowheadPath{fill:#333333;}#mermaid-1690161675037 .edgePath .path{stroke:#666;stroke-width:2.0px;}#mermaid-1690161675037 .flowchart-link{stroke:#666;fill:none;}#mermaid-1690161675037 .edgeLabel{background-color:white;text-align:center;}#mermaid-1690161675037 .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#mermaid-1690161675037 .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#mermaid-1690161675037 .cluster text{fill:#333;}#mermaid-1690161675037 .cluster span{color:#333;}#mermaid-1690161675037 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1690161675037 .node rect{fill:var(--color-mermaid-bg);}#mermaid-1690161675037 .node .label{font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690161675037 .edgeLabels .edgeLabel{background-color:var(--sky-bottom);font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690161675037 :root{--mermaid-font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;}\"), mdx(\"g\", {\n parentName: \"svg\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"output\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"clusters\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePaths\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-A LE-B\",\n \"id\": \"L-A-B\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M315.9239386792453,46L297.22828223270443,51.666666666666664C278.5326257861635,57.333333333333336,241.1413128930818,68.66666666666667,222.44565644654088,80C203.75,91.33333333333333,203.75,102.66666666666667,203.75,108.33333333333333L203.75,114\",\n \"markerEnd\": \"url(#arrowhead27)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead27\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-B LE-C\",\n \"id\": \"L-B-C\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M203.75,152L203.75,157.66666666666666C203.75,163.33333333333334,203.75,174.66666666666666,203.75,186C203.75,197.33333333333334,203.75,208.66666666666666,203.75,214.33333333333334L203.75,220\",\n \"markerEnd\": \"url(#arrowhead28)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead28\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-C LE-D\",\n \"id\": \"L-C-D\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M203.75,258L203.75,263.6666666666667C203.75,269.3333333333333,203.75,280.6666666666667,222.44565644654088,292C241.1413128930818,303.3333333333333,278.5326257861635,314.6666666666667,297.22828223270443,320.3333333333333L315.9239386792453,326\",\n \"markerEnd\": \"url(#arrowhead29)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead29\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-D LE-A\",\n \"id\": \"L-D-A\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M441.2948113207547,326L459.99046776729557,320.3333333333333C478.6861242138364,314.6666666666667,516.0774371069182,303.3333333333333,534.7730935534591,288.8333333333333C553.46875,274.3333333333333,553.46875,256.6666666666667,553.46875,239C553.46875,221.33333333333334,553.46875,203.66666666666666,553.46875,186C553.46875,168.33333333333334,553.46875,150.66666666666666,553.46875,133C553.46875,115.33333333333333,553.46875,97.66666666666667,534.7730935534591,83.16666666666667C516.0774371069182,68.66666666666667,478.6861242138364,57.333333333333336,459.99046776729557,51.666666666666664L441.2948113207547,46\",\n \"markerEnd\": \"url(#arrowhead30)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead30\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n }))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabels\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(203.75,80)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-195.75,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"391.5\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"391.5\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-A-B\",\n \"className\": \"edgeLabel L-LS-A' L-LE-B\"\n }, \"HTTP Request triggers Github Action via workflow dispatch\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(203.75,186)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-B-C\",\n \"className\": \"edgeLabel L-LS-B' L-LE-C\"\n }, \"Adds new comment via GitHub GraphQL mutation\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(203.75,292)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-153.296875,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-C-D\",\n \"className\": \"edgeLabel L-LS-C' L-LE-D\"\n }, \"Fetch all comments via GitHub GraphQL query\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(553.46875,186)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-D-A\",\n \"className\": \"edgeLabel L-LS-D' L-LE-A\"\n }, \"New build contains comments embedded in HTML\")))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"nodes\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-A-8\",\n \"transform\": \"translate(378.609375,27)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-116.7109375\",\n \"y\": \"-19\",\n \"width\": \"233.421875\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-106.7109375,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"213.421875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"1. User Posts a new comment\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-B-9\",\n \"transform\": \"translate(203.75,133)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-129.171875\",\n \"y\": \"-19\",\n \"width\": \"258.34375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-119.171875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"238.34375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"2. Github Actions: Discussion Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-C-11\",\n \"transform\": \"translate(203.75,239)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-108.28125\",\n \"y\": \"-19\",\n \"width\": \"216.5625\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-98.28125,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"196.5625\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"3. Github Actions: Build Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-D-13\",\n \"transform\": \"translate(378.609375,345)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-125.1796875\",\n \"y\": \"-19\",\n \"width\": \"250.359375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-115.1796875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"230.359375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"4. Github Actions: Redeploy Site\")))))))))), mdx(\"h2\", null, \"Authentication, Tokens and cloud function later\"), mdx(\"p\", null, \"This all seemed like a relatively solid plan, you may have spotted the first flaw already. My intention was to trigger GitHub Actions via a \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"workflow dispatch\"), \" (HTTP request). In order to do this you need a valid GitHub token. GitHub tokens do not have a huge \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://github.com/dear-github/dear-github/issues/113\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"amount of granularity\"), \", putting a token in the browser could expose more than is intended.\"), mdx(\"p\", null, \"At this point I had to compromise slightly on my \\\"No Extra Providers\\\" criteria. The only way I could be confident was to put a cloud function in the middle. The cloud function would effectively act as a gate keeper. It would provide some simple validation logic. If the function is happy with the comment it will append the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub token\"), \" to the request that triggers the workflow.\"), mdx(\"p\", null, \"The flow now has an extra step, not one that makes me happy but one that I believe is necessary.\"), mdx(\"div\", {\n \"className\": \"mermaid\",\n \"data-processed\": \"true\"\n }, mdx(\"svg\", {\n parentName: \"div\",\n \"id\": \"mermaid-1690161674908\",\n \"width\": \"100%\",\n \"xmlns\": \"http://www.w3.org/2000/svg\",\n \"xmlnsXLink\": \"http://www.w3.org/1999/xlink\",\n \"height\": \"478\",\n \"style\": {\n \"maxWidth\": \"762.0859375px\"\n },\n \"viewBox\": \"0 0 762.0859375 478\"\n }, mdx(\"style\", {\n parentName: \"svg\"\n }, \"#mermaid-1690161674908 {font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#mermaid-1690161674908 .error-icon{fill:#552222;}#mermaid-1690161674908 .error-text{fill:#552222;stroke:#552222;}#mermaid-1690161674908 .edge-thickness-normal{stroke-width:2px;}#mermaid-1690161674908 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1690161674908 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1690161674908 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1690161674908 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1690161674908 .marker{fill:#666;stroke:#666;}#mermaid-1690161674908 .marker.cross{stroke:#666;}#mermaid-1690161674908 svg{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;}#mermaid-1690161674908 .label{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;color:#000000;}#mermaid-1690161674908 .cluster-label text{fill:#333;}#mermaid-1690161674908 .cluster-label span{color:#333;}#mermaid-1690161674908 .label text,#mermaid-1690161674908 span{fill:#000000;color:#000000;}#mermaid-1690161674908 .node rect,#mermaid-1690161674908 .node circle,#mermaid-1690161674908 .node ellipse,#mermaid-1690161674908 .node polygon,#mermaid-1690161674908 .node path{fill:#eee;stroke:#999;stroke-width:1px;}#mermaid-1690161674908 .node .label{text-align:center;}#mermaid-1690161674908 .node.clickable{cursor:pointer;}#mermaid-1690161674908 .arrowheadPath{fill:#333333;}#mermaid-1690161674908 .edgePath .path{stroke:#666;stroke-width:2.0px;}#mermaid-1690161674908 .flowchart-link{stroke:#666;fill:none;}#mermaid-1690161674908 .edgeLabel{background-color:white;text-align:center;}#mermaid-1690161674908 .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#mermaid-1690161674908 .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#mermaid-1690161674908 .cluster text{fill:#333;}#mermaid-1690161674908 .cluster span{color:#333;}#mermaid-1690161674908 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1690161674908 .node rect{fill:var(--color-mermaid-bg);}#mermaid-1690161674908 .node .label{font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690161674908 .edgeLabels .edgeLabel{background-color:var(--sky-bottom);font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690161674908 :root{--mermaid-font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;}\"), mdx(\"g\", {\n parentName: \"svg\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"output\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"clusters\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePaths\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-A LE-B\",\n \"id\": \"L-A-B\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M365.7336379716981,46L348.1439170597484,51.666666666666664C330.55419614779873,57.333333333333336,295.37475432389937,68.66666666666667,277.78503341194966,80C260.1953125,91.33333333333333,260.1953125,102.66666666666667,260.1953125,108.33333333333333L260.1953125,114\",\n \"markerEnd\": \"url(#arrowhead35)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead35\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-B LE-C\",\n \"id\": \"L-B-C\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M260.1953125,152L260.1953125,157.66666666666666C260.1953125,163.33333333333334,260.1953125,174.66666666666666,260.1953125,186C260.1953125,197.33333333333334,260.1953125,208.66666666666666,260.1953125,214.33333333333334L260.1953125,220\",\n \"markerEnd\": \"url(#arrowhead36)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead36\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-C LE-D\",\n \"id\": \"L-C-D\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M260.1953125,258L260.1953125,263.6666666666667C260.1953125,269.3333333333333,260.1953125,280.6666666666667,260.1953125,292C260.1953125,303.3333333333333,260.1953125,314.6666666666667,260.1953125,320.3333333333333L260.1953125,326\",\n \"markerEnd\": \"url(#arrowhead37)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead37\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-D LE-E\",\n \"id\": \"L-D-E\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M260.1953125,364L260.1953125,369.6666666666667C260.1953125,375.3333333333333,260.1953125,386.6666666666667,277.78503341194966,398C295.37475432389937,409.3333333333333,330.55419614779873,420.6666666666667,348.14391705974845,426.3333333333333L365.7336379716981,432\",\n \"markerEnd\": \"url(#arrowhead38)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead38\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-E LE-A\",\n \"id\": \"L-E-A\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M483.6882370283019,432L501.27795794025155,426.3333333333333C518.8676788522013,420.6666666666667,554.0471206761007,409.3333333333333,571.6368415880503,394.8333333333333C589.2265625,380.3333333333333,589.2265625,362.6666666666667,589.2265625,345C589.2265625,327.3333333333333,589.2265625,309.6666666666667,589.2265625,292C589.2265625,274.3333333333333,589.2265625,256.6666666666667,589.2265625,239C589.2265625,221.33333333333334,589.2265625,203.66666666666666,589.2265625,186C589.2265625,168.33333333333334,589.2265625,150.66666666666666,589.2265625,133C589.2265625,115.33333333333333,589.2265625,97.66666666666667,571.6368415880503,83.16666666666667C554.0471206761007,68.66666666666667,518.8676788522013,57.333333333333336,501.2779579402516,51.666666666666664L483.6882370283019,46\",\n \"markerEnd\": \"url(#arrowhead39)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead39\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n }))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabels\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,80)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.8671875,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.734375\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.734375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-A-B\",\n \"className\": \"edgeLabel L-LS-A' L-LE-B\"\n }, \"HTTP Request sends parameters to Cloud Function\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,186)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-252.1953125,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"504.390625\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"504.390625\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-B-C\",\n \"className\": \"edgeLabel L-LS-B' L-LE-C\"\n }, \"Cloud function appends the token and sends HTTP Request to GitHub Actions\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,292)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-C-D\",\n \"className\": \"edgeLabel L-LS-C' L-LE-D\"\n }, \"Adds new comment via GitHub GraphQL mutation\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,398)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-153.296875,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-D-E\",\n \"className\": \"edgeLabel L-LS-D' L-LE-E\"\n }, \"Fetch all comments via GitHub GraphQL query\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(589.2265625,239)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-E-A\",\n \"className\": \"edgeLabel L-LS-E' L-LE-A\"\n }, \"New build contains comments embedded in HTML\")))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"nodes\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-A-10\",\n \"transform\": \"translate(424.7109375,27)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-116.7109375\",\n \"y\": \"-19\",\n \"width\": \"233.421875\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-106.7109375,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"213.421875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"1. User Posts a new comment\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-B-11\",\n \"transform\": \"translate(260.1953125,133)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-109.46875\",\n \"y\": \"-19\",\n \"width\": \"218.9375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-99.46875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"198.9375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"1. Cloud Function: Validates\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-C-13\",\n \"transform\": \"translate(260.1953125,239)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-129.171875\",\n \"y\": \"-19\",\n \"width\": \"258.34375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-119.171875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"238.34375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"2. Github Actions: Discussion Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-D-15\",\n \"transform\": \"translate(260.1953125,345)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-108.28125\",\n \"y\": \"-19\",\n \"width\": \"216.5625\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-98.28125,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"196.5625\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"3. Github Actions: Build Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-E-17\",\n \"transform\": \"translate(424.7109375,451)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-125.1796875\",\n \"y\": \"-19\",\n \"width\": \"250.359375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-115.1796875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"230.359375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"4. Github Actions: Redeploy Site\")))))))))), mdx(\"h2\", null, \"The Code\"), mdx(\"p\", null, \"With the approach laid out it's time to look at some code. I'll keep things at a relatively high level and assume you have a baseline knowledge of JavaScript. There is plenty of opportunity to iterate or modify this approach for your own needs. I'll run through the code in the same order that we set out in the flow above.\"), mdx(\"h3\", null, \"React component.\"), mdx(\"p\", null, \"Gatsby uses React, so lets look at creating a simple form component. There are lots of libraries available to abstract away some of the form complexity. Given this may be the only form on my site I'm opting to just use standard React. The below code will work but I have pulled out the validation and user feedback code to keep it short.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"js\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"\\nexport default function CommentForm({ discussionId }) {\\n\\n const [body, setBody] = useState(\\\"\\\")\\n const [author, setAuthor] = useState(\\\"\\\")\\n\\n const handleSubmit = async (evt) => {\\n evt.preventDefault()\\n\\n try {\\n await fetch(\\\"\\\", {\\n method: 'POST',\\n headers: {\\n 'Content-Type': 'application/json'\\n },\\n body: JSON.stringify({\\n body,\\n discussionId,\\n author,\\n })\\n })\\n setBody(\\\"\\\")\\n setAuthor(\\\"\\\")\\n }\\n }\\n\\n return (\\n
\\n \\n \\n \\n
\\n )\\n}\\n\\nCommentForm.propTypes = {\\n discussionId: PropTypes.string.isRequired,\\n}\\n\"), \"\\n \"), mdx(\"p\", null, \"This code uses a couple of React \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://reactjs.org/docs/hooks-state.html\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"useState\"), \" hooks to store the comment information. On submit it will post this information to the cloud function. You will see from the PropTypes that this form expects a \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"discussionId\"), \" parameter. This is what we will eventually use to tell GitHub which discussion we are posting to. For each blog post I create a new Github Discussion, currently I do this manually but you probably could automate it. In the frontmatter for the blog posts I then include the discussionId as a parameter. This is what we use to tie a blog post to a specific discussion.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"md\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\",\n \"highlight-lines\": \"6\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"---\\ntitle: Landing your first Engineering Manager role\\nslug: blog/first-engineering-manager-role/\\nauthor: Andy Polhill\\ndate: 2021-12-15\\ndiscussionId: D_kwDOEZ2jC84AOZk5\\n---\"), \"\\n \"), mdx(\"p\", null, \"Getting hold of the discussionId is a little bit tricky. The easiest way is to use the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/graphql/overview/explorer\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub GraphQL Explorer\"), \" and run a query similar to the below. The output will contain the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"id\"), \" which in this case is the discussionId that we need.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"graphql\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"query {\\n repository(name: \\\"andy-polhill.github.io\\\", owner: \\\"andy-polhill\\\") {\\n discussions(first:100) {\\n edges {\\n node {\\n id\\n }\\n }\\n }\\n }\\n}\"), \"\\n \"), mdx(\"h3\", null, \"Cloud function\"), mdx(\"p\", null, \"With the frontend code in place, lets take a look at creating that cloud function. I opted to use \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://cloud.google.com\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Google Cloud\"), \". For simplicity and given the small scale of this task I created the function via the web console.\"), mdx(\"p\", null, \"If you are the cautious type or (like me) you have been stung with an accidental cloud bill there are some simple steps to help prevent accidental spend. At the bare minumum you should:\"), mdx(\"ol\", null, mdx(\"li\", {\n parentName: \"ol\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Set a \", mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"https://cloud.google.com/functions/docs/configuring/max-instances\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"low number of maximum instances\"), \":\"), \" I'm not expecting a constant flurry of comments, although that would be nice. Setting a low number if instances reduces the risk of an undetected DDOS attach. I opted for just 2.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Set a \", mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"https://cloud.google.com/functions/docs/concepts/exec\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Low Memory Allocation\"), \":\"), \" A function with a lower memory allocated is less likely to incur a charge. We don't intend to do much in the cloud function so no need to over provision it.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Set up a \", mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"https://cloud.google.com/billing/docs/how-to/budgets\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"spending budget\"), \":\"), \" The \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://cloud.google.com/free/docs/gcp-free-tier/#cloud-functions\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"free tier for Cloud Functions\"), \" is very generous, 2 million invocations, 400,000 GB-seconds of compute time. Even the smallest of spends would imply you have recived over 2 million comments.\")), mdx(\"p\", null, \"There are plenty more \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://cloud.google.com/billing/docs/how-to/notify\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"advanced ways\"), \" of making sure you don't incur an accidental spend. Make sure you are comfortable with your setup. In my opinion the cloud providers could do more to protect people on the lower tiers who are learning how to use the service.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"js\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"const axios = require(\\\"axios\\\")\\n\\nconst owner = process.env.GITHUB_OWNER\\nconst repo = process.env.GITHUB_REPO\\nconst token = process.env.GITHUB_ACCESS_TOKEN\\nconst workflow_id = process.env.GITHUB_WORKFLOW_ID\\nconst ref = process.env.GITHUB_BRANCH\\n\\nconst github_actions_hook = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow_id}/dispatches`\\n\\nexports.receive_message = function(req, res) {\\n\\n res.set('Access-Control-Allow-Origin', 'https://andypolhill.com')\\n res.set('Access-Control-Allow-Methods', 'POST')\\n\\n if (req.method === 'OPTIONS') {\\n res.set('Access-Control-Allow-Headers', 'Content-Type')\\n res.set('Access-Control-Max-Age', '3600')\\n return res.status(204).send('')\\n }\\n\\n if (req.method === 'POST') {\\n const { author, body, discussionId } = req.body\\n\\n if(!discussionId) {\\n return res.status(422).send({\\n discussionId: \\\"missing discussionId\\\"\\n })\\n }\\n\\n if(!body) {\\n return res.status(422).send({\\n message: \\\"missing body\\\"\\n })\\n }\\n\\n if(!author) {\\n return res.status(422).send({\\n message: \\\"missing author\\\"\\n })\\n }\\n\\n return axios.post(github_actions_hook,\\n {\\n ref,\\n inputs : {\\n author,\\n body,\\n discussionId,\\n }\\n }, {\\n headers: {\\n \\\"Authorization\\\": `token ${ token }`\\n }\\n }\\n )\\n .then(() => res.status(200).send({body: \\\"sent\\\"}))\\n .catch(error => res.status(error.response.status).send({\\n ...error.response.data\\n }))\\n }\\n}\\n\"), \"\\n \"), mdx(\"ul\", null, mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Secrets:\"), \" To keep the GitHub Token out of the code I use a secret stored in the \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://cloud.google.com/secret-manager\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Secret Manager\"), \".\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \" CORS:\"), \" I haven't set up a custom domain for my cloud function. This means the browser has to make a \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://en.wikipedia.org/wiki/Cross-origin_resource_sharing\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"cross origin request\"), \" to the google domain. The Access Control headers and OPTIONS block of code cover this off.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \" Validation:\"), \" This function acts as the gate keeper for the GitHub Action. Lets make it work for us by carrying out the input validation. I've kept most of the rules out for the sake of brevity, but you get the picture.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \" Trigger the Action:\"), \" The GitHub Action is triggered by an HTTP request (curently via the \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://axios-http.com/docs/intro\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Axios\"), \" library). The format of the data that we post needs to be of the correct format. Where 'ref' is the branch and 'inputs' contains the parameters we are sending. To correctly format the URL for the workflow we need to know the 'workflowId'. GitHub actions have a consistent url for workflow dispatch \", mdx(\"inlineCode\", {\n parentName: \"li\"\n }, \"https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow_id}/dispatches\"))), mdx(\"h3\", null, \"Github Action\"), mdx(\"p\", null, \"The cloud action will trigger the GitHub workflow via the workflow dispatch. We have passed down the body, author and discussionId all the way from the React form. Luckily there is a GitHub Action we can use directly from the workflow. The \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://github.com/octokit/graphql-action\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Ocktokit GraphQL Action\"), \" allows us to apply queries and mutations to the GitHub GraphQL API. This saves us the effort of manually having to create an action to do it for us.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"yml\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"name: post-comment\\n\\non:\\n workflow_dispatch:\\n inputs:\\n author:\\n description: 'Author of the commentor'\\n required: true\\n body:\\n description: 'Body of the comment'\\n required: true\\n discussionId:\\n description: 'ID of the discussion'\\n required: true\\n\\njobs:\\n post-comment:\\n runs-on: ubuntu-latest\\n steps:\\n - uses: octokit/graphql-action@v2.x\\n id: post_discussion_comment\\n with:\\n query: |\\n mutation addDiscussionComment($discussion: ID!, $body: String!) {\\n addDiscussionComment(input: {discussionId: $discussion, body: $body}) {\\n comment {\\n id\\n }\\n }\\n }\\n discussion: \\\"'${{github.event.inputs.discussionId}}'\\\"\\n body: |\\n ---\\n author: ${{github.event.inputs.author}}\\n ---\\n ${{github.event.inputs.body}}\\\"'\\n env:\\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\"), \"\\n \"), mdx(\"p\", null, \"They key part of this action is the mutation and the inputs that are passed to it. The \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/graphql/overview/explorer\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub GraphQL Explorer\"), \" is a great way to test and debug queries and mutations.\"), mdx(\"p\", null, \"At this point we stumble upon a new problem. As stated from the outset I don't want people to have to login. Using this flow the discussion comment is made by whoever authenticated the GitHub token, In this case it is the GitHub Actions bot user. We can't just set an Author field on the mutation as the user isn't the one logged in. What we can do instead is record it using frontmatter formatting in the body of the comment. Similar to what we do for blog posts in \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://www.gatsbyjs.com/docs/how-to/routing/adding-markdown-pages/#frontmatter-for-metadata-in-markdown-files\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Gatsby\"), \". It provides a nice flexible solution as you can add as many additional fields as you want. In the second post we will look at extracting the frontmatter back out.\"), mdx(\"h2\", null, \"Summary\"), mdx(\"p\", null, \"That is the first part done, we are now able to get comments added to a GitHub discussion. A quick recap of how we did that.\"), mdx(\"ol\", null, mdx(\"li\", {\n parentName: \"ol\"\n }, \"We added a discussion Id to each blog post.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"We added a comment form which posts the comment, author and discussion ID to a cloud function.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The cloud function carries out some basic validation, and triggers a GitHub Actions workflow by attached a securely stored token.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The GitHub Actions workflow recives the information and triggers a GraphQL mutation to update the discussion.\")));\n}\n;\nMDXContent.isMDXComponent = true;","comments":[{"body":"Thanks for sharing your story, it's great to hear about somebody else's experiences of changing roles.","date":"September 05, 2022","author":"Yasmin","url":null},{"body":"If you're not getting some rejections, you're not pushing yourself!","date":"October 11, 2022","author":"Aidan","url":null}],"frontmatter":{"date":"February 18, 2022","description":"Part 1 of a 2 part guide to use GitHub Discussions as datastore for your Gatsby blog comments. In this episode we focus on getting the data into GitHub.","author":"Andy Polhill","slug":"blog/gatsby-comments-github-discussions-part-1/","title":"Gatsby comments with GitHub Discussions (part 1)","discussionId":"D_kwDOEZ2jC84AOZk_","inlineImages":null}}},"pageContext":{"id":"2a1c4418-5896-55ae-aa85-dc4dc10e5dd6","slug":"gatsby-comments-github-discussions-part-1/","__params":{"slug":"gatsby-comments-github-discussions-part-1"}}}, + "result": {"data":{"mdx":{"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n \"title\": \"Gatsby comments with GitHub Discussions (part 1)\",\n \"slug\": \"blog/gatsby-comments-github-discussions-part-1/\",\n \"author\": \"Andy Polhill\",\n \"date\": \"2022-02-18T00:00:00.000Z\",\n \"discussionId\": \"D_kwDOEZ2jC84AOZk_\",\n \"description\": \"Part 1 of a 2 part guide to use GitHub Discussions as datastore for your Gatsby blog comments. In this episode we focus on getting the data into GitHub.\"\n};\nvar layoutProps = {\n _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n var components = _ref.components,\n props = _objectWithoutProperties(_ref, _excluded);\n\n return mdx(MDXLayout, _extends({}, layoutProps, props, {\n components: components,\n mdxType: \"MDXLayout\"\n }), mdx(\"p\", null, \"This is the first of a two part series where I talk through a recent side project that uses \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/discussions\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub Discussions\"), \" as a datastore for blog comments.\"), mdx(\"p\", null, mdx(\"strong\", {\n parentName: \"p\"\n }, \"Part One:\"), \" Focuses on getting the data into GitHub from the browser\"), mdx(\"p\", null, mdx(\"strong\", {\n parentName: \"p\"\n }, mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"/blog/gatsby-comments-github-discussions-part-2/\"\n }, \"Part Two\"), \":\"), \" Focuses on pulling the new data out at build time and rebuilding the site.\"), mdx(\"h2\", null, \"Why are you doing this?\"), mdx(\"p\", null, \"The idea of generating html files on the fly, a process we often call \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://www.smashingmagazine.com/2020/07/differences-static-generated-sites-server-side-rendered-apps/\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"static site rendering\"), \", has always appealed to me. There are some real practical benefits, hosting is simple and cheap, response times are super quick, search engine optimisation is straightforward. There are of course some downsides, one of those being when you have to deal with dynamic or user generated data. A prime example being comments hosted on a blog.\"), mdx(\"p\", null, \"For this blog I use Gatsby. It would be overkill for many blogs, however this is also where I intend to play around with a few code projects. Under the hood Gatsby harnesses the power of React and GraphQL. This provides a good toolkit for progressing beyond straight forward blogging. I use \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://github.com/features/actions\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Github Actions\"), \" for the build process and host the output on \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://pages.github.com/\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Github Pages\"), \". The GitHub ecosystem integrates nicely together, I also like the pace of change with new features rolling out all the time.\"), mdx(\"p\", null, \"I'm hoping to provide a slow but steady output of blog posts. I would love to hear what people think about what I have written.\\nThere are a load of \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-comments/\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Gatsby plugins\"), \" out there for dealing with comments. Lots of them are very comprehensive and I recommend checking them out before embarking on something like this. For me, none of the plugins quite ticked all of the boxes.\"), mdx(\"p\", null, \"These were the main critera I was looking for.\"), mdx(\"ul\", null, mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Cookies:\"), \" This site doesn't have any cookies and it doesn't need any, I'd like it to stay that way.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Auth:\"), \" I don't want people to have to login to another provider or create an account just to write a comment.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Full html/css control:\"), \" I want the comments to look like they are a first class part of the site.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Cost:\"), \" The only thing I pay for on this site is cookie free analytics via \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"Plausible.io\"\n }, \"Plausible.io\"), \".\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"No Extra Providers:\"), \" The Github Ecosystem does most of what I need from one interface.\")), mdx(\"h2\", null, \"What is the plan?\"), mdx(\"p\", null, \"I'd heard about people using GitHub Issues as a datastore for comments. This sounds like a neat idea, only I think it makes more sense to use GitHub Discussions, after all semantically it's more correct. If I was doing this in the work place I would do lots of upfront planning and research. The beauty of a side project is you can put all of that to one side temporarily, and get creative. I have no qualms in going straight to code and taking a little coding adventure.\"), mdx(\"p\", null, \"My initial idea was to create a flow that looks something like this.\"), mdx(\"div\", {\n \"className\": \"mermaid\",\n \"data-processed\": \"true\"\n }, mdx(\"svg\", {\n parentName: \"div\",\n \"id\": \"mermaid-1690246236329\",\n \"width\": \"100%\",\n \"xmlns\": \"http://www.w3.org/2000/svg\",\n \"xmlnsXLink\": \"http://www.w3.org/1999/xlink\",\n \"height\": \"372\",\n \"style\": {\n \"maxWidth\": \"726.328125px\"\n },\n \"viewBox\": \"0 0 726.328125 372\"\n }, mdx(\"style\", {\n parentName: \"svg\"\n }, \"#mermaid-1690246236329 {font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#mermaid-1690246236329 .error-icon{fill:#552222;}#mermaid-1690246236329 .error-text{fill:#552222;stroke:#552222;}#mermaid-1690246236329 .edge-thickness-normal{stroke-width:2px;}#mermaid-1690246236329 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1690246236329 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1690246236329 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1690246236329 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1690246236329 .marker{fill:#666;stroke:#666;}#mermaid-1690246236329 .marker.cross{stroke:#666;}#mermaid-1690246236329 svg{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;}#mermaid-1690246236329 .label{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;color:#000000;}#mermaid-1690246236329 .cluster-label text{fill:#333;}#mermaid-1690246236329 .cluster-label span{color:#333;}#mermaid-1690246236329 .label text,#mermaid-1690246236329 span{fill:#000000;color:#000000;}#mermaid-1690246236329 .node rect,#mermaid-1690246236329 .node circle,#mermaid-1690246236329 .node ellipse,#mermaid-1690246236329 .node polygon,#mermaid-1690246236329 .node path{fill:#eee;stroke:#999;stroke-width:1px;}#mermaid-1690246236329 .node .label{text-align:center;}#mermaid-1690246236329 .node.clickable{cursor:pointer;}#mermaid-1690246236329 .arrowheadPath{fill:#333333;}#mermaid-1690246236329 .edgePath .path{stroke:#666;stroke-width:2.0px;}#mermaid-1690246236329 .flowchart-link{stroke:#666;fill:none;}#mermaid-1690246236329 .edgeLabel{background-color:white;text-align:center;}#mermaid-1690246236329 .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#mermaid-1690246236329 .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#mermaid-1690246236329 .cluster text{fill:#333;}#mermaid-1690246236329 .cluster span{color:#333;}#mermaid-1690246236329 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1690246236329 .node rect{fill:var(--color-mermaid-bg);}#mermaid-1690246236329 .node .label{font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690246236329 .edgeLabels .edgeLabel{background-color:var(--sky-bottom);font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690246236329 :root{--mermaid-font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;}\"), mdx(\"g\", {\n parentName: \"svg\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"output\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"clusters\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePaths\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-A LE-B\",\n \"id\": \"L-A-B\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M315.9239386792453,46L297.22828223270443,51.666666666666664C278.5326257861635,57.333333333333336,241.1413128930818,68.66666666666667,222.44565644654088,80C203.75,91.33333333333333,203.75,102.66666666666667,203.75,108.33333333333333L203.75,114\",\n \"markerEnd\": \"url(#arrowhead27)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead27\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-B LE-C\",\n \"id\": \"L-B-C\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M203.75,152L203.75,157.66666666666666C203.75,163.33333333333334,203.75,174.66666666666666,203.75,186C203.75,197.33333333333334,203.75,208.66666666666666,203.75,214.33333333333334L203.75,220\",\n \"markerEnd\": \"url(#arrowhead28)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead28\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-C LE-D\",\n \"id\": \"L-C-D\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M203.75,258L203.75,263.6666666666667C203.75,269.3333333333333,203.75,280.6666666666667,222.44565644654088,292C241.1413128930818,303.3333333333333,278.5326257861635,314.6666666666667,297.22828223270443,320.3333333333333L315.9239386792453,326\",\n \"markerEnd\": \"url(#arrowhead29)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead29\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-D LE-A\",\n \"id\": \"L-D-A\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M441.2948113207547,326L459.99046776729557,320.3333333333333C478.6861242138364,314.6666666666667,516.0774371069182,303.3333333333333,534.7730935534591,288.8333333333333C553.46875,274.3333333333333,553.46875,256.6666666666667,553.46875,239C553.46875,221.33333333333334,553.46875,203.66666666666666,553.46875,186C553.46875,168.33333333333334,553.46875,150.66666666666666,553.46875,133C553.46875,115.33333333333333,553.46875,97.66666666666667,534.7730935534591,83.16666666666667C516.0774371069182,68.66666666666667,478.6861242138364,57.333333333333336,459.99046776729557,51.666666666666664L441.2948113207547,46\",\n \"markerEnd\": \"url(#arrowhead30)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead30\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n }))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabels\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(203.75,80)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-195.75,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"391.5\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"391.5\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-A-B\",\n \"className\": \"edgeLabel L-LS-A' L-LE-B\"\n }, \"HTTP Request triggers Github Action via workflow dispatch\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(203.75,186)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-B-C\",\n \"className\": \"edgeLabel L-LS-B' L-LE-C\"\n }, \"Adds new comment via GitHub GraphQL mutation\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(203.75,292)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-153.296875,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-C-D\",\n \"className\": \"edgeLabel L-LS-C' L-LE-D\"\n }, \"Fetch all comments via GitHub GraphQL query\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(553.46875,186)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-D-A\",\n \"className\": \"edgeLabel L-LS-D' L-LE-A\"\n }, \"New build contains comments embedded in HTML\")))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"nodes\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-A-8\",\n \"transform\": \"translate(378.609375,27)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-116.7109375\",\n \"y\": \"-19\",\n \"width\": \"233.421875\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-106.7109375,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"213.421875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"1. User Posts a new comment\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-B-9\",\n \"transform\": \"translate(203.75,133)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-129.171875\",\n \"y\": \"-19\",\n \"width\": \"258.34375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-119.171875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"238.34375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"2. Github Actions: Discussion Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-C-11\",\n \"transform\": \"translate(203.75,239)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-108.28125\",\n \"y\": \"-19\",\n \"width\": \"216.5625\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-98.28125,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"196.5625\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"3. Github Actions: Build Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-D-13\",\n \"transform\": \"translate(378.609375,345)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-125.1796875\",\n \"y\": \"-19\",\n \"width\": \"250.359375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-115.1796875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"230.359375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"4. Github Actions: Redeploy Site\")))))))))), mdx(\"h2\", null, \"Authentication, Tokens and cloud function later\"), mdx(\"p\", null, \"This all seemed like a relatively solid plan, you may have spotted the first flaw already. My intention was to trigger GitHub Actions via a \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"workflow dispatch\"), \" (HTTP request). In order to do this you need a valid GitHub token. GitHub tokens do not have a huge \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://github.com/dear-github/dear-github/issues/113\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"amount of granularity\"), \", putting a token in the browser could expose more than is intended.\"), mdx(\"p\", null, \"At this point I had to compromise slightly on my \\\"No Extra Providers\\\" criteria. The only way I could be confident was to put a cloud function in the middle. The cloud function would effectively act as a gate keeper. It would provide some simple validation logic. If the function is happy with the comment it will append the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub token\"), \" to the request that triggers the workflow.\"), mdx(\"p\", null, \"The flow now has an extra step, not one that makes me happy but one that I believe is necessary.\"), mdx(\"div\", {\n \"className\": \"mermaid\",\n \"data-processed\": \"true\"\n }, mdx(\"svg\", {\n parentName: \"div\",\n \"id\": \"mermaid-1690246236562\",\n \"width\": \"100%\",\n \"xmlns\": \"http://www.w3.org/2000/svg\",\n \"xmlnsXLink\": \"http://www.w3.org/1999/xlink\",\n \"height\": \"478\",\n \"style\": {\n \"maxWidth\": \"762.0859375px\"\n },\n \"viewBox\": \"0 0 762.0859375 478\"\n }, mdx(\"style\", {\n parentName: \"svg\"\n }, \"#mermaid-1690246236562 {font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#mermaid-1690246236562 .error-icon{fill:#552222;}#mermaid-1690246236562 .error-text{fill:#552222;stroke:#552222;}#mermaid-1690246236562 .edge-thickness-normal{stroke-width:2px;}#mermaid-1690246236562 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1690246236562 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1690246236562 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1690246236562 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1690246236562 .marker{fill:#666;stroke:#666;}#mermaid-1690246236562 .marker.cross{stroke:#666;}#mermaid-1690246236562 svg{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:16px;}#mermaid-1690246236562 .label{font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;color:#000000;}#mermaid-1690246236562 .cluster-label text{fill:#333;}#mermaid-1690246236562 .cluster-label span{color:#333;}#mermaid-1690246236562 .label text,#mermaid-1690246236562 span{fill:#000000;color:#000000;}#mermaid-1690246236562 .node rect,#mermaid-1690246236562 .node circle,#mermaid-1690246236562 .node ellipse,#mermaid-1690246236562 .node polygon,#mermaid-1690246236562 .node path{fill:#eee;stroke:#999;stroke-width:1px;}#mermaid-1690246236562 .node .label{text-align:center;}#mermaid-1690246236562 .node.clickable{cursor:pointer;}#mermaid-1690246236562 .arrowheadPath{fill:#333333;}#mermaid-1690246236562 .edgePath .path{stroke:#666;stroke-width:2.0px;}#mermaid-1690246236562 .flowchart-link{stroke:#666;fill:none;}#mermaid-1690246236562 .edgeLabel{background-color:white;text-align:center;}#mermaid-1690246236562 .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#mermaid-1690246236562 .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#mermaid-1690246236562 .cluster text{fill:#333;}#mermaid-1690246236562 .cluster span{color:#333;}#mermaid-1690246236562 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1690246236562 .node rect{fill:var(--color-mermaid-bg);}#mermaid-1690246236562 .node .label{font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690246236562 .edgeLabels .edgeLabel{background-color:var(--sky-bottom);font-family:\\\"Libre Baskeville\\\",Georgia,serif;color:var(--color-body);}#mermaid-1690246236562 :root{--mermaid-font-family:\\\"trebuchet ms\\\",verdana,arial,sans-serif;}\"), mdx(\"g\", {\n parentName: \"svg\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"output\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"clusters\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePaths\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-A LE-B\",\n \"id\": \"L-A-B\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M365.7336379716981,46L348.1439170597484,51.666666666666664C330.55419614779873,57.333333333333336,295.37475432389937,68.66666666666667,277.78503341194966,80C260.1953125,91.33333333333333,260.1953125,102.66666666666667,260.1953125,108.33333333333333L260.1953125,114\",\n \"markerEnd\": \"url(#arrowhead35)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead35\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-B LE-C\",\n \"id\": \"L-B-C\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M260.1953125,152L260.1953125,157.66666666666666C260.1953125,163.33333333333334,260.1953125,174.66666666666666,260.1953125,186C260.1953125,197.33333333333334,260.1953125,208.66666666666666,260.1953125,214.33333333333334L260.1953125,220\",\n \"markerEnd\": \"url(#arrowhead36)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead36\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-C LE-D\",\n \"id\": \"L-C-D\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M260.1953125,258L260.1953125,263.6666666666667C260.1953125,269.3333333333333,260.1953125,280.6666666666667,260.1953125,292C260.1953125,303.3333333333333,260.1953125,314.6666666666667,260.1953125,320.3333333333333L260.1953125,326\",\n \"markerEnd\": \"url(#arrowhead37)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead37\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-D LE-E\",\n \"id\": \"L-D-E\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M260.1953125,364L260.1953125,369.6666666666667C260.1953125,375.3333333333333,260.1953125,386.6666666666667,277.78503341194966,398C295.37475432389937,409.3333333333333,330.55419614779873,420.6666666666667,348.14391705974845,426.3333333333333L365.7336379716981,432\",\n \"markerEnd\": \"url(#arrowhead38)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead38\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n })))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgePath LS-E LE-A\",\n \"id\": \"L-E-A\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"path\", {\n parentName: \"g\",\n \"className\": \"path\",\n \"d\": \"M483.6882370283019,432L501.27795794025155,426.3333333333333C518.8676788522013,420.6666666666667,554.0471206761007,409.3333333333333,571.6368415880503,394.8333333333333C589.2265625,380.3333333333333,589.2265625,362.6666666666667,589.2265625,345C589.2265625,327.3333333333333,589.2265625,309.6666666666667,589.2265625,292C589.2265625,274.3333333333333,589.2265625,256.6666666666667,589.2265625,239C589.2265625,221.33333333333334,589.2265625,203.66666666666666,589.2265625,186C589.2265625,168.33333333333334,589.2265625,150.66666666666666,589.2265625,133C589.2265625,115.33333333333333,589.2265625,97.66666666666667,571.6368415880503,83.16666666666667C554.0471206761007,68.66666666666667,518.8676788522013,57.333333333333336,501.2779579402516,51.666666666666664L483.6882370283019,46\",\n \"markerEnd\": \"url(#arrowhead39)\",\n \"style\": {\n \"fill\": \"none\"\n }\n }), mdx(\"defs\", {\n parentName: \"g\"\n }, mdx(\"marker\", {\n parentName: \"defs\",\n \"id\": \"arrowhead39\",\n \"viewBox\": \"0 0 10 10\",\n \"refX\": \"9\",\n \"refY\": \"5\",\n \"markerUnits\": \"strokeWidth\",\n \"markerWidth\": \"8\",\n \"markerHeight\": \"6\",\n \"orient\": \"auto\"\n }, mdx(\"path\", {\n parentName: \"marker\",\n \"d\": \"M 0 0 L 10 5 L 0 10 z\",\n \"className\": \"arrowheadPath\",\n \"style\": {\n \"strokeWidth\": \"1\",\n \"strokeDasharray\": \"1, 0\"\n }\n }))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabels\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,80)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.8671875,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.734375\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.734375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-A-B\",\n \"className\": \"edgeLabel L-LS-A' L-LE-B\"\n }, \"HTTP Request sends parameters to Cloud Function\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,186)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-252.1953125,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"504.390625\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"504.390625\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-B-C\",\n \"className\": \"edgeLabel L-LS-B' L-LE-C\"\n }, \"Cloud function appends the token and sends HTTP Request to GitHub Actions\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,292)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-C-D\",\n \"className\": \"edgeLabel L-LS-C' L-LE-D\"\n }, \"Adds new comment via GitHub GraphQL mutation\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(260.1953125,398)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-153.296875,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"306.59375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-D-E\",\n \"className\": \"edgeLabel L-LS-D' L-LE-E\"\n }, \"Fetch all comments via GitHub GraphQL query\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"edgeLabel\",\n \"transform\": \"translate(589.2265625,239)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-164.859375,-9)\",\n \"className\": \"label\"\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"0\",\n \"ry\": \"0\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }), mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"329.71875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, mdx(\"span\", {\n parentName: \"div\",\n \"id\": \"L-L-E-A\",\n \"className\": \"edgeLabel L-LS-E' L-LE-A\"\n }, \"New build contains comments embedded in HTML\")))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"nodes\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-A-10\",\n \"transform\": \"translate(424.7109375,27)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-116.7109375\",\n \"y\": \"-19\",\n \"width\": \"233.421875\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-106.7109375,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"213.421875\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"1. User Posts a new comment\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-B-11\",\n \"transform\": \"translate(260.1953125,133)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-109.46875\",\n \"y\": \"-19\",\n \"width\": \"218.9375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-99.46875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"198.9375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"1. Cloud Function: Validates\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-C-13\",\n \"transform\": \"translate(260.1953125,239)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-129.171875\",\n \"y\": \"-19\",\n \"width\": \"258.34375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-119.171875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"238.34375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"2. Github Actions: Discussion Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-D-15\",\n \"transform\": \"translate(260.1953125,345)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-108.28125\",\n \"y\": \"-19\",\n \"width\": \"216.5625\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-98.28125,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"196.5625\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"3. Github Actions: Build Job\"))))), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"node default\",\n \"id\": \"flowchart-E-17\",\n \"transform\": \"translate(424.7109375,451)\",\n \"style\": {\n \"opacity\": \"1\"\n }\n }, mdx(\"rect\", {\n parentName: \"g\",\n \"rx\": \"5\",\n \"ry\": \"5\",\n \"x\": \"-125.1796875\",\n \"y\": \"-19\",\n \"width\": \"250.359375\",\n \"height\": \"38\",\n \"className\": \"label-container\"\n }), mdx(\"g\", {\n parentName: \"g\",\n \"className\": \"label\",\n \"transform\": \"translate(0,0)\"\n }, mdx(\"g\", {\n parentName: \"g\",\n \"transform\": \"translate(-115.1796875,-9)\"\n }, mdx(\"foreignObject\", {\n parentName: \"g\",\n \"width\": \"230.359375\",\n \"height\": \"18\"\n }, mdx(\"div\", {\n parentName: \"foreignObject\",\n \"xmlns\": \"http://www.w3.org/1999/xhtml\",\n \"style\": {\n \"display\": \"inline-block\",\n \"whiteSpace\": \"nowrap\"\n }\n }, \"4. Github Actions: Redeploy Site\")))))))))), mdx(\"h2\", null, \"The Code\"), mdx(\"p\", null, \"With the approach laid out it's time to look at some code. I'll keep things at a relatively high level and assume you have a baseline knowledge of JavaScript. There is plenty of opportunity to iterate or modify this approach for your own needs. I'll run through the code in the same order that we set out in the flow above.\"), mdx(\"h3\", null, \"React component.\"), mdx(\"p\", null, \"Gatsby uses React, so lets look at creating a simple form component. There are lots of libraries available to abstract away some of the form complexity. Given this may be the only form on my site I'm opting to just use standard React. The below code will work but I have pulled out the validation and user feedback code to keep it short.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"js\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"\\nexport default function CommentForm({ discussionId }) {\\n\\n const [body, setBody] = useState(\\\"\\\")\\n const [author, setAuthor] = useState(\\\"\\\")\\n\\n const handleSubmit = async (evt) => {\\n evt.preventDefault()\\n\\n try {\\n await fetch(\\\"\\\", {\\n method: 'POST',\\n headers: {\\n 'Content-Type': 'application/json'\\n },\\n body: JSON.stringify({\\n body,\\n discussionId,\\n author,\\n })\\n })\\n setBody(\\\"\\\")\\n setAuthor(\\\"\\\")\\n }\\n }\\n\\n return (\\n
\\n \\n \\n \\n
\\n )\\n}\\n\\nCommentForm.propTypes = {\\n discussionId: PropTypes.string.isRequired,\\n}\\n\"), \"\\n \"), mdx(\"p\", null, \"This code uses a couple of React \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://reactjs.org/docs/hooks-state.html\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"useState\"), \" hooks to store the comment information. On submit it will post this information to the cloud function. You will see from the PropTypes that this form expects a \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"discussionId\"), \" parameter. This is what we will eventually use to tell GitHub which discussion we are posting to. For each blog post I create a new Github Discussion, currently I do this manually but you probably could automate it. In the frontmatter for the blog posts I then include the discussionId as a parameter. This is what we use to tie a blog post to a specific discussion.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"md\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\",\n \"highlight-lines\": \"6\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"---\\ntitle: Landing your first Engineering Manager role\\nslug: blog/first-engineering-manager-role/\\nauthor: Andy Polhill\\ndate: 2021-12-15\\ndiscussionId: D_kwDOEZ2jC84AOZk5\\n---\"), \"\\n \"), mdx(\"p\", null, \"Getting hold of the discussionId is a little bit tricky. The easiest way is to use the \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/graphql/overview/explorer\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub GraphQL Explorer\"), \" and run a query similar to the below. The output will contain the \", mdx(\"inlineCode\", {\n parentName: \"p\"\n }, \"id\"), \" which in this case is the discussionId that we need.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"graphql\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"query {\\n repository(name: \\\"andy-polhill.github.io\\\", owner: \\\"andy-polhill\\\") {\\n discussions(first:100) {\\n edges {\\n node {\\n id\\n }\\n }\\n }\\n }\\n}\"), \"\\n \"), mdx(\"h3\", null, \"Cloud function\"), mdx(\"p\", null, \"With the frontend code in place, lets take a look at creating that cloud function. I opted to use \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://cloud.google.com\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Google Cloud\"), \". For simplicity and given the small scale of this task I created the function via the web console.\"), mdx(\"p\", null, \"If you are the cautious type or (like me) you have been stung with an accidental cloud bill there are some simple steps to help prevent accidental spend. At the bare minumum you should:\"), mdx(\"ol\", null, mdx(\"li\", {\n parentName: \"ol\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Set a \", mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"https://cloud.google.com/functions/docs/configuring/max-instances\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"low number of maximum instances\"), \":\"), \" I'm not expecting a constant flurry of comments, although that would be nice. Setting a low number if instances reduces the risk of an undetected DDOS attach. I opted for just 2.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Set a \", mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"https://cloud.google.com/functions/docs/concepts/exec\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Low Memory Allocation\"), \":\"), \" A function with a lower memory allocated is less likely to incur a charge. We don't intend to do much in the cloud function so no need to over provision it.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Set up a \", mdx(\"a\", {\n parentName: \"strong\",\n \"href\": \"https://cloud.google.com/billing/docs/how-to/budgets\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"spending budget\"), \":\"), \" The \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://cloud.google.com/free/docs/gcp-free-tier/#cloud-functions\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"free tier for Cloud Functions\"), \" is very generous, 2 million invocations, 400,000 GB-seconds of compute time. Even the smallest of spends would imply you have recived over 2 million comments.\")), mdx(\"p\", null, \"There are plenty more \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://cloud.google.com/billing/docs/how-to/notify\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"advanced ways\"), \" of making sure you don't incur an accidental spend. Make sure you are comfortable with your setup. In my opinion the cloud providers could do more to protect people on the lower tiers who are learning how to use the service.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"js\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"const axios = require(\\\"axios\\\")\\n\\nconst owner = process.env.GITHUB_OWNER\\nconst repo = process.env.GITHUB_REPO\\nconst token = process.env.GITHUB_ACCESS_TOKEN\\nconst workflow_id = process.env.GITHUB_WORKFLOW_ID\\nconst ref = process.env.GITHUB_BRANCH\\n\\nconst github_actions_hook = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow_id}/dispatches`\\n\\nexports.receive_message = function(req, res) {\\n\\n res.set('Access-Control-Allow-Origin', 'https://andypolhill.com')\\n res.set('Access-Control-Allow-Methods', 'POST')\\n\\n if (req.method === 'OPTIONS') {\\n res.set('Access-Control-Allow-Headers', 'Content-Type')\\n res.set('Access-Control-Max-Age', '3600')\\n return res.status(204).send('')\\n }\\n\\n if (req.method === 'POST') {\\n const { author, body, discussionId } = req.body\\n\\n if(!discussionId) {\\n return res.status(422).send({\\n discussionId: \\\"missing discussionId\\\"\\n })\\n }\\n\\n if(!body) {\\n return res.status(422).send({\\n message: \\\"missing body\\\"\\n })\\n }\\n\\n if(!author) {\\n return res.status(422).send({\\n message: \\\"missing author\\\"\\n })\\n }\\n\\n return axios.post(github_actions_hook,\\n {\\n ref,\\n inputs : {\\n author,\\n body,\\n discussionId,\\n }\\n }, {\\n headers: {\\n \\\"Authorization\\\": `token ${ token }`\\n }\\n }\\n )\\n .then(() => res.status(200).send({body: \\\"sent\\\"}))\\n .catch(error => res.status(error.response.status).send({\\n ...error.response.data\\n }))\\n }\\n}\\n\"), \"\\n \"), mdx(\"ul\", null, mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \"Secrets:\"), \" To keep the GitHub Token out of the code I use a secret stored in the \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://cloud.google.com/secret-manager\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Secret Manager\"), \".\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \" CORS:\"), \" I haven't set up a custom domain for my cloud function. This means the browser has to make a \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://en.wikipedia.org/wiki/Cross-origin_resource_sharing\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"cross origin request\"), \" to the google domain. The Access Control headers and OPTIONS block of code cover this off.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \" Validation:\"), \" This function acts as the gate keeper for the GitHub Action. Lets make it work for us by carrying out the input validation. I've kept most of the rules out for the sake of brevity, but you get the picture.\"), mdx(\"li\", {\n parentName: \"ul\"\n }, mdx(\"strong\", {\n parentName: \"li\"\n }, \" Trigger the Action:\"), \" The GitHub Action is triggered by an HTTP request (curently via the \", mdx(\"a\", {\n parentName: \"li\",\n \"href\": \"https://axios-http.com/docs/intro\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Axios\"), \" library). The format of the data that we post needs to be of the correct format. Where 'ref' is the branch and 'inputs' contains the parameters we are sending. To correctly format the URL for the workflow we need to know the 'workflowId'. GitHub actions have a consistent url for workflow dispatch \", mdx(\"inlineCode\", {\n parentName: \"li\"\n }, \"https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow_id}/dispatches\"))), mdx(\"h3\", null, \"Github Action\"), mdx(\"p\", null, \"The cloud action will trigger the GitHub workflow via the workflow dispatch. We have passed down the body, author and discussionId all the way from the React form. Luckily there is a GitHub Action we can use directly from the workflow. The \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://github.com/octokit/graphql-action\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Ocktokit GraphQL Action\"), \" allows us to apply queries and mutations to the GitHub GraphQL API. This saves us the effort of manually having to create an action to do it for us.\"), mdx(\"deckgo-highlight-code\", {\n \"language\": \"yml\",\n \"terminal\": \"carbon\",\n \"theme\": \"one-light\"\n }, \"\\n \", mdx(\"code\", {\n parentName: \"deckgo-highlight-code\",\n \"slot\": \"code\"\n }, \"name: post-comment\\n\\non:\\n workflow_dispatch:\\n inputs:\\n author:\\n description: 'Author of the commentor'\\n required: true\\n body:\\n description: 'Body of the comment'\\n required: true\\n discussionId:\\n description: 'ID of the discussion'\\n required: true\\n\\njobs:\\n post-comment:\\n runs-on: ubuntu-latest\\n steps:\\n - uses: octokit/graphql-action@v2.x\\n id: post_discussion_comment\\n with:\\n query: |\\n mutation addDiscussionComment($discussion: ID!, $body: String!) {\\n addDiscussionComment(input: {discussionId: $discussion, body: $body}) {\\n comment {\\n id\\n }\\n }\\n }\\n discussion: \\\"'${{github.event.inputs.discussionId}}'\\\"\\n body: |\\n ---\\n author: ${{github.event.inputs.author}}\\n ---\\n ${{github.event.inputs.body}}\\\"'\\n env:\\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\"), \"\\n \"), mdx(\"p\", null, \"They key part of this action is the mutation and the inputs that are passed to it. The \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://docs.github.com/en/graphql/overview/explorer\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"GitHub GraphQL Explorer\"), \" is a great way to test and debug queries and mutations.\"), mdx(\"p\", null, \"At this point we stumble upon a new problem. As stated from the outset I don't want people to have to login. Using this flow the discussion comment is made by whoever authenticated the GitHub token, In this case it is the GitHub Actions bot user. We can't just set an Author field on the mutation as the user isn't the one logged in. What we can do instead is record it using frontmatter formatting in the body of the comment. Similar to what we do for blog posts in \", mdx(\"a\", {\n parentName: \"p\",\n \"href\": \"https://www.gatsbyjs.com/docs/how-to/routing/adding-markdown-pages/#frontmatter-for-metadata-in-markdown-files\",\n \"target\": \"_blank\",\n \"rel\": \"noopener\"\n }, \"Gatsby\"), \". It provides a nice flexible solution as you can add as many additional fields as you want. In the second post we will look at extracting the frontmatter back out.\"), mdx(\"h2\", null, \"Summary\"), mdx(\"p\", null, \"That is the first part done, we are now able to get comments added to a GitHub discussion. A quick recap of how we did that.\"), mdx(\"ol\", null, mdx(\"li\", {\n parentName: \"ol\"\n }, \"We added a discussion Id to each blog post.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"We added a comment form which posts the comment, author and discussion ID to a cloud function.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The cloud function carries out some basic validation, and triggers a GitHub Actions workflow by attached a securely stored token.\"), mdx(\"li\", {\n parentName: \"ol\"\n }, \"The GitHub Actions workflow recives the information and triggers a GraphQL mutation to update the discussion.\")));\n}\n;\nMDXContent.isMDXComponent = true;","comments":[{"body":"Thanks for sharing your story, it's great to hear about somebody else's experiences of changing roles.","date":"September 05, 2022","author":"Yasmin","url":null},{"body":"If you're not getting some rejections, you're not pushing yourself!","date":"October 11, 2022","author":"Aidan","url":null}],"frontmatter":{"date":"February 18, 2022","description":"Part 1 of a 2 part guide to use GitHub Discussions as datastore for your Gatsby blog comments. In this episode we focus on getting the data into GitHub.","author":"Andy Polhill","slug":"blog/gatsby-comments-github-discussions-part-1/","title":"Gatsby comments with GitHub Discussions (part 1)","discussionId":"D_kwDOEZ2jC84AOZk_","inlineImages":null}}},"pageContext":{"id":"2a1c4418-5896-55ae-aa85-dc4dc10e5dd6","slug":"gatsby-comments-github-discussions-part-1/","__params":{"slug":"gatsby-comments-github-discussions-part-1"}}}, "staticQueryHashes": ["2147664024"]} \ No newline at end of file diff --git a/sitemap/sitemap-0.xml b/sitemap/sitemap-0.xml index d59b37f08..dd4bea8ce 100644 --- a/sitemap/sitemap-0.xml +++ b/sitemap/sitemap-0.xml @@ -1 +1 @@ -https://andypolhill.com/daily0.7https://andypolhill.com/blog/daily0.7https://andypolhill.com/blog/a-little-spot-of-bother/daily0.7https://andypolhill.com/blog/first-engineering-manager-role/daily0.7https://andypolhill.com/blog/gatsby-comments-github-discussions-part-1/daily0.7https://andypolhill.com/blog/gatsby-comments-github-discussions-part-2/daily0.7https://andypolhill.com/blog/lake-district-solo/daily0.7https://andypolhill.com/blog/sketch-notes/daily0.7https://andypolhill.com/blog/time-for-a-change/daily0.7https://andypolhill.com/blog/.js/daily0.7 \ No newline at end of file +https://andypolhill.com/daily0.7https://andypolhill.com/blog/daily0.7https://andypolhill.com/blog/a-little-spot-of-bother/daily0.7https://andypolhill.com/blog/first-engineering-manager-role/daily0.7https://andypolhill.com/blog/gatsby-comments-github-discussions-part-1/daily0.7https://andypolhill.com/blog/lake-district-solo/daily0.7https://andypolhill.com/blog/gatsby-comments-github-discussions-part-2/daily0.7https://andypolhill.com/blog/sketch-notes/daily0.7https://andypolhill.com/blog/time-for-a-change/daily0.7https://andypolhill.com/blog/.js/daily0.7 \ No newline at end of file