[{"data":1,"prerenderedAt":587},["ShallowReactive",2],{"$B3SvcJdz-H":3,"pages-links":23,"posts-links":29,"posts-search":58,"blog-conventional-commit-caveats":129,"decks-for-conventional-commit-caveats":582,"meta":583},{"id":4,"title":5,"author":6,"description":14,"extension":15,"meta":16,"repository":17,"stem":21,"__hash__":22},"meta\u002Fmeta.yml","bmo.dev",{"name":7,"github":8,"gitlab":9,"email":10,"linkedin":11,"mastodon":12,"bio":13},"William Little","https:\u002F\u002Fgithub.com\u002Fbmodotdev\u002F","https:\u002F\u002Fgitlab.com\u002Fbmodotdev\u002F","L29hqTSwqROvoJ8hMTI2","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fwilliam-e-little-jr\u002F","https:\u002F\u002Fdefcon.social\u002F@bmodotdev","I am a Full Stack Software & DevOps Engineer who loves building and experimenting with Software, Linux, & electronics.\n","A personal blog where I share my thoughts, projects, and anything I might find interesting or helpful.\n","yml",{},{"url":18,"git":19,"branch":20},"https:\u002F\u002Fgitlab.com\u002Fbmodotdev\u002Fbmo.dev\u002F","git@gitlab.com:bmodotdev\u002Fbmo.dev.git","main","meta","MPtcc2KHfBCBy4V4SDhZFIvjyZ_Ba4e-yDnx_Ok_KOs",[24],{"path":25,"extension":26,"title":27,"id":28},"\u002Fabout","md","About","pages\u002Fabout.md",[30,34,38,42,46,50,54],{"path":31,"extension":26,"title":32,"id":33},"\u002Fblog\u002Fconventional-commit-caveats","Conventional Commit Caveats","blog\u002Fblog\u002Fconventional-commit-caveats.md",{"path":35,"extension":26,"title":36,"id":37},"\u002Fblog\u002Fdodging-a-critical-cve-with-zta","Dodging a Critical CVE with ZTA","blog\u002Fblog\u002Fdodging-a-critical-cve-with-zta.md",{"path":39,"extension":26,"title":40,"id":41},"\u002Fblog\u002Flog4perl-mixed-screen-appender","Mixed Output Streams with Log4perl Screen Appender","blog\u002Fblog\u002Flog4perl-mixed-screen-appender.md",{"path":43,"extension":26,"title":44,"id":45},"\u002Fblog\u002Fmake-version","make version","blog\u002Fblog\u002Fmake-version.md",{"path":47,"extension":26,"title":48,"id":49},"\u002Fblog\u002Fpadding-oracle-attacks-rubiks-cow","Padding Oracle Attacks & Rubik’s Cow","blog\u002Fblog\u002Fpadding-oracle-attacks-rubiks-cow.md",{"path":51,"extension":26,"title":52,"id":53},"\u002Fblog\u002Ftracing-signals-in-linux","Tracing Signals in Linux","blog\u002Fblog\u002Ftracing-signals-in-linux.md",{"path":55,"extension":26,"title":56,"id":57},"\u002Fblog\u002Fwhy-are-go-modules-in-my-nodes-modules","Why are Go modules in my node_modules?","blog\u002Fblog\u002Fwhy-are-go-modules-in-my-nodes-modules.md",[59,69,81,90,100,108,117],{"path":55,"title":56,"description":60,"tags":61,"thumbnail":64,"created":68},"Let’s dissect this developer pattern that leads to arbitrary code execution across languages.",[62,63],"go","js",{"sm":65,"md":66,"lg":67},"\u002Fassets\u002Fdavid-mitchell-great-british-baking-show_sm.webp","\u002Fassets\u002Fdavid-mitchell-great-british-baking-show_md.webp","\u002Fassets\u002Fdavid-mitchell-great-british-baking-show_lg.webp","2026-04-07",{"path":35,"title":36,"description":70,"tags":71,"thumbnail":76,"created":80},"Let’s discuss tenets of Zero Trust Architecture, Go, gRPC, and why everyone dodged CVE-2026-33186, right?",[72,73,62,74,75],"awk","cve","grpc","zta",{"sm":77,"md":78,"lg":79},"\u002Fassets\u002Fdodging-the-flyer_sm.webp","\u002Fassets\u002Fdodging-the-flyer_md.webp","\u002Fassets\u002Fdodging-the-flyer_lg.webp","2026-04-02",{"path":47,"title":48,"description":82,"tags":83,"thumbnail":85,"created":89},"In this thought experiment, you’ll learn about cryptographic primitives, combinitorical group theory, and along the way, gain a deeper appreciation for 1999’s film, The Matrix. Oh yeah, and a Cow!",[84],"cryptography",{"sm":86,"md":87,"lg":88},"\u002Fassets\u002Fbournemouth_sm.webp","\u002Fassets\u002Fbournemouth_md.webp","\u002Fassets\u002Fbournemouth_lg.webp","2026-03-06",{"path":43,"title":44,"description":91,"tags":92,"thumbnail":95,"created":99},"Here is how I went about implementing a simple versioning system for my Go project using Conventional Commmits, awk, and make.",[72,93,62,94],"git","semver",{"sm":96,"md":97,"lg":98},"\u002Fassets\u002Flego-pieces_sm.webp","\u002Fassets\u002Flego-pieces_md.webp","\u002Fassets\u002Flego-pieces_lg.webp","2025-11-11",{"path":31,"title":32,"description":101,"tags":102,"thumbnail":103,"created":107},"The Conventional Commmits spec aims to bring structure to commit messages so that we write succint yet meaningful commits. That sounds great, but let’s examine some potential drawbacks.",[93,94],{"sm":104,"md":105,"lg":106},"\u002Fassets\u002Fgit-commit_sm.webp","\u002Fassets\u002Fgit-commit_md.webp","\u002Fassets\u002Fgit-commit_lg.webp","2025-11-07",{"path":39,"title":40,"description":109,"tags":110,"thumbnail":112,"created":116},"Log4perl provides a powerful logging framework, but how can we dynamically and meaninfully use stdout and stderr?",[111],"perl",{"sm":113,"md":114,"lg":115},"\u002Fassets\u002Fperl-latte-onion_100x100.webp","\u002Fassets\u002Fperl-latte-onion_250x250.webp","\u002Fassets\u002Fperl-latte-onion_600x600.webp","2022-02-14",{"path":51,"title":52,"description":118,"tags":119,"thumbnail":124,"created":128},"Signals, or software interrupts, are used to communicate with a process. They could be used to kill a process, restart, or signal a segmentation fault, but how can we know where that signal came from?",[120,121,122,123],"signals","systemtap","strace","linux",{"sm":125,"md":126,"lg":127},"\u002Fassets\u002Ftraffic-light_sm.webp","\u002Fassets\u002Ftraffic-light_md.webp","\u002Fassets\u002Ftraffic-light_lg.webp","2022-02-13",{"id":33,"title":32,"body":130,"created":107,"description":101,"extension":26,"meta":575,"navigation":576,"path":31,"seo":577,"stem":578,"tags":579,"thumbnail":580,"updated":99,"__hash__":581},{"type":131,"value":132,"toc":561},"minimark",[133,158,163,171,181,184,243,264,267,270,274,277,293,298,301,304,307,310,314,325,329,332,349,352,355,366,369,383,401,410,413,417,439,456,460,469,481,485,494,504,519,523,526,543,546,553,556],[134,135,136,144],"with-caption",{},[137,138,139],"p",{},[140,141],"img",{"alt":142,"src":106,"target":143},"Photo by Horia Varlan","_blank",[145,146,148],"template",{"v-slot:caption":147},"",[137,149,150,151],{},"Photo by ",[152,153,157],"a",{"href":154,"rel":155,"target":143},"https:\u002F\u002Funsplash.com\u002Fphotos\u002Fa-close-up-of-a-text-description-on-a-computer-screen-842ofHC6MaI",[156],"nofollow","Yancy Min",[159,160,162],"h2",{"id":161},"what-are-conventional-commits","What are Conventional Commits",[137,164,165,170],{},[152,166,169],{"href":167,"rel":168,"target":143},"https:\u002F\u002Fwww.conventionalcommits.org\u002F",[156],"Conventional Commits","\nis a specification for formatting git commit messages with the aim to make them\neasily understandable to both humans and machines.\nBelow is the commit message structure:",[172,173,178],"pre",{"className":174,"code":176,"language":177},[175],"language-text","\u003Ctype>[optional scope]: \u003Cdescription>\n\n[optional body]\n\n[optional footer(s)]\n","text",[179,180,176],"code",{"__ignoreMap":147},[137,182,183],{},"In our git subject, we must include a supported type:",[185,186,187,195,201,207,213,219,225,231,237],"ul",{},[188,189,190,194],"li",{},[191,192,193],"strong",{},"build",": Changes that affect the build system or external dependencies",[188,196,197,200],{},[191,198,199],{},"ci",": Changes to our CI configuration files and scripts",[188,202,203,206],{},[191,204,205],{},"docs",": Documentation only changes",[188,208,209,212],{},[191,210,211],{},"feat",": A new feature",[188,214,215,218],{},[191,216,217],{},"fix",": A bug fix",[188,220,221,224],{},[191,222,223],{},"perf",": A code change that improves performance",[188,226,227,230],{},[191,228,229],{},"refactor",": A code change that neither fixes a bug nor adds a feature",[188,232,233,236],{},[191,234,235],{},"style",": Changes that do not affect the meaning of the code",[188,238,239,242],{},[191,240,241],{},"test",": Adding missing tests or correcting existing tests",[137,244,245,246,249,250,253,254,259,260,263],{},"For example ",[179,247,248],{},"fix(html): center div"," or ",[179,251,252],{},"feat(auth): support github OAuth2",".\nThe idea is that we can also programmatically parse our commit messages to\nproduce a ",[152,255,258],{"href":256,"rel":257,"target":143},"https:\u002F\u002Fsemver.org\u002F",[156],"Semantic Version",", which takes the format ",[179,261,262],{},"MAJOR.MINOR.PATCH",".\nWe increment the MAJOR when we encounter a breaking change (more on that later),\nthe MINOR when we see the “feat” type, and the PATCH when we see the “fix” type.",[137,265,266],{},"This method to versioning can make consuming your software more manageable.\nFrom the consumer’s point of view, breaking changes, or changes to the MAJOR\nversion, should be handled manually.\nAs such, your consumer will likely only automatically apply updates from the\nsame MAJOR version, granting them stability.\nAt the same time, they can permit automatic updates to the MINOR version and\nPATCH version to get new improvements and bug fixes.\nYour consumer may also specify minimum PATCH versions to guarantee the features\nthey require.",[137,268,269],{},"Additionally, you could use this structured way of writing commit messages to\ngenerate changelogs.",[159,271,273],{"id":272},"caveats","Caveats",[137,275,276],{},"Generally, I think this is a great approach to maintaining a structured git log;\nhowever, there are certainly some drawbacks.\nA common complaint is that it can already be challenging to write a meaningful\nand succinct commit subject using just 50 characters; now we must include a\ntype, and potentially a scope too.\nInevitably, you will come to violate this semi-arbitrary git commit subject\ncharacter limit, or perhaps worse, you may find yourself compromising on\nthe contents of your subject, writing worse commit subjects.\nHonestly, I have caught myself doing both, even in the same commit subject.",[278,279,281,288],"block-quote",{"link":280},"https:\u002F\u002Fgit-scm.com\u002Fdocs\u002Fgit-commit\u002F2.51.0",[137,282,283,284],{},"Though not required, it’s a good idea to begin the commit message with a single\nshort (less than 50 character) line summarizing the change",[285,286,287],"span",{},"...",[145,289,290],{"v-slot:cite":147},[137,291,292],{},"man git-commit",[294,295,297],"h3",{"id":296},"feat-fix-only","feat & fix only?",[137,299,300],{},"Another caveat is that only types “feat” and “fix” bump the MINOR and PATCH\nversions.\nThis makes sense at first, but I find it a bit restrictive.\nSure, when we add more testing, we don’t need to release the project again,\nneedlessly use CI resources, or bother our consumers to update.\nBut ask yourself, is there any project or scenario, when using the other types,\nthat you would want to release your project?\nIf yes, then it would be appropriate to bump the version again.",[137,302,303],{},"Take for instance the “build”, “ci”, or “perf” types.\nPerhaps something about your build or CI process is broken, but it does not\nrelate to your code.\nYou want to use an accurate type, but it won’t produce a new version number,\nand therefore, a release would never make it out.\nSurely if you add significant performance improvements to your project, you\nwould want to issue a release?",[137,305,306],{},"The point is, you may decide your project needs a release, and therefore\nmay be forced to compromise on what you feel is the best commit type.\nUpdating dependencies without a known vulnerability may not be a feature or a\nfix, but updating dependencies with a known vulnerability would be a fix, even\nthough they may be the exact same process or CI job.",[137,308,309],{},"Our goal of meaningful commits for humans is dropped because we need the robot\nto properly parse and release our application.",[294,311,313],{"id":312},"breaking-change","BREAKING CHANGE",[137,315,316,317,320,321,324],{},"Another caveat is how breaking changes are implemented.\nTo bump the major version via the git commit subject, we can add an ",[179,318,319],{},"!",", e.g.\n",[179,322,323],{},"fix(api)!: foo no longer bars",".\nThat is quite a nonchalant way to communicate a breaking change.\nWe owe it to our consumers and our future selves to write clear commit messages,\nand I believe this is a bit esoteric.",[294,326,328],{"id":327},"what-are-git-footers","What are git footer’s?",[137,330,331],{},"There is another method to introduce a breaking change, using a “git footer”,\nbut what’s that?\nWell, it appears to be a convention invented by Conventional Commits, or\nperhaps a cargo cult that the authors borrowed and loosely defined.",[278,333,335,344],{"link":334},"https:\u002F\u002Fwww.conventionalcommits.org\u002Fen\u002Fv1.0.0\u002F#specification",[137,336,337,338],{},"Each footer MUST consist of a word token, followed by either a :",[339,340,249,341],"space",{},[339,342,343],{},"#\nseparator, followed by a string value (this is inspired by the git trailer convention).",[145,345,346],{"v-slot:cite":147},[137,347,348],{},"Conventional Commits v1.0.0",[137,350,351],{},"What is a “word token”?\nSemantic Versioning actually includes a regex directly in their specification.\nSure, there might be an oversight in the regex, but the pattern we consider\nvalid is set in stone.\nIt would be appropriate for the Conventional Commits specification to define a\nregex or character class too.",[137,353,354],{},"Let’s ask Perl-Compatible Regular Expressions, one of the most popular flavors,\nwhat is a “word”?",[278,356,358,361],{"link":357},"https:\u002F\u002Fwww.pcre.org\u002Fcurrent\u002Fdoc\u002Fhtml\u002Fpcre2pattern.html#SEC5",[137,359,360],{},"A  “word”  character  is  an  underscore  or  any character that is a letter or digit.",[145,362,363],{"v-slot:cite":147},[137,364,365],{},"man 3 pcrepattern",[137,367,368],{},"What about POSIX Extended Regular Expressions?",[278,370,372,378],{"link":371},"https:\u002F\u002Fwww.man7.org\u002Flinux\u002Fman-pages\u002Fman1\u002Fgrep.1.html",[137,373,374,375,377],{},"Word-constituent characters are letters, digits, and the underscore. ",[285,376,287],{},"\nThe symbol \\w is a synonym for [_[:alnum:]]",[145,379,380],{"v-slot:cite":147},[137,381,382],{},"man 1 grep",[137,384,385,386,390,391,396,397,400],{},"What do we gain by creating the new concept of git footer’s?\nIt was ",[387,388,389],"em",{},"inspired"," by a seemingly suitable alternative,\n",[152,392,395],{"href":393,"rel":394,"target":143},"https:\u002F\u002Fgit-scm.com\u002Fdocs\u002Fgit-interpret-trailers\u002F2.51.0",[156],"git trailers",".\nSo the Conventional Commits summary instructs us to use the text “BREAKING CHANGE:”\nin our “git footer”, e.g. ",[179,398,399],{},"BREAKING CHANGE: foo no longer bars",".\nIs “BREAKING CHANGE” what they consider a word token?\nDoes git consider this a trailer?\nSo we have two different methods to indicate a breaking change and bump our\nMAJOR version, but wait, there’s more buried in the final spec bullet:",[278,402,403,406],{"link":147},[137,404,405],{},"BREAKING-CHANGE MUST be synonymous with BREAKING CHANGE, when used as a token in a footer",[145,407,408],{"v-slot:cite":147},[137,409,348],{},[137,411,412],{},"What a strange phrase “word token” is to use in a specification, when both\nproposed keys “BREAKING CHANGE” and “BREAKING-CHANGE” violate definitions of a\n“word” that have been established in popular specifications between 28 and 39\nyears ago.",[294,414,416],{"id":415},"git-interpret-trailers","git interpret-trailers",[137,418,419,420,422,423,426,427,430,431,434,435,438],{},"git provides a builtin command ",[179,421,416],{}," to properly parse git\ntrailers, which is actually used by ",[179,424,425],{},"git log"," to format text using the\nplaceholder ",[179,428,429],{},"%(trailers)",", for example ",[179,432,433],{},"git log --pretty='format:%s %(trailers)",".\nThe manual for ",[179,436,437],{},"interpret-trailers"," is fairly thorough in its definition of git\ntrailers and even references a another specification for inspiration:",[278,440,442,451],{"link":441},"https:\u002F\u002Fgit-scm.com\u002Fdocs\u002Fgit-interpret-trailers\u002F2.51.1",[137,443,444,445],{},"Add or parse trailer lines that look similar to RFC 822 e-mail headers[...].\nWhen reading trailers, there can be no whitespace before or inside the ",[446,447,448,450],"key",{},[285,449,287],{},".\nNote that trailers do not follow (nor are they intended to follow) many of the\nrules for RFC 822 headers.",[145,452,453],{"v-slot:cite":147},[137,454,455],{},"man 1 git-interpret-trailers",[294,457,459],{"id":458},"rfc-822-standard-for-arpa-internet-text-messages","RFC 822: Standard for ARPA Internet Text Messages",[137,461,462,463,468],{},"So Conventional Commits uses “git footers”, which are inspired by “git trailers”,\nwhich are inspired by ",[152,464,467],{"href":465,"rel":466,"target":143},"https:\u002F\u002Fwww.w3.org\u002FProtocols\u002Frfc822\u002F3_Lexical.html#z1",[156],"RFC 822",",\nneither of which support spaces in the “token”!",[137,470,471,472,474,475,480],{},"This means when we use ",[179,473,433],{},", we will never\nsee a “git footer” with a space in the key, i.e. “BREAKING CHANGE”.\nUltimately, three methods to bump the MAJOR version, one quite esoteric and\nnonchalant, another which is completely broken, and finally one that works.\nThis issue has been raised in ",[152,476,479],{"href":477,"rel":478,"target":143},"https:\u002F\u002Fgithub.com\u002Fconventional-commits\u002Fconventionalcommits.org\u002Fissues\u002F413",[156],"#413",".",[294,482,484],{"id":483},"how-did-we-get-here","How did we get here?",[137,486,487,488,493],{},"Well, there was a time when git was quite lazy in parsing trailer tokens.\nCommit ",[152,489,492],{"href":490,"rel":491,"target":143},"https:\u002F\u002Fgithub.com\u002Fgit\u002Fgit\u002Fcommit\u002Fe4319562bc2834096fade432fd90c985b96476db",[156],"e431956","\nmade the parsing much stricter:",[278,495,496,499],{"link":490},[137,497,498],{},"Currently, a line is interpreted to be a trailer line if it contains a\nseparator. Make parsing stricter by requiring the text on the left of\nthe separator, if not the empty string, to be of the \"\u003Ctoken>\u003Coptional\nwhitespace>\" form.",[145,500,501],{"v-slot:cite":147},[137,502,503],{},"November 29, 2016",[137,505,506,507,512,513,518],{},"But the documentation was not updated until late 2022 in commit\n",[152,508,511],{"href":509,"rel":510,"target":143},"https:\u002F\u002Fgithub.com\u002Fgit\u002Fgit\u002Fcommit\u002Fb46dd1726c139c930d7b4b7c3262e3f2699987d3",[156],"b46dd17",",\nreflected in version ",[152,514,517],{"href":515,"rel":516,"target":143},"https:\u002F\u002Fgit-scm.com\u002Fdocs\u002Fgit-interpret-trailers\u002F2.38.0",[156],"2.38","\nof the documentation.\nAlso, the git documentation at the time still aimed the trailer syntax at RFC\n822’s e-mail headers, which did not support spaces.\nChecking the git history of Conventional Commit spec, it looks like it went live\naround August 2016, so it’s understandable where the confusion came from.",[159,520,522],{"id":521},"conclusion","Conclusion",[137,524,525],{},"Who might have thought that the intersection of four different specifications\ncould lead to such confusion?\nI cannot place blame on any organization, software evolves over time and we\nhave to work together to maintain it.",[134,527,528,534],{},[137,529,530],{},[140,531],{"alt":532,"src":533,"target":143},"Standards","\u002Fassets\u002Fxkcd-927-standards.png",[145,535,536],{"v-slot:caption":147},[137,537,538,539],{},"XKCD issue #927 ",[152,540,532],{"href":541,"rel":542,"target":143},"https:\u002F\u002Fxkcd.com\u002F927\u002F",[156],[137,544,545],{},"I do find it amusing that the Conventional Commit specification  centers around\nmaintaining a structured git log and an automatic versioning system, yet is\nseemingly stuck at v1.0.0.\nIt’s strange, because they have quite the git log and use Conventional\nCommits themselves.",[137,547,548,549,552],{},"It might be the right choice, after all, what would happen if they fix\n",[152,550,479],{"href":477,"rel":551,"target":143},[156],"?\nIt certainly would be a breaking change, as it may stop recognizing the token\n“BREAKING CHANGE”, which could cause consumer releases to not accurately\nreflect the historical MAJOR version, potentially releasing newer MAJOR\nversion with breaking changes, under older MAJOR versions.",[137,554,555],{},"So did we gain much from universally conforming to this specification and the\ntools it provides?\nI would argue you should own the means of your versioning system and Conventional\nCommits is a lightweight specification that enables us to do exactly that.\nYou the author know the best commit message, type, and when to release your\nproject; take the concepts from Conventional Commits specification and apply\nthem how they best fit your project’s needs.",[137,557,558,559,480],{},"See how I’ve written my own automated version management system using\nConventional Commits and Semantic Versioning for a Go project,\n",[152,560,44],{"href":43},{"title":147,"searchDepth":562,"depth":562,"links":563},2,[564,565,574],{"id":161,"depth":562,"text":162},{"id":272,"depth":562,"text":273,"children":566},[567,569,570,571,572,573],{"id":296,"depth":568,"text":297},3,{"id":312,"depth":568,"text":313},{"id":327,"depth":568,"text":328},{"id":415,"depth":568,"text":416},{"id":458,"depth":568,"text":459},{"id":483,"depth":568,"text":484},{"id":521,"depth":562,"text":522},{},true,{"title":32,"description":101},"blog\u002Fconventional-commit-caveats",[93,94],{"sm":104,"md":105,"lg":106},"n6_B3M1NO9fePNNYTn7Pbvj4QFN1yEHqSWnt9hJn8MQ",[],{"id":4,"title":5,"author":584,"description":14,"extension":15,"meta":585,"repository":586,"stem":21,"__hash__":22},{"name":7,"github":8,"gitlab":9,"email":10,"linkedin":11,"mastodon":12,"bio":13},{},{"url":18,"git":19,"branch":20},1793313045135]