{"version":"https://jsonfeed.org/version/1","title":"Human Who Codes","home_page_url":"https://humanwhocodes.com","feed_url":"https://humanwhocodes.com/feeds/snippets.json","description":"The Official Web Site of Nicholas C. Zakas","expired":false,"author":{"name":"Nicholas C. Zakas"},"items":[{"id":"https://humanwhocodes.com/snippets/2026/02/using-your-own-supabase-signing-keys/","url":"https://humanwhocodes.com/snippets/2026/02/using-your-own-supabase-signing-keys/","title":"Using your own Supabase signing keys","author":{"name":"Nicholas C. Zakas"},"summary":"Supabase generates JWT signing keys automatically, but you can also use your own if you want to share keys across multiple instances or generate tokens manually.","content_text":"\n[Supabase](https://supabase.com) uses JWT signing keys to sign authentication tokens. By default, Supabase generates a new signing key on startup, but you can also use your own signing keys. This is useful if you want to generate your own tokens or if you want to use the same signing keys across multiple Supabase instances.\n\n## Generate a signing key\n\nFirst, generate a new signing key using the Supabase CLI:\n\n```shell\nnpx supabase gen signing-key --algorithm ES256\n```\n\nThis will output a new signing key in JSON format to the terminal.\n\n**Note:** If you already have a `supabase/signing_key.json` file, the CLI will ask if you want to overwrite it. If you want to keep the existing signing key, you can choose \"No\" and the CLI will not output a new signing key.\n\n## Local Development\n\nFor local development, save the signing key in your `supabase` directory, such as `supabase/signing_key.json`. Supabase requires the local signing key to be contained in an array, so you need to wrap the output in square brackets. For example, if your output looks like this:\n\n```json\n{\n  \"kty\": \"EC\",\n  \"d\": \"N8sXo9n2e5Z19sXo9n2e5Z1\",\n  \"use\": \"sig\",\n  \"crv\": \"P-256\",\n  \"kid\": \"my-key-id\",\n  \"x\": \"f83OJ3D2xF4\",\n  \"y\": \"x_FEzRu9c\"\n}\n```\n\nYou need to wrap it like this:\n\n```json\n[\n  {\n    \"kty\": \"EC\",\n    \"d\": \"N8sXo9n2e5Z19sXo9n2e5Z1\",\n    \"use\": \"sig\",\n    \"crv\": \"P-256\",\n    \"kid\": \"my-key-id\",\n    \"x\": \"f83OJ3D2xF4\",\n    \"y\": \"x_FEzRu9c\"\n  }\n]\n```\n\nSave this into a file called `supabase/signing_key.json`.\n\nThen, edit the `config.toml` file and add the following lines to the `[auth]` section:\n\n```toml\n[auth]\nsigning_keys_path = \"./signing_key.json\"\n```\n\nThis will tell Supabase to use your signing key instead of generating a new one on startup. After editing `config.toml`, you need to restart Supabase to reload the configuration:\n\n```shell\nnpx supabase stop\nnpx supabase start\n```\n\n## Hosted Supabase\n\nIf you are using the hosted version of Supabase, you can set the signing keys in the Supabase dashboard. Go to the \"Settings\" tab, then click on \"JWT Signing Keys\".\n\nIf you already have a standby key, you'll need to remove it before you can add a new one. To remove a standby key, click the three dots next to the key and select \"Move to previously used\". After removing the existing standby key, you can add your new signing key as described above.\n\n**Important:** This signing key must *not* by in an array. It should be the raw JSON object that was generated by the Supabase CLI. \n\nClick \"Create Standby Key\". In the dialog select \"Import an existing key\" and paste in your previously generated signing key. Click the \"Create Standby Key\" button to save the new signing key.\n\nClick \"Rotate Keys\" to make the new signing key active. This will rotate the keys and make the new signing key the active key for signing tokens.\n\n## Important Notes\n\n* The Supabase CLI-generated signing key contains both `verify` and `sign` keys because Supabase itself needs to do both. However, some tools like [`jose`](https://npmjs.com/package/jose) will fail signing if object contains a `verify` key. If you encounter this issue, you can remove the `verify` key from the signing key JSON file before using it with `jose`.\n* If your app has users with a persisted session, changing the signing key will invalidate all existing tokens. This means that users will need to log in again to obtain new tokens signed with the new key. Make sure to communicate this change to your users if you are changing the signing key in a production environment. If you're using the JavaScript client, you can call `supabase.auth.refreshSession()` to refresh the session and obtain a new token without requiring the user to log in again.\n* You can tell if a user has an invalid JWT by checking the `error` property of the user object returned by `supabase.auth.getUser()`. If the JWT is invalid, the `error` property will contain a `code` property of `\"bad_jwt\"`.\n\n ","content_html":"&lt;p&gt;&lt;a href=&quot;https://supabase.com&quot;&gt;Supabase&lt;/a&gt; uses JWT signing keys to sign authentication tokens. By default, Supabase generates a new signing key on startup, but you can also use your own signing keys. This is useful if you want to generate your own tokens or if you want to use the same signing keys across multiple Supabase instances.&lt;/p&gt;\n&lt;h2 id=&quot;generate-a-signing-key&quot;&gt;Generate a signing key&lt;/h2&gt;\n&lt;p&gt;First, generate a new signing key using the Supabase CLI:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;gen&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;signing-key&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;--algorithm&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;ES256&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This will output a new signing key in JSON format to the terminal.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you already have a &lt;code&gt;supabase/signing_key.json&lt;/code&gt; file, the CLI will ask if you want to overwrite it. If you want to keep the existing signing key, you can choose “No” and the CLI will not output a new signing key.&lt;/p&gt;\n&lt;h2 id=&quot;local-development&quot;&gt;Local Development&lt;/h2&gt;\n&lt;p&gt;For local development, save the signing key in your &lt;code&gt;supabase&lt;/code&gt; directory, such as &lt;code&gt;supabase/signing_key.json&lt;/code&gt;. Supabase requires the local signing key to be contained in an array, so you need to wrap the output in square brackets. For example, if your output looks like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;kty&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;EC&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;N8sXo9n2e5Z19sXo9n2e5Z1&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;use&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;sig&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;crv&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;P-256&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;kid&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;my-key-id&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;f83OJ3D2xF4&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;x_FEzRu9c&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;You need to wrap it like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;[&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;kty&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;EC&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;N8sXo9n2e5Z19sXo9n2e5Z1&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;use&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;sig&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;crv&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;P-256&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;kid&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;my-key-id&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;f83OJ3D2xF4&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;x_FEzRu9c&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Save this into a file called &lt;code&gt;supabase/signing_key.json&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;Then, edit the &lt;code&gt;config.toml&lt;/code&gt; file and add the following lines to the &lt;code&gt;[auth]&lt;/code&gt; section:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;signing_keys_path = &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;./signing_key.json&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This will tell Supabase to use your signing key instead of generating a new one on startup. After editing &lt;code&gt;config.toml&lt;/code&gt;, you need to restart Supabase to reload the configuration:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;stop&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&quot;hosted-supabase&quot;&gt;Hosted Supabase&lt;/h2&gt;\n&lt;p&gt;If you are using the hosted version of Supabase, you can set the signing keys in the Supabase dashboard. Go to the “Settings” tab, then click on “JWT Signing Keys”.&lt;/p&gt;\n&lt;p&gt;If you already have a standby key, you’ll need to remove it before you can add a new one. To remove a standby key, click the three dots next to the key and select “Move to previously used”. After removing the existing standby key, you can add your new signing key as described above.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This signing key must &lt;em&gt;not&lt;/em&gt; by in an array. It should be the raw JSON object that was generated by the Supabase CLI.&lt;/p&gt;\n&lt;p&gt;Click “Create Standby Key”. In the dialog select “Import an existing key” and paste in your previously generated signing key. Click the “Create Standby Key” button to save the new signing key.&lt;/p&gt;\n&lt;p&gt;Click “Rotate Keys” to make the new signing key active. This will rotate the keys and make the new signing key the active key for signing tokens.&lt;/p&gt;\n&lt;h2 id=&quot;important-notes&quot;&gt;Important Notes&lt;/h2&gt;\n&lt;ul&gt;\n&lt;li&gt;\n&lt;p&gt;The Supabase CLI-generated signing key contains both &lt;code&gt;verify&lt;/code&gt; and &lt;code&gt;sign&lt;/code&gt; keys because Supabase itself needs to do both. However, some tools like &lt;a href=&quot;https://npmjs.com/package/jose&quot;&gt;&lt;code&gt;jose&lt;/code&gt;&lt;/a&gt; will fail signing if object contains a &lt;code&gt;verify&lt;/code&gt; key. If you encounter this issue, you can remove the &lt;code&gt;verify&lt;/code&gt; key from the signing key JSON file before using it with &lt;code&gt;jose&lt;/code&gt;.&lt;/p&gt;\n&lt;/li&gt;\n&lt;li&gt;\n&lt;p&gt;If your app has users with a persisted session, changing the signing key will invalidate all existing tokens. This means that users will need to log in again to obtain new tokens signed with the new key. Make sure to communicate this change to your users if you are changing the signing key in a production environment. If you’re using the JavaScript client, you can call &lt;code&gt;supabase.auth.refreshSession()&lt;/code&gt; to refresh the session and obtain a new token without requiring the user to log in again.&lt;/p&gt;\n&lt;/li&gt;\n&lt;li&gt;\n&lt;p&gt;You can tell if a user has an invalid JWT by checking the &lt;code&gt;error&lt;/code&gt; property of the user object returned by &lt;code&gt;supabase.auth.getUser()&lt;/code&gt;. If the JWT is invalid, the &lt;code&gt;error&lt;/code&gt; property will contain a &lt;code&gt;code&lt;/code&gt; property of &lt;code&gt;&quot;bad_jwt&quot;&lt;/code&gt;.&lt;/p&gt;\n&lt;/li&gt;\n&lt;/ul&gt;","tags":["Supabase","JWT","Security"],"date_published":"2026-02-16T00:00:00.000Z","date_updated":"2026-02-16T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2025/08/setup-local-supabase-oauth-logins/","url":"https://humanwhocodes.com/snippets/2025/08/setup-local-supabase-oauth-logins/","title":"Set up local Supabase OAuth logins","author":{"name":"Nicholas C. Zakas"},"summary":"While it's easy to set up Supabase OAuth logins in the cloud product, setting it up for a local development environment is a bit tricky.","content_text":"\nOne of the benefits of [Supabase](https://supabase.com) is its integrated login system that supports many OAuth providers, including Google and GitHub. While there is plenty of documentation explaining how to set up OAuth providers in hosted Supabase, the [instructions for local Supabase](https://supabase.com/docs/guides/local-development/overview#use-auth-locally) are fairly terse and are missing several steps. \n\nFirst, create a callback endpoint in your application, for example, `/auth/callback`. This is the callback that will receive the OAuth information from Supabase. Specifically, you should receive either a `code` query string parameter that will allow the user to login or an `error` parameter indicating there was an error logging in. Note that for server-side authentication you must use [`@supabase/ssr`](https://npmjs.com/package/@supabase/ssr). Here's an example written using [Astro](https://astro.build).\n\n```ts\n// Astro example\nimport { createServerClient, parseCookieHeader } from \"@supabase/ssr\";\n\nconst supabaseUrl = import.meta.env.SUPABASE_URL;\nconst supabaseAnonKey = import.meta.env.SUPABASE_ANON_KEY;\n\nexport const GET: APIRoute = async ({ url, request, cookies, redirect }) => {\n\n    // if there's no code then redirect to login\n    const code = url.searchParams.get(\"code\");\n    if (!code) {\n        redirect(\"/login?error=no-code\");\n    }\n    \n    // there is a code, try to log in\n    const supabase = createServerClient(supabaseUrl, supabaseAnonKey, {\n        cookies: {\n            getAll() {\n                return parseCookieHeader(request.headers.get(\"cookie\") || \"\");\n            },\n            setAll(cookiesToSet) {\n                cookiesToSet.forEach(({ name, value, options }) =>\n                    cookies.set(name, value, options)\n                );\n            },\n        },\n    });\n    \n    const { data, error } = await supabase.auth.exchangeCodeForSession(code);\n    if (error || !data?.session) {\n        return redirect(\"/login?error=unauthorized\");\n    }\n\n    return redirect(\"/\");\n}\n```\n\nWith the callback set up, you now need to let Supabase know how to call it. In general, there are three steps to the OAuth login process for Supabase:\n\n1. Your application links off to the OAuth provider's authentication service.\n2. The OAuth provider redirects to the Supabase auth service.\n3. Supabase redirects to your application callback.\n\nTo ensure that happens locally, you need to edit the `config.toml` file. First, locate the `[auth]` section at the top and make sure the value for `site_url` is your local application URL and `additional_redirect_urls` contains the full URL for the callback endpoint. Here's an example:\n\n```toml\n[auth]\nenabled = true\n# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used\n# in emails.\nsite_url = \"http://localhost:4321\"\n# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.\nadditional_redirect_urls = [\"http://localhost:4321/auth/callback\"]\n```\n\n(Without specifying `additional_redirect_urls`, you'll always have to redirect back to the application homepage.)\n\nNext, create an entry for your OAuth provider including the client ID, client secret, and redirect URI. For GitHub, it would look like this:\n\n```toml\n[auth.external.github]\nenabled = true\nclient_id = \"env(GITHUB_CLIENT_ID)\"\nsecret = \"env(GITHUB_CLIENT_SECRET)\"\nredirect_uri = \"http://localhost:54321/auth/v1/callback\"\n```\n\nThe `redirect_uri` here needs to be the local Supabase URL for OAuth callbacks. The default port is 54321, so double-check the port.\n\nAfter editing `config.toml`, you need to restart Supabase to reload the configuration:\n\n```shell\nnpx supabase stop\nnpx supabase start\n```\n\nNext, you need to generate the OAuth URL to use in your application. Here's an example generating a URL to login with GitHub:\n\n```ts\nimport { createServerClient, parseCookieHeader } from \"@supabase/ssr\";\n\nconst supabaseUrl = import.meta.env.SUPABASE_URL;\nconst supabaseAnonKey = import.meta.env.SUPABASE_ANON_KEY;\n\nconst supabaseUrl = import.meta.env.SUPABASE_URL;\nconst supabaseAnonKey = import.meta.env.SUPABASE_ANON_KEY;\n\nexport const GET: APIRoute = async ({ url, request, cookies, redirect }) => {\n\n    // if there's no code then redirect to login\n    const code = url.searchParams.get(\"code\");\n    if (!code) {\n        redirect(\"/login?error=no-code\");\n    }\n    \n    // there is a code, try to log in\n    const supabase = createServerClient(supabaseUrl, supabaseAnonKey, {\n        cookies: {\n            getAll() {\n                return parseCookieHeader(request.headers.get(\"cookie\") || \"\");\n            },\n            setAll(cookiesToSet) {\n                cookiesToSet.forEach(({ name, value, options }) =>\n                    cookies.set(name, value, options)\n                );\n            },\n        },\n    });\n        \n    const { data, error } = await supabase.auth.signInWithOAuth({\n        provider: \"github\",\n        options: {\n            // must be listed as additional_redirect_urls\n            redirectTo: `http://localhost:4321/auth/callback`\n        }\n    });\n\n    if (error || !data?.url) {\n        // handle error\n    }\n\n    redirect(data.url);\n}\n```\n\nEverything is now wired up for the correct end-to-end flow.\n","content_html":"&lt;p&gt;One of the benefits of &lt;a href=&quot;https://supabase.com&quot;&gt;Supabase&lt;/a&gt; is its integrated login system that supports many OAuth providers, including Google and GitHub. While there is plenty of documentation explaining how to set up OAuth providers in hosted Supabase, the &lt;a href=&quot;https://supabase.com/docs/guides/local-development/overview#use-auth-locally&quot;&gt;instructions for local Supabase&lt;/a&gt; are fairly terse and are missing several steps.&lt;/p&gt;\n&lt;p&gt;First, create a callback endpoint in your application, for example, &lt;code&gt;/auth/callback&lt;/code&gt;. This is the callback that will receive the OAuth information from Supabase. Specifically, you should receive either a &lt;code&gt;code&lt;/code&gt; query string parameter that will allow the user to login or an &lt;code&gt;error&lt;/code&gt; parameter indicating there was an error logging in. Note that for server-side authentication you must use &lt;a href=&quot;https://npmjs.com/package/@supabase/ssr&quot;&gt;&lt;code&gt;@supabase/ssr&lt;/code&gt;&lt;/a&gt;. Here’s an example written using &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt;.&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// Astro example&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; { createServerClient, parseCookieHeader } &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;@supabase/ssr&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabaseUrl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;SUPABASE_URL&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabaseAnonKey&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;SUPABASE_ANON_KEY&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;APIRoute&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;cookies&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// if there&apos;s no code then redirect to login&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; url.searchParams.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;code) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/login?error=no-code&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// there is a code, try to log in&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;createServerClient&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(supabaseUrl, supabaseAnonKey, {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        cookies: {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;getAll&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;parseCookieHeader&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(request.headers.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;cookie&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;setAll&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;cookiesToSet&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                cookiesToSet.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(({ &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                    cookies.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(name, value, options)&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                );&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; supabase.auth.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;exchangeCodeForSession&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(code);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; (error &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;data?.session) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/login?error=unauthorized&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;With the callback set up, you now need to let Supabase know how to call it. In general, there are three steps to the OAuth login process for Supabase:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;Your application links off to the OAuth provider’s authentication service.&lt;/li&gt;\n&lt;li&gt;The OAuth provider redirects to the Supabase auth service.&lt;/li&gt;\n&lt;li&gt;Supabase redirects to your application callback.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;To ensure that happens locally, you need to edit the &lt;code&gt;config.toml&lt;/code&gt; file. First, locate the &lt;code&gt;[auth]&lt;/code&gt; section at the top and make sure the value for &lt;code&gt;site_url&lt;/code&gt; is your local application URL and &lt;code&gt;additional_redirect_urls&lt;/code&gt; contains the full URL for the callback endpoint. Here’s an example:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;enabled = &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# in emails.&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;site_url = &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;http://localhost:4321&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;additional_redirect_urls = [&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;http://localhost:4321/auth/callback&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;(Without specifying &lt;code&gt;additional_redirect_urls&lt;/code&gt;, you’ll always have to redirect back to the application homepage.)&lt;/p&gt;\n&lt;p&gt;Next, create an entry for your OAuth provider including the client ID, client secret, and redirect URI. For GitHub, it would look like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;external&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;github&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;enabled = &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;client_id = &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;env(GITHUB_CLIENT_ID)&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;secret = &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;env(GITHUB_CLIENT_SECRET)&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;redirect_uri = &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;http://localhost:54321/auth/v1/callback&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;The &lt;code&gt;redirect_uri&lt;/code&gt; here needs to be the local Supabase URL for OAuth callbacks. The default port is 54321, so double-check the port.&lt;/p&gt;\n&lt;p&gt;After editing &lt;code&gt;config.toml&lt;/code&gt;, you need to restart Supabase to reload the configuration:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;stop&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next, you need to generate the OAuth URL to use in your application. Here’s an example generating a URL to login with GitHub:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; { createServerClient, parseCookieHeader } &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;@supabase/ssr&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabaseUrl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;SUPABASE_URL&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabaseAnonKey&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;SUPABASE_ANON_KEY&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabaseUrl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;SUPABASE_URL&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabaseAnonKey&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;SUPABASE_ANON_KEY&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;APIRoute&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;cookies&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// if there&apos;s no code then redirect to login&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; url.searchParams.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;code) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/login?error=no-code&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// there is a code, try to log in&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;supabase&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;createServerClient&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(supabaseUrl, supabaseAnonKey, {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        cookies: {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;getAll&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;parseCookieHeader&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(request.headers.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;cookie&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;setAll&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;cookiesToSet&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                cookiesToSet.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(({ &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                    cookies.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(name, value, options)&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;                );&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; supabase.auth.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;signInWithOAuth&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        provider: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;github&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        options: {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// must be listed as additional_redirect_urls&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            redirectTo: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;`http://localhost:4321/auth/callback`&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; (error &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;data?.url) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// handle error&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;redirect&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(data.url);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Everything is now wired up for the correct end-to-end flow.&lt;/p&gt;","tags":["JavaScript","Supabase","OAuth","Login"],"date_published":"2025-08-27T00:00:00.000Z","date_updated":"2025-08-27T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2025/08/run-multiple-cloudflare-workers-locally/","url":"https://humanwhocodes.com/snippets/2025/08/run-multiple-cloudflare-workers-locally/","title":"Run multiple Cloudflare workers locally","author":{"name":"Nicholas C. Zakas"},"summary":"Wrangler is made primarily to run one worker at a time. You can also use it to run all of your workers at the same time.","content_text":"\nWhen you're testing an application locally, you ideally want an environment that\nmimics production as much as possible. Especially when you're using multiple\n[Cloudflare workers](https://workers.cloudflare.com/), you'll want to make sure\nyou can run them together for end-to-end testing during development. Without\nthis ability, you either need to run everything in the cloud (problematic when\nyour internet connection is down or slow) and test each worker individually.\n\n[Wrangler](https://developers.cloudflare.com/workers/wrangler/) is capable of\nrunning multiple workers at the same time, like this:\n\n```shell\nnpx wrangler dev -c worker1/wrangler.jsonc -c worker2/wrangler.jsonc\n```\n\nThis is important because workers run together in one dev session share\nresources, including queues, and can communicate with one another. That's key\nfor creating a production-like environment locally.\n\nHowever, Wrangler only starts _one server_ for all workers and there's no way to\nroute between them. After some digging, I found this note in the\n[docs](https://developers.cloudflare.com/workers/wrangler/commands/#dev):\n\n> You can provide multiple configuration files to run multiple Workers in one\n> dev session like this:\n> `wrangler dev -c ./wrangler.toml -c ../other-worker/wrangler.toml`. The first\n> config will be treated as the _primary Worker_, which will be exposed over\n> HTTP. The remaining config files will only be accessible via a service binding\n> from the primary Worker.\n\nThat means we need the first worker listed to both list other workers as service\nbindings and route requests to the correct worker.\n\n## The `http` worker\n\nYou can create a simple worker, that I name `http`, to handle this for you. The\nfirst step is to list your other services in the `wrangler.jsonc` file:\n\n```jsonc\n{\n    \"$schema\": \"node_modules/wrangler/config-schema.json\",\n    \"name\": \"http\",\n    \"main\": \"src/index.ts\",\n    \"compatibility_date\": \"2025-08-22\",\n    \"services\": [\n        {\n            \"binding\": \"worker1\",\n            \"service\": \"worker1\"\n        },\n        {\n            \"binding\": \"worker2\",\n            \"service\": \"worker2\"\n        }\n    ]\n}\n```\n\nNext, you'll use [Hono](https://hono.dev) to route requests based on the request\npath. To make things easy, the path will be the worker binding name, which means\nyou'll only need to update the `wrangler.jsonc` file when you want to add or\nremove workers. Here's the code:\n\n```typescript\nimport { Hono } from \"hono\";\n\ninterface Bindings {\n    [binding: string]: Fetcher;\n}\n\nconst app = new Hono<{ Bindings: Bindings }>();\n\napp.all(\"/:worker/*\", (c) => {\n    const worker = c.req.param(\"worker\");\n    const binding = c.env[worker];\n\n    if (!binding || typeof binding.fetch !== \"function\") {\n        return c.text(`Worker binding '${worker}' not found`, 404);\n    }\n\n    // Rewrite the URL to remove the worker prefix\n    const url = URL.parse(c.req.url) as URL;\n    url.pathname = url.pathname.slice(`/${worker}`.length) || \"/\";\n\n    // create a new request object to avoid issues with reused requests\n    const request = new Request(url, c.req.raw.clone());\n\n    return binding.fetch(request);\n});\n\nexport default app;\n```\n\nNote that you need to pass `c.req.raw` to the worker rather than `c.req`, which\nis a Hono-specific object. In this way, all of the request information is passed\ndirectly to the worker. (You can, optionally, modify it as necessary.)\n\nNow, make sure that the `http` worker is the first one passed to Wrangler:\n\n```shell\nnpx wrangler dev -c http/wrangler.jsonc -c worker1/wrangler.jsonc -c worker2/wrangler.jsonc\n```\n\nThen you can test out your workers locally:\n\n```shell\n# call worker1\ncurl -i -X POST http://localhost:8787/worker1 \\\n    -H \"Content-Type: application/json\" \\\n    -d '{\"message\":\"Hello worker1!\"}'\n\n# call worker2\ncurl -i -X POST http://localhost:8787/worker2 \\\n    -H \"Content-Type: application/json\" \\\n    -d '{\"message\":\"Hello worker2!\"}'\n```\n\nEnjoy your local multi-worker development environment!\n\n**Updated (2025-08-25):** Cleaned up TypeScript code to match best practices for\nHono.\n\n**Updated (2025-09-08):** Enhanced the Hono app so that it handles URL rewriting\nand all HTTP verbs.\n","content_html":"&lt;p&gt;When you’re testing an application locally, you ideally want an environment that\nmimics production as much as possible. Especially when you’re using multiple\n&lt;a href=&quot;https://workers.cloudflare.com/&quot;&gt;Cloudflare workers&lt;/a&gt;, you’ll want to make sure\nyou can run them together for end-to-end testing during development. Without\nthis ability, you either need to run everything in the cloud (problematic when\nyour internet connection is down or slow) and test each worker individually.&lt;/p&gt;\n&lt;p&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/wrangler/&quot;&gt;Wrangler&lt;/a&gt; is capable of\nrunning multiple workers at the same time, like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;wrangler&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dev&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-c&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;worker1/wrangler.jsonc&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-c&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;worker2/wrangler.jsonc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This is important because workers run together in one dev session share\nresources, including queues, and can communicate with one another. That’s key\nfor creating a production-like environment locally.&lt;/p&gt;\n&lt;p&gt;However, Wrangler only starts &lt;em&gt;one server&lt;/em&gt; for all workers and there’s no way to\nroute between them. After some digging, I found this note in the\n&lt;a href=&quot;https://developers.cloudflare.com/workers/wrangler/commands/#dev&quot;&gt;docs&lt;/a&gt;:&lt;/p&gt;\n&lt;blockquote&gt;\n&lt;p&gt;You can provide multiple configuration files to run multiple Workers in one\ndev session like this:\n&lt;code&gt;wrangler dev -c ./wrangler.toml -c ../other-worker/wrangler.toml&lt;/code&gt;. The first\nconfig will be treated as the &lt;em&gt;primary Worker&lt;/em&gt;, which will be exposed over\nHTTP. The remaining config files will only be accessible via a service binding\nfrom the primary Worker.&lt;/p&gt;\n&lt;/blockquote&gt;\n&lt;p&gt;That means we need the first worker listed to both list other workers as service\nbindings and route requests to the correct worker.&lt;/p&gt;\n&lt;h2 id=&quot;the-http-worker&quot;&gt;The &lt;code&gt;http&lt;/code&gt; worker&lt;/h2&gt;\n&lt;p&gt;You can create a simple worker, that I name &lt;code&gt;http&lt;/code&gt;, to handle this for you. The\nfirst step is to list your other services in the &lt;code&gt;wrangler.jsonc&lt;/code&gt; file:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;node_modules/wrangler/config-schema.json&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;http&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;src/index.ts&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;compatibility_date&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;2025-08-22&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;services&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;binding&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;worker1&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;service&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;worker1&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;binding&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;worker2&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;service&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;worker2&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next, you’ll use &lt;a href=&quot;https://hono.dev&quot;&gt;Hono&lt;/a&gt; to route requests based on the request\npath. To make things easy, the path will be the worker binding name, which means\nyou’ll only need to update the &lt;code&gt;wrangler.jsonc&lt;/code&gt; file when you want to add or\nremove workers. Here’s the code:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; { Hono } &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;hono&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;Bindings&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;binding&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;Fetcher&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;Hono&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;&amp;#x3C;{ &lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;Bindings&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;Bindings&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; }&gt;();&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;app.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/:worker/*&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;worker&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; c.req.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;param&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;worker&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;binding&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; c.env[worker];&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;binding &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; binding.fetch &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; c.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;`Worker binding &apos;${&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;worker&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;}&apos; not found`&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;404&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// Rewrite the URL to remove the worker prefix&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;URL&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(c.req.url) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;URL&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    url.pathname &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; url.pathname.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;`/${&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;worker&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// create a new request object to avoid issues with reused requests&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(url, c.req.raw.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;());&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; binding.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(request);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; app;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Note that you need to pass &lt;code&gt;c.req.raw&lt;/code&gt; to the worker rather than &lt;code&gt;c.req&lt;/code&gt;, which\nis a Hono-specific object. In this way, all of the request information is passed\ndirectly to the worker. (You can, optionally, modify it as necessary.)&lt;/p&gt;\n&lt;p&gt;Now, make sure that the &lt;code&gt;http&lt;/code&gt; worker is the first one passed to Wrangler:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;wrangler&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dev&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-c&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;http/wrangler.jsonc&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-c&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;worker1/wrangler.jsonc&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-c&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;worker2/wrangler.jsonc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then you can test out your workers locally:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# call worker1&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-i&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-X&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;POST&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;http://localhost:8787/worker1&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;\\&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-H&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;\\&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-d&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&apos;{&quot;message&quot;:&quot;Hello worker1!&quot;}&apos;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# call worker2&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-i&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-X&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;POST&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;http://localhost:8787/worker2&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;\\&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-H&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;\\&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-d&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&apos;{&quot;message&quot;:&quot;Hello worker2!&quot;}&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Enjoy your local multi-worker development environment!&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Updated (2025-08-25):&lt;/strong&gt; Cleaned up TypeScript code to match best practices for\nHono.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Updated (2025-09-08):&lt;/strong&gt; Enhanced the Hono app so that it handles URL rewriting\nand all HTTP verbs.&lt;/p&gt;","tags":["JavaScript","Cloudflare","Edge Workers"],"date_published":"2025-08-22T00:00:00.000Z","date_updated":"2025-08-22T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2025/02/passing-github-action-step-output-to-script/","url":"https://humanwhocodes.com/snippets/2025/02/passing-github-action-step-output-to-script/","title":"Passing GitHub Actions workflow step output to JavaScript","author":{"name":"Nicholas C. Zakas"},"summary":"When you want your JavaScript code to access all of the data from a previous GitHub Actions workflow step.","content_text":"\nGitHub Actions workflows allow each step to create output that is available in other steps, such as:\n\n```yaml\nname: Test Steps Output\non: workflow_dispatch\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - id: release\n        run: |\n          echo \"release_created=true\" >> \"$GITHUB_OUTPUT\"\n          echo \"project_name=MyProject\" >> \"$GITHUB_OUTPUT\"\n\n      - run: echo ${{ steps.release.outputs.release_created }}\n```\n\nThis outputs the `release_created` value from the `release` step in the next step. You can pass this individually into a JavaScript file by creating environment variables:\n\n```yaml\nname: Test Steps Output\non: workflow_dispatch\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repo\n        uses: actions/checkout@v4\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n\n      - id: release\n        run: |\n          echo \"release_created=true\" >> \"$GITHUB_OUTPUT\"\n          echo \"project_name=MyProject\" >> \"$GITHUB_OUTPUT\"\n\n      - run: node extract.mjs\n        env:\n          RELEASE_CREATED: ${{ steps.release.outputs.release_created }}\n          PROJECT_NAME: ${{ steps.release.outputs.project_name }}\n```\n\nHowever, that can get unwieldy if there are more than a few step outputs, and especially if you're unsure exactly which outputs will be created. \n\nInstead, you can use the `toJSON()` helper to serialize all of the step outputs into a JSON object and create one environment variable to capture it:\n\n```yaml\nname: Test Steps Output\non: workflow_dispatch\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repo\n        uses: actions/checkout@v4\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n\n      - id: release\n        run: |\n          echo \"release_created=true\" >> \"$GITHUB_OUTPUT\"\n          echo \"project_name=MyProject\" >> \"$GITHUB_OUTPUT\"\n\n      - run: node extract.mjs\n        env:\n          RELEASE_OUTPUTS: ${{ toJSON(steps.release.outputs) }}\n```\n\nNow, inside of `extract.mjs`, you can use retrieve all of the data from the `release` step by parsing the `RELEASE_OUTPUTS` environment variable:\n\n```js\nconst releaseOutputs = JSON.parse(process.env.RELEASE_OUTPUTS || \"{}\");\n\nconsole.log(releaseOutputs.released_created);   // true\nconsole.log(releaseOutputs.project_name);   // MyProject\n```\n\nThis makes it a lot easier to create JavaScript files to work in your GitHub Actions workflow.\n","content_html":"&lt;p&gt;GitHub Actions workflows allow each step to create output that is available in other steps, such as:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Test Steps Output&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;runs-on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;steps&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;release&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          echo &quot;release_created=true&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          echo &quot;project_name=MyProject&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;echo ${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This outputs the &lt;code&gt;release_created&lt;/code&gt; value from the &lt;code&gt;release&lt;/code&gt; step in the next step. You can pass this individually into a JavaScript file by creating environment variables:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Test Steps Output&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;runs-on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;steps&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Check out repo&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Set up Node.js&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;actions/setup-node@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;release&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          echo &quot;release_created=true&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          echo &quot;project_name=MyProject&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;node extract.mjs&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;RELEASE_CREATED&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.project_name }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;However, that can get unwieldy if there are more than a few step outputs, and especially if you’re unsure exactly which outputs will be created.&lt;/p&gt;\n&lt;p&gt;Instead, you can use the &lt;code&gt;toJSON()&lt;/code&gt; helper to serialize all of the step outputs into a JSON object and create one environment variable to capture it:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Test Steps Output&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;runs-on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;steps&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Check out repo&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Set up Node.js&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;actions/setup-node@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;release&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          echo &quot;release_created=true&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          echo &quot;project_name=MyProject&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;node extract.mjs&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;RELEASE_OUTPUTS&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ toJSON(steps.release.outputs) }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now, inside of &lt;code&gt;extract.mjs&lt;/code&gt;, you can use retrieve all of the data from the &lt;code&gt;release&lt;/code&gt; step by parsing the &lt;code&gt;RELEASE_OUTPUTS&lt;/code&gt; environment variable:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;releaseOutputs&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(process.env.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;RELEASE_OUTPUTS&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;console.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(releaseOutputs.released_created);   &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// true&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;console.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(releaseOutputs.project_name);   &lt;/span&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;// MyProject&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;This makes it a lot easier to create JavaScript files to work in your GitHub Actions workflow.&lt;/p&gt;","tags":["JavaScript","GitHub","GitHub Actions","Environment Variables"],"date_published":"2025-02-21T00:00:00.000Z","date_updated":"2025-02-21T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2024/03/publishing-to-jsr-release-please/","url":"https://humanwhocodes.com/snippets/2024/03/publishing-to-jsr-release-please/","title":"Publishing to JSR using release-please","author":{"name":"Nicholas C. Zakas"},"summary":"With a little work, you can use release-please and GitHub Actions to publish to JSR.","content_text":"\nThere is no JSR-specific release strategy for [release-please](https://github.com/google-github-actions/release-please-action), so you'll need to use the `\"node\"` strategy and include a `package.json` file in your root. Then, you'll need to tell release-please to update your `jsr.json` file version (in addition to the `package.json` file) when a new release is created. To do so, update your `release-please-config.json` file to use `extra-files`, like this:\n\n```json\n{\n  \"packages\": {\n    \".\": {\n      \"release-type\": \"node\",\n      \"extra-files\": [\n        {\n          \"type\": \"json\",\n          \"path\": \"jsr.json\",\n          \"jsonpath\": \"$.version\"\n        }\n      ]\n    }\n  }\n}\n```\n\nThe `\"path\"` should be to the location of your `jsr.json` file. The `\"jsonpath\"` specifies the location of the field with a version number to update.\n\nThen, you can publish to JSR using a GitHub action. Assuming you want to publish to both npm and JSR, it's easiest to use the `jsr` npm module, like this:\n\n```yaml\non:\n  push:\n    branches:\n      - main\nname: release-please\n\npermissions:\n  contents: write\n  pull-requests: write\n  id-token: write\n\njobs:\n  release-please:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: GoogleCloudPlatform/release-please-action@v4\n        id: release\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n  \n      - uses: actions/checkout@v4\n        if: ${{ steps.release.outputs.release_created }}\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: lts/*\n          registry-url: 'https://registry.npmjs.org'\n        if: ${{ steps.release.outputs.release_created }}\n\n      - run: npm ci\n        if: ${{ steps.release.outputs.release_created }}\n\n      - name: Publish to npm\n        run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}\n        if: ${{ steps.release.outputs.release_created }}\n\n      - name: Publish to JSR\n        run: |\n          npm run build --if-present\n          npx jsr publish\n        if: ${{ steps.release.outputs.release_created }}\n```\n\nKeep in mind that `jsr publish` does not run any scripts beforehand, so if you want to run something like `npm run build`, you'll need to explicitly specify it as part of your `run` command.\n","content_html":"&lt;p&gt;There is no JSR-specific release strategy for &lt;a href=&quot;https://github.com/google-github-actions/release-please-action&quot;&gt;release-please&lt;/a&gt;, so you’ll need to use the &lt;code&gt;&quot;node&quot;&lt;/code&gt; strategy and include a &lt;code&gt;package.json&lt;/code&gt; file in your root. Then, you’ll need to tell release-please to update your &lt;code&gt;jsr.json&lt;/code&gt; file version (in addition to the &lt;code&gt;package.json&lt;/code&gt; file) when a new release is created. To do so, update your &lt;code&gt;release-please-config.json&lt;/code&gt; file to use &lt;code&gt;extra-files&lt;/code&gt;, like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;packages&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;release-type&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;extra-files&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;json&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;jsr.json&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;&quot;jsonpath&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;$.version&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      ]&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;The &lt;code&gt;&quot;path&quot;&lt;/code&gt; should be to the location of your &lt;code&gt;jsr.json&lt;/code&gt; file. The &lt;code&gt;&quot;jsonpath&quot;&lt;/code&gt; specifies the location of the field with a version number to update.&lt;/p&gt;\n&lt;p&gt;Then, you can publish to JSR using a GitHub action. Assuming you want to publish to both npm and JSR, it’s easiest to use the &lt;code&gt;jsr&lt;/code&gt; npm module, like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;branches&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;main&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;release-please&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;permissions&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;write&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;pull-requests&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;write&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;id-token&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;write&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;release-please&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;runs-on&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;steps&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;GoogleCloudPlatform/release-please-action@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;release&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;actions/setup-node@v4&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;node-version&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;lts/*&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;registry-url&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&apos;https://registry.npmjs.org&apos;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;npm ci&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Publish to npm&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;npm publish&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;          &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{secrets.NPM_TOKEN}}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;Publish to JSR&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;run&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          npm run build --if-present&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;          npx jsr publish&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #85E89D&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Keep in mind that &lt;code&gt;jsr publish&lt;/code&gt; does not run any scripts beforehand, so if you want to run something like &lt;code&gt;npm run build&lt;/code&gt;, you’ll need to explicitly specify it as part of your &lt;code&gt;run&lt;/code&gt; command.&lt;/p&gt;","tags":["JavaScript","TypeScript","npm","JSR"],"date_published":"2024-03-19T00:00:00.000Z","date_updated":"2024-03-19T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2023/11/how-to-setup-known-hosts-jenkins-pipeline/","url":"https://humanwhocodes.com/snippets/2023/11/how-to-setup-known-hosts-jenkins-pipeline/","title":"How to setup a known_hosts file for a Jenkins pipeline job","author":{"name":"Nicholas C. Zakas"},"summary":"The builtin Git Host Key Verification for Jenkins doesn't work in pipeline jobs. Here's what to do instead.","content_text":"\n[Jenkins](https://jenkins.io) pipelines are a great way to programmatically describe your build steps. Unforunately, that power also comes with responsibility, as a lot of the built-in Jenkins functionality may not work the way you imagine. Specifically, the Git Host Key Verification setting for Jenkins isn't used with pipeline jobs, making it common to get \"Host key verification errors\" when using Git. If you're using Git in a pipeline job, you'll need to set up your own `known_hosts` file.\n\nFirst, SSH into your host with an account that has `sudo` access.\n\nThen, make sure you have a `known_hosts` file that has all of the host information already present. In my case, I needed to add the keys for `github.com`, so I used this (via [GitHub blog](https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/)):\n\n```sh\ncurl -L https://api.github.com/meta | jq -r '.ssh_keys | .[]' | sed -e 's/^/github.com /' >> ~/.ssh/known_hosts\n```\n\nNext, verify that `/var/lib/jenkins/.ssh` exists. If it doesn't, then create it like this:\n\n```sh\n# create directory\nsudo mkdir /var/lib/jenkins/.ssh\n\n# ensure the directory is owned by the Jenkins user\nchown -R jenkins:jenkins /var/lib/.ssh\n```\n\nCopy your `known_hosts` file over:\n\n```sh\nsudo cp ~/.ssh/known_hosts /var/lib/jenkins/.ssh\n```\n\nAt this point, your Jenkins pipeline job has access to the new `known_hosts` file so you should be able to run it without any problem.\n","content_html":"&lt;p&gt;&lt;a href=&quot;https://jenkins.io&quot;&gt;Jenkins&lt;/a&gt; pipelines are a great way to programmatically describe your build steps. Unforunately, that power also comes with responsibility, as a lot of the built-in Jenkins functionality may not work the way you imagine. Specifically, the Git Host Key Verification setting for Jenkins isn’t used with pipeline jobs, making it common to get “Host key verification errors” when using Git. If you’re using Git in a pipeline job, you’ll need to set up your own &lt;code&gt;known_hosts&lt;/code&gt; file.&lt;/p&gt;\n&lt;p&gt;First, SSH into your host with an account that has &lt;code&gt;sudo&lt;/code&gt; access.&lt;/p&gt;\n&lt;p&gt;Then, make sure you have a &lt;code&gt;known_hosts&lt;/code&gt; file that has all of the host information already present. In my case, I needed to add the keys for &lt;code&gt;github.com&lt;/code&gt;, so I used this (via &lt;a href=&quot;https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/&quot;&gt;GitHub blog&lt;/a&gt;):&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-L&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;https://api.github.com/meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;jq&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-r&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&apos;.ssh_keys | .[]&apos;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-e&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&apos;s/^/github.com /&apos;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;~/.ssh/known_hosts&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next, verify that &lt;code&gt;/var/lib/jenkins/.ssh&lt;/code&gt; exists. If it doesn’t, then create it like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# create directory&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/var/lib/jenkins/.ssh&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# ensure the directory is owned by the Jenkins user&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;chown&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-R&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;jenkins:jenkins&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/var/lib/.ssh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Copy your &lt;code&gt;known_hosts&lt;/code&gt; file over:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;cp&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;~/.ssh/known_hosts&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/var/lib/jenkins/.ssh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;At this point, your Jenkins pipeline job has access to the new &lt;code&gt;known_hosts&lt;/code&gt; file so you should be able to run it without any problem.&lt;/p&gt;","tags":["Jenkins","Continuous Integration","Automation"],"date_published":"2023-11-15T00:00:00.000Z","date_updated":"2023-11-15T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2023/01/mimicking-dirname-filename-nodejs-esm/","url":"https://humanwhocodes.com/snippets/2023/01/mimicking-dirname-filename-nodejs-esm/","title":"Mimicking __dirname and __filename in ESM modules in Node.js","author":{"name":"Nicholas C. Zakas"},"summary":"One of the things that I miss most about using ESM is the lack of __dirname and __filename. Here's how to recreate those variables.","content_text":"\nCommonJS files running in Node.js have access to two very helpful variables:\n\n1. `__dirname` - the directory in which the current file lives.\n1. `__filename` - the full path to the current file.\n\nIn ECMAScript modules, however, these are no longer available by default. Fortunately, you can recreate them yourself to get the same information that to the `import.meta.url` property:\n\n```js\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n```\n\nThe `import.meta.url` property is a file URL, not a file path, so you first have to convert it into a file path. After that, you just need to use `path.dirname()` to pull off the directory.\n","content_html":"&lt;p&gt;CommonJS files running in Node.js have access to two very helpful variables:&lt;/p&gt;\n&lt;ol&gt;\n&lt;li&gt;&lt;code&gt;__dirname&lt;/code&gt; - the directory in which the current file lives.&lt;/li&gt;\n&lt;li&gt;&lt;code&gt;__filename&lt;/code&gt; - the full path to the current file.&lt;/li&gt;\n&lt;/ol&gt;\n&lt;p&gt;In ECMAScript modules, however, these are no longer available by default. Fortunately, you can recreate them yourself to get the same information that to the &lt;code&gt;import.meta.url&lt;/code&gt; property:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; { fileURLToPath } &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;node:url&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; path &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;node:path&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;__filename&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;fileURLToPath&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.url);&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;__dirname&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; path.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dirname&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(__filename);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;The &lt;code&gt;import.meta.url&lt;/code&gt; property is a file URL, not a file path, so you first have to convert it into a file path. After that, you just need to use &lt;code&gt;path.dirname()&lt;/code&gt; to pull off the directory.&lt;/p&gt;","tags":["Node.js","ECMAScript Modules"],"date_published":"2023-01-18T00:00:00.000Z","date_updated":"2023-01-18T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2022/02/eleventy-npm-modules-client/","url":"https://humanwhocodes.com/snippets/2022/02/eleventy-npm-modules-client/","title":"How to add npm packages for client-side use in Eleventy","author":{"name":"Nicholas C. Zakas"},"summary":"It's not immediately obvious how to use npm packages in an Eleventy website front end. Here's how to do it.","content_text":"\nSuppose that you want to use my [ArrayWithDefault](https://npmjs.com/package/@humanwhocodes/array-with-default) package on the front end of an [Eleventy](https://www.11ty.dev/) site. What's the easiest way to make that package available for inclusion in HTML?\n\nFirst, install the package:\n\n```\nnpm install @humanwhocodes/array-with-default\n```\n\nThen, in your Eleventy config file (`.eleventy.js`), add this inside of your default config function:\n\n```js\nmodule.exports = function(eleventyConfig) {\n\n    eleventyConfig.addPassthroughCopy({\n        \"./node_modules/@humanwhocodes/array-with-default/dist/array-with-default.js\": \"/assets/js/array-with-default.js\"\n    });\n\n};\n```\n\nNow, `array-with-default.js` is placed inside the `/assets/js` directory, and you can modify, process, or reference that file the same way that you do any other files in that directory.\n\nThis process works for any npm package that has a defined `dist` directory with standalone files; for other package you may need to first generate a distributable file before copying over into the correct location.\n","content_html":"&lt;p&gt;Suppose that you want to use my &lt;a href=&quot;https://npmjs.com/package/@humanwhocodes/array-with-default&quot;&gt;ArrayWithDefault&lt;/a&gt; package on the front end of an &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; site. What’s the easiest way to make that package available for inclusion in HTML?&lt;/p&gt;\n&lt;p&gt;First, install the package:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #e1e4e8&quot;&gt;npm install @humanwhocodes/array-with-default&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then, in your Eleventy config file (&lt;code&gt;.eleventy.js&lt;/code&gt;), add this inside of your default config function:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #FFAB70&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    eleventyConfig.&lt;/span&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;addPassthroughCopy&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;./node_modules/@humanwhocodes/array-with-default/dist/array-with-default.js&quot;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;&quot;/assets/js/array-with-default.js&quot;&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Now, &lt;code&gt;array-with-default.js&lt;/code&gt; is placed inside the &lt;code&gt;/assets/js&lt;/code&gt; directory, and you can modify, process, or reference that file the same way that you do any other files in that directory.&lt;/p&gt;\n&lt;p&gt;This process works for any npm package that has a defined &lt;code&gt;dist&lt;/code&gt; directory with standalone files; for other package you may need to first generate a distributable file before copying over into the correct location.&lt;/p&gt;","tags":["Eleventy","Static Site Generators","JavaScript"],"date_published":"2022-02-17T00:00:00.000Z","date_updated":"2022-02-17T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2021/03/create-user-linux-ssh-key/","url":"https://humanwhocodes.com/snippets/2021/03/create-user-linux-ssh-key/","title":"Creating a new user with an SSH key on Linux","author":{"name":"Nicholas C. Zakas"},"summary":"The various steps to successfully setup a new user with the best security.","content_text":"\nFirst, create a new user with `useradd`:\n\n```bash\nsudo useradd -m -d /home/username -s /bin/bash username\n```\n\nNext, set the user's password:\n\n```bash\npasswd username\n```\n\n**Note:** Even if you don't want the user to have a password, you should set the password to an empty string.\n\nThen, create the user's `.ssh` directory:\n\n```bash\nmkdir /home/username/.ssh\n```\n\nAnd copy the user's public key into the `authorized_keys` file. This is a plain text file where you can paste one public key per line. Here's an example of downloading the public key from their GitHub profile:\n\n```bash\ncurl https://github.com/<username>.keys > /home/username/.ssh/authorized_keys\n```\n\nAfter that, set up the correct permissions for both the `.ssh` directory and the `authorized_keys` file:\n\n```bash\n# ensure the directory ir owned by the new user\nchown -R username:username /home/username/.ssh\n\n# make sure only the new user has permissions\nchmod 700 /home/username/.ssh\nchmod 600 /home/username/.ssh/authorized_keys\n```\n\nLast, if you want the new user to have sudo access, be sure to add them to the `sudo` group:\n\n```bash\nsudo usermod -a -G sudo username\n```\n\nIf you don't have a `sudo` group,  you can manually edit the `/etc/sudoers` file.\n","content_html":"&lt;p&gt;First, create a new user with &lt;code&gt;useradd&lt;/code&gt;:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;useradd&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-m&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-d&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/home/username&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-s&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/bin/bash&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;username&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Next, set the user’s password:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;passwd&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;username&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Even if you don’t want the user to have a password, you should set the password to an empty string.&lt;/p&gt;\n&lt;p&gt;Then, create the user’s &lt;code&gt;.ssh&lt;/code&gt; directory:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/home/username/.ssh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;And copy the user’s public key into the &lt;code&gt;authorized_keys&lt;/code&gt; file. This is a plain text file where you can paste one public key per line. Here’s an example of downloading the public key from their GitHub profile:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;https://github.com/&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;usernam&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;.keys&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/home/username/.ssh/authorized_keys&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;After that, set up the correct permissions for both the &lt;code&gt;.ssh&lt;/code&gt; directory and the &lt;code&gt;authorized_keys&lt;/code&gt; file:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# ensure the directory ir owned by the new user&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;chown&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-R&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;username:username&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/home/username/.ssh&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# make sure only the new user has permissions&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;chmod&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;700&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/home/username/.ssh&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;chmod&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/home/username/.ssh/authorized_keys&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Last, if you want the new user to have sudo access, be sure to add them to the &lt;code&gt;sudo&lt;/code&gt; group:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;usermod&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-a&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;-G&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;username&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;If you don’t have a &lt;code&gt;sudo&lt;/code&gt; group,  you can manually edit the &lt;code&gt;/etc/sudoers&lt;/code&gt; file.&lt;/p&gt;","tags":["Linux","Users"],"date_published":"2021-03-01T00:00:00.000Z","date_updated":"2021-03-01T00:00:00.000Z"},{"id":"https://humanwhocodes.com/snippets/2021/02/how-to-setup-deploy-web-application-dokku/","url":"https://humanwhocodes.com/snippets/2021/02/how-to-setup-deploy-web-application-dokku/","title":"How to setup and deploy a web application on Dokku","author":{"name":"Nicholas C. Zakas"},"summary":"The path to set up a web application on Dokku is straightforward if you know the correct steps.","content_text":"\n[Dokku](https://dokku.com) is a lightweight, Heroku-like utility for deploying web applications. While there is a quickstart, web-based UI for getting started, there are a bunch of steps you'll need to go through in order to get your application deployed.\n\n## Create the app\n\nThe first step is to SSH into the droplet and create the application. I'll use the name `appname` for this post:\n\n```bash\ndokku apps:create appname\n```\n\n## Add a domain for the app\n\nDokku can use virtual hosts to identify which application to route a request to. You can [add a hostname](https://dokku.com/docs/configuration/domains/) to your application like this:\n\n```bash\ndokku domains:add appname app.example.com\n```\n\nHere, the hostname is set to `app.example.com`, so all requests coming in to that host will route to `appname`.\n\n**Important:** Don't forget to setup the DNS settings for your hostname.\n\n## Add a deploy key\n\nIf you are using your Dokku application in production, you probably want to [add a separate key](https://dokku.com/docs/deployment/user-management/#adding-ssh-keys) for a deployment account.\n\n```bash\ndokku ssh-keys:add KEY_NAME /path/to/key.pub\n```\n\n`KEY_NAME` is just the name of the key so you can refer to it later.\n\n## Setup SSL\n\nBy default, your Dokku application will be running over plain HTTP without any SSL certificates. To enable SSL, you'll need to install the [Let's Encrypt Dokku plugin](https://github.com/dokku/dokku-letsencrypt). To enable Let's Encrypt on your Dokku application, follow these steps (credit: [Setup Dokku on Digital Ocean](https://www.robertcooper.me/setup-dokku-digital-ocean)):\n\n```bash\n# Install the plugin\nsudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git\n\n# Set an email address for Let's Encrypt to use\ndokku config:set --no-restart appname DOKKU_LETSENCRYPT_EMAIL=name@example.com\n\n# Install Let's Encrypt for the given app\ndokku letsencrypt appname\n\n# Set a cron job to automatically renew Let's Encrypt certificates\ndokku letsencrypt:cron-job --add\n```\n\n## Deploy your application\n\nWith all of that setup, you're now ready to deploy your application. To do so, add a Git remote that points to the Dokku location:\n\n```bash\ngit remote add dokku dokku@app.example.com:appname\n```\n\nThen, you can deploy by pushing the code to Dokku:\n\n```bash\ngit push dokku master\n```\n\nDokku expects the primary branch to be `master`, so if your primary branch is `main`, then use this command:\n\n```bash\ngit push dokku main:master\n```\n\nEnjoy your newly deployed Dokku app!\n","content_html":"&lt;p&gt;&lt;a href=&quot;https://dokku.com&quot;&gt;Dokku&lt;/a&gt; is a lightweight, Heroku-like utility for deploying web applications. While there is a quickstart, web-based UI for getting started, there are a bunch of steps you’ll need to go through in order to get your application deployed.&lt;/p&gt;\n&lt;h2 id=&quot;create-the-app&quot;&gt;Create the app&lt;/h2&gt;\n&lt;p&gt;The first step is to SSH into the droplet and create the application. I’ll use the name &lt;code&gt;appname&lt;/code&gt; for this post:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;apps:create&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;appname&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&quot;add-a-domain-for-the-app&quot;&gt;Add a domain for the app&lt;/h2&gt;\n&lt;p&gt;Dokku can use virtual hosts to identify which application to route a request to. You can &lt;a href=&quot;https://dokku.com/docs/configuration/domains/&quot;&gt;add a hostname&lt;/a&gt; to your application like this:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;domains:add&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;appname&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;app.example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Here, the hostname is set to &lt;code&gt;app.example.com&lt;/code&gt;, so all requests coming in to that host will route to &lt;code&gt;appname&lt;/code&gt;.&lt;/p&gt;\n&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Don’t forget to setup the DNS settings for your hostname.&lt;/p&gt;\n&lt;h2 id=&quot;add-a-deploy-key&quot;&gt;Add a deploy key&lt;/h2&gt;\n&lt;p&gt;If you are using your Dokku application in production, you probably want to &lt;a href=&quot;https://dokku.com/docs/deployment/user-management/#adding-ssh-keys&quot;&gt;add a separate key&lt;/a&gt; for a deployment account.&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;ssh-keys:add&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;/path/to/key.pub&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;&lt;code&gt;KEY_NAME&lt;/code&gt; is just the name of the key so you can refer to it later.&lt;/p&gt;\n&lt;h2 id=&quot;setup-ssl&quot;&gt;Setup SSL&lt;/h2&gt;\n&lt;p&gt;By default, your Dokku application will be running over plain HTTP without any SSL certificates. To enable SSL, you’ll need to install the &lt;a href=&quot;https://github.com/dokku/dokku-letsencrypt&quot;&gt;Let’s Encrypt Dokku plugin&lt;/a&gt;. To enable Let’s Encrypt on your Dokku application, follow these steps (credit: &lt;a href=&quot;https://www.robertcooper.me/setup-dokku-digital-ocean&quot;&gt;Setup Dokku on Digital Ocean&lt;/a&gt;):&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# Install the plugin&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;plugin:install&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;https://github.com/dokku/dokku-letsencrypt.git&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# Set an email address for Let&apos;s Encrypt to use&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;config:set&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;--no-restart&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;appname&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;DOKKU_LETSENCRYPT_EMAIL=name@example.com&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# Install Let&apos;s Encrypt for the given app&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;letsencrypt&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;appname&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #6A737D&quot;&gt;# Set a cron job to automatically renew Let&apos;s Encrypt certificates&lt;/span&gt;&lt;/span&gt;\n&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;letsencrypt:cron-job&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #79B8FF&quot;&gt;--add&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;h2 id=&quot;deploy-your-application&quot;&gt;Deploy your application&lt;/h2&gt;\n&lt;p&gt;With all of that setup, you’re now ready to deploy your application. To do so, add a Git remote that points to the Dokku location:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;remote&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dokku@app.example.com:appname&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Then, you can deploy by pushing the code to Dokku:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Dokku expects the primary branch to be &lt;code&gt;master&lt;/code&gt;, so if your primary branch is &lt;code&gt;main&lt;/code&gt;, then use this command:&lt;/p&gt;\n&lt;pre is:raw=&quot;&quot; class=&quot;astro-code github-dark&quot; style=&quot;background-color: #24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color: #B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;dokku&lt;/span&gt;&lt;span style=&quot;color: #E1E4E8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9ECBFF&quot;&gt;main:master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;p&gt;Enjoy your newly deployed Dokku app!&lt;/p&gt;","tags":["Dokku","Docker","Web Applications"],"date_published":"2021-02-25T00:00:00.000Z","date_updated":"2021-02-25T00:00:00.000Z"}]}