Skip to content
Open
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
45 changes: 45 additions & 0 deletions .github/mergeable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
version: 2
mergeable:
- when: pull_request.*, pull_request_review.*
validate:
# Validate PR title
- do: title
must_include:
regex: '^(feat|docs|chore|fix|refactor|test|style|perf)(\(\w+\))?: .{5,}'
message: "Semantic release conventions must be followed. Example: feat(auth): add login page. Title must be at least 5 characters after the prefix."

# Ensure PR description is provided
- do: description
must_include:
regex: "[\\s\\S]{20,}" # At least 20 characters
message: "Please provide a meaningful description of the PR (minimum 20 characters)."

# Ensure PR references an associated issue
- do: description
must_include:
regex: "(Closes|Fixes|Resolves|Addresses)\\s+#[0-9]+(,?\\s*#[0-9]+)*"
message: "PR must reference at least one issue (e.g., Closes #123, Fixes #123, #124)."

# Ensure at least one required label is applied
- do: label
must_include:
regex: "^(bug|enhancement|documentation|feature|refactor|performance|chore|wip|test|ci|security|dependencies)$"
message: "PR must include at least one valid label."
# Ensure PR has at least one assignee

- do: assignee
min:
count: 1
message: "PR must have at least one assignee."
# Ensure PR has at least one reviewer requested
- do: approvals
min:
count: 1
message: "PR must have at least one reviewer requested or approved."

pass:
- do: labels
add:
- "validated"
- do: checks
status: "success"
178 changes: 178 additions & 0 deletions .github/workflows/github-event-notification.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
name: Notify (GitHub Events)

permissions:
issues: write
pull-requests: write
contents: read

on:
issues:
types: [opened, edited, reopened, closed, assigned, labeled]
pull_request:
types: [opened, closed, reopened, ready_for_review, review_requested]
pull_request_review:
types: [submitted, edited, dismissed]
issue_comment:
types: [created, edited]
pull_request_review_comment:
types: [created, edited]

jobs:
notify:
runs-on: ubuntu-latest
name: Send Notifications

steps:
- name: Determine Event Type
id: event_info
run: |
EVENT="${{ github.event_name }}"
ACTION="${{ github.event.action }}"
REPO="${{ github.repository }}"
ACTOR="${{ github.actor }}"
REPO_URL="${{ github.server_url }}/${{ github.repository }}"

if [[ "$EVENT" == "issues" ]]; then
TITLE="Issue $ACTION: ${{ github.event.issue.title }}"
URL="${{ github.event.issue.html_url }}"
NUMBER="${{ github.event.issue.number }}"
USER="${{ github.event.issue.user.login }}"
DESCRIPTION="Issue #$NUMBER by $USER"
STATE="${{ github.event.issue.state }}"
EMOJI="🐛"

elif [[ "$EVENT" == "pull_request" ]]; then
NUMBER="${{ github.event.pull_request.number }}"
USER="${{ github.event.pull_request.user.login }}"
URL="${{ github.event.pull_request.html_url }}"
STATE="${{ github.event.pull_request.state }}"
SOURCE="${{ github.event.pull_request.head.ref }}"
TARGET="${{ github.event.pull_request.base.ref }}"

if [[ "$ACTION" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then
TITLE="PR merged: ${{ github.event.pull_request.title }}"
EMOJI="✅"
STATE="merged"
elif [[ "$ACTION" == "closed" ]]; then
TITLE="PR closed: ${{ github.event.pull_request.title }}"
EMOJI="❌"
else
TITLE="PR $ACTION: ${{ github.event.pull_request.title }}"
EMOJI="🔀"
fi
DESCRIPTION="PR #$NUMBER by $USER: $SOURCE → $TARGET"

elif [[ "$EVENT" == "pull_request_review" ]]; then
TITLE="PR Review: ${{ github.event.pull_request.title }}"
URL="${{ github.event.review.html_url }}"
NUMBER="${{ github.event.pull_request.number }}"
USER="${{ github.event.review.user.login }}"
REVIEW_STATE="${{ github.event.review.state }}"
STATE="${{ github.event.review.state }}"
SOURCE="${{ github.event.pull_request.head.ref }}"
TARGET="${{ github.event.pull_request.base.ref }}"
DESCRIPTION="Review by $USER on PR #$NUMBER"
EMOJI="👀"

elif [[ "$EVENT" == "issue_comment" ]]; then
NUMBER="${{ github.event.issue.number }}"
USER="${{ github.event.comment.user.login }}"
URL="${{ github.event.comment.html_url }}"
STATE="commented"

if [[ "${{ github.event.issue.pull_request }}" != "" ]]; then
TITLE="Comment on PR: ${{ github.event.issue.title }}"
EMOJI="💬"
else
TITLE="Comment on Issue: ${{ github.event.issue.title }}"
EMOJI="💬"
fi
DESCRIPTION="Comment by $USER on #$NUMBER"

elif [[ "$EVENT" == "pull_request_review_comment" ]]; then
TITLE="Comment on PR: ${{ github.event.pull_request.title }}"
URL="${{ github.event.comment.html_url }}"
NUMBER="${{ github.event.pull_request.number }}"
USER="${{ github.event.comment.user.login }}"
STATE="commented"
SOURCE="${{ github.event.pull_request.head.ref }}"
TARGET="${{ github.event.pull_request.base.ref }}"
DESCRIPTION="Review comment by $USER on PR #$NUMBER"
EMOJI="💬"
else
TITLE="GitHub Event: $EVENT"
URL="${{ github.event.repository.html_url }}"
DESCRIPTION="Event triggered in $REPO"
STATE="N/A"
NUMBER="N/A"
USER="$ACTOR"
EMOJI="📢"
fi

echo "title=$TITLE" >> $GITHUB_OUTPUT
echo "url=$URL" >> $GITHUB_OUTPUT
echo "description=$DESCRIPTION" >> $GITHUB_OUTPUT
echo "emoji=$EMOJI" >> $GITHUB_OUTPUT
echo "number=${NUMBER:-N/A}" >> $GITHUB_OUTPUT
echo "user=${USER:-$ACTOR}" >> $GITHUB_OUTPUT
echo "state=${STATE:-N/A}" >> $GITHUB_OUTPUT
echo "source=${SOURCE:-N/A}" >> $GITHUB_OUTPUT
echo "target=${TARGET:-N/A}" >> $GITHUB_OUTPUT
echo "repo_url=$REPO_URL" >> $GITHUB_OUTPUT

- name: Google Chat Notification
if: always()
uses: Co-qn/google-chat-notification@releases/v1
with:
name: ${{ steps.event_info.outputs.title }}
url: ${{ secrets.GOOGLE_CHAT_WEBHOOK }}
status: ${{ job.status }}

- name: Slack Notification
if: always()
uses: slackapi/slack-github-action@v1.24.0
with:
channel-id: ${{ secrets.SLACK_CHANNEL_ID }}
payload: |
{
"text": "${{ steps.event_info.outputs.title }}",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "${{ steps.event_info.outputs.emoji }} ${{ steps.event_info.outputs.title }}"
}
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*Repository:*\n<${{ steps.event_info.outputs.repo_url }}|${{ github.repository }}>" },
{ "type": "mrkdwn", "text": "*Event:*\n${{ github.event_name }}" },
{ "type": "mrkdwn", "text": "*Author:*\n${{ steps.event_info.outputs.user }}" },
{ "type": "mrkdwn", "text": "*Action:*\n${{ github.event.action }}" },
{ "type": "mrkdwn", "text": "*Number:*\n<${{ steps.event_info.outputs.url }}|#${{ steps.event_info.outputs.number }}>" },
{ "type": "mrkdwn", "text": "*State:*\n${{ steps.event_info.outputs.state }}" }
]
},
{
"type": "section",
"text": { "type": "mrkdwn", "text": "${{ steps.event_info.outputs.description }}" }
},
{
"type": "actions",
"elements": [
{ "type": "button", "text": { "type": "plain_text", "text": "View on GitHub" }, "url": "${{ steps.event_info.outputs.url }}", "style": "primary" },
{ "type": "button", "text": { "type": "plain_text", "text": "View Repository" }, "url": "${{ steps.event_info.outputs.repo_url }}" }
]
},
{
"type": "context",
"elements": [
{ "type": "mrkdwn", "text": "Triggered by ${{ github.actor }} • ${{ github.event_name }} event" }
]
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
126 changes: 126 additions & 0 deletions .github/workflows/pre-commit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Pre-Commit Checks

on:
push:
branches:
- main
- develop
- 'feat/**'
- 'feature/**'
- 'test/**'
- 'chore/**'
- 'fix/**'
- 'hotfix/**'
- 'docs/**'
pull_request:
types: [opened, synchronize, reopened]

jobs:
precommit:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22.0'

- name: Install goimports
run: |
go install golang.org/x/tools/cmd/goimports@latest
echo "$HOME/go/bin" >> $GITHUB_PATH

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install pre-commit
run: |
python -m pip install --upgrade pip
pip install pre-commit

- name: Load Pre-commit Config
run: |
if [ ! -f ".pre-commit-config.yaml" ]; then
echo " No .pre-commit-config.yaml found — downloading BerryBytes global config..."
curl -sSL \
https://raw.githubusercontent.com/BerryBytes/precommit-util/main/global/precommitFile/.pre-commit-config.yaml \
-o .pre-commit-config.yaml
else
echo "✔ Using project's existing .pre-commit-config.yaml"
fi

- name: Inject temporary Stylelint config for CI
run: |
if [ ! -f ".stylelintrc.json" ]; then
echo " Creating temporary .stylelintrc.json for CI..."
cat <<EOF > .stylelintrc.json
{
"extends": "stylelint-config-standard",
"rules": {
"no-duplicate-selectors": true,
"color-hex-length": "short",
"selector-no-qualifying-type": true,
"selector-max-id": 0
}
}
EOF
else
echo "✔ .stylelintrc.json already exists — skipping"
fi

# --------------------------------------------------------------------
# STEP 1: Run pre-commit (capture full logs and exit code safely)
# --------------------------------------------------------------------
- name: Run pre-commit (full logs)
id: runprecommit
run: |
echo "🔍 Running full pre-commit checks..."

set +e # allow failure
pre-commit run --all-files --verbose --show-diff-on-failure --color never \
| tee full_precommit.log
exit_code=${PIPESTATUS[0]}

echo "Pre-commit exit code: $exit_code"
echo "$exit_code" > precommit_exit_code.txt

# --------------------------------------------------------------------
# STEP 2: Summary of FAILED hooks
# --------------------------------------------------------------------
- name: Pre-commit summary of failed hooks
run: |
echo "====================================================="
echo " PRE-COMMIT SUMMARY"
echo "====================================================="

exit_code=$(cat precommit_exit_code.txt)

if [ "$exit_code" = "0" ]; then
echo " All hooks passed!"
exit 0
fi

echo " Hooks failed — showing summary:"
echo ""

echo " FAILED HOOKS:"
grep -E "^\w.*\.{3,}Failed" full_precommit.log || echo " None"
echo "-----------------------------------------------------"

echo " FILES WITH ISSUES:"
grep -E "files were modified by this hook" -A3 full_precommit.log \
| sed 's/^/ - /' || echo " None"
echo "-----------------------------------------------------"

echo " ERROR DETAILS:"
grep -Ei "(error|failed|violation|missing|line too long|could not|warning)" full_precommit.log \
| grep -Ev "^(---|\+\+\+|@@|diff --git|index )" \
| sed 's/^/ • /' || echo " None"
echo "-----------------------------------------------------"

exit $exit_code
47 changes: 47 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Schedule Stale PRs and Issues Management

permissions:
actions: write
contents: write
issues: write
pull-requests: write

on:
schedule:
- cron: '0 9 * * 1,4' # Monday & Thursday at 9 AM UTC
workflow_dispatch:

concurrency:
group: stale-management
cancel-in-progress: false

jobs:
stale-management:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Manage Stale Issues and PRs
uses: actions/stale@v10.1.0
with:
repo-token: ${{ github.token }}

# General settings
days-before-stale: '30'
days-before-close: '7'
operations-per-run: '60'
remove-stale-when-updated: 'true'

# Issue-specific settings
stale-issue-label: 'stale'
stale-issue-message: 'This issue has been marked as stale due to inactivity. It will be closed in 7 days unless there is further activity.'
close-issue-message: 'This issue has been closed due to inactivity. Please feel free to reopen if you think it is still relevant.'
exempt-issue-labels: 'bug,critical,security,high-priority,enhancement,discussion,help wanted,good first issue'

# PR-specific settings
stale-pr-label: 'stale'
stale-pr-message: 'This pull request has been marked as stale due to inactivity. It will be closed in 7 days unless further changes are made or a review is requested.'
close-pr-message: 'This pull request has been closed due to inactivity. Please feel free to reopen if you think it is still relevant.'
exempt-pr-labels: 'WIP,In Progress'
Loading
Loading