Post

My Blogging Setup

My blogging setup, aka how to create a blog with Obsidian and Chirpy (Jekyll theme)

My Blogging Setup

Though I use Jekyll, this should technically work with any blogging framework. This tutorial assumes that you are familiar with Jekyll, GitHub Pages, etc.

Requirements

Setup

Obsidian Theme

I’ve always been a fan of dark mode color scheme, especially solarized dark. I also prefer to avoid cluttered UI, which is why I use the Minimal Theme Settings plugin.

  1. Open Settings (Command ⌘ + ,) in Obsidian

  2. Go to Community plugins tab
  3. If it isn’t already, turn off Restricted mode to enable community plugins

  4. Click Browse, which is next to Community plugins

  5. Enter Minimal Theme Settings in the searchbar

  6. Click on Minimal Theme Settings, then click Install

  7. Once it’s installed, go to Appearance tab

  8. Next to Themes, select Minimal to apply the theme

  9. Go to Minimal Theme Settings tab

  10. Modify the settings to your liking

    Here are mine, for reference:

    color_scheme.png features.png layout_typography.png

Obsidian Mobile App

I use Obsidian’s iPhone app with iCloud, which I’ll walk through in this section, though Obsidian also offers Obsidian Sync as an alternative. If you don’t have an Apple device, iCloud isn’t applicable.

  1. Once the Obsidian app is downloaded, check your iCloud Drive: There should be an Obsidian folder; if not, make one

  2. Move all your vaults (i.e. directories) to Obsidian folder. E.g. my vault is named Writing, with path of iCloud Drive/Obsidian/Writing/ (which, in my case, points to $HOME/Library/Mobile Documents/iCloud~md~obsidian/Documents/Writing/)

Setup Enveloppe

  1. Launch Obsidian

  2. Open Settings (Command ⌘ + ,)

  3. Go to Community plugins tab

  4. Turn off Restricted mode to enable community plugins

  5. Click Browse, which is next to Community plugins

  6. Enter Enveloppe in the searchbar

  7. Click on Enveloppe, then click Install

  8. Once Enveloppe’s installed, go to its settings (i.e. under Community plugins section in the settings sidebar, click Enveloppe)

  9. Copy my Enveloppe settings

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    
     {
       "github": {
         "branch": "main",
         "automaticallyMergePR": true,
         "dryRun": {
           "enable": false,
           "folderName": "enveloppe"
         },
         "api": {
           "tiersForApi": "Github Free/Pro/Team (default)",
           "hostname": ""
         },
         "workflow": {
           "commitMessage": "[OBSIDIAN] Merge",
           "name": ""
         },
         "verifiedRepo": true
       },
       "upload": {
         "behavior": "fixed",
         "defaultName": "_posts",
         "rootFolder": "",
         "yamlFolderKey": "",
         "frontmatterTitle": {
           "enable": true,
           "key": "filename"
         },
         "replaceTitle": [
           {
             "regex": "/\\s+/",
             "replacement": "-",
             "type": "title"
           }
         ],
         "replacePath": [],
         "autoclean": {
           "includeAttachments": true,
           "enable": false,
           "excluded": []
         },
         "folderNote": {
           "enable": false,
           "rename": "index.md",
           "addTitle": {
             "enable": false,
             "key": "title"
           }
         },
         "metadataExtractorPath": ""
       },
       "conversion": {
         "hardbreak": false,
         "dataview": true,
         "censorText": [],
         "tags": {
           "inline": false,
           "exclude": [],
           "fields": []
         },
         "links": {
           "internal": true,
           "unshared": true,
           "wiki": true,
           "slugify": "strict",
           "unlink": true,
           "relativePath": true,
           "textPrefix": "/"
         }
       },
       "embed": {
         "attachments": true,
         "overrideAttachments": [],
         "keySendFile": [],
         "notes": false,
         "folder": "assets/img/obsidian",
         "convertEmbedToLinks": "keep",
         "charConvert": "->",
         "unHandledObsidianExt": [],
         "sendSimpleLinks": true,
         "forcePush": true,
         "useObsidianFolder": false
       },
       "plugin": {
         "shareKey": "share",
         "excludedFolder": [
           "templates"
         ],
         "copyLink": {
           "enable": false,
           "links": "",
           "removePart": [],
           "addCmd": false,
           "transform": {
             "toUri": true,
             "slugify": "lower",
             "applyRegex": []
           }
         },
         "setFrontmatterKey": "Set"
       }
     }
    
  10. Click Import settings and paste the copied enveloppe.json (from the previous step) where it says Paste configuration here..., then click Save

  11. Under GitHub config, enter your GitHub username, Repository name, and — if your main branch is not named mainMain branch name

  12. Generate a fine-grained personal access token for your GitHub repository in order to give Enveloppe necessary permissions to work by going to your GitHub settings

  13. Scroll down and click Developer settings

  14. Click Personal access tokens, click Fine-grained tokens, then click Generate new token

  15. Enter a descriptive Token name (e.g. Enveloppe (Obsidian)) and Description (e.g. Enveloppe (Obsidian Vault → GitHub Repo))

  16. Choose your GitHub account as Resource owner

  17. Select No expiration for Expiration

  18. Under Repository access, click Only select repositories then click Select repositories and select the GitHub repository for your Jekyll blog (e.g. lynkos/blog)

  19. Click Repository permissions under Permissions

  20. Always choose the minimal permissions necessary, so all options should be set to Access: No access, with the exception of the following:

    PermissionAccessReason
    ContentsRead and writeCreate branch
    MetadataRead-onlyMandatory
    Pull requestsRead and writeCreate and merge pull requests
    WorkflowsRead and writeCreate/update file
  21. Click Generate token

  22. Copy the generated GitHub personal access token; it should start with github_ followed by a long, random string of alphanumeric characters and underscores

  23. Back in Enveloppe settings, paste it in the GitHub token area

Setup Comments

  1. If you haven’t already, make your blog’s GitHub repository public

  2. Go to your GitHub repository’s settings (i.e. https://github.com/GITHUB_USERNAME/REPO/settings), scroll down to Features and enable the Discussions feature

  3. Go to the Discussions tab (i.e. https://github.com/GITHUB_USERNAME/REPO/discussions), then click the pencil icon next to Categories

  4. Edit the default Announcements category:
    • Category name: Comments
    • Category icon: 💬
    • Description: Comments on posts
    • Discussion Format: Announcement
  5. Click Save changes

  6. (OPTIONAL) Delete all other default categories, if you don’t need it, by clicking the trash icon next to the category name

  7. Install the Giscus app on GitHub

  8. Go to Giscus and fill out the form

  9. Copy the auto-generated <script> under Enable giscus

    Example code with my configuration:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
     <script src="https://giscus.app/client.js"
         data-repo="<GITHUB_USERNAME>/<REPO>"
         data-repo-id="<REPO_ID>"
         data-category="Comments"
         data-category-id="<CATEGORY_ID>"
         data-mapping="pathname"
         data-strict="0"
         data-reactions-enabled="1"
         data-emit-metadata="0"
         data-input-position="top"
         data-lang="en"
         data-loading="lazy"
         crossorigin="anonymous"
         async>
     </script>
    
  10. Add the <script> tag
    • If you’re NOT using jekyll-theme-chirpy, to your website’s template where you want the comments to appear (Note: If an element with the giscus class exists, the comments will be placed there instead)
    • If you ARE using the Jekyll theme Chirpy, continue to the next section

OPTIONAL: ADD COMMENTS FEATURE TO jekyll-theme-chirpy

  1. Go to _config.yml in your repository

  2. Find the comments section

  3. Set provider to giscus

  4. Fill in all options under giscus with the values from the <script> you copied in Step #5

    Example code with my configuration for comments section:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    comments:
      # Global switch for the post-comment system. Keeping it empty means disabled.
      provider: giscus # [disqus | utterances | giscus]
      # The provider options are as follows:
      disqus:
        shortname: # fill with the Disqus shortname. › https://help.disqus.com/en/articles/1717111-what-s-a-shortname
      # utterances settings › https://utteranc.es/
      utterances:
        repo: # <gh-username>/<repo>
        issue_term: # < url | pathname | title | ...>
      # Giscus options › https://giscus.app
      giscus:
        repo: <GITHUB_USERNAME>/<REPO>
        repo_id: <REPO_ID>
        category: "Comments"
        category_id: <CATEGORY_ID>
        mapping: # optional, default to 'pathname'
        strict: # optional, default to '0'
        input_position: "top" # optional, default to 'bottom'
        lang: # optional, default to the value of `site.lang`
        loading: lazy # comments loading will be deferred till user scrolls near comments container
        reactions_enabled: # optional, default to the value of `1`
    

Setup Image Proxy

A custom Cloudflare Worker hotlinks images from sites that may restrict it; this way I can embed images from Twitter/X. Custom Ruby plugin _plugins/gallery.rb automatically prepends Twitter/X URLs within a gallery with the value of worker_base_url. Additional sites will be added as needed. This proxy currently only supports Twitter/X.

  1. Sign up and/or login to Cloudflare

  2. Go to your dashboard

  3. In the left pane, click Compute (Workers), then Workers & Pages

  4. Click the blue Create application button in the upper-right corner

  5. In the Workers tab, click the blue Get Started button to the right of Start with Hello World!

  6. Name the worker img-proxy

  7. Click Deploy

  8. You should be redirected to a page that says Success! Your project is deployed to Region: …

  9. Click the Edit Code button; you can also access this page later by going to Compute (Workers) > Workers & Pages, clicking the worker (i.e. img-proxy), then clicking the small icon </> in the upper-right corner

  10. This should open the Cloudflare Workers IDE; replace the default code in worker.js with the following code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    export default {
      /**
       * @param  request
       */
      async fetch(request) {
        const url = new URL(request.url);
        const target = url.searchParams.get("url"); // i.e. ?url=https://pbs.twimg.com/media/...
    
        if (!target) {
          return new Response("Missing ?url= param", { status: 400 });
        }
    
        try {
          const resp = await fetch(target, {
            headers: { "User-Agent": "Mozilla/5.0" }
          });
    
          const headers = new Headers(resp.headers);
          headers.set("Access-Control-Allow-Origin", "*");
    
          return new Response(await resp.arrayBuffer(), {
            status: resp.status,
            headers
          });
        } catch (err) {
          return new Response("Fetch error: " + err.message, { status: 502 });
        }
      }
    };
    

    Test the worker in the Preview pane by adding ?url= and the link to an image on Twitter (e.g. https://pbs.twimg.com/media/GzPhoaKWoAA16uA?format=jpg&name=medium) following AFTER your worker’s URL (i.e. https://img-proxy.<YOUR_DOMAIN_NAME>.workers.dev/) in the input field

    So, in this example, the full URL is: https://img-proxy.<YOUR_DOMAIN_NAME>.workers.dev/?url=https://pbs.twimg.com/media/GzPhoaKWoAA16uA?format=jpg&name=medium

    Click the Reload button (circular arrow icon) to the right of the input field to test it; if successful, you should see the image in the Preview pane

  11. Click Deploy in the upper-right corner

  12. You can now use this worker as an image proxy by using the following URL format, where <YOUR_DOMAIN_NAME> is your Cloudflare domain (e.g. example.com) and <IMAGE_URL> is the full URL of the image you want to hotlink (e.g. https://pbs.twimg.com/media/GzPhoaKWoAA16uA?format=jpg&name=medium):

    1
    
    https://img-proxy.<YOUR_DOMAIN_NAME>.workers.dev/?url=<IMAGE_URL>
    

OPTIONAL: USE CUSTOM DOMAIN (e.g. img-proxy.<YOUR_ROOT_DOMAIN>)

  1. In the left pane, click DNS, then Records

  2. Click the blue + Add record button, then add a new record with the following details:
    • Type: CNAME
    • Name: img-proxy (or whatever subdomain you want to use)
    • IPv4 address: workers.dev
    • Proxy status: Proxied aka Enabled (orange cloud icon)
    • TTL: Auto
    • Comment: Map img-proxy worker (at img-proxy.<YOUR_DOMAIN_NAME>.workers.dev) to img-proxy.<YOUR_ROOT_DOMAIN>
  3. Click Save

  4. Click Workers Routes in the left pane, then click the blue Add route button in the HTTP Routes section

  5. In the Route field, enter img-proxy.<YOUR_ROOT_DOMAIN>/*

  6. Under Worker, select img-proxy worker (or whatever you named it) from the dropdown

  7. Click Save

  8. You can now use the custom domain for your image proxy at https://img-proxy.<YOUR_ROOT_DOMAIN>/?url=<IMAGE_URL> (e.g. https://img-proxy.example.com/?url=https://pbs.twimg.com/media/GzPhoaKWoAA16uA?format=jpg&name=medium)

Create Hotkeys

  1. Launch Obsidian

  2. Save the template in Default Template to your vault (e.g. templates/default.txt)

  3. Open Settings (Command ⌘ + ,)

  4. Go to Hotkeys tab

  5. Scroll to Templates: Insert template

  6. Click the + icon (says Customize this command when you hover over it)

  7. Press Command ⌘ + Shift ⇧ + T, or your preferred hotkey

  8. Scroll to Enveloppe: Upload single current active note

  9. Click the + icon (says Customize this command when you hover over it)

  10. Press Command ⌘ + Shift ⇧ + P, or your preferred hotkey

Now we can automatically apply the template to any new post and auto-publish your Obsidian post to your blog with those hotkeys!

Make sure share: true is in the frontmatter, otherwise it won’t post

Writing Posts

Default Template

You’ll need to add the following to the top of every Markdown post you make

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---
share: false
title: {{title}}
date: {{date:YYYY-MM-DD}} {{time:HH:mm:ss ZZ}}
filename: "{{date:YYYY-MM-DD}}-{{title}}"
description: Post summary.
math: false
mermaid: false
pin: false
toc: true
comments: true
categories: [top_category, sub_category]
tags: [tag]
image:
  path: /relative/path/to/image
  lqip: minified_image_encoded_in_base64
  alt: Image caption
---
KeyDescription
shareWhen true, it will push to Github
titlePost title
dateDate and time created; format yyyy-mm-dd hh:mm:ss utc_offset
filenameWhere your file will be saved in Github; format yyyy-mm-dd-title-here
descriptionOptional post description; will appear below post title and in previews
mathWhen true, enables MathJax for LaTeX processing
mermaidWhen true, enables Mermaid for diagram rendering
pinWhen true, it will pin this post on website
tocWhen false, the table of contents is hidden
commentsWhen false, the comments section is hidden
categoriesOptional list of categories; 1st element is main category, remaining elements are subcategories
tagsOptional list of tags
pathOptional path to preview image
lqipOptional preview image’s base64-encoded LQIP
altOptional caption and alt text for preview image (can be left blank)

Add Preview Image

To add a preview image to a post, make sure the following is in your Markdown file’s front matter

1
2
3
4
image:
  path: /path/to/image.png
  lqip: base64-encoded-lqip
  alt: Optional image caption and alt text

To generate a base64-encoded LQIP for the lqip field:

  1. Visit lqip generator

  2. Upload your preview image

  3. Customize the options as needed; here are my recommendations:
    • Placeholder Size: Small
    • Quality: 60
    • Blur Amount: 5px
  4. Click Generate LQIP

  5. Right-click the generated image that appears under LQIP (Scaled & Blurred)

  6. Depending on your browser, copy the image link directly (i.e. click Copy Image Link) OR open the image in a new tab (i.e. Open Image in New Tab) and copy the URL in the address bar

    Image link MUST be a base64 string that starts with data:content/type;base64

  7. Paste the base64 string (aka image link) in the lqip field of the front matter (i.e. use it to replace base64-encoded-lqip)

    To verify that the base64 string works, visit Base64 Image Viewer and paste the base64 string in the input field below Base64 Image String

Add Runnable Code Blocks

  1. Add {: run="<language>" } AFTER the Markdown code snippet of a supported language

    LanguageValues
    JavaScriptjavascript, js
    Pythonpython, py
  2. Click the ▶︎ button in the upper-right corner to run the code snippet in browser

E.g. This Markdown code:

1
2
3
4
5
6
7
```python
def main():
    print("Hello World from Python!")

main()
```
{: run="python" }

Results in this executable Python code block:

1
2
3
4
def main():
    print("Hello World from Python!")

main()

As another example, this Markdown code:

1
2
3
4
5
6
7
8
```js
function main() {
    console.log("Hello World from JavaScript!");
}

main();
```
{: run="js" }

Results in this executable JavaScript code block:

1
2
3
4
5
function main() {
    console.log("Hello World from JavaScript!");
}

main();

Appendix

Workflow

---
title: CI/CD Pipeline
---
graph TD
    Trigger([Push to Main / PR]) --> Checkout[Checkout Code]
            
    Checkout --> SetupEnv[Setup Pages]
    
    subgraph Setup Environment
        SetupEnv --> SetupRuby[Setup Ruby]
    end
    
    subgraph Build
        SetupRuby --> BuildSite[Build Site]
        BuildSite --> GenerateCNAME[Generate CNAME]
        GenerateCNAME --> GenerateNojekyll[Generate .nojekyll]
    end

    GenerateNojekyll --> JoinNode{Successful?}
    JoinNode -- Yes --> UploadArtifact[Upload site artifact]
    JoinNode -- No --> JobFail([Pipeline Failed])
    UploadArtifact --> JobPass([Deploy to GitHub Pages])
    
    classDef fail fill:#cf222e,stroke:#fff,stroke-width:1px,color:#fff;
    classDef pass fill:green,stroke:#fff,stroke-width:1px,color:#fff;
    class JobFail fail;
    class JobPass pass;

Sync Fork with Upstream

To keep fork up-to-date with original repository (i.e. Chirpy)

  1. Link the upstream (i.e. original) repository to fork

    1
    
     git remote add upstream https://github.com/cotes2020/jekyll-theme-chirpy.git
    

    Use this command if you’ve already linked the upstream repository and want to re-link it

    1
    
    git remote set-url upstream https://github.com/cotes2020/jekyll-theme-chirpy.git
    
  2. Confirm the remote URL with either command
    • Command #1
      1
      
      git remote show
      
    • Example Output #1
      1
      2
      
      origin
      upstream
      
    • Command #2
      1
      
      git remote -v
      
    • Example Output #2
      1
      2
      3
      4
      
      origin  https://github.com/lynkos/blog.git (fetch)
      origin  https://github.com/lynkos/blog.git (push)
      upstream        https://github.com/cotes2020/jekyll-theme-chirpy.git (fetch)
      upstream        https://github.com/cotes2020/jekyll-theme-chirpy.git (push)
      
  3. Fetch latest changes from upstream repository

    1
    
     git fetch upstream master
    
  4. Switch to master branch (so it’s recognized)

    1
    
     git checkout master
    
  5. Switch to the branch you want to sync

    1
    
     git checkout main
    
  6. Merge changes from upstream into local branch

    1
    
     git merge upstream/main
    
  7. Push changes to your fork

    1
    
     git push origin main
    

Contribute Upstream

Continue reading if you want to create a pull request in jekyll-theme-chirpy with only a subset of your commits. This is useful if you want to add a feature to upstream without committing all your changes.

Create Branch

If you haven’t already, complete Step 1 and Step 2 in Sync Fork with Upstream section before continuing.

Manually via GitHub
  1. Go to your GitHub repository (i.e. https://github.com/USERNAME/REPOSITORY_NAME)

  2. Navigate to your GitHub repository’s branches page (i.e. https://github.com/USERNAME/REPOSITORY_NAME/branches):
    • Click N Branches, where N is your GitHub repository’s current number of branches
    • Alternatively, click the button for your default branch, which is main in my case, then click View all branches
  3. Click New branch

  4. Enter the new branch’s name (i.e. BRANCH_NAME), then select cotes2020/jekyll-theme-chirpy repository and master branch (under Source)

  5. Click Create new branch

  6. The branch should now appear on your GitHub repository’s branches page under Your branches and Active branches

  7. Go (i.e. cd /path/to/REPOSITORY_NAME) to your repository and fetch the newly created branch

    1
    
     git fetch origin BRANCH_NAME
    

    To fetch ALL remote branches:

    1
    
    git fetch origin
    
  8. Switch to your branch

    1
    
     git checkout -b BRANCH_NAME
    
  9. Make your changes, then stage, commit, and push them

    1
    2
    
     git commit -am "COMMIT_MESSAGE"
     git push origin BRANCH_NAME
    

    Refer to Commit Message Conventions section for commit message guidelines

  10. Go to the originally forked repository, i.e. jekyll-theme-chirpy

  11. Click the New pull request button

  12. Complete the pull request template accordingly

  13. Click the Create pull request button
Cherry Picking Commits
  1. Create a new branch, e.g. BRANCH_NAME

    1
    
     git checkout -b BRANCH_NAME upstream/master
    
  2. Cherry pick the commit(s) you want to include in the PR

    1
    
     git cherry-pick COMMIT_HASH
    
  3. Push your branch

    1
    
     git push origin BRANCH_NAME
    
  4. If successful, the terminal should output something similar to this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    Enumerating objects: 111, done.
    Counting objects: 100% (83/83), done.
    Delta compression using up to 16 threads
    Compressing objects: 100% (50/50), done.
    Writing objects: 100% (51/51), 8.50 KiB | 1.70 MiB/s, done.
    Total 51 (delta 36), reused 0 (delta 0), pack-reused 0 (from 0)
    remote: Resolving deltas: 100% (36/36), completed with 26 local objects.
    remote: 
    remote: Create a pull request for 'BRANCH_NAME' on GitHub by visiting:
    remote:      https://github.com/GITHUB_USERNAME/REPOSITORY_NAME/pull/new/BRANCH_NAME
    remote: 
    To https://github.com/GITHUB_USERNAME/REPOSITORY_NAME.git
     * [new branch]      BRANCH_NAME -> BRANCH_NAME
    
  5. Go to the originally forked repository, i.e. jekyll-theme-chirpy

  6. Click the New pull request button

  7. Complete the pull request template accordingly

  8. Click the Create pull request button

Delete Branch (Optional)

Once you no longer need a branch (e.g. its PR has been approved and merged), you can delete it

  1. Delete it remotely with one of the following methods
    • Via command line
      1
      
      git push origin -d BRANCH_NAME
      
    • Via GitHub.com
      1. Go to your GitHub repository’s branches page (i.e. https://github.com/USERNAME/REPOSITORY_NAME/branches)
      2. Find your branch
      3. Click the Trash icon on the right-hand side of the branch’s row
  2. Remove stale references (i.e. pruning)

    1
    
     git fetch -p
    

    To automatically prune after every fetch:

    • Command line
      1
      
        git config --global fetch.prune true
      
    • Visual Studio Code
      1. Open Command Palette (shortcut: Command ⌘ + Shift ⇧ + P)

      2. Select Preferences: Open User Settings (JSON) (global settings) OR select Preferences: Open Workspace Settings (JSON) (local settings)

      3. Add the following setting, then save your changes (shortcut: Command ⌘ + S)

        1
        
         "git.allowForcePush": true
        
  3. Delete it locally

    1
    
     git branch -D BRANCH_NAME
    
  4. Confirm it’s successfully been pruned and deleted by making sure it doesn’t appear in the output
    • Command
      1
      
      git branch
      
    • Example Output

      1
      
      * main
      

Commit Message Conventions

Commit messages should follow this format:

1
2
3
4
5
<type>(<optional scope>): <description>

[optional body]

[optional footer]
TypeDescription
fixChange that fixes a bug or error
featChange that adds, adjusts, or removes a feature
refactorChange that rewrites or restructures code without altering behavior
perfChange that improves performance; subset of refactor
styleChange that addresses code style (e.g. white-space, formatting, missing semi-colons) without affecting behavior
testChange that adds or changes test(s)
buildChange that affects build system or component(s) (e.g. build tools, external dependencies, project version)
docsChange to documentation
opsChange that affects operational aspects (e.g. infra, DevOps, backups, monitoring, recovery procedures)
choreChange to routine or automated tasks (e.g. dependency update, modifying config files, non-functional tasks)
releaseChange that relates to a new version release
deprecateChange that deprecates functionality
revertReverts to a previous commit
---
title: Commit Message Flowchart
---
flowchart TD
   T[Did you deprecate a feature?]
   T -- Yes --> U[deprecate]
   T -- No --> V[Did you revert to a previous commit?]

   V -- Yes --> W[revert]
   V -- No --> X[Did you create a new release?]

   X -- Yes --> Y[release]
   X -- No --> A[Did you fix a bug?]

   A -- Yes --> B[fix]
   A -- No --> C[Did you change functionality or UI?]

   C -- Yes --> D[feat]
   C -- No --> E[Did you add or change tests?]

   E -- Yes --> F[test]
   E -- No --> G[Did you change code style or formatting?]

   G -- Yes --> H[style]
   G -- No --> I[Did you make changes to documentation?]

   I -- Yes --> J[docs]
   I -- No --> K[Did you change things related to build or deploy operations?]

   K -- Yes --> L[build]
   K -- No --> M[Did you change something related to DevOps, infra, or backups?]

   M -- Yes --> N[ops]
   M -- No --> O[Did you complete a maintenance or non-code task?]

   O -- Yes --> P[chore]
   O -- No --> Q[Did you rewrite or restructure code specifically for performance?]

   Q -- Yes --> R[perf]
   Q -- No --> S[refactor]

Credits

This post is licensed under CC BY 4.0 by the author.