From 74e6654dee752699205d2e6d273cd1644e348bb2 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 29 Jul 2025 08:19:30 -0400 Subject: [PATCH 1/7] Update GitHub Actions checkout action from v3 to v4 in Brakeman workflow --- .github/workflows/brakeman.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/brakeman.yml b/.github/workflows/brakeman.yml index 6c1433ca..55018c74 100644 --- a/.github/workflows/brakeman.yml +++ b/.github/workflows/brakeman.yml @@ -16,7 +16,7 @@ jobs: security: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 From 8948f7b7d1e2ae434397446c32823285b5c3aedc Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 29 Jul 2025 08:48:24 -0400 Subject: [PATCH 2/7] Add admin check to pre-push hook for direct pushes to protected branches --- .git-hooks/pre-push | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.git-hooks/pre-push b/.git-hooks/pre-push index 73d4ffd9..4f42c0d4 100755 --- a/.git-hooks/pre-push +++ b/.git-hooks/pre-push @@ -11,9 +11,28 @@ NC='\033[0m' # No Color echo -e "${YELLOW}Running pre-push hook on branch ${current_branch}${NC}" +# --- Admins list (add more usernames or emails as needed) --- +ADMINS=("rsmoke") +GIT_USER=$(git config user.name) +GIT_EMAIL=$(git config user.email) + +is_admin=false +for admin in "${ADMINS[@]}"; do + if [[ "$GIT_USER" == "$admin" || "$GIT_EMAIL" == "$admin" ]]; then + is_admin=true + break + fi +done + # Check if we're pushing to staging or main protected_branches="^(staging|main)$" if [[ "$current_branch" =~ $protected_branches ]]; then + # Allow admins to push directly + if [[ "$is_admin" == "true" ]]; then + echo -e "${GREEN}Admin detected ($GIT_USER). Direct push allowed.${NC}" + exit 0 + fi + # Check if this is a git hooks setup commit if git diff --name-only HEAD~1 HEAD | grep -q "^\.git-hooks/"; then echo -e "${YELLOW}Detected git hooks setup changes - allowing direct push${NC}" @@ -29,6 +48,12 @@ while read local_ref local_sha remote_ref remote_sha do target_branch=${remote_ref##refs/heads/} if [[ "$target_branch" =~ $protected_branches ]]; then + # Allow admins to push directly + if [[ "$is_admin" == "true" ]]; then + echo -e "${GREEN}Admin detected ($GIT_USER). Direct push allowed.${NC}" + exit 0 + fi + # Check if this is a git hooks setup commit if git diff --name-only HEAD~1 HEAD | grep -q "^\.git-hooks/"; then echo -e "${YELLOW}Detected git hooks setup changes - allowing push without tests${NC}" From f6a97f56b8103a6b43abcbe83477d100234cab4a Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 29 Jul 2025 08:58:06 -0400 Subject: [PATCH 3/7] Add admins list for Git hooks to enforce admin checks --- .git-hooks/admins.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 .git-hooks/admins.txt diff --git a/.git-hooks/admins.txt b/.git-hooks/admins.txt new file mode 100644 index 00000000..1cc93a0a --- /dev/null +++ b/.git-hooks/admins.txt @@ -0,0 +1 @@ +rsmoke From 567ed8d01b45863186cf6ca4c27e0db3986e5dd5 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 29 Jul 2025 08:58:12 -0400 Subject: [PATCH 4/7] Enhance pre-push hook with configurable admin list and branch protection checks. Implemented logging for failed pushes and refined test execution conditions based on code changes. --- .git-hooks/pre-push | 115 +++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/.git-hooks/pre-push b/.git-hooks/pre-push index 4f42c0d4..346800a9 100755 --- a/.git-hooks/pre-push +++ b/.git-hooks/pre-push @@ -1,7 +1,12 @@ #!/usr/bin/env bash -# Get the current branch name -current_branch=$(git symbolic-ref --short HEAD) +# Configurable variables +PROTECTED_BRANCHES=("staging" "main") +ADMIN_FILE=".git-hooks/admins.txt" +LOG_FILE=".git-hooks/pre-push.log" +TEST_CMD="${TEST_CMD:-bundle exec rspec}" + +CODE_EXTENSIONS="rb|js|py|go|java|ts|cpp|c|cs|php|swift|kt|rs|scala|pl|sh" # Colors for output RED='\033[0;31m' @@ -9,90 +14,110 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color +function log_failure { + echo "$(date +'%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" +} + +current_branch=$(git symbolic-ref --short HEAD) echo -e "${YELLOW}Running pre-push hook on branch ${current_branch}${NC}" -# --- Admins list (add more usernames or emails as needed) --- -ADMINS=("rsmoke") +# Load admin list from file if it exists +if [[ -f "$ADMIN_FILE" ]]; then + mapfile -t ADMINS < "$ADMIN_FILE" +else + # fallback to hardcoded + ADMINS=("rsmoke") +fi + GIT_USER=$(git config user.name) GIT_EMAIL=$(git config user.email) - is_admin=false for admin in "${ADMINS[@]}"; do if [[ "$GIT_USER" == "$admin" || "$GIT_EMAIL" == "$admin" ]]; then is_admin=true break fi + # Partial match for email domain + if [[ "$admin" == *"@"* ]] && [[ "$GIT_EMAIL" == *"${admin#*@}" ]]; then + is_admin=true + break + fi done -# Check if we're pushing to staging or main -protected_branches="^(staging|main)$" -if [[ "$current_branch" =~ $protected_branches ]]; then - # Allow admins to push directly +protected_regex="^($(IFS='|'; echo "${PROTECTED_BRANCHES[*]}"))$" + +# DRY RUN or HELP +if [[ "$1" == "--dry-run" || "$1" == "--help" ]]; then + echo -e "${GREEN}Pre-push hook dry-run/help mode${NC}" + echo "Admins: ${ADMINS[*]}" + echo "Protected branches: ${PROTECTED_BRANCHES[*]}" + echo "Test command: $TEST_CMD" + exit 0 +fi + +# Print hook configuration summary +echo -e "${YELLOW}Admins: ${ADMINS[*]}" +Protected branches: ${PROTECTED_BRANCHES[*]} +Test command: $TEST_CMD${NC}" + +# Main branch check +if [[ "$current_branch" =~ $protected_regex ]]; then if [[ "$is_admin" == "true" ]]; then echo -e "${GREEN}Admin detected ($GIT_USER). Direct push allowed.${NC}" exit 0 fi - - # Check if this is a git hooks setup commit if git diff --name-only HEAD~1 HEAD | grep -q "^\.git-hooks/"; then echo -e "${YELLOW}Detected git hooks setup changes - allowing direct push${NC}" exit 0 fi - echo -e "${RED}Direct pushes to $current_branch are not allowed. Please create a pull request.${NC}" + log_failure "Direct push blocked for $GIT_USER to $current_branch" exit 1 fi -# Get the target branch while read local_ref local_sha remote_ref remote_sha do target_branch=${remote_ref##refs/heads/} - if [[ "$target_branch" =~ $protected_branches ]]; then - # Allow admins to push directly + if [[ "$target_branch" =~ $protected_regex ]]; then if [[ "$is_admin" == "true" ]]; then echo -e "${GREEN}Admin detected ($GIT_USER). Direct push allowed.${NC}" exit 0 fi - - # Check if this is a git hooks setup commit if git diff --name-only HEAD~1 HEAD | grep -q "^\.git-hooks/"; then echo -e "${YELLOW}Detected git hooks setup changes - allowing push without tests${NC}" exit 0 fi - echo -e "${YELLOW}Pushing to $target_branch - running full test suite...${NC}" - - # Stash any uncommitted changes - if ! git diff --quiet HEAD; then - echo "Stashing uncommitted changes..." - git stash push -u - STASHED=1 - fi - - # Run the test suite - if bundle exec rspec; then - echo -e "${GREEN}All tests passed!${NC}" - - # Pop stashed changes if we stashed them - if [ "$STASHED" = "1" ]; then - echo "Popping stashed changes..." - git stash pop + # Only run tests if code files changed + if git diff --name-only origin/$target_branch | grep -E "\.($CODE_EXTENSIONS)$" > /dev/null; then + # Stash any uncommitted changes + if ! git diff --quiet HEAD; then + echo "Stashing uncommitted changes..." + git stash push -u + STASHED=1 fi - exit 0 - else - echo -e "${RED}Tests failed. Push aborted.${NC}" - - # Pop stashed changes if we stashed them - if [ "$STASHED" = "1" ]; then - echo "Popping stashed changes..." - git stash pop + if [[ -z "$SKIP_TESTS" ]]; then + if $TEST_CMD; then + echo -e "${GREEN}All tests passed!${NC}" + [ "$STASHED" = "1" ] && git stash pop + exit 0 + else + echo -e "${RED}Tests failed. Push aborted.${NC}" + [ "$STASHED" = "1" ] && git stash pop + log_failure "Tests failed for $GIT_USER on $target_branch" + exit 1 + fi + else + echo -e "${YELLOW}SKIP_TESTS set, skipping test run.${NC}" + [ "$STASHED" = "1" ] && git stash pop + exit 0 fi - - exit 1 + else + echo -e "${YELLOW}No code changes detected, skipping tests.${NC}" + exit 0 fi fi done -# If we're not pushing to a protected branch, allow the push without running tests exit 0 From 74476882dfebc340a020e4a1c81e2310441480e8 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 29 Jul 2025 09:02:46 -0400 Subject: [PATCH 5/7] Refactor pre-push hook to improve admin list loading and enhance output formatting. Made admin list loading POSIX compatible and separated echo statements for better readability. --- .git-hooks/pre-push | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.git-hooks/pre-push b/.git-hooks/pre-push index 346800a9..c5c0dda8 100755 --- a/.git-hooks/pre-push +++ b/.git-hooks/pre-push @@ -14,18 +14,20 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color -function log_failure { +log_failure() { echo "$(date +'%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" } current_branch=$(git symbolic-ref --short HEAD) echo -e "${YELLOW}Running pre-push hook on branch ${current_branch}${NC}" -# Load admin list from file if it exists +# Load admin list from file if it exists (POSIX compatible) +ADMINS=() if [[ -f "$ADMIN_FILE" ]]; then - mapfile -t ADMINS < "$ADMIN_FILE" + while IFS= read -r line || [[ -n "$line" ]]; do + ADMINS+=("$line") + done < "$ADMIN_FILE" else - # fallback to hardcoded ADMINS=("rsmoke") fi @@ -44,6 +46,7 @@ for admin in "${ADMINS[@]}"; do fi done +# Build protected branch regex protected_regex="^($(IFS='|'; echo "${PROTECTED_BRANCHES[*]}"))$" # DRY RUN or HELP @@ -55,10 +58,10 @@ if [[ "$1" == "--dry-run" || "$1" == "--help" ]]; then exit 0 fi -# Print hook configuration summary -echo -e "${YELLOW}Admins: ${ADMINS[*]}" -Protected branches: ${PROTECTED_BRANCHES[*]} -Test command: $TEST_CMD${NC}" +# Print hook configuration summary (separate echos) +echo -e "${YELLOW}Admins: ${ADMINS[*]}${NC}" +echo -e "${YELLOW}Protected branches: ${PROTECTED_BRANCHES[*]}${NC}" +echo -e "${YELLOW}Test command: $TEST_CMD${NC}" # Main branch check if [[ "$current_branch" =~ $protected_regex ]]; then From 34b6ff098dc9c065bbc8d84917c79b5121454063 Mon Sep 17 00:00:00 2001 From: rsmokeUM Date: Tue, 29 Jul 2025 09:12:13 -0400 Subject: [PATCH 6/7] Update README and .git-hooks documentation to clarify protected branches and pre-push hook usage. Added details on branch protection rules, admin permissions, and contributing workflow for better developer guidance. --- .git-hooks/README.md | 37 +++++++++++++++++++++++++++++++++++++ README.md | 12 ++++++++++++ 2 files changed, 49 insertions(+) diff --git a/.git-hooks/README.md b/.git-hooks/README.md index bc11d353..f5b28462 100644 --- a/.git-hooks/README.md +++ b/.git-hooks/README.md @@ -35,3 +35,40 @@ git push --no-verify ``` However, this should be used sparingly and only in exceptional circumstances. + +## Working with Protected Branches + +### Branch Protection + +- The `staging` and `main` branches are protected. Direct pushes are only allowed for admins. +- Non-admins must create pull requests and get approval before merging to protected branches. + +### Pre-Push Hook + +- This repo uses a pre-push hook (`.git-hooks/pre-push`) for additional safety: + - Blocks direct pushes to protected branches for non-admins. + - Runs the test suite when pushing code changes to protected branches. + - Allows skipping tests with: `SKIP_TESTS=1 git push origin ` + - Admins listed in `.git-hooks/admins.txt` can push directly. + +### Admins + +- To allow a user to push directly, add their Git username or email to `.git-hooks/admins.txt`. + +### Common Errors + +- **Direct pushes to staging are not allowed. Please create a pull request.** + - Solution: Open a pull request instead of pushing directly. +- **Tests failed. Push aborted.** + - Solution: Fix test failures before pushing. + +### Contributing Workflow + +1. Make changes on a feature branch. +2. Open a pull request to `staging` or `main`. +3. Wait for required approvals and test suite to pass. +4. Admins may push directly if necessary. + +--- + +For more details, see `.git-hooks/pre-push` and `.git-hooks/admins.txt`. diff --git a/README.md b/README.md index 1320ce5c..b08ac305 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,16 @@ This application uses SendGrid for email delivery in the production environment. Emails are automatically configured to be sent asynchronously through Sidekiq background jobs. +## Protected Branches and Pre-Push Hook + +This repository uses [branch protection rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/branch-protection-rules) for `staging` and `main` branches. +Direct pushes are restricted and enforced by a pre-push hook. + +**Summary:** + +- Non-admins: Must open a Pull Request to contribute to protected branches. +- Admins: Can push directly if listed in `.git-hooks/admins.txt`. +- All pushes to protected branches run tests automatically, unless skipped. +- For details on hook installation, admin setup, and troubleshooting, see [.git-hooks/README.md](.git-hooks/README.md). + ## This project is licensed under the [MIT License](https://github.com/your-repo/lsa-evaluate/blob/main/LICENSE) From 64b69756e5433534159f0bf278213baed710fa49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 13:22:17 +0000 Subject: [PATCH 7/7] Bump ruby-saml in the bundler group across 1 directory Bumps the bundler group with 1 update in the / directory: [ruby-saml](https://github.com/saml-toolkits/ruby-saml). Updates `ruby-saml` from 1.18.0 to 1.18.1 - [Release notes](https://github.com/saml-toolkits/ruby-saml/releases) - [Changelog](https://github.com/SAML-Toolkits/ruby-saml/blob/master/CHANGELOG.md) - [Commits](https://github.com/saml-toolkits/ruby-saml/compare/v1.18.0...v1.18.1) --- updated-dependencies: - dependency-name: ruby-saml dependency-version: 1.18.1 dependency-type: indirect dependency-group: bundler ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5d72e6a0..8146a258 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -441,7 +441,7 @@ GEM rubocop (~> 1.61) rubocop-rspec (~> 3, >= 3.0.1) ruby-progressbar (1.13.0) - ruby-saml (1.18.0) + ruby-saml (1.18.1) nokogiri (>= 1.13.10) rexml ruby-vips (2.2.2)