Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/check-version-bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Catches when developers forget to bump package.json for release-affecting changes.
# App code changes (app.js, bin/, config/, routes/, views/, etc.) require a version bump vs latest tag.
# Skips for: test-only, docs, .github (workflows/config), dependency-only bumps without app edits.
name: Check Version Bump

on:
pull_request:

jobs:
version-bump:
name: Version bump
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Detect changed files and version bump
id: detect
run: |
if git rev-parse HEAD^2 >/dev/null 2>&1; then
FILES=$(git diff --name-only HEAD^1 HEAD^2)
else
FILES=$(git diff --name-only HEAD~1 HEAD)
fi
VERSION_FILES_CHANGED=false
echo "$FILES" | grep -qx 'package.json' && VERSION_FILES_CHANGED=true
echo "version_files_changed=$VERSION_FILES_CHANGED" >> $GITHUB_OUTPUT
# App source paths for this boilerplate (no lib/webpack/dist); .github/ and test/ do not count
CODE_CHANGED=false
echo "$FILES" | grep -qE '^app\.js$|^bin/|^config/|^middlewares/|^models/|^public/|^routes/|^views/|^schemaNentries/' && CODE_CHANGED=true
echo "$FILES" | grep -qx 'package.json' && CODE_CHANGED=true
echo "code_changed=$CODE_CHANGED" >> $GITHUB_OUTPUT

- name: Skip when only test/docs/.github changed
if: steps.detect.outputs.code_changed != 'true'
run: |
echo "No release-affecting files changed (e.g. only test/docs/.github). Skipping version-bump check."
exit 0

- name: Fail when version bump was missed
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed != 'true'
run: |
echo "::error::This PR has code changes but no version bump. Please bump the version in package.json."
exit 1

- name: Setup Node
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
uses: actions/setup-node@v4
with:
node-version: '22.x'

- name: Check version bump
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
run: |
set -e
PKG_VERSION=$(node -p "require('./package.json').version.replace(/^v/, '')")
if [ -z "$PKG_VERSION" ]; then
echo "::error::Could not read version from package.json"
exit 1
fi
git fetch --tags --force 2>/dev/null || true
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
if [ -z "$LATEST_TAG" ]; then
echo "No existing tags found. Skipping version-bump check (first release)."
exit 0
fi
LATEST_VERSION="${LATEST_TAG#v}"
LATEST_VERSION="${LATEST_VERSION%%-*}"
if [ "$(printf '%s\n' "$LATEST_VERSION" "$PKG_VERSION" | sort -V | tail -1)" != "$PKG_VERSION" ]; then
echo "::error::Version bump required: package.json version ($PKG_VERSION) is not greater than latest tag ($LATEST_TAG). Please bump the version in package.json."
exit 1
fi
if [ "$PKG_VERSION" = "$LATEST_VERSION" ]; then
echo "::error::Version bump required: package.json version ($PKG_VERSION) equals latest tag ($LATEST_TAG). Please bump the version in package.json."
exit 1
fi
echo "Version bump check passed: package.json is at $PKG_VERSION (latest tag: $LATEST_TAG)."
54 changes: 0 additions & 54 deletions .github/workflows/release.yml

This file was deleted.

40 changes: 40 additions & 0 deletions .husky/post-checkout
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env sh
# When switching to a branch that doesn't exist on remote (e.g. newly created),
# pull and merge origin/main or origin/master into current branch. Does not push.

# Only run on branch checkout (not file checkout)
if [ "$3" != "1" ]; then
exit 0
fi

# Skip if we don't have a remote
if ! git rev-parse --verify origin 2>/dev/null; then
exit 0
fi

CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)

# Skip main/master - no need to merge base into these
case "$CURRENT_BRANCH" in
main|master) exit 0 ;;
esac

# Only run when current branch does not exist on origin (treat as new local branch)
if git ls-remote --heads origin "$CURRENT_BRANCH" 2>/dev/null | grep -q .; then
echo "post-checkout: $CURRENT_BRANCH exists on origin, skipping merge."
exit 0
fi

# Prefer main, fallback to master
if git rev-parse --verify origin/main 2>/dev/null; then
BASE=origin/main
elif git rev-parse --verify origin/master 2>/dev/null; then
BASE=origin/master
else
exit 0
fi

echo "New branch detected: merging latest $BASE into $CURRENT_BRANCH (local only, not pushing)..."
git fetch origin
git merge "$BASE" --no-edit --no-ff
echo "Done. Merge is local only; push when ready."
69 changes: 69 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env sh
# Pre-commit hook to run Snyk and Talisman scans, completing both before deciding to commit

# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}

# Check if Snyk is installed
if ! command_exists snyk; then
echo "Error: Snyk is not installed. Please install it and try again."
exit 1
fi

# Check if Talisman is installed
if ! command_exists talisman; then
echo "Error: Talisman is not installed. Please install it and try again."
exit 1
fi

# Allow bypassing the hook with an environment variable
if [ "$SKIP_HOOK" = "1" ]; then
echo "Skipping Snyk and Talisman scans (SKIP_HOOK=1)."
exit 0
fi

# Initialize variables to track scan results
snyk_failed=false
talisman_failed=false

# Run Snyk vulnerability scan
echo "Running Snyk vulnerability scan..."
snyk test --all-projects > snyk_output.log 2>&1
snyk_exit_code=$?

if [ $snyk_exit_code -eq 0 ]; then
echo "Snyk scan passed: No vulnerabilities found."
elif [ $snyk_exit_code -eq 1 ]; then
echo "Snyk found vulnerabilities. See snyk_output.log for details."
snyk_failed=true
else
echo "Snyk scan failed with error (exit code $snyk_exit_code). See snyk_output.log for details."
snyk_failed=true
fi

# Run Talisman secret scan (continues even if Snyk failed)
echo "Running Talisman secret scan..."
talisman --githook pre-commit > talisman_output.log 2>&1
talisman_exit_code=$?

if [ $talisman_exit_code -eq 0 ]; then
echo "Talisman scan passed: No secrets found."
else
echo "Talisman scan failed (exit code $talisman_exit_code). See talisman_output.log for details."
talisman_failed=true
fi

# Evaluate results after both scans
if [ "$snyk_failed" = true ] || [ "$talisman_failed" = true ]; then
echo "Commit aborted due to issues found in one or both scans."
[ "$snyk_failed" = true ] && echo "- Snyk issues: Check snyk_output.log"
[ "$talisman_failed" = true ] && echo "- Talisman issues: Check talisman_output.log"
exit 1
fi

# If both scans pass, allow the commit
echo "All scans passed. Proceeding with commit.cd ."
rm -f snyk_output.log talisman_output.log
exit 0
Loading
Loading