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
2 changes: 1 addition & 1 deletion .github/env/00-core.env
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ GO_PRIMARY_VERSION=1.24.x
GO_SECONDARY_VERSION=1.24.x

# Govulncheck-specific Go version for vulnerability scanning
GOVULNCHECK_GO_VERSION=1.27.1
GOVULNCHECK_GO_VERSION=1.27.2

# ================================================================================================
# 📦 GO MODULE CONFIGURATION
Expand Down
4 changes: 2 additions & 2 deletions .github/env/10-mage-x.env
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# ================================================================================================

# MAGE-X version
MAGE_X_VERSION=v1.27.3
MAGE_X_VERSION=v1.28.0

# For mage-x development, set to 'true' to use local version instead of downloading from releases
MAGE_X_USE_LOCAL=false
Expand Down Expand Up @@ -117,7 +117,7 @@ MAGE_X_YAMLFMT_VERSION=v0.21.0
# runners on Go >= _LATEST_MIN_GO install _LATEST; Go 1.25.x runners install the held
# MAGE_X_BENCHSTAT_VERSION; Go < 1.25 is skipped (benchstat's floor).
MAGE_X_BENCHSTAT_VERSION=v0.0.0-20260813145340-fd4a688df892
MAGE_X_BENCHSTAT_VERSION_LATEST=v0.0.0-20260908200009-22c9c6c9d4da
MAGE_X_BENCHSTAT_VERSION_LATEST=v0.0.0-20260929162123-406019bb8b68
MAGE_X_BENCHSTAT_VERSION_LATEST_MIN_GO=1.26
MAGE_X_MAGE_VERSION=v1.17.2

Expand Down
2 changes: 1 addition & 1 deletion .github/env/10-pre-commit.env
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# 🪝 PRE-COMMIT TOOL VERSION
# ================================================================================================

GO_PRE_COMMIT_VERSION=v1.11.0
GO_PRE_COMMIT_VERSION=v1.13.0
GO_PRE_COMMIT_USE_LOCAL=false

# ================================================================================================
Expand Down
6 changes: 6 additions & 0 deletions .github/env/20-workflows.env
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ DEPENDABOT_MAINTAINER_USERNAME=mrz1836
# Auto-merge patch version updates
DEPENDABOT_AUTO_MERGE_PATCH=true

# Auto-merge patch updates for direct production dependencies, but ONLY when every
# updated dependency is hosted under this repository's own GitHub owner (org/user).
# The owner is read at runtime from github.repository_owner (never hardcoded).
# Third-party production patches still require manual review.
DEPENDABOT_AUTO_MERGE_PATCH_SAME_OWNER=true

# Auto-merge minor version updates for dev dependencies
DEPENDABOT_AUTO_MERGE_MINOR_DEV=true

Expand Down
157 changes: 147 additions & 10 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
# Triggers: Pull request events for immediate response to Dependabot PRs
#
# Auto-merge Rules (configurable via .github/env/):
# - Patch updates: Auto-merge by default
# - Patch updates (dev/indirect): Auto-merge by default
# - Patch updates (direct production): Manual review, unless every dependency
# is owned by this repo's GitHub owner (DEPENDABOT_AUTO_MERGE_PATCH_SAME_OWNER)
# - Minor dev dependencies: Auto-merge by default
# - Minor prod dependencies: Manual review by default
# - Minor prod dependencies: Configurable (DEPENDABOT_AUTO_MERGE_MINOR_PROD)
# - Major updates: Always require manual review with alert
# - Security updates: Auto-merge non-major by default
#
Expand Down Expand Up @@ -66,6 +68,7 @@ jobs:
# 📊 Fetch metadata → official Dependabot metadata
# 📋 Log dependency details → trace logging
# 🔒 Check for security → classify security vs non-security
# 🏢 Check same owner → are all deps owned by this repo's owner?
# 🎯 Determine action → decision tree over config + metadata
# ⚠️ Alert (major/security) → comment + manual-review label
# 🔍 Alert (minor prod) → comment for review
Expand Down Expand Up @@ -130,6 +133,7 @@ jobs:
{
echo "MAINTAINER=$MAINTAINER"
echo "AUTO_MERGE_PATCH=$(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_PATCH')"
echo "AUTO_MERGE_PATCH_SAME_OWNER=$(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_PATCH_SAME_OWNER // "false"')"
echo "AUTO_MERGE_MINOR_DEV=$(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_MINOR_DEV')"
echo "AUTO_MERGE_MINOR_PROD=$(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_MINOR_PROD')"
echo "AUTO_MERGE_PATCH_INDIRECT=$(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_PATCH_INDIRECT')"
Expand All @@ -147,6 +151,7 @@ jobs:
echo "🔍 Configuration loaded:"
echo " 👤 Maintainer: @$MAINTAINER"
echo " 🔧 Auto-merge patch: $(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_PATCH')"
echo " 🔧 Auto-merge patch same-owner prod: $(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_PATCH_SAME_OWNER // "false"')"
echo " 🔧 Auto-merge minor dev: $(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_MINOR_DEV')"
echo " 🔧 Auto-merge minor prod: $(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_MINOR_PROD')"
echo " 🔧 Auto-merge patch indirect: $(echo "$ENV_JSON" | jq -r '.DEPENDABOT_AUTO_MERGE_PATCH_INDIRECT')"
Expand Down Expand Up @@ -314,6 +319,108 @@ jobs:
fi
echo "════════════════════════════════════════════════════════════════"
# --------------------------------------------------------------------
# Check whether every updated dependency is owned by this repo's owner
#
# Used to let production PATCH updates of first-party modules auto-merge
# while third-party production patches still require manual review.
# The owner comes from github.repository_owner (never hardcoded), so this
# works unchanged in any org or user account the workflow is synced to.
#
# Fails closed: anything unparseable, unsupported, or unverifiable → false.
# - Only ecosystems whose package name encodes the GitHub owner are
# supported (go_modules: github.com/<owner>/<repo>[/...],
# github_actions: <owner>/<repo>[/...]). Vanity Go import paths and
# registry scopes (e.g. npm @scope) are NOT proof of ownership.
# - EVERY name in a grouped PR must match; one outside dep → false.
# - The repo is resolved via the GitHub API so a repo transferred out of
# the owner (old path redirects to a new owner) does not match.
# --------------------------------------------------------------------
- name: 🏢 Check for same-owner dependency
id: check-owner
env:
REPO_OWNER: ${{ github.repository_owner }}
# Metadata via env: (never string-spliced into the shell)
DEPENDENCY: ${{ steps.metadata.outputs.dependency-names }}
PKG_ECOSYSTEM: ${{ steps.metadata.outputs.package-ecosystem }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "════════════════════════════════════════════════════════════════"
echo "🏢 SAME-OWNER DEPENDENCY DETECTION"
echo "════════════════════════════════════════════════════════════════"
echo " Repository owner: $REPO_OWNER"
echo " Ecosystem: $PKG_ECOSYSTEM"
echo " Dependencies: $DEPENDENCY"
result() {
echo " $2"
echo "same_owner=$1" >> "$GITHUB_OUTPUT"
echo "════════════════════════════════════════════════════════════════"
echo "🎯 RESULT: same_owner=$1"
echo "════════════════════════════════════════════════════════════════"
exit 0
}
# GitHub owner logins are alphanumeric + hyphen only
if [[ ! "$REPO_OWNER" =~ ^[A-Za-z0-9-]+$ ]]; then
result false "❌ Unexpected repository owner format"
fi
OWNER_LC="${REPO_OWNER,,}"
case "$PKG_ECOSYSTEM" in
go_modules|gomod) PREFIX="github.com/" ;;
github_actions) PREFIX="" ;;
*) result false "⏭️ Ecosystem '$PKG_ECOSYSTEM' does not encode a GitHub owner — not eligible" ;;
esac
if [[ -z "$DEPENDENCY" || "$DEPENDENCY" == "null" ]]; then
result false "❌ No dependency names"
fi
declare -A VERIFIED=()
COUNT=0
IFS=',' read -ra NAMES <<< "$DEPENDENCY"
for raw in "${NAMES[@]}"; do
# Trim surrounding whitespace only; embedded whitespace fails the charset check
name="${raw#"${raw%%[![:space:]]*}"}"
name="${name%"${name##*[![:space:]]}"}"
[[ -z "$name" ]] && continue
COUNT=$((COUNT + 1))
if [[ ! "$name" =~ ^[A-Za-z0-9._~/+-]+$ || "$name" == *..* || "$name" == *//* ]]; then
result false "❌ '$name': unexpected characters in dependency name"
fi
name_lc="${name,,}"
if [[ "$name_lc" != "$PREFIX"* ]]; then
result false "❌ '$name': not hosted under ${PREFIX:-<owner>/}"
fi
IFS='/' read -r dep_owner dep_repo _ <<< "${name_lc#"$PREFIX"}"
if [[ "$dep_owner" != "$OWNER_LC" ]]; then
result false "❌ '$name': owner '$dep_owner' != '$OWNER_LC'"
fi
if [[ -z "$dep_repo" ]]; then
result false "❌ '$name': no repository segment"
fi
# Confirm the repo currently lives under the owner (follows transfer redirects).
# Fails closed if the token cannot see it (e.g. private repo in the same org).
if [[ -z "${VERIFIED[$dep_repo]:-}" ]]; then
actual_owner=$(gh api "repos/$dep_owner/$dep_repo" --jq '.owner.login' 2>/dev/null || echo "")
if [[ "${actual_owner,,}" != "$OWNER_LC" ]]; then
result false "❌ '$name': repo resolves to owner '${actual_owner:-<unavailable>}'"
fi
VERIFIED[$dep_repo]=1
fi
echo " ✅ '$name' is owned by '$REPO_OWNER'"
done
if [[ "$COUNT" -eq 0 ]]; then
result false "❌ No dependency names"
fi
result true "✅ All $COUNT dependencies are owned by '$REPO_OWNER'"
# --------------------------------------------------------------------
# Determine action based on configuration and update type
# --------------------------------------------------------------------
Expand All @@ -324,6 +431,7 @@ jobs:
UPDATE_TYPE_IN: ${{ steps.metadata.outputs.update-type }}
DEP_TYPE_IN: ${{ steps.metadata.outputs.dependency-type }}
IS_SECURITY_IN: ${{ steps.check-security.outputs.is_security }}
SAME_OWNER_IN: ${{ steps.check-owner.outputs.same_owner }}
run: |
echo "════════════════════════════════════════════════════════════════"
echo "🎯 ACTION DETERMINATION LOGIC"
Expand All @@ -332,16 +440,19 @@ jobs:
UPDATE_TYPE="$UPDATE_TYPE_IN"
DEP_TYPE="$DEP_TYPE_IN"
IS_SECURITY="$IS_SECURITY_IN"
SAME_OWNER="$SAME_OWNER_IN"
ACTION="none"
echo "📊 Input Variables:"
echo " Update Type: $UPDATE_TYPE"
echo " Dependency Type: $DEP_TYPE"
echo " Is Security: $IS_SECURITY"
echo " Same Owner: $SAME_OWNER"
echo ""
echo "📋 Active Configuration:"
echo " AUTO_MERGE_SECURITY: ${{ env.AUTO_MERGE_SECURITY }}"
echo " AUTO_MERGE_PATCH: ${{ env.AUTO_MERGE_PATCH }}"
echo " AUTO_MERGE_PATCH_SAME_OWNER: ${{ env.AUTO_MERGE_PATCH_SAME_OWNER }}"
echo " AUTO_MERGE_PATCH_INDIRECT: ${{ env.AUTO_MERGE_PATCH_INDIRECT }}"
echo " AUTO_MERGE_MINOR_DEV: ${{ env.AUTO_MERGE_MINOR_DEV }}"
echo " AUTO_MERGE_MINOR_PROD: ${{ env.AUTO_MERGE_MINOR_PROD }}"
Expand Down Expand Up @@ -370,8 +481,12 @@ jobs:
echo " 📝 Reason: Major security update requires manual review"
fi
else
# Security updates must never fall through to the regular patch/minor rules
# when security auto-merge is disabled, or they could auto-merge anyway.
echo " ❌ AUTO_MERGE_SECURITY == false"
echo " ⏭️ Skipping security auto-merge (disabled)"
ACTION="manual-review"
echo " 🎯 MATCH! Action: $ACTION"
echo " 📝 Reason: Security auto-merge is disabled in configuration"
fi
else
echo " ❌ IS_SECURITY == false"
Expand All @@ -385,13 +500,30 @@ jobs:
if [[ "$UPDATE_TYPE" == "version-update:semver-patch" ]]; then
echo " ✅ UPDATE_TYPE == patch"
if [[ "$DEP_TYPE" == "direct:production" ]]; then
# SECURITY: never auto-merge NON-security production patches. A malicious or
# broken patch release of a production dependency ships straight to users, so
# require a human. Genuine security patches are handled earlier by Rule 1.
echo " 🔒 DEP_TYPE == direct:production — excluded from patch auto-merge"
ACTION="manual-review"
echo " 🎯 MATCH! Action: $ACTION"
echo " 📝 Reason: Production patch requires manual review (supply-chain safety)"
# SECURITY: never auto-merge NON-security THIRD-PARTY production patches. A
# malicious or broken patch release of a production dependency ships straight
# to users, so require a human. Genuine security patches are handled earlier by
# Rule 1. Exception: dependencies verified to live under this repo's own owner
# (see the same-owner check step) are first-party code, so they may auto-merge
# when AUTO_MERGE_PATCH_SAME_OWNER is enabled. Security updates are excluded as
# defense in depth (Rule 1 already routes them to manual review when disabled).
echo " 🔒 DEP_TYPE == direct:production"
if [[ "$IS_SECURITY" != "true" && "$SAME_OWNER" == "true" && "${{ env.AUTO_MERGE_PATCH_SAME_OWNER }}" == "true" && "${{ env.AUTO_MERGE_PATCH }}" == "true" ]]; then
echo " ✅ SAME_OWNER == true (all dependencies owned by this repo's owner)"
echo " ✅ AUTO_MERGE_PATCH_SAME_OWNER == true"
ACTION="auto-merge-patch-same-owner"
echo " 🎯 MATCH! Action: $ACTION"
echo " 📝 Reason: Production patch of a same-owner (first-party) dependency"
else
echo " ❌ Not eligible for same-owner auto-merge (SAME_OWNER=$SAME_OWNER, AUTO_MERGE_PATCH_SAME_OWNER=${{ env.AUTO_MERGE_PATCH_SAME_OWNER }}, AUTO_MERGE_PATCH=${{ env.AUTO_MERGE_PATCH }})"
ACTION="manual-review"
echo " 🎯 MATCH! Action: $ACTION"
if [[ "$SAME_OWNER" == "true" ]]; then
echo " 📝 Reason: Same-owner production patch, but same-owner/patch auto-merge is disabled in configuration"
else
echo " 📝 Reason: Third-party production patch requires manual review (supply-chain safety)"
fi
fi
elif [[ "$DEP_TYPE" != "indirect" ]]; then
echo " ✅ DEP_TYPE is direct:development"
if [[ "${{ env.AUTO_MERGE_PATCH }}" == "true" ]]; then
Expand Down Expand Up @@ -931,6 +1063,9 @@ jobs:
"auto-merge-patch-indirect")
APPROVAL_MSG="✅ Auto-approving patch update (indirect dependency)"
;;
"auto-merge-patch-same-owner")
APPROVAL_MSG="✅ Auto-approving patch update (same-owner production dependency)"
;;
"auto-merge-minor-dev")
APPROVAL_MSG="✅ Auto-approving minor development dependency update"
;;
Expand Down Expand Up @@ -1126,6 +1261,7 @@ jobs:
case "$EFFECTIVE_ACTION" in
"auto-merge-patch") ACTION_DESC="✅ Auto-merged (patch update)" ;;
"auto-merge-patch-indirect") ACTION_DESC="✅ Auto-merged (patch update - indirect dependency)" ;;
"auto-merge-patch-same-owner") ACTION_DESC="✅ Auto-merged (patch update - same-owner production dependency)" ;;
"auto-merge-minor-dev") ACTION_DESC="✅ Auto-merged (minor dev dependency)" ;;
"auto-merge-minor-prod") ACTION_DESC="✅ Auto-merged (minor prod dependency)" ;;
"auto-merge-minor-indirect") ACTION_DESC="✅ Auto-merged (minor update - indirect dependency)" ;;
Expand Down Expand Up @@ -1162,6 +1298,7 @@ jobs:
echo "| Setting | Value |"
echo "|---------|-------|"
echo "| Auto-merge patch | ${AUTO_MERGE_PATCH} |"
echo "| Auto-merge patch same-owner prod | ${AUTO_MERGE_PATCH_SAME_OWNER} |"
echo "| Auto-merge minor dev | ${AUTO_MERGE_MINOR_DEV} |"
echo "| Auto-merge minor prod | ${AUTO_MERGE_MINOR_PROD} |"
echo "| Auto-merge patch indirect | ${AUTO_MERGE_PATCH_INDIRECT} |"
Expand Down
5 changes: 5 additions & 0 deletions .golangci.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@
"ATTENTION"
]
},
"gomoddirectives": {
"replace-allow-list": [
"github.com/centrifugal/protocol"
]
},
"govet": {
"enable": [
"atomicalign",
Expand Down
9 changes: 1 addition & 8 deletions examples/ws-subscribe-to-new-headers/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,9 @@ go 1.26.0
require github.com/centrifugal/centrifuge-go v0.12.1

require (
github.com/centrifugal/protocol v0.22.1 // indirect
github.com/centrifugal/protocol v0.23.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/mailru/easyjson v0.9.2 // indirect
github.com/planetscale/vtprotobuf v0.6.0 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/segmentio/encoding v0.5.4 // indirect
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/sys v0.47.0 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
Loading
Loading