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: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github: Nadav011
custom: ["https://mcpize.com/mcp/rtl-fixer"]
28 changes: 28 additions & 0 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Security Scan

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true

jobs:
semgrep:
runs-on: [self-hosted, linux, x64, pop-os]
steps:
- uses: actions/checkout@v4
- name: Semgrep SAST
run: |
pip3 install --user semgrep 2>/dev/null || true
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pip3 install --user semgrep does not guarantee semgrep is on PATH (user installs often land in ~/.local/bin). On a fresh runner this can make semgrep scan fail even though the install succeeded. Prefer installing in a way that ensures availability (e.g., add the user bin dir to PATH, use pipx, or invoke via python -m semgrep if supported).

Suggested change
pip3 install --user semgrep 2>/dev/null || true
pip3 install --user semgrep 2>/dev/null || true
export PATH="$HOME/.local/bin:$PATH"

Copilot uses AI. Check for mistakes.
semgrep scan . --config=auto --error --severity ERROR

trivy:
runs-on: [self-hosted, linux, x64, pop-os]
steps:
- uses: actions/checkout@v4
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Trivy job assumes trivy is already installed on the self-hosted runner. If the runner is rebuilt or used elsewhere, this job will fail with command not found. Consider installing Trivy in the workflow (or explicitly documenting the runner prerequisites and pinning the expected Trivy version).

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@v4
- name: Install Trivy
run: |
TRIVY_VERSION=0.51.2
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin "v${TRIVY_VERSION}"

Copilot uses AI. Check for mistakes.
- name: Trivy vulnerability scan
run: trivy fs . --severity HIGH,CRITICAL --exit-code 1
174 changes: 174 additions & 0 deletions CI/rtl-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
name: RTL Check
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid adding an uppercase CI directory alongside ci

Adding CI/rtl-check.yml introduces a case-only directory distinction from the existing ci/ tree, which breaks or behaves inconsistently on case-insensitive filesystems (default macOS/Windows). Contributors on those environments can hit checkout/path conflicts, so this should be moved to a non-colliding path (for example .github/workflows/ or ci/).

Useful? React with 👍 / 👎.


on:
pull_request:
paths:
- "**/*.tsx"
- "**/*.jsx"
- "**/*.css"
- "**/*.vue"

jobs:
rtl-lint:
name: RTL Logical Properties Check
runs-on: ubuntu-latest
permissions:
contents: read
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow uses github.rest.issues.createComment, which typically requires issues: write permission. Currently only pull-requests: write is granted, so posting the PR comment may fail with a permissions error. Add issues: write (or switch to a PR-review comment API that matches the granted scope).

Suggested change
contents: read
contents: read
issues: write

Copilot uses AI. Check for mistakes.
pull-requests: write

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

- name: Install ripgrep
run: |
sudo apt-get update -qq
sudo apt-get install -y ripgrep

- name: Scan for physical direction classes
id: rtl-scan
run: |
# Pattern: physical direction classes that must be replaced with logical equivalents
# Suppression: any line containing "// rtl-ok" is skipped
#
# Classes checked:
# ml-{n} mr-{n} pl-{n} pr-{n} — physical margin/padding
# text-left text-right — physical text alignment
# border-l- border-r- — physical border sides
# rounded-tl rounded-tr — physical border radius (corners)
# rounded-bl rounded-br
# float-left float-right — physical float
# left-{n} right-{n} — physical inset positioning (when directional)

PATTERN='\bml-[0-9a-z\[]|\bmr-[0-9a-z\[]|\bpl-[0-9a-z\[]|\bpr-[0-9a-z\[]|text-left\b|text-right\b|border-l-|border-r-|\brounded-tl\b|\brounded-tr\b|\brounded-bl\b|\brounded-br\b|float-left\b|float-right\b'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include left/right utility classes in violation regex

The regex does not include left-* or right-*, even though the check description says those classes are enforced. As a result, physical positioning utilities can be introduced without being flagged, creating false negatives for one of the directional patterns this workflow is intended to block.

Useful? React with 👍 / 👎.

Comment on lines +34 to +43
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says left-{n} / right-{n} are checked, but the PATTERN does not include them. Either add \bleft- / \bright- (and handle the documented exception for left-0 right-0), or remove the claim from the comments so behavior matches documentation.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +43
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scan regex misses common variants, so the workflow will silently pass on violations (e.g., rounded-tl-md, rounded-tr-lg, border-l without a trailing dash, negative classes like -ml-4, and ml-1/2 / other non-alnum modifiers). Update the pattern to cover these forms (or switch to a safer tokenization approach) so the check actually enforces the documented policy.

Suggested change
# ml-{n} mr-{n} pl-{n} pr-{n} — physical margin/padding
# text-left text-right — physical text alignment
# border-l- border-r- — physical border sides
# rounded-tl rounded-tr — physical border radius (corners)
# rounded-bl rounded-br
# float-left float-right — physical float
# left-{n} right-{n} — physical inset positioning (when directional)
PATTERN='\bml-[0-9a-z\[]|\bmr-[0-9a-z\[]|\bpl-[0-9a-z\[]|\bpr-[0-9a-z\[]|text-left\b|text-right\b|border-l-|border-r-|\brounded-tl\b|\brounded-tr\b|\brounded-bl\b|\brounded-br\b|float-left\b|float-right\b'
# ml-* mr-* pl-* pr-* — physical margin/padding (including negatives, fractions, arbitrary values)
# text-left text-right — physical text alignment
# border-l border-r — physical border sides (bare or with modifiers)
# rounded-tl rounded-tr — physical border radius (corners, bare or with modifiers)
# rounded-bl rounded-br
# float-left float-right — physical float
# left-* right-* — physical inset positioning (when directional)
PATTERN="(^|[^[:alnum:]_/-])(-?(ml|mr|pl|pr|left|right)-[^[:space:]\"'\`<>{}]+|text-(left|right)([^[:alnum:]_-]|$)|border-(l|r)(-[^[:space:]\"'\`<>{}]+)?([^[:alnum:]_-]|$)|rounded-(tl|tr|bl|br)(-[^[:space:]\"'\`<>{}]+)?([^[:alnum:]_-]|$)|float-(left|right)([^[:alnum:]_-]|$))"

Copilot uses AI. Check for mistakes.

# Run ripgrep:
# - search only changed files in the PR (fallback: all source files)
# - exclude lines containing "rtl-ok" suppression comment
# - show file, line number, and matching content
VIOLATIONS=$(rg --glob "*.tsx" --glob "*.jsx" --glob "*.css" --glob "*.vue" \
--line-number \
--no-heading \
--color never \
"$PATTERN" \
src/ components/ pages/ app/ styles/ 2>/dev/null \
Comment on lines +49 to +54
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scan repository files instead of hardcoded app folders

The RTL scan command only searches src/ components/ pages/ app/ styles/ and suppresses missing-path errors, so projects that keep UI code elsewhere (for example packages/*, apps/*, or lib/*) will report zero violations and incorrectly pass CI. This contradicts the step comment that it scans PR changes with a fallback, and it allows physical-direction regressions to merge unnoticed.

Useful? React with 👍 / 👎.

| grep -v "rtl-ok" \
Comment on lines +45 to +55
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scan is limited to src/ components/ pages/ app/ styles/, which can miss violations in other directories (and doesn’t implement the stated “search only changed files in the PR”). Consider scanning the whole repo (excluding vendor/build dirs) or deriving the changed file list from the PR and scanning only those paths.

Copilot uses AI. Check for mistakes.
| grep -v "//.*rtl-ok" \
|| true)
Comment on lines +55 to +57
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grep -v "rtl-ok" will exclude any line containing that substring (including string literals), not just suppression comments. If suppression is meant to be // rtl-ok, prefer a tighter filter (e.g., match comment patterns) and drop the redundant second grep once the filter is precise.

Copilot uses AI. Check for mistakes.

if [ -z "$VIOLATIONS" ]; then
echo "violations_found=false" >> "$GITHUB_OUTPUT"
echo "violation_count=0" >> "$GITHUB_OUTPUT"
else
COUNT=$(echo "$VIOLATIONS" | wc -l | tr -d ' ')
echo "violations_found=true" >> "$GITHUB_OUTPUT"
echo "violation_count=$COUNT" >> "$GITHUB_OUTPUT"
# Store violations in a file for use in later steps
echo "$VIOLATIONS" > /tmp/rtl-violations.txt
fi

- name: Generate violation report
if: steps.rtl-scan.outputs.violations_found == 'true'
id: report
run: |
COUNT="${{ steps.rtl-scan.outputs.violation_count }}"
VIOLATIONS=$(cat /tmp/rtl-violations.txt)

echo "## RTL Violations Found — Fix Before Merge" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**${COUNT} violation(s) detected.** Physical direction classes break Arabic and Hebrew layouts." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Violations" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

# Format each violation with the recommended replacement
while IFS= read -r line; do
FILE_LINE=$(echo "$line" | cut -d: -f1-2)
CONTENT=$(echo "$line" | cut -d: -f3-)

# Determine the replacement hint
HINT=""
if echo "$CONTENT" | grep -qE '\bml-'; then HINT="→ use ms-* (margin-inline-start)"; fi
if echo "$CONTENT" | grep -qE '\bmr-'; then HINT="→ use me-* (margin-inline-end)"; fi
if echo "$CONTENT" | grep -qE '\bpl-'; then HINT="→ use ps-* (padding-inline-start)"; fi
if echo "$CONTENT" | grep -qE '\bpr-'; then HINT="→ use pe-* (padding-inline-end)"; fi
if echo "$CONTENT" | grep -qE 'text-left'; then HINT="→ use text-start"; fi
if echo "$CONTENT" | grep -qE 'text-right'; then HINT="→ use text-end"; fi
if echo "$CONTENT" | grep -qE 'border-l-'; then HINT="→ use border-s-* (border-inline-start)"; fi
if echo "$CONTENT" | grep -qE 'border-r-'; then HINT="→ use border-e-* (border-inline-end)"; fi
if echo "$CONTENT" | grep -qE 'rounded-tl'; then HINT="→ use rounded-ss-* (border-start-start-radius)"; fi
if echo "$CONTENT" | grep -qE 'rounded-tr'; then HINT="→ use rounded-se-* (border-start-end-radius)"; fi
if echo "$CONTENT" | grep -qE 'rounded-bl'; then HINT="→ use rounded-es-* (border-end-start-radius)"; fi
if echo "$CONTENT" | grep -qE 'rounded-br'; then HINT="→ use rounded-ee-* (border-end-end-radius)"; fi
if echo "$CONTENT" | grep -qE 'float-left'; then HINT="→ use float-start"; fi
if echo "$CONTENT" | grep -qE 'float-right'; then HINT="→ use float-end"; fi

echo "${FILE_LINE} ${HINT}" >> $GITHUB_STEP_SUMMARY
done <<< "$VIOLATIONS"

echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### How to fix" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "# In Claude Code:" >> $GITHUB_STEP_SUMMARY
echo "/rtl-fix" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "# Or with sed (preview first):" >> $GITHUB_STEP_SUMMARY
echo "# grep -rn 'ml-[0-9]' src/ | grep -v rtl-ok" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "> To suppress a line intentionally: add \`// rtl-ok — reason\` comment. Requires \`dir=\"ltr\"\` on the element or ancestor." >> $GITHUB_STEP_SUMMARY

# Also write to console for visibility in raw logs
echo ""
echo "=========================================="
echo "RTL violations found — fix before merge:"
echo "=========================================="
echo ""
cat /tmp/rtl-violations.txt
echo ""
echo "${COUNT} violation(s). Run /rtl-fix to auto-convert."
echo "Add // rtl-ok to suppress intentionally directional lines."
echo ""

- name: Post PR comment on first violation
if: steps.rtl-scan.outputs.violations_found == 'true' && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const count = ${{ steps.rtl-scan.outputs.violation_count }};
const body = [
`## RTL Check Failed — ${count} violation(s) found`,
'',
'Physical direction classes (`ml-`, `mr-`, `pl-`, `pr-`, `text-left`, `text-right`, `border-l-`, `border-r-`, `float-left`, `float-right`) break Arabic and Hebrew layouts.',
'',
'**Fix:** Run `/rtl-fix` in Claude Code, or see the [full class mapping](https://github.com/${{ github.repository }}/blob/main/cheatsheet/tailwind-rtl.md).',
'',
'**Suppress a specific line** by adding `// rtl-ok — reason` comment (requires `dir="ltr"` on element or ancestor).',
'',
'See the **Checks** tab for the full violation list with line numbers.',
].join('\n');

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});

- name: Fail if violations found
if: steps.rtl-scan.outputs.violations_found == 'true'
run: |
echo "RTL check failed with ${{ steps.rtl-scan.outputs.violation_count }} violation(s)."
echo "See the step summary for the full list and fix instructions."
exit 1

- name: Pass — no violations
if: steps.rtl-scan.outputs.violations_found == 'false'
run: |
echo "RTL check passed — all direction classes use logical properties."
echo "## RTL Check Passed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All direction classes use logical properties. No physical direction classes detected." >> $GITHUB_STEP_SUMMARY
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2026 Nadav011
Copyright (c) 2026 Nadav Cohen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Loading
Loading