From 3cd132e923b85e48c2976bf712ec7fbf0bf105eb Mon Sep 17 00:00:00 2001 From: MoritzWeber Date: Wed, 4 Oct 2023 16:31:39 +0200 Subject: [PATCH] feat: Run `pre-commit` framework for all Git repositories This addition introduces the "local hooks" feature, as described in #211. To utilize this feature, add a `.pre-commit-config.yaml` file to a Git repository. Git will then automatically run the defined hooks. More information is available in the mkdocs documentation. In addition, Git LFS is now auto-installed for all repositories. --- .pre-commit-config.yaml | 2 +- LICENSES/MIT.txt | 17 ++++++ Makefile | 1 + base/Dockerfile | 19 +++++- base/hooks/+pre-commit-and-lfs.sh | 8 +++ base/hooks/+pre-commit-only.sh | 5 ++ base/hooks/+pre-commit.sh | 31 ++++++++++ capella/Dockerfile | 2 +- docs/docs/git-hooks/egit-failed-git-hook.png | Bin 0 -> 30121 bytes .../egit-failed-git-hook.png.license | 2 + docs/docs/git-hooks/git-hooks.md | 55 ++++++++++++++++++ docs/mkdocs.yml | 1 + eclipse/Dockerfile | 2 +- papyrus/Dockerfile | 2 +- 14 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 LICENSES/MIT.txt create mode 100755 base/hooks/+pre-commit-and-lfs.sh create mode 100755 base/hooks/+pre-commit-only.sh create mode 100755 base/hooks/+pre-commit.sh create mode 100644 docs/docs/git-hooks/egit-failed-git-hook.png create mode 100644 docs/docs/git-hooks/egit-failed-git-hook.png.license create mode 100644 docs/docs/git-hooks/git-hooks.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 607e4ab7..c7dfbdc3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: - id: insert-license name: Insert license headers (shell-style comments) files: '(?:^|/)(?:.*\.(?:py|sh|toml|ya?ml|cfg|ini)|Dockerfile|Makefile|nginx.conf)$' - exclude: '(?:^|/)\..+|^docs/Makefile$' + exclude: '(?:^|/)\..+|^docs/Makefile$|^base/hooks/\+pre-commit.sh$' args: - --detect-license-in-X-top-lines=15 - --license-filepath diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 00000000..89de3547 --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile index 287791eb..2b9e9709 100644 --- a/Makefile +++ b/Makefile @@ -310,6 +310,7 @@ run-jupyter-notebook: jupyter-notebook run-capella/remote: capella/remote docker run $(DOCKER_RUN_FLAGS) \ + -v $$(pwd)/volumes/workspace:/workspace \ -e RMT_PASSWORD=$(RMT_PASSWORD) \ -p $(RDP_PORT):3389 \ -p $(METRICS_PORT):9118 \ diff --git a/base/Dockerfile b/base/Dockerfile index cbfea8d3..ade6d679 100644 --- a/base/Dockerfile +++ b/base/Dockerfile @@ -42,19 +42,32 @@ RUN useradd -l -m -u $UID techuser && \ && chown techuser /home/techuser ENV HOME=/home/techuser - # This in analogous to the virtualenv activate script # To allow deactivation of the virtualenv, we need to save the old PATH ENV _OLD_VIRTUAL_PATH="$PATH" ENV VIRTUAL_ENV=/opt/.venv ENV PATH="$VIRTUAL_ENV/bin:$PATH" +COPY --chmod=755 hooks/* /opt/git/global-hooks/ + +WORKDIR /opt/git/global-hooks + RUN ln -s "$(which python3.11)" /usr/bin/python && \ ln -sf "$(which python3.11)" /usr/bin/python3 && \ ln -sf "$(which pip3.11)" /usr/local/bin/pip && \ ln -sf "$(which pip3.11)" /usr/local/bin/pip3 && \ python -m venv /opt/.venv && \ - chmod -R 777 /opt/.venv/bin/ && \ - chmod -R 777 /opt/.venv/lib/python3.11/site-packages + # Configure pre-commit + pip install pre-commit==3.4.0 --no-cache-dir && \ + echo "commit-msg post-rewrite pre-commit pre-merge-commit pre-rebase prepare-commit-msg" | xargs -n 1 cp /opt/git/global-hooks/+pre-commit-only.sh && \ + echo "pre-push post-checkout post-commit post-merge" | xargs -n 1 cp /opt/git/global-hooks/+pre-commit-and-lfs.sh && \ + git config --global core.hooksPath /opt/git/global-hooks && \ + chmod -R 755 /opt/git/global-hooks && \ + chown -R techuser /opt/.venv/bin/ /opt/.venv/lib/python3.11/site-packages + +# Make pre-commit cache persistent +ENV PRE_COMMIT_HOME=/workspace/.pre-commit + +WORKDIR /workspace USER techuser diff --git a/base/hooks/+pre-commit-and-lfs.sh b/base/hooks/+pre-commit-and-lfs.sh new file mode 100755 index 00000000..0d781073 --- /dev/null +++ b/base/hooks/+pre-commit-and-lfs.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +STAGE=$(basename "$0") + +exec /opt/git/global-hooks/+pre-commit.sh $STAGE "$@" +exec git lfs $STAGE "${@:2}" diff --git a/base/hooks/+pre-commit-only.sh b/base/hooks/+pre-commit-only.sh new file mode 100755 index 00000000..916fa16a --- /dev/null +++ b/base/hooks/+pre-commit-only.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +exec /opt/git/global-hooks/+pre-commit.sh $(basename "$0") "$@" diff --git a/base/hooks/+pre-commit.sh b/base/hooks/+pre-commit.sh new file mode 100755 index 00000000..d79f4466 --- /dev/null +++ b/base/hooks/+pre-commit.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) 2014 pre-commit dev team: Anthony Sottile, Ken Struys +# SPDX-License-Identifier: MIT + +# File generated by pre-commit: https://pre-commit.com +# Modified by the Capella Docker images maintainers. + +STAGE=$1 + +# Skip pre-commit when running in the pre-commit cache directory. +# Otherwise, it will lead to a deadlock. +# pre-commit locks the cache directory, then clones the required repositories in the cache directory. +# This triggers the post-checkout hook of the repository, which tries to acquire the pre-commit lock. +[[ $PWD != /workspace/.pre-commit/* ]] || exit 0 + +# start templated +INSTALL_PYTHON=/opt/.venv/bin/python +ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=$STAGE --skip-on-missing-config) +# end templated + +HERE="$(cd "$(dirname "$0")" && pwd)" +ARGS+=(--hook-dir "$HERE" -- "${@:2}") + +if [ -x "$INSTALL_PYTHON" ]; then + exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}" +elif command -v pre-commit > /dev/null; then + exec pre-commit "${ARGS[@]}" +else + echo '`pre-commit` not found. Please report this error: https://github.com/DSD-DBS/capella-dockerimages/issues' 1>&2 + exit 1 +fi diff --git a/capella/Dockerfile b/capella/Dockerfile index 08004296..a2749667 100644 --- a/capella/Dockerfile +++ b/capella/Dockerfile @@ -133,7 +133,7 @@ RUN mkdir -p /opt/capella/configuration/.settings; \ ## Do not show WORKSPACE_SELECTION_DIALOG echo "SHOW_WORKSPACE_SELECTION_DIALOG=false" >> /opt/capella/configuration/.settings/org.eclipse.ui.ide.prefs; \ # Setup workspace - mkdir /workspace; \ + mkdir -p /workspace; \ ## Disable Welcome Screen mkdir -p /workspace/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs; \ # Set workspace permissions diff --git a/docs/docs/git-hooks/egit-failed-git-hook.png b/docs/docs/git-hooks/egit-failed-git-hook.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b36297fc608edf598e0b00fb16beaab0fc1ef0 GIT binary patch literal 30121 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 literal 0 HcmV?d00001 diff --git a/docs/docs/git-hooks/egit-failed-git-hook.png.license b/docs/docs/git-hooks/egit-failed-git-hook.png.license new file mode 100644 index 00000000..a990c045 --- /dev/null +++ b/docs/docs/git-hooks/egit-failed-git-hook.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/docs/git-hooks/git-hooks.md b/docs/docs/git-hooks/git-hooks.md new file mode 100644 index 00000000..70df336f --- /dev/null +++ b/docs/docs/git-hooks/git-hooks.md @@ -0,0 +1,55 @@ + + +# Support for Git Hooks + +To support Git hooks, we have decided to utilize the [pre-commit](https://pre-commit.com/) framework. This framework allows for automatic fetching and updating of hooks from external sources, providing users with a high level of flexibility and adaptability. Additionally, it effectively decouples the development process of Git hooks from our Docker image release lifecycle. + +The implementation was tested with the official Git CLI and JGit/EGit (in Eclipse). + +## Project-Specific Hooks + +Project-specific hooks can be defined separately for each Git repository. The defined Git hooks will not be auto-updated by us. Instead, the responsibility falls on the repository owners to keep them up-to-date and maintain them. + +### Installation + +To install Git hooks, add a `.pre-commit-config.yaml` file to the root of your repository. Simply follow the official pre-commit instructions: [Add a pre-commit configuration](https://pre-commit.com/#2-add-a-pre-commit-configuration). Commit the file after making changes. Git will automatically detect it, eliminating the need to explicitly install the hooks via `pre-commit install`. + + +!!! info + `pre-commit` is installed for all stages by default. This means that your pre-commit hooks will run at every stage. + If you want to limit the hooks to specific stages, for example, to run hooks only at the `pre-commit` stage, add the following to your `.pre-commit-config.yaml` file: + + ```yaml + default_stages: [pre-commit] + ``` + + Alternatively, specify stages for each hook individually: + + ```yaml + - repo: local + hooks: + - id: my-hook + stages: [pre-commit] + ``` + + For more information, refer to the `pre-commit` documentation: [Confining hooks to run at certain stages](https://pre-commit.com/#confining-hooks-to-run-at-certain-stages). + +### Updates + +To auto-update Git hooks, update the versions in your repository's `.pre-commit-config.yaml` file. Refer to the official documentation for [updating hooks automatically](https://pre-commit.com/#updating-hooks-automatically). + +## Technical Background + +To facilitate this implementation, we have globally set the `core.hooksPath` configuration option in Git to `/opt/git/global-hooks`. We initialize the pre-commit framework in this directory. This ensures that the hook is registered as `/opt/git/global-hooks/pre-commit`. Since the initialization of Git hooks with the pre-commit framework can be slow, we maintain a cache of the pre-commit environment and store it in the persistent `/workspace` directory. + +## Troubleshooting + + +!!! warning "Error Message in Eclipse with EGit" + If you receive an error message in Eclipse (as shown below) while committing, your pre-commit has failed. + ![Empty error message in Eclipse with EGit](./egit-failed-git-hook.png) + + Please check the Capella logs, located at `/var/logs`. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index f29d6bf0..81fbbfb8 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -13,6 +13,7 @@ theme: nav: - Introduction: index.md - Base: base.md + - Git hooks (pre-commit): git-hooks/git-hooks.md - CI/CD templates: - Index: ci-templates/index.md - Gitlab: diff --git a/eclipse/Dockerfile b/eclipse/Dockerfile index 1b9b0cd8..744ff320 100644 --- a/eclipse/Dockerfile +++ b/eclipse/Dockerfile @@ -35,7 +35,7 @@ RUN tar -xf eclipse.tar.gz && \ ARG MEMORY_LIMIT=5500m -RUN mkdir /workspace; \ +RUN mkdir -p /workspace; \ # Disable Welcome Screen mkdir -p /workspace/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs; \ # Set workspace permissions diff --git a/papyrus/Dockerfile b/papyrus/Dockerfile index b8bb78c5..6e230cea 100644 --- a/papyrus/Dockerfile +++ b/papyrus/Dockerfile @@ -19,7 +19,7 @@ RUN tar -xf papyrus.tar.gz && \ ARG MEMORY_LIMIT=5500m -RUN mkdir /workspace; \ +RUN mkdir -p /workspace; \ # Disable Welcome Screen mkdir -p /workspace/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs; \ # Set workspace permissions