Ga naar inhoud

Git Development Guide

Table of Contents


Git Configuration

At Lemone we use Git for version control. To work effectively with Git, it's important to configure certain settings. There are also optional settings that can make your life easier.

Standard Configuration

User Settings

Standard user settings with SSH signing through 1Password. It's important to sign your commits with your name and unique key.

[user]
    name = {{ YOUR NAME }}
    email = {{ YOUR EMAIL }}
    signingkey = {{ SSH-ED25519 KEY }}
[gpg]
    format = ssh
[gpg "ssh"]
    program = /Applications/1Password.app/Contents/MacOS/op-ssh-sign
[commit]
    gpgsign = true

Core Configuration

Besides user settings, you'll want to configure your editor and create a global gitignore. You should also set the default branch to main, which is the standard name.

The global gitignore is always included in your git projects and can be used to block system files, for example.

[core]
    excludesfile = /Users/kris/.gitignore_global
    editor = code --wait
[init]
    defaultBranch = main

Conditional Configuration

If you want to override certain git settings, you can do so with includeIf. For example, for a personal GitHub account:

[includeIf "gitdir:~/Code/_github/"]
    path = ~/.config/git/.gitconfig-personal

Automatic Maintenance

If you work extensively in a git repository, you can enable maintenance. This starts a background process that performs maintenance at certain times. docs

To start maintenance, run git maintenance start in your git repo.

[maintenance]
    repo = /Users/kris/Code/spa-001-dashboard
    repo = /Users/kris/Code/ccv-006-website

Note

If your keys come from 1Password, you may randomly get a popup to identify yourself.

Better Default Settings

At Lemone, since May 2024, we preferably use Merge. But if you still need to use rebase, here are some sensible settings.

The git function rerere remembers the choices you make in merge conflicts and applies them automatically.

The setting rebase.updateRefs ensures that your references are updated, so old references aren't accidentally used.

[rebase]
    updateRefs = true
[rerere]
    enabled = true
    autoUpdate = true

The setting branch.sort changes the sorting from alphabetical to date, so the most recent branches are at the top.

[branch]
    sort = -committerdate

Commit Template

To help with writing conventional commits in the terminal, you can add a git commit template. This is no guarantee it works in the TUI or GUI of your choice.

  1. Create a template file in a path of your choice and open it in your editor:
touch ~/.git-commit-template.txt
  1. Add the template for conventional commits to ~/.git-commit-template.txt:
# Commit Message Template

# Type: feat, fix, chore, docs, style, refactor, test, perf, build, ci, revert
# Scope (optional): specify e.g. a module, component or feature
# Description: write a short, clear summary here

# Example:
# feat(auth): add support for OAuth 2.0

<type>(<scope>): <description>

# Body (optional): Provide a more detailed description of the change.
# - What changed?
# - Why was this change necessary?
# - What is the impact?

# Footer (optional): add BREAKING CHANGE or references such as issue numbers
# Refs: <issue_id>
# Closes: <issue_id>
  1. Add the commit template to your .gitconfig:
git config --global commit.template ~/.git-commit-template.txt

Optional Settings

Colors

This is purely a visual setting to make everything look nicer.

[color]
    ui = auto
[color "branch"]
    current = yellow bold
    local = green bold
    remote = cyan bold
[color "diff"]
    meta = yellow bold
    frag = magenta bold
    old = red bold
    new = green bold
    whitespace = red reverse
[color "status"]
    added = green bold
    changed = yellow bold
    untracked = red bold

Aliases

To work faster, you can create custom commands, or aliases.

In this example you see an alias for --force-with-lease, which is a safer variant of --force.

[alias]
    fpush = push --force-with-lease

Commit Message Guidelines

To keep commit messages as structured as possible, it's helpful if everyone uses the same structure to keep things clear.

Why Conventional Commits

We follow the Conventional Commits guidelines for writing commit messages.

Conventional Commits is a structured style for writing commit messages in Git. It helps standardize commit messages, making it easier to understand changes in a project, set up automated release processes, and generate changelogs.

The structure of a conventional commit is as follows:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Why conventional commits:

  1. Consistency: All commit messages follow the same format
  2. Automation: Tools like Semantic Release or standard-version can automatically generate version numbers and changelogs
  3. Readability: It becomes easier to quickly understand the impact of commits
  4. Collaboration: New team members can more easily understand what has changed in commits

Structure Components

Component Required Description
Type Indicates what kind of change was made
Scope Specifies which part of the code the change concerns (e.g., a module or file)
Description A short, clear description of the change
Body A detailed explanation of the change, if needed
Footer For additional information, such as linking issues or breaking changes

Types

For commits, only use the allowed types:

Type Description
feat A new feature or functionality
fix A bug fix
chore Maintenance, such as dependency updates or configuration changes (no functional impact)
docs Documentation changes
style Minor code changes (like linting or formatting) that don't change functionality
refactor Code changes that are neither a bug fix nor a new feature
test Adding or modifying tests
perf Performance improvements
build Changes to build systems or external dependencies
ci Changes to CI configuration (Continuous Integration)
revert Reverting a previous commit

Scope

  1. A scope is optional
  2. A scope can be a module, function, component, or other clear part of the code (e.g., wcag, theme, ux, block, api)
  3. Always lowercase and avoid spaces, use an underscore if needed
  4. Always use the same naming for a scope

Description

  1. Start with a capital letter
  2. Use 1 line of maximum 50-72 characters
  3. Indicate what changed, not why or how
  4. Always start with a verb in present tense (e.g., add, fix, update, etc.)
  5. Be specific, not generic
  6. The description should be understandable to other developers

Good examples:

Add OAuth2 login support
Update WordPress and plugins
Optimize query for fetching custom post types

Bad examples:

Added OAuth2 login support
Fixed stuff
add new feature to theme

Body

  1. The body is optional, but desirable
  2. Explains why and how a change was implemented
  3. Provides context for other developers
  4. Contains details that don't fit in the description
  5. Write complete sentences, but keep it concise
  6. Use bullet points if there are multiple points
  7. Limit width to approximately 72 characters
fix(block): Resolve alignment issue in gallery block

The gallery block was not respecting custom alignment settings.
This fix ensures proper rendering by updating the CSS rules.
  1. The footer is optional
  2. Use this to link an issue, such as a task in Productive
  3. If there's a breaking change, you can describe it here
  4. For further applications, see https://git-scm.com/docs/git-interpret-trailers
fix(api): Correct data validation in REST endpoint

Ensure the `email` field is properly validated before processing.

Closes Productive-5678

Breaking Changes

You can indicate a breaking change in two ways:

  1. With an exclamation mark in the type: Add ! directly after the type to indicate the change is not compatible with previous versions:
feat!: remove deprecated API methods
  1. With a BREAKING CHANGE note in the footer:
feat(api): remove deprecated endpoints

BREAKING CHANGE: The old `/v1/legacy` endpoint is no longer available.

You can combine both methods for maximum clarity.

Commit Message Linting

Using linting tools and git hooks, we can ensure commit messages are automatically checked.

Setup Linting

Note

We use npm because support for yarn is lacking. For more details, visit Husky or commitlint.

Step 1: Install Required Tools

Install husky and commitlint in your project root, where the .git folder is located, not in the theme folder.

npm install --save-dev husky @commitlint/{cli,config-conventional}

Open package.json and add "type": "module". If you don't do this, you may get an error.

Your package.json should now look something like this:

{
  "type": "module",
  "devDependencies": {
    "@commitlint/cli": "^19.6.0",
    "@commitlint/config-conventional": "^19.6.0",
    "husky": "^9.1.7"
  },
  "scripts": {
    "prepare": "husky"
  }
}

Step 2: Setup Linting Rules

Create a commitlint.config.js file in your project root with the following configuration:

const Configuration = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    // Allow sentence-case for subject
    "subject-case": [2, "never", ["start-case", "pascal-case", "upper-case"]],
  },
};

export default Configuration;

Step 3: Initialize Husky

To make husky work, run the following commands:

npx husky init

Then add commitlint to the commit-msg hook:

echo "npx --no -- commitlint --edit $1" > .husky/commit-msg

Navigate to the .husky folder and rename pre-commit to pre-commit.bak or delete the file. Otherwise you'll get an error that the test task doesn't exist.

Step 4: Test Your Setup

You can test commitlint with this command:

npx commitlint --from HEAD~1 --to HEAD --verbose

Then test linting by creating a commit:

# Create a file
echo "Test" > test.md

# Add the file to your next commit
git add test.md

# Commit this file with invalid message
git commit -m "test: this won't work"

If everything is configured correctly, you'll see the linting error message.

Examples

Feature:

feat(auth): add support for social login

Bug fix:

fix(payment): resolve VAT calculation error

Documentation update:

docs(readme): update installation guide

Refactoring:

refactor(core): restructure validation logic

Breaking change with exclamation mark:

feat!: deprecate old authentication system

Issue linking:

fix(ui): fix button alignment

Closes Productive-987

Breaking change with footer:

feat(api): remove legacy endpoints

BREAKING CHANGE: The `/v1/legacy` endpoint has been removed.

Additional Resources

  1. https://www.conventionalcommits.org/
  2. https://typicode.github.io/husky/
  3. https://commitlint.js.org/guides/getting-started.html

Merge Request Guidelines

For Merge Requests, we maintain a fixed template so that all requests are easily readable and the problem/issue is well described.

Everyone who views/reviews your MR should be well informed about the changes and requirements of an MR. It's therefore important that the information section contains as much relevant information as possible to avoid miscommunication.

Below is the template we use for Merge Requests:

**Information**

- Mention issue number
- Brief description of what needs to be done
- Mention feedback from designers/devs if applicable
- Other relevant information regarding this MR/ISSUE

**What has changed?**

- Brief description of code changes
- Defend code choices/missing code here if needed
- To avoid the same feedback

**How to test**

- What should be tested?
- How can people who review your MR best test this?
- Link to test environment

**Additional information**
- Any additional information for this MR

GitHub Flow

We use GitHub Flow for a simple and efficient workflow. Here's a brief overview of our processes.

Basic Principles

  1. main branch: This is our production-ready branch. Everything in main should be ready for deployment to production.
  2. Feature Branches: For new features, bug fixes, or improvements, create a separate branch from main with this structure:
[type]/[issue-id]-[short-description]

Choose from these types: - feature → for new features ✨ - bugfix → for bug fixes 🐛 - refactor → for code improvements ♻️ - chore → for routine tasks not tied to features, config changes, or tool/library updates - test → adding tests ✅ - docs → documentation updates 📝

Merge Requests (MRs)

  • Open an MR to main for code review and feedback
  • MRs must be reviewed and approved by at least one team member
  • Our CI/CD pipelines automatically run tests on all MRs

Workflow

  1. New tasks:
  2. Create a new branch:
    git checkout -b [type]/[issue-id]-description
    
  3. Push your branch and open an MR on GitLab:

    git push --set-upstream origin docs/22-document-github-flow
    

  4. Review and Merging:

  5. Ensure all CI tests pass
  6. Merge the MR to main after approval
  7. main is automatically deployed to staging

Staging and Production

  • Changes are first tested in the staging environment
  • After approval, deploy manually to production

Follow these guidelines for a smooth and effective workflow. Contact your team lead if you have questions.


Project Setup

Lemone uses GitHub flow for projects.

New Project

Create a Project in Codepot

  1. Create a project under the company group
  2. Give the project a name with the code from Productive. The easiest way to find this is the Project overview. A code usually consists of 4 letters and 3 numbers.
  3. Example: FITC-001-Nieuw-platform
  4. Example: MOOL-002-Website-2025
  5. Use a clear slug
  6. Example: fitc-001-nieuw-platform
  7. Example: mool-002-website-2025
  8. For completeness, you can also fill in the project avatar and description.

Update the Pipeline

Copy the following code into .gitlab-ci.yml:

include:
  - project: "dev-ops/buildbot/templates"
    file: "/deploy-container/template-githubflow-ci.yml"

variables:
  THEME_NAME: "[THEME]"
  SITE_NAME: "[NAME]"
  STAGING_URL: "https://[STAGING].lemone.review"
  PRODUCTION_URL: "https://[PRODUCTION]"
  ANSIBLE_VERSION: "2.15.1"
  NODE_VERSION: [NODE VERSION]

Update Repository Settings

Ensure the repository settings are correct:

  1. The main branch is protected:
  2. Developers and Maintainers are allowed to merge
  3. No one is allowed to push directly
  4. The v* tag is protected (production deployments are done from this tag):
  5. Maintainers are allowed to create
  6. The correct deploy keys are added:
  7. deploykey@codepot
  8. plugins@codepot
  9. deploy@web01.exonet.lemone.network

Other Settings

Ensure other settings are correct:

  1. Merge request: Under Merge checks, "All threads must be resolved" should be checked
  2. CI/CD Settings: Under Variables, a VAULT_PASS must be added

Migrating Projects

For migrating a project, additional steps need to be followed.

Update Branches

  1. If there's a master branch, create a new main branch and set it up as described in "New Project"
  2. Verify all code is committed and merged into the new main branch
  3. Once you're certain develop and master have no code that hasn't been merged, you can delete these branches

Update Code References

In the code, several changes are needed to make deployments work properly. The new CI/CD script uses the name staging instead of test, to stay closer to the Roots Framework.

The following files must be renamed:

- trellis/group_vars/test/
+ trellis/group_vars/staging/
- trellis/hosts/test
+ trellis/hosts/staging

The following files must be modified:

Replace branch develop with main in trellis/group_vars/production/wordpress_sites.yml:

# trellis/group_vars/production/wordpress_sites.yml
-    branch: master
+    branch: main

Replace branch develop with main in trellis/group_vars/staging/wordpress_sites.yml:

-    branch: develop
+    branch: main

Replace all test with staging in trellis/hosts/staging:

# Add each host to the [staging] group and to a "type" group such as [web] or [db].
# List each machine only once per [group], even if it will host multiple sites.

exonet_staging ansible_host=178.22.60.186 ansible_ssh_extra_args='-o StrictHostKeyChecking=no'

[web]
exonet_staging

[staging]
exonet_staging

See Also