From 053119419c4cfc8e0d7f2b1e36db209015789d5f Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Thu, 18 Dec 2025 11:44:03 +0100 Subject: [PATCH] Git Hooks: Merge hook scripts and tests from `blender-devops` --- git-hooks/.gitignore | 1 + git-hooks/README.md | 35 ++++++ git-hooks/blender_merge_hook | 116 ++++++++++++++++++ git-hooks/deny-binary | 94 ++++++++++++++ git-hooks/notice | 17 +++ git-hooks/test/common/functions | 45 +++++++ .../deny_binary/10_initial_text_file_test.sh | 31 +++++ .../test/deny_binary/11_add_text_file_test.sh | 38 ++++++ .../20_initial_binary_file_test.sh | 32 +++++ .../deny_binary/21_add_binary_file_test.sh | 38 ++++++ .../22_add_suspicious_binary_file_test.sh | 41 +++++++ .../30_add_binary_lfs_file_test.sh | 46 +++++++ .../40_add_binary_file_and_convert_test.sh | 47 +++++++ .../50_migrate_legacy_file_to_lfs_test.sh | 53 ++++++++ git-hooks/test/deny_binary/functions | 47 +++++++ git-hooks/test/run_deny_binary_tests.sh | 10 ++ 16 files changed, 691 insertions(+) create mode 100644 git-hooks/.gitignore create mode 100644 git-hooks/README.md create mode 100755 git-hooks/blender_merge_hook create mode 100755 git-hooks/deny-binary create mode 100644 git-hooks/notice create mode 100644 git-hooks/test/common/functions create mode 100755 git-hooks/test/deny_binary/10_initial_text_file_test.sh create mode 100755 git-hooks/test/deny_binary/11_add_text_file_test.sh create mode 100755 git-hooks/test/deny_binary/20_initial_binary_file_test.sh create mode 100755 git-hooks/test/deny_binary/21_add_binary_file_test.sh create mode 100755 git-hooks/test/deny_binary/22_add_suspicious_binary_file_test.sh create mode 100755 git-hooks/test/deny_binary/30_add_binary_lfs_file_test.sh create mode 100755 git-hooks/test/deny_binary/40_add_binary_file_and_convert_test.sh create mode 100755 git-hooks/test/deny_binary/50_migrate_legacy_file_to_lfs_test.sh create mode 100644 git-hooks/test/deny_binary/functions create mode 100755 git-hooks/test/run_deny_binary_tests.sh diff --git a/git-hooks/.gitignore b/git-hooks/.gitignore new file mode 100644 index 0000000..09abd83 --- /dev/null +++ b/git-hooks/.gitignore @@ -0,0 +1 @@ +test/**/tmp diff --git a/git-hooks/README.md b/git-hooks/README.md new file mode 100644 index 0000000..3e8ba64 --- /dev/null +++ b/git-hooks/README.md @@ -0,0 +1,35 @@ +# Git Hooks + +## Reject Merge Commits + +`blender_merged_hook` rejects merge commits to branches were we don't want them. +It is installed as an `pre-receive` hook. + +## Deny Binary Files + +The `deny-binary` rejects binary files, that should have used LFS instead. +It is installed as a `pre-receive` hook. + +To circumvent the rejection you can add `override restrictions` anywhere in the commit message. + +## Repositories + +Repositories with hooks: + +- `blender/blender` +- `blender/blender-assets` +- `blender/blender-benchmarks` +- `blender/blender-developer-docs` +- `blender/blender-manual` +- `blender/lib-linux_x64` +- `blender/lib-macos_arm64` +- `blender/lib-macos_x64` +- `blender/lib-source` +- `blender/lib-windows_arm64` +- `blender/lib-windows_x64` + +Archived repositories that may have hooks, but are not longer being updated: + +- `blender/blender-addons` +- `blender/blender-addons-contrib` +- `blender/blender-test-data` diff --git a/git-hooks/blender_merge_hook b/git-hooks/blender_merge_hook new file mode 100755 index 0000000..74bdc8f --- /dev/null +++ b/git-hooks/blender_merge_hook @@ -0,0 +1,116 @@ +#!/bin/bash -eu +# +# Blender: modified version of update.sample in hooks directory to: +# - Deny merge commits to release branches +# - Deny merge of non-release branches to main +# +# This indirectly checks for accidentally merging main into release +# branches, as main is almost certain to contain merge commits not +# in the release branch. +# +# To enable this hook, rename this file to "pre-receive" or place it in +# an "pre-receive.d" folder. + +# No revision indicator. +zero_revision="0000000000000000000000000000000000000000" + +# Check if we need to verify merge commits for this branch. +need_verify_merge_commit() +{ + local branch="$1" + local old_revision="$2" + + if [ "$old_revision" = "$zero_revision" ]; then + # New branch, always ok to push. + false + elif [ "$branch" = "main" ]; then + # Some merge commits allowed in main branch. + true + elif (echo "${branch}" | grep -Eq ^blender-v[0-9]\.[0-9][0-9]?-release$); then + # No merge commits in release branch. + true + else + false + fi +} + +# Check if this is an invalid merge commit. +is_invalid_merge_commit() +{ + local branch="$1" + local revision="$2" + + # Detect if this revision is a merge commit. It's based on the commit merge + # message which is weak, but not clear how to do it otherwise. + if git show -s --format=%B ${revision} | grep -Eq "Merge (remote-tracking )?branch '"; then + if (echo "${branch}" | grep -Eq ^blender-v[0-9]\.[0-9][0-9]?-release$); then + # In release branch, all merge commits are invalid. + true + elif [ "$branch" = "main" ]; then + # In main branch, only merge commits from release branches. + if git show -s --format=%B ${revision} | grep -Eq "Merge (remote-tracking )?branch '(origin\/)?blender-v[0-9]\.[0-9][0-9]?-release'"; then + false + else + true + fi + else + false + fi + else + false + fi +} + +# Read stdin for ref information. +while read oldrev newrev refname; do + # Detect type of update. + if [ "$newrev" = "$zero_revision" ]; then + newrev_type=delete + else + newrev_type=$(git cat-file -t $newrev) + fi + + case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + ;; + refs/tags/*,delete) + # delete tag + ;; + refs/tags/*,tag) + # annotated tag + ;; + refs/heads/*,commit) + # branch + branch=${refname##refs/heads/} + + if need_verify_merge_commit "$branch" "$oldrev"; then + for r in $(test "$oldrev" = $zero_revision \ + && git rev-list $newrev \ + || git rev-list $newrev ^$oldrev); do + is_invalid_merge_commit ${branch} ${r} && { + printf "error: *** %s\n" \ + "You may only merge release branches into main, no other merge commits are allowed." \ + "See Blender's release branch developer documentation for details." >&2 + exit 1 + } + done + fi + + ;; + refs/heads/*,delete) + # delete branch + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + ;; + *) + # Anything else (is there anything else?) + ;; + esac +done + +exit 0 diff --git a/git-hooks/deny-binary b/git-hooks/deny-binary new file mode 100755 index 0000000..b164928 --- /dev/null +++ b/git-hooks/deny-binary @@ -0,0 +1,94 @@ +#!/bin/bash -eu +# +# Reject commits with binary files, which should have used LFS instead. Allow +# overriding when "override restrictions" is in the commit message. +# +# To enable this hook, rename this file to "pre-receive" or place it in +# a "pre-receive.d" folder. + +# Use 'strict mode': http://redsymbol.net/articles/unofficial-bash-strict-mode/ +set -o pipefail + +nullsha="0000000000000000000000000000000000000000" +status=0 +# This is the message to include in your commit to override. +commit_override_msg="override restrictions" + +handle_pipefails() { + # ignore exit code 141 from simple command pipes + # - use with: cmd1 | cmd2 || handle_pipefails $? + (( $1 == 141 )) && return 0 + return $1 +} + +# Read stdin for ref information. +while read oldrev newrev refname; do + # Skip branch deletions. + if [ "$newrev" = "$nullsha" ]; then + continue + fi + + # Set oldrev to HEAD if this is branch creation. + if [ "$oldrev" = "$nullsha" ]; then + oldrev="HEAD" + fi + + # Check for branches and tags, but not pull requests in refs/pull. Otherwise Gitea + # fails to create a pull request without explaining why. + if [[ "$refname" == refs/heads/* ]]; then + : + elif [[ "$refname" == refs/tags/* ]]; then + : + else + exit 0 + fi + + # Loop over each commit. + for commit in $(git rev-list --objects ${oldrev}..${newrev} | + git cat-file --batch-check='%(objectname) %(objecttype) %(objectsize) %(rest)' | grep commit | awk '{print $1}'); do + + # Get list of potentially binary files in this commit + mapfile -t binary_files < <(git log --diff-filter=d --pretty=format:%H -M100% --numstat ${commit}^! | grep -e "- - \w" | awk '{print $3}') + + if [ ${#binary_files[@]} -eq 0 ]; then + continue + fi + + # Check for override message + override=0 + if git log --pretty=format:%B ${commit}^! | grep -qi "${commit_override_msg}"; then + override=1 + fi + + rejected_files=() + for file in "${binary_files[@]}"; do + # Check if it's an LFS pointer + # Use handle_pipefails as we may run into SIGPIPE error code + if git show "${commit}:${file}" | git lfs pointer --check --stdin || handle_pipefails $?; then + continue + fi + rejected_files+=("$file") + done + + if [ ${#rejected_files[@]} -gt 0 ]; then + if [ "$override" -gt 0 ]; then + echo "Your push contains binary files but was accepted because of override commit message:" + else + status=1 + echo "Your push was rejected because it contains binary files not tracked by Git LFS:" + fi + echo "Commit: $commit" + for file in "${rejected_files[@]}"; do + echo "File: $file" + done + fi + done +done + +if [ "$status" -ne "0" ]; then + echo + echo "You must make sure that the binary files are tracked with Git LFS or seek help from the repo administrators." + echo "(Perhaps you did not setup Git LFS with 'git lfs install'?)" + echo +fi +exit $status diff --git a/git-hooks/notice b/git-hooks/notice new file mode 100644 index 0000000..21fbeb1 --- /dev/null +++ b/git-hooks/notice @@ -0,0 +1,17 @@ +#!/bin/sh + +REPO_PATH=$(pwd) +REPO_NAME=$(basename "$REPO_PATH" .git) +ORG_NAME=$(basename "$(dirname "$REPO_PATH")") +NEW_SERVER="git.blender.org" + +echo "" +echo "*********************************************************************************" +echo "* IMPORTANT NOTICE *" +echo "* Blender Projects is moving its Git SSH domain to git.blender.org! *" +echo "* If you haven't already, please update your Git remote to use the *" +echo "* git@git.blender.org host instead of the git@projects.blender.org one. *" +echo "* *" +echo "* More information: https://devtalk.blender.org/t/-/41098 *" +echo "*********************************************************************************" +echo "" \ No newline at end of file diff --git a/git-hooks/test/common/functions b/git-hooks/test/common/functions new file mode 100644 index 0000000..8fdd144 --- /dev/null +++ b/git-hooks/test/common/functions @@ -0,0 +1,45 @@ +# Run single test provided via $1. +# Prints "ok" on success, "FAIL" on failure, and returns the appropriate status code. +run_test() { + local TEST_SCRIPT=$1 + printf '%s' "Running $(basename "${TEST_SCRIPT}")... " + + # Run test silently. + # Return 0 on success, 1 on failure. + if "${TEST_SCRIPT}" > /dev/null 2>&1; then + echo "ok" + return 0 + else + echo "FAIL" + return 1 + fi +} + +# Run all tests in the given directory (specified as $1). +# All .sh tests are executed in sorted order. +# The script reports how many tests failed and exits with non-zero status if any did. +run_tests_in_directory() { + local DIR=$1 + local failed=0 + + echo "== Running tests in directory $(basename "${DIR}") ==" + + # Iterate over all .sh files and run them one by one. + for test_script in $(ls "${DIR}" | sort); do + case "${test_script}" in + *.sh) + if ! run_test "${DIR}/${test_script}"; then + failed=$((failed + 1)) + fi + ;; + esac + done + + # After running all tests, report overall result. + if [ "$failed" -ne 0 ]; then + echo "$failed test(s) failed." + exit 1 + else + echo "All tests passed." + fi +} diff --git a/git-hooks/test/deny_binary/10_initial_text_file_test.sh b/git-hooks/test/deny_binary/10_initial_text_file_test.sh new file mode 100755 index 0000000..f20dc43 --- /dev/null +++ b/git-hooks/test/deny_binary/10_initial_text_file_test.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# Test: create text file in the empty repository and try to push it. +# +# It is expected to pass as it is perfectly nomial usage of Git for text +# based files. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/11_add_text_file_test.sh b/git-hooks/test/deny_binary/11_add_text_file_test.sh new file mode 100755 index 0000000..0820f95 --- /dev/null +++ b/git-hooks/test/deny_binary/11_add_text_file_test.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Test: add text file to the existing repository and try to push it. +# +# It is expected to pass as it is perfectly nomial usage of Git for text +# based files. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo "Hello, World!" > "${WORK_GIT_DIR}/NOTES.txt" +git -C "${WORK_GIT_DIR}" add NOTES.txt +git -C "${WORK_GIT_DIR}" commit --message "Add file" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/20_initial_binary_file_test.sh b/git-hooks/test/deny_binary/20_initial_binary_file_test.sh new file mode 100755 index 0000000..1ea3b91 --- /dev/null +++ b/git-hooks/test/deny_binary/20_initial_binary_file_test.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Test: Add binary file to an empty repository and try to push. +# +# It is expected to pass as part of a logic which allows to branch off +# existing branches where it is possible to have binary files that were +# added prior to migration to Git LFS. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +dd if=/dev/zero of="${WORK_GIT_DIR}/data.bin" bs=1 count=32k +git -C "${WORK_GIT_DIR}" add data.bin +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/21_add_binary_file_test.sh b/git-hooks/test/deny_binary/21_add_binary_file_test.sh new file mode 100755 index 0000000..3cb00c1 --- /dev/null +++ b/git-hooks/test/deny_binary/21_add_binary_file_test.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Test: add binary file to the existing repository and try to push it. +# +# This test is expected to fail as new binary files are expected to be +# using Git LFS. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +dd if=/dev/zero of="${WORK_GIT_DIR}/data.bin" bs=1 count=32k +git -C "${WORK_GIT_DIR}" add data.bin +git -C "${WORK_GIT_DIR}" commit --message "Add binary file" +if git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/22_add_suspicious_binary_file_test.sh b/git-hooks/test/deny_binary/22_add_suspicious_binary_file_test.sh new file mode 100755 index 0000000..1d999ee --- /dev/null +++ b/git-hooks/test/deny_binary/22_add_suspicious_binary_file_test.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# Test: add binary file that starts with Git LFS reference content. +# +# This test is expected to fail as it is actually a binary file. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + + +echo "version https://git-lfs.github.com/spec/v1" > "${WORK_GIT_DIR}/data.bin" +echo "oid sha256:c35020473aed1b4642cd726cad727b63fff2824ad68cedd7ffb73c7cbd890479" >> "${WORK_GIT_DIR}/data.bin" +echo "size 32768" >> "${WORK_GIT_DIR}/data.bin" +dd if=/dev/zero of="${WORK_GIT_DIR}/data.bin" bs=1 count=32k oflag=append conv=notrunc +git -C "${WORK_GIT_DIR}" add data.bin +git -C "${WORK_GIT_DIR}" commit --message "Add binary file" +if git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/30_add_binary_lfs_file_test.sh b/git-hooks/test/deny_binary/30_add_binary_lfs_file_test.sh new file mode 100755 index 0000000..8f2db6f --- /dev/null +++ b/git-hooks/test/deny_binary/30_add_binary_lfs_file_test.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# Test: add binary file to the existing repository with proper LFS filters +# installed prior to adding a binary file. +# +# This test is expected to pass as it is an expected usage of Git LFS. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +git -C "${WORK_GIT_DIR}" lfs install +git -C "${WORK_GIT_DIR}" lfs track "*.bin" +git -C "${WORK_GIT_DIR}" add .gitattributes +git -C "${WORK_GIT_DIR}" commit --message "Track .bin files with Git LFS" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +dd if=/dev/zero of="${WORK_GIT_DIR}/data.bin" bs=1 count=32k +git -C "${WORK_GIT_DIR}" add data.bin +git -C "${WORK_GIT_DIR}" commit --message "Add binary file" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/40_add_binary_file_and_convert_test.sh b/git-hooks/test/deny_binary/40_add_binary_file_and_convert_test.sh new file mode 100755 index 0000000..c116852 --- /dev/null +++ b/git-hooks/test/deny_binary/40_add_binary_file_and_convert_test.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +# Test: add binary file to the existing repository before LFS filters is +# configured, configure filter, and convert to LFS. +# +# This test is expected to fail as all new files after adding filter are +# supposed to be covered by Git LFS. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +dd if=/dev/zero of="${WORK_GIT_DIR}/data.bin" bs=1 count=32k +git -C "${WORK_GIT_DIR}" add data.bin +git -C "${WORK_GIT_DIR}" commit --message "Add binary file" + +git -C "${WORK_GIT_DIR}" lfs install +git -C "${WORK_GIT_DIR}" lfs track "*.bin" +git -C "${WORK_GIT_DIR}" add .gitattributes +git -C "${WORK_GIT_DIR}" commit --message "Track .bin files with Git LFS" + +git -C "${WORK_GIT_DIR}" lfs migrate import --no-rewrite --yes data.bin + +if git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/50_migrate_legacy_file_to_lfs_test.sh b/git-hooks/test/deny_binary/50_migrate_legacy_file_to_lfs_test.sh new file mode 100755 index 0000000..0d06a67 --- /dev/null +++ b/git-hooks/test/deny_binary/50_migrate_legacy_file_to_lfs_test.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +# Test: Add filter and convert binary file to LFS on an existing repository. +# +# This test is expected to pass since it is perfectly nominal operation to do +# when dealing with legacy code base where binary files were added before LFS +# was configured, and when conversion is needed after the install of the deny +# hook. + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. ${SCRIPT_PATH}/functions + +setup_temp_git_identity + +ORIGIN_REPO_DIR=`setup_bare_origin_repository` +WORK_GIT_DIR=`clone_repository "${ORIGIN_REPO_DIR}"` + +echo "Hello, World!" > "${WORK_GIT_DIR}/README.txt" +git -C "${WORK_GIT_DIR}" add README.txt +git -C "${WORK_GIT_DIR}" commit --message "Initial commit" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +dd if=/dev/zero of="${WORK_GIT_DIR}/data.bin" bs=1 count=32k +git -C "${WORK_GIT_DIR}" add data.bin +git -C "${WORK_GIT_DIR}" commit --message "Add binary file" +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +install_hook "${ORIGIN_REPO_DIR}" "${SCRIPT_PATH}/../../deny-binary" "pre-receive" + +git -C "${WORK_GIT_DIR}" lfs install +git -C "${WORK_GIT_DIR}" lfs track "*.bin" +git -C "${WORK_GIT_DIR}" add .gitattributes +git -C "${WORK_GIT_DIR}" commit --message "Track .bin files with Git LFS" + +git -C "${WORK_GIT_DIR}" lfs migrate import --no-rewrite --yes data.bin +git -C "${WORK_GIT_DIR}" reset --soft HEAD~1 +git -C "${WORK_GIT_DIR}" commit --amend --message "Track .bin files with Git LFS and convert existing file" + +if ! git -C "${WORK_GIT_DIR}" push; then + exit 1 +fi + +echo +echo "Test passed!" +exit 0 diff --git a/git-hooks/test/deny_binary/functions b/git-hooks/test/deny_binary/functions new file mode 100644 index 0000000..dc91f41 --- /dev/null +++ b/git-hooks/test/deny_binary/functions @@ -0,0 +1,47 @@ +# Set up temp Git identity if none is set +setup_temp_git_identity() { + export GIT_AUTHOR_NAME="Test User" + export GIT_AUTHOR_EMAIL="tests@example.com" + export GIT_COMMITTER_NAME="Test User" + export GIT_COMMITTER_EMAIL="tests@example.com" +} + +# Get temporary directory where test data will be generated. +# The function echo's the directory. +get_tmp_dir() { + local SCRIPT=$(readlink -f "$0") + local SCRIPT_PATH=$(dirname "$SCRIPT") + + echo "$(dirname ${0})/tmp/$(basename "${SCRIPT%.*}")" +} + +# Setup bare repository which could be used as a server-side upstream. +# The function echo's the path to the repository. +setup_bare_origin_repository() { + local TMP_DIR=`get_tmp_dir` + local REPO_DIR="${TMP_DIR}/origin/test-repo.git" + rm -rf "${REPO_DIR}" + mkdir -p "${REPO_DIR}" + git -C "${REPO_DIR}" init --bare --initial-branch=main > /dev/null 2>&1 + echo "${REPO_DIR}" +} + +# Install hook to the given bare repository. +# The bare git repository is provided as $1, hook script is provided as $2, +# and the hook name is provided as $3. +install_hook() { + local GIT_DIR="${1}" + local HOOK_SCRIPT="${2}" + local HOOK_NAME="${3}" + cp "${HOOK_SCRIPT}" "${GIT_DIR}/hooks/${HOOK_NAME}" +} + +# Clone repository provided via $1. The path to the working directory is +# echo'd. +clone_repository() { + local TMP_DIR=`get_tmp_dir` + local WORK_GIT_DIR="${TMP_DIR}/checkout/test-repo" + rm -rf "${WORK_GIT_DIR}" + git clone "${1}" "${WORK_GIT_DIR}" > /dev/null 2>&1 + echo "${WORK_GIT_DIR}" +} diff --git a/git-hooks/test/run_deny_binary_tests.sh b/git-hooks/test/run_deny_binary_tests.sh new file mode 100755 index 0000000..8120db3 --- /dev/null +++ b/git-hooks/test/run_deny_binary_tests.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +SCRIPT=$(readlink -f "$0") +SCRIPT_PATH=$(dirname "$SCRIPT") + +. "${SCRIPT_PATH}/common/functions" + +run_tests_in_directory "${SCRIPT_PATH}/deny_binary"