# changelog ## tl;dr Proponuję przyjąć stosowanie konwencji nazewnictwa commitów zaczynając od typu zmian `fix` lub `feat` (np. `feat: add ability to parse arrays`). Przynajmniej w repozytoriach libów/aplikacji z zewnętrznymi użytkownikami, tam gdzie potrzebny będzie CHANGELOG. Taka konwencja pozwala na wykorzstanie istniejącego procesu do generowania pliku `CHANGELOG.md` zawierającego tylko znaczące informacje na temat zmian z punktu widzenia użytkownika. --- Ostatnio zajmowałem się tematem CHANGELOG na potrzeby `xls-parser-oca`, ale chciałem zaproponować jakieś uniwersalne rozwiązanie, więc wnioskami dzielę się do dyskusji. Z jednej strony można pisać CHANGELOG z palca, ale to wiąże się z dodatkową, żmudną pracą dokumentowania zmian w aplikacji, która to praca nie jest wymuszona w procesie, więc z niezerowym prawdopodobieństem można założyć, że z czasem może brakować jakichś wpisów. Z drugiej strony zmiany w kodzie są dokumentowane w gicie, więc możnaby całkowicie zautomatyzować proces generowania CHANGELOG na podstawie git log (mam na myśli log commitów, a nie komendę w gicie), ale wtedy informacje nieistotne dla klienta (np. poprawienie literówki w dokumentacji) będą się znajdować z informacjami kluczowymi (jak breaking changes). Jednak przy odpowiedniej konwencji nazywania commitów można je wykorzystać w pół-automatycznym procesie generowania sensownego CHANGELOG. Dodatkow każdy wpis w CHANGELOG powiązany jest z commitem, który daną zmianę wprowadził. ## conventianal commits | Angular convention Jedną z konwencji, która jest stworzona na bazie [propozycji Angulara](https://github.com/angular/angular/blob/68a6a07/CONTRIBUTING.md#commit), jest [conventional commits](https://www.conventionalcommits.org/). Opiera się na dodawaniu na początku commit message typu zmian, jakie w danym commicie się znajdują. Wrzucę fragment z ich specyfikacji: >The commit message should be structured as follows: >``` ><type>[optional scope]: <description> > >[optional body] > >[optional footer(s)] >``` > The commit contains the following structural elements, to communicate intent to the consumers of your library: > > 1. **fix:** a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning). > 1. **feat:** a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning). > 1. **BREAKING CHANGE:** a commit that has a footer `BREAKING CHANGE:`, or appends a `!` after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type. > 1. types other than `fix:` and `feat:` are allowed, for example `build:`, `chore:`, `ci:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`, and others. > 1. `footers` other than `BREAKING CHANGE: <description>` may be provided and follow a convention similar to git trailer format. > > Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE). A scope may be provided to a commit’s type, to provide additional contextual information and is contained within parenthesis, e.g., `feat(parser): add ability to parse arrays`. W ten sposób można wykorzystać narzędzia do generowania CHANGELOG na podstawie git log, które dzięki oznaczeniu typu commita, są w stanie zinterpretować jego ważność. Miłym dodatkiem jest też podpowiedź, jakiego rodzaju powinien być kolejny release zgodnie z SemVer. ## tools Większość [narzędzi](https://www.conventionalcommits.org/en/about) jest napisanych w Go, PHP, JS i Python (🦀 + 😞). Skupiłem się na JS, bo w większości i tak mamy na głowie nodejs. - [conventional-changelog-cli](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-cli#readme) CLI do generowania pliku `CHANGELOG.md`. Np. [takiego](https://github.com/yargs/yargs/blob/main/CHANGELOG.md). Zainstalowałem jako global (`npm install -g conventional-changelog-cli`) i dodałem alias w `~/.alias` (`alias chlog="conventional-changelog -p conventionalcommits -i CHANGELOG.md -s"`). W ten sposób komenda `chlog` dodaje do istniejącego `CHANGELOG.md` ostatnie zmiany. - [commitlint](https://github.com/conventional-changelog/commitlint#readme) Linter commit message sprawdzający message z przyjętą konwencją. Też jako global (`npm install -g @commitlint/cli @commitlint/config-conventional`), ale w tym przypadku potrzebny jest jeszcze dodatkowy plik konifuracyjny (`echo "module.exports = {extends: ['@commitlint/config-conventional']};" > ~/.commitlintrc.js`). Dodałem też sobie prosty git hook w projekcie: `.git/hooks/commit-msg` ```bash #!/bin/sh IS_AMEND=$(ps -ocommand= -p $PPID | grep -e '--amend'); commit_msg=$(cat "${1:?Missing commit message file}") if [ -n "$IS_AMEND" ]; then return; fi echo $commit_msg | commitlint ``` dzięki czemu domyślnie wymusza porawny commit message lub podpowiada co trzeba zmienić. Można ominąć lintera dodając flagę `-n` do `git commit`. - [conventional-recommended-bump](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-recommended-bump#readme) CLI które zwraca jakiego rodzaju powinien być release (`major`/`minor`/`patch`) na podstawie commitów. Zainstalowany globalnie (`npm i -g conventional-recommended-bump conventional-changelog-angular`) i dodany alias `alias chb="conventional-recommended-bump -p angular"` - dodatkowo [espanso](https://espanso.org/) ([GitHub](https://github.com/espanso/espanso)) > [...] program that detects when you type a specific **keyword** and replaces it with **something else**. dodałem sobie triggery do pisania commitów (`gc` to alias na `git commit`): ```yaml - trigger: "gc " replace: "gc -m '$|$'" - trigger: "gcx " replace: "gc -m 'fix: $|$'" - regex: "gcx\\((?P<scope>.*) " replace: "gc -m 'fix({{scope}}): $|$'" - trigger: "gcxx " replace: "gc -m 'fix!: $|$' -m '' -m 'BREAKING CHANGE: '" - regex: "gcxx\\((?P<scope>.*) " replace: "gc -m 'fix({{scope}})!: $|$' -m '' -m 'BREAKING CHANGE: '" - trigger: "gcf " replace: "gc -m 'feat: $|$'" - regex: "gcf\\((?P<scope>.*) " replace: "gc -m 'feat({{scope}}): $|$'" - trigger: "gcff " replace: "gc -m 'feat!: $|$' -m '' -m 'BREAKING CHANGE: '" - regex: "gcff\\((?P<scope>.*) " replace: "gc -m 'feat({{scope}})!: $|$' -m '' -m 'BREAKING CHANGE: '" - trigger: "bc " replace: "BREAKING CHANGE: " - trigger: "dep " replace: "DEPRECATED: " ``` taraz jak wpiszę w konsoli `gc` + 2 spacje to zamienia mi to na `gc -m ''` i ustawia kursor między apostrofami. Z kolei `gcff(parser` + 2 spacje zamienia na `gc -m 'feat(parser)!: ' -m '' -m 'BREAKING CHANGE: '` z kursorem po `:`. - [googleapis/release-please](https://github.com/googleapis/release-please) Na to wcześniej nie patrzyłem, ale też wygląda ciekawie, więc zostawiam link. ## Przydatne źródła: - https://www.conventionalcommits.org/ - https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit# - https://keepachangelog.com/ - https://semver.org/