Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: [[Wikilinks]] to [MDlinks](links) not working with headers links #334

Closed
3 tasks done
mathisgauthey opened this issue Apr 28, 2024 · 4 comments
Closed
3 tasks done
Assignees
Labels
🐛 Bug Something isn't working

Comments

@mathisgauthey
Copy link

mathisgauthey commented Apr 28, 2024

Issue validation

  • I checked the issue to prevent duplicate
  • I checked my configurations files and the documentation
  • My issue concern doesn't concern the Mkdocs template

Command used

Upload single current active note

Plugin version

7.0.5

Describe the bug

When using the option [[Wikilinks]] to [MDlinks](links) Convert Wikilinks to MDlinks, without changing the contents., the links are not correctly translated if they link to a file header.

How to reproduce ?

  1. Create a file to publish.
  2. Use a wikilink with a link to a header in the current file, and use the | argument to define a custom text link. Example : [[How to Enforce Commit Conventions and Automate the Release and Changelog Process#Configuration|previous section]]
  3. The link is badly converted : [previous section](How%20to%20Enforce%20Commit%20Conventions%20and%20Automate%20the%20Release%20and%20Changelog%20Process.mdconfiguration) and it leads to a 404 error page not found.

Minimal Reproducible Example

The link is present just before the ### Launch Locally to Test it Out section, in the callout. It links to ### Configuration

---
title: How to Enforce Commit Conventions and Automate the Release and Changelog Process
aliases: 
description: In this post, I'll explain how to enforce conventional commits rules and automate the release and changelog process of a git repository.
authors:
  - Mathis Gauthey
categories:
  - guides
tags:
  - git
  - github-workflows
  - commit
  - convention
  - release
  - semantic-versioning
date_created: 2024-01-18T08:25:00+01:00
date_modified: 2024-04-24T18:17:09+02:00
share: true
comments: true
---

# How to Enforce Commit Conventions and Automate the Release and Changelog Process

In this post, I'll explain how to enforce conventional commits rules and automate the release and changelog process of a git repository.

<!-- more -->

## Introduction

### A Little Bit of Definitions First

- [Semantic Versioning](https://semver.org/) is a simple set of rules and requirements that dictate how version numbers are assigned and incremented.
- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) is a specification for adding human and machine readable meaning to commit messages. That's the one we'll use in every configuration here, it is close to the Angular specifications but has a few more types I reckon.

Common types according to [commitlint-config-conventional (based on the Angular convention)](https://github.com/conventional-changelog/commitlint/tree/master/@commitlint/config-conventional#type-enum) can be:

- `build`
- `chore`
- `ci`
- `docs`
- `feat`
- `fix`
- `perf`
- `refactor`
- `revert`
- `style`
- `test`

> [!info] Why use Conventional Commits instead of the Angular convention ?
> This is an extension of the Angular convention that contains a little bit more types for a more flexible management. It also aims to become a standard for all developers across every languages.

### Why Should We Use Such Conventions and Automated Tools ?

- Conventional commits and semantic release standards allow for a standard way to communicate changes made on a codebase, which helps developers communicate better. And god knows we're bad at this.
- Automated release process allows for easier changelog managements with easy rollbacks if required.
- In continuous integration and deployment, we aim to automate most of the things (testing, releasing, building and deploying) in order to be able to focus 100% on our programming and not on manual and repetitive tasks.

### Tools We'll Use

- [semantic-release](https://github.com/semantic-release/semantic-release) to automate the release process and changelog based on the Semantic Versioning rules.
- [VS Code extension](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits) for conventional commits modal on committing changes.
- [commitlint](https://github.com/conventional-changelog/commitlint) to check and enforce that commit messages meet the conventional commit format and prevent other types of commits to go through.
- [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog/tree/master) is also used for defining the Conventional Commits convention in semantic-release.
- [commitizen](https://github.com/commitizen/cz-cli) to add a CLI prompt when committing changes that will use conventional commits specifications. It is an alternative to an editor extension.

### Requirements

- [[Install Nvm, Npm, Yarn - Setting up Node on Ubuntu]]
- A git repository

## Semantic Release

### Goals

1. Trigger `semantic-release` on new commits made on `main` branch.
2. Analyze commits based on [conventional commits style](https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/).
3. Generate releases notes automatically.
4. Put these release notes inside `docs/CHANGELOG.md`.
5. Create a release commit on main with the CHANGELOG.
6. Automatically tag based on conventional commit and semantic release conventions.

### Installation

Make sure you installed NVM like explained above, and go inside your git repository.

Install the packages locally using the following command :

bash
npm install semantic-release -D
npm install @semantic-release/commit-analyzer -D
npm install @semantic-release/release-notes-generator -D
npm install @semantic-release/changelog -D
npm install @semantic-release/git -D
npm install @semantic-release/exec -D
npm install conventional-changelog-conventionalcommits -D
# npm install @semantic-release/github -D
```

> [!NOTE] Notes
> - It can also be installed globally, but it's not our goal as we just want to automate the release process of our current repository here.
> - The `-D` means the package will appear in your `devDependencies`.

It should create a `package.json` and `package.lock` file in your repository. Don't forget to add `node_modules/` to your `.gitignore` file **but keep the other two committed as they manage the packages and packages dependencies versions respectively.**

### Configuration

Then, create a configuration file named `.releaserc` at the root of your repository. More on that [here](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration), but just copy and paste that for now :

```yml
{
    "branches": [
        "main"
    ],
    "tagFormat": "${version}",
    "debug": true,
    "ci": true,
    "dryRun": false,
    "plugins": [
        [
            "@semantic-release/commit-analyzer",
            {
                "preset": "conventionalcommits"
            }
        ],
        [
            "@semantic-release/release-notes-generator",
            {
                "preset": "conventionalcommits",
                "presetConfig": {
                    "compareUrlFormat": "{{host}}/{{owner}}/{{repository}}/branchCompare?baseVersion=GT{{previousTag}}&targetVersion=GT{{currentTag}}&_a=files"
                }
            }
        ],
        [
            "@semantic-release/changelog",
            {
                "changelogFile": "docs/CHANGELOG.md"
            }
        ],
        [
            "@semantic-release/exec",
            {
                "prepareCmd": "bash prepare.sh '${nextRelease.version}' 'module/__manifest__.py'"
            }
        ],
        [
            "@semantic-release/git",
            {
                "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}",
                "assets": [
                    "docs/CHANGELOG.md",
                    "module/__manifest__.py"
                ]
            }
        ]
    ]
}
```

> [!warning] Prevent infinite loops !
> Note that the `[skip ci]` is only important when working on Azuredevops. Indeed, if your continuous integration system involves a trigger on main that will make semantic-release generate a commit on main… You can definitely see where this is going. Make sure not to do that.

Note that there are two ways to modify the `release-notes-generator` to your liking, and they both involves adding another key below `preset` in your config file.

According to the plugin's [README](https://github.com/semantic-release/release-notes-generator) :

> [!summary]
> Note: config will be overwritten by the values of preset. You should use either preset or config, but not both.
> Note: Individual properties of parserOpts and writerOpts will override ones loaded with an explicitly set preset or config. If preset or config are not set, only the properties set in parserOpts and writerOpts will be used.
> Note: For presets that expects a configuration object, such as conventionalcommits, the presetConfig optionmust be set.

So you can modify the settings :

- Either add the `writerOpts` key and change the different [templates parts](https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-conventionalcommits/src/writer.js) like `headerPartial` or `commitPartial`. Example can be found [here](https://github.com/semantic-release/semantic-release/issues/1339#issuecomment-548179389).
- An easier and simpler modification, useful in case you just want to change the `compareUrlFormat` for example, is to use the `presetConfig` option. Example can be found [here](https://github.com/semantic-release/release-notes-generator/issues/131#issuecomment-627830409).

> [!tip] But how to find the variable names and presets ?
> The [semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator#options) is using [conventional-changelog presets](https://github.com/conventional-changelog/conventional-changelog/tree/c2c4b3a4cb60f784a4e7ee83d189b85c0acac960/packages) and you can check their [package repository](https://github.com/conventional-changelog/conventional-changelog/tree/c2c4b3a4cb60f784a4e7ee83d189b85c0acac960/packages/conventional-changelog-conventionalcommits) or [specs and documentation if provided](https://github.com/conventional-changelog/conventional-changelog-config-spec/blob/master/versions/2.2.0/README.md).

### Auto-increment the Version

You might want to auto-increment your app version when you release it. It is possible to do it by using `semantic-release/exec` and execute a bash script like this one that replaces the version in the `__manifest__.py` file of a Odoo module :

```bash
#!/bin/bash

# Get the next version from the first argument
next_version=$1

# Get the filename from the second argument
filename=$2

# Perform version bumping using sed
sed -i -E "s/('version':\s*'[0-9]+\.[0-9]+\.)[0-9]+\.[0-9]+\.[0-9]+(',)/\1$next_version\2/" "$filename"
```

> [!warning] Don't forget to commit these changes !
> The files you modify using `exec` part of `semantic-release` needs to be added to the `assets` of the `git` plugin configuration. An example can be found in the [[How to Enforce Commit Conventions and Automate the Release and Changelog Process#Configuration|previous section]]

### Launch Locally to Test it Out

If you want to run the `semantic-release` locally, use `./node_modules/.bin/semantic-release --no-ci --dry-run`. If you're satisfied with the results, just get rid of the `--dry-run`, and it will create a release commit and tag **locally that you can then push if you like.**

### Add Semantic-release to Your Continuous Integration & Deployment Solution

If you want to automatically trigger a release when a new commit is detected on your main branch (that's only an example, you can fine tune it obviously), you can add it to your CI configurations. You can find [examples on the semantic-release repository](https://github.com/semantic-release/semantic-release/tree/master/docs/recipes/ci-configurations) for Github, Gitlab, Jenkins or Travis.

If you happen to use AzureDevOps, first of all best of luck to you too, and feel free to use my simple template here :

```yml
name: Release

trigger:
- main

pool:
  name: default
  demands: Agent.OS -equals Linux

stages:
- stage: Release
  jobs:
  - job : Install_semantic_release
    steps:
      - checkout: self
        persistCredentials: true
      - task: NodeTool@0
        displayName: Use node v20
        inputs:
          versionSpec: '20.x'
      - script: |
          npm install
          ./node_modules/.bin/semantic-release
        env:
          GIT_CREDENTIALS: $(System.AccessToken)
```

You can use a different agent, try using a Ubuntu LTS if possible. Don't forget to use the `GIT_CREDENTIALS` as they are required for semantic-release to be able to commit and push the release commit.

## Enforce Commit Conventions in Your Team

### Introduction

Creating new rules and standards can be pretty hard, especially in a team that has been working differently for a very long time.

But a developer should know how to [write a good commit message](https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/), it is an essential part of his job as he won't always be the one to work on complex pieces of software, and needs to communicate for the present colleagues and the futures ones.

> [!success] In short, what is a good commit message ? Ask yourself these questions :
> - What are the changes I made
> - Why have I made these changes? Why was the change needed?
> - What effect have my changes made?
> - What are the changes in reference to? (Issue, bug report, etc.)

A good example that follows directly the flow from above might be :

```text
fix: fix foo to enable bar

This fixes the broken behavior of the component by doing xyz. 

BREAKING CHANGE
Before this fix foo wasn't enabled at all, behavior changes from <old> to <new>

Closes D2IQ-12345
```

### Use an Editor Extension

There's probably an extension that might allow for a streamlined conventional commit process in your editor, for example :

[Here's an extension that allows to use a commit modal when commiting changes on VS Code](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits). Simply install and use it when committing changes.

### Use a CLI Tool

Alternative to an editor extension, here on the CLI directly, you can install it globally using :

```bash
npm install -g commitizen
```

Simply use `git cz` or just `cz` instead of `git commit` when committing. You can also use `git-cz`, which is an alias for `cz`. If you use npm 5.2+, you can also use `npx cz`.

### Force Conventional Commits Using Commitlint

If you want to enforce it and don't allow for random commit messages, you can enforce it with pre-commit hooks using commitlint :

At the root of your project, execute the following commands :

```bash
# Install commitlint cli and conventional config
npm install --save-dev @commitlint/{cli,config-conventional}

# Configure commitlint to use conventional config
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

# Install Husky v6
npm install husky --save-dev

# Activate hooks
npx husky install

# Add hook
npx husky add .husky/commit-msg  'npx --no -- commitlint --edit ${1}'
```
```
```


### Configuration

```JSON
{
  "github": {
    "branch": "main",
    "automaticallyMergePR": true,
    "dryRun": {
      "enable": false,
      "folderName": "github-publisher"
    },
    "tokenPath": "%configDir%/plugins/%pluginID%/env",
    "api": {
      "tiersForApi": "Github Free/Pro/Team (default)",
      "hostname": ""
    },
    "workflow": {
      "commitMessage": "[PUBLISHER] Merge",
      "name": ""
    },
    "verifiedRepo": true
  },
  "upload": {
    "behavior": "fixed",
    "defaultName": "docs/posts",
    "rootFolder": "docs",
    "yamlFolderKey": "categories",
    "frontmatterTitle": {
      "enable": false,
      "key": "title"
    },
    "replaceTitle": [],
    "replacePath": [
      {
        "regex": "docs/blog",
        "replacement": "docs/blog/posts",
        "type": "path"
      }
    ],
    "autoclean": {
      "enable": true,
      "excluded": [
        "/^((?!docs\\/posts|docs\\/images).)*$/"
      ]
    },
    "folderNote": {
      "enable": false,
      "rename": "index.md",
      "addTitle": {
        "enable": false,
        "key": "title"
      }
    },
    "metadataExtractorPath": ""
  },
  "conversion": {
    "hardbreak": false,
    "dataview": true,
    "censorText": [
      {
        "entry": "/title:\\s\"([^\"]+)\"/i",
        "replace": "title: $1",
        "flags": "",
        "after": false
      },
      {
        "entry": "/(date_)(created: )(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}:\\d{2})\\n(date_)(modified: )(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}:\\d{2})/i",
        "replace": "date:\\n  $2$3\\n  updated: $6",
        "flags": "",
        "after": true
      }
    ],
    "tags": {
      "inline": false,
      "exclude": [],
      "fields": []
    },
    "links": {
      "internal": true,
      "unshared": false,
      "wiki": true,
      "slugify": "strict"
    }
  },
  "embed": {
    "attachments": true,
    "overrideAttachments": [],
    "keySendFile": [],
    "notes": false,
    "folder": "docs/images",
    "convertEmbedToLinks": "keep",
    "charConvert": "->",
    "useObsidianFolder": false
  },
  "plugin": {
    "shareKey": "share",
    "excludedFolder": [],
    "copyLink": {
      "enable": false,
      "links": "",
      "removePart": [],
      "transform": {
        "toUri": true,
        "slugify": "lower",
        "applyRegex": []
      }
    },
    "setFrontmatterKey": "Set"
  }
}

Relevant log output

None

OS

Windows

Anything else?

No response

Plugin settings

As for my mkdocs settings, you can find them here.

{
  "github": {
    "branch": "main",
    "automaticallyMergePR": true,
    "dryRun": {
      "enable": false,
      "folderName": "github-publisher"
    },
    "tokenPath": "%configDir%/plugins/%pluginID%/env",
    "api": {
      "tiersForApi": "Github Free/Pro/Team (default)",
      "hostname": ""
    },
    "workflow": {
      "commitMessage": "[PUBLISHER] Merge",
      "name": ""
    },
    "verifiedRepo": true
  },
  "upload": {
    "behavior": "fixed",
    "defaultName": "docs/posts",
    "rootFolder": "docs",
    "yamlFolderKey": "categories",
    "frontmatterTitle": {
      "enable": false,
      "key": "title"
    },
    "replaceTitle": [],
    "replacePath": [
      {
        "regex": "docs/blog",
        "replacement": "docs/blog/posts",
        "type": "path"
      }
    ],
    "autoclean": {
      "enable": true,
      "excluded": [
        "/^((?!docs\\/posts|docs\\/images).)*$/"
      ]
    },
    "folderNote": {
      "enable": false,
      "rename": "index.md",
      "addTitle": {
        "enable": false,
        "key": "title"
      }
    },
    "metadataExtractorPath": ""
  },
  "conversion": {
    "hardbreak": false,
    "dataview": true,
    "censorText": [
      {
        "entry": "/title:\\s\"([^\"]+)\"/i",
        "replace": "title: $1",
        "flags": "",
        "after": false
      },
      {
        "entry": "/(date_)(created: )(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}:\\d{2})\\n(date_)(modified: )(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}:\\d{2})/i",
        "replace": "date:\\n  $2$3\\n  updated: $6",
        "flags": "",
        "after": true
      }
    ],
    "tags": {
      "inline": false,
      "exclude": [],
      "fields": []
    },
    "links": {
      "internal": true,
      "unshared": false,
      "wiki": true,
      "slugify": "strict"
    }
  },
  "embed": {
    "attachments": true,
    "overrideAttachments": [],
    "keySendFile": [],
    "notes": false,
    "folder": "docs/images",
    "convertEmbedToLinks": "keep",
    "charConvert": "->",
    "useObsidianFolder": false
  },
  "plugin": {
    "shareKey": "share",
    "excludedFolder": [],
    "copyLink": {
      "enable": false,
      "links": "",
      "removePart": [],
      "transform": {
        "toUri": true,
        "slugify": "lower",
        "applyRegex": []
      }
    },
    "setFrontmatterKey": "Set"
  }
}

Obsidian information

SYSTEM INFO:
	Obsidian version: v1.5.12
	Installer version: v1.4.16
	Operating system: Windows 10 Pro 10.0.22631
	Login status: logged in
	Catalyst license: none
	Insider build toggle: off
	Live preview: on
	Base theme: adapt to system
	Community theme: AnuPpuccin v1.4.5
	Snippets enabled: 4
	Restricted mode: off
	Plugins installed: 33
	Plugins enabled: 33
		1: Excalidraw v2.0.22
		2: Auto Link Title v1.5.3
		3: Better footnote v1.0.1
		4: Better Word Count v0.10.1
		5: Callout Manager v1.0.1
		6: Clear Unused Images v1.1.0
		7: Code Editor Shortcuts v1.14.0
		8: Commander v0.5.1
		9: Completr v3.2.0
		10: Editing Toolbar v2.4.0
		11: File Explorer Note Count v1.2.1
		12: Filename Heading Sync v1.9.0
		13: Find orphaned files and broken links v1.9.1
		14: Footnote Shortcut v0.1.3
		15: Iconize v2.10.0
		16: Image Toolkit v1.4.1
		17: LanguageTool Integration v0.3.7
		18: Linter v1.22.0
		19: Templater v2.2.1
		20: Tag Wrangler v0.6.1
		21: Symbols Prettifier v1.1.1
		22: Style Settings v1.0.7
		23: Show Whitespace v0.3.1
		24: Shortcuts extender v2.2.0
		25: Scroll to Top v2.1.4
		26: Paste URL into selection v1.7.0
		27: Paste image rename v1.6.1
		28: Omnivore v1.9.4
		29: Note Refactor v1.8.2
		30: Natural Language Dates v0.6.2
		31: Custom Frames v2.4.7
		32: Raindrop Highlights v0.0.20
		33: Github Publisher v7.0.5

RECOMMENDATIONS:
	Custom theme and snippets: for cosmetic issues, please first try updating your theme and disabling your snippets. If still not fixed, please try to make the issue happen in the Sandbox Vault or disable community theme and snippets.
	Community plugins: for bugs, please first try updating all your plugins to latest. If still not fixed, please try to make the issue happen in the Sandbox Vault or disable community plugins.
@mathisgauthey mathisgauthey added the 🐛 Bug Something isn't working label Apr 28, 2024
@Mara-Li
Copy link
Member

Mara-Li commented Apr 28, 2024

I need you settings.

@mathisgauthey
Copy link
Author

I need you settings.

Oh, pretty sure I copied them inside the template :o I edited the post o/

@Mara-Li
Copy link
Member

Mara-Li commented Apr 28, 2024

Fixed in v7.0.6
Caused by the slugify encoding of the anchor, that removed the #.

@mathisgauthey
Copy link
Author

Fixed in v7.0.6
Caused by the slugify encoding of the anchor, that removed the #.

Thanks again for your reactivity ✌️ Still enjoying your plugin after more than a year 💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants