Skip to content

Commit 081cd5d

Browse files
authored
Merge branch 'RezaSi:main' into main
2 parents 5fad7d6 + a525e95 commit 081cd5d

File tree

1,037 files changed

+95643
-397
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,037 files changed

+95643
-397
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Optimize badge file handling for better compression
2+
badges/*.svg text eol=lf
3+
badges/*.json text eol=lf
4+
badges/*.md text eol=lf

.github/FUNDING.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# These are supported funding model platforms
2+
3+
github: RezaSi
4+
patreon: # Replace with a single Patreon username
5+
open_collective: # Replace with a single Open Collective username
6+
ko_fi: # Replace with a single Ko-fi username
7+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9+
liberapay: # Replace with a single Liberapay username
10+
issuehunt: # Replace with a single IssueHunt username
11+
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12+
polar: # Replace with a single Polar username
13+
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14+
thanks_dev: # Replace with a single thanks.dev username
15+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

.github/workflows/pr-tests.yml

Lines changed: 219 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
name: PR Tests
22

3+
permissions:
4+
contents: read
5+
36
on:
47
pull_request:
58
branches:
@@ -14,6 +17,8 @@ jobs:
1417
outputs:
1518
validation_needed: ${{ steps.validate-dirs.outputs.validation_needed }}
1619
validation_passed: ${{ steps.validate-dirs.outputs.validation_passed }}
20+
manual_approval_needed: ${{ steps.validate-dirs.outputs.manual_approval_needed }}
21+
changed_challenges: ${{ steps.validate-dirs.outputs.changed_challenges }}
1722
runs-on: ubuntu-latest
1823
name: Validate Submission Security
1924

@@ -44,12 +49,66 @@ jobs:
4449
echo "Changed files:"
4550
echo "$CHANGED_FILES"
4651
47-
# Extract submission directories that were modified
48-
MODIFIED_SUBMISSION_DIRS=$(echo "$CHANGED_FILES" | grep -E "challenge-[0-9]+/submissions/" | cut -d'/' -f3 | sort -u || true)
52+
# Check if any files outside of submissions directories are modified
53+
# Allow both regular challenge submissions and package challenge submissions
54+
NON_SUBMISSION_FILES=$(echo "$CHANGED_FILES" | grep -v -E "(challenge-[0-9]+/submissions/|packages/[^/]+/challenge-[^/]+/submissions/)" || true)
55+
56+
# Check for manual approval in PR labels or comments
57+
MANUAL_APPROVAL_GRANTED="false"
58+
59+
# Check if PR has manual approval label
60+
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'manual-approval-granted') }}" == "true" ]]; then
61+
MANUAL_APPROVAL_GRANTED="true"
62+
echo "✅ Manual approval granted via label"
63+
fi
64+
65+
if [ -n "$NON_SUBMISSION_FILES" ] && [ "$MANUAL_APPROVAL_GRANTED" != "true" ]; then
66+
echo "⚠️ WARNING: User '$USERNAME' modified files outside of submission directories:"
67+
echo "$NON_SUBMISSION_FILES"
68+
echo "🔍 This PR requires manual approval from maintainers"
69+
echo "✅ Maintainers can approve by adding the 'manual-approval-granted' label"
70+
echo "manual_approval_needed=true" >> $GITHUB_OUTPUT
71+
else
72+
echo "manual_approval_needed=false" >> $GITHUB_OUTPUT
73+
fi
74+
75+
# Extract challenges that were modified (either submissions or other files)
76+
# Include both regular challenges and package challenges
77+
REGULAR_CHALLENGES=$(echo "$CHANGED_FILES" | grep -E "^challenge-[0-9]+/" | sed 's|/.*||' || true)
78+
PACKAGE_CHALLENGES=$(echo "$CHANGED_FILES" | grep -E "^packages/[^/]+/challenge-[^/]+/" | sed -E 's|^(packages/[^/]+/challenge-[^/]+)/.*|\1|' || true)
79+
CHANGED_CHALLENGES=$(echo -e "$REGULAR_CHALLENGES\n$PACKAGE_CHALLENGES" | grep -v '^$' | sort -u || true)
80+
81+
# Extract submission directories that were modified
82+
# Include both regular challenge submissions and package challenge submissions
83+
REGULAR_SUBMISSION_DIRS=$(echo "$CHANGED_FILES" | grep -E "challenge-[0-9]+/submissions/" | cut -d'/' -f3 || true)
84+
PACKAGE_SUBMISSION_DIRS=$(echo "$CHANGED_FILES" | grep -E "packages/[^/]+/challenge-[^/]+/submissions/" | cut -d'/' -f5 || true)
85+
MODIFIED_SUBMISSION_DIRS=$(echo -e "$REGULAR_SUBMISSION_DIRS\n$PACKAGE_SUBMISSION_DIRS" | grep -v '^$' | sort -u || true)
86+
87+
if [ -n "$CHANGED_CHALLENGES" ]; then
88+
echo "Changed challenges: $CHANGED_CHALLENGES"
89+
# Convert to JSON array format for matrix
90+
CHALLENGES_JSON=$(echo "$CHANGED_CHALLENGES" | tr ' ' '\n' | sed 's/^/"/;s/$/"/' | tr '\n' ',' | sed 's/,$//')
91+
echo "changed_challenges=[$CHALLENGES_JSON]" >> $GITHUB_OUTPUT
92+
else
93+
echo "No challenges modified in this PR"
94+
echo "changed_challenges=[]" >> $GITHUB_OUTPUT
95+
fi
4996
5097
if [ -z "$MODIFIED_SUBMISSION_DIRS" ]; then
5198
echo "No submission directories modified in this PR"
52-
echo "validation_needed=false" >> $GITHUB_OUTPUT
99+
100+
# Still need to check if manual approval is required for non-submission files
101+
if [ -n "$NON_SUBMISSION_FILES" ] && [ "$MANUAL_APPROVAL_GRANTED" != "true" ]; then
102+
echo "⚠️ PR contains non-submission file changes that require manual approval"
103+
echo "validation_needed=true" >> $GITHUB_OUTPUT
104+
echo "validation_passed=false" >> $GITHUB_OUTPUT
105+
echo "manual_approval_needed=true" >> $GITHUB_OUTPUT
106+
else
107+
echo "✅ No submission files modified and no manual approval needed"
108+
echo "validation_needed=false" >> $GITHUB_OUTPUT
109+
echo "validation_passed=true" >> $GITHUB_OUTPUT
110+
echo "manual_approval_needed=false" >> $GITHUB_OUTPUT
111+
fi
53112
exit 0
54113
fi
55114
@@ -68,56 +127,112 @@ jobs:
68127
fi
69128
done
70129
71-
if [ -n "$INVALID_DIRS" ]; then
72-
echo "❌ SECURITY VIOLATION: User '$USERNAME' attempted to modify submission directories for other users:$INVALID_DIRS"
73-
echo "✅ You can only modify submissions in directories named after your GitHub username: $USERNAME"
130+
# Validate that all changed files are within the user's own submission directories
131+
USERNAME_LOWER=$(echo "$USERNAME" | tr '[:upper:]' '[:lower:]')
132+
INVALID_FILES=""
133+
for FILE in $CHANGED_FILES; do
134+
# Check regular challenge submissions
135+
if [[ "$FILE" =~ ^challenge-[0-9]+/submissions/([^/]+)/ ]]; then
136+
SUBMISSION_USER="${BASH_REMATCH[1]}"
137+
SUBMISSION_USER_LOWER=$(echo "$SUBMISSION_USER" | tr '[:upper:]' '[:lower:]')
138+
if [ "$SUBMISSION_USER_LOWER" != "$USERNAME_LOWER" ]; then
139+
INVALID_FILES="$INVALID_FILES $FILE"
140+
fi
141+
fi
142+
# Check package challenge submissions
143+
if [[ "$FILE" =~ ^packages/[^/]+/challenge-[^/]+/submissions/([^/]+)/ ]]; then
144+
SUBMISSION_USER="${BASH_REMATCH[1]}"
145+
SUBMISSION_USER_LOWER=$(echo "$SUBMISSION_USER" | tr '[:upper:]' '[:lower:]')
146+
if [ "$SUBMISSION_USER_LOWER" != "$USERNAME_LOWER" ]; then
147+
INVALID_FILES="$INVALID_FILES $FILE"
148+
fi
149+
fi
150+
done
151+
152+
# Check for strict security violations (modifying other users' submissions)
153+
# Skip this check if manual approval is granted
154+
if [ -n "$INVALID_DIRS" ] || [ -n "$INVALID_FILES" ]; then
155+
if [ "$MANUAL_APPROVAL_GRANTED" == "true" ]; then
156+
echo "✅ Manual approval granted - bypassing submission directory security checks"
157+
echo "⚠️ WARNING: User '$USERNAME' modified submission directories for other users:$INVALID_DIRS"
158+
if [ -n "$INVALID_FILES" ]; then
159+
echo "⚠️ WARNING: User '$USERNAME' modified files in other users' submission directories:"
160+
echo "$INVALID_FILES"
161+
fi
162+
echo "🔓 Manual approval allows bypassing normal security restrictions"
163+
else
164+
if [ -n "$INVALID_DIRS" ]; then
165+
echo "❌ STRICT SECURITY VIOLATION: User '$USERNAME' attempted to modify submission directories for other users:$INVALID_DIRS"
166+
fi
167+
if [ -n "$INVALID_FILES" ]; then
168+
echo "❌ STRICT SECURITY VIOLATION: User '$USERNAME' attempted to modify files in other users' submission directories:"
169+
echo "$INVALID_FILES"
170+
fi
171+
echo "✅ You can only modify submissions in directories named after your GitHub username: $USERNAME"
172+
echo "✅ Allowed directories: challenge-*/submissions/$USERNAME or packages/*/challenge-*/submissions/$USERNAME"
173+
echo "validation_passed=false" >> $GITHUB_OUTPUT
174+
echo "validation_needed=true" >> $GITHUB_OUTPUT
175+
exit 1
176+
fi
177+
fi
178+
179+
# Check if manual approval is needed and not granted
180+
if [ -n "$NON_SUBMISSION_FILES" ] && [ "$MANUAL_APPROVAL_GRANTED" != "true" ]; then
181+
echo "⚠️ PR contains non-submission file changes that require manual approval"
74182
echo "validation_passed=false" >> $GITHUB_OUTPUT
75183
echo "validation_needed=true" >> $GITHUB_OUTPUT
76-
exit 1
77184
else
78-
echo "✅ Security validation passed: User '$USERNAME' only modified their own submission directory"
185+
if [ "$MANUAL_APPROVAL_GRANTED" == "true" ]; then
186+
echo "✅ Manual approval granted - security validation bypassed"
187+
echo "🔓 All security checks bypassed due to manual approval"
188+
else
189+
echo "✅ Security validation passed"
190+
fi
79191
echo "validation_passed=true" >> $GITHUB_OUTPUT
80192
echo "validation_needed=false" >> $GITHUB_OUTPUT
81193
fi
82194
195+
manual-approval-status:
196+
runs-on: ubuntu-latest
197+
needs: validate-submission-security
198+
if: needs.validate-submission-security.outputs.manual_approval_needed == 'true'
199+
200+
steps:
201+
- name: Manual Approval Required
202+
run: |
203+
echo "🚨 MANUAL APPROVAL REQUIRED 🚨"
204+
echo ""
205+
echo "🔍 This PR modifies files outside of submission directories"
206+
echo "📝 Files modified outside challenge-*/submissions/ or packages/*/challenge-*/submissions/ need admin review"
207+
echo ""
208+
echo "📢 @RezaSi - Please review and approve this PR by:"
209+
echo " 1. ✅ Reviewing the changes carefully"
210+
echo " 2. 🏷️ Adding the 'manual-approval-granted' label"
211+
echo " 3. 🔄 Re-running this workflow (close/reopen PR)"
212+
echo ""
213+
echo "⚠️ SECURITY: Only approve changes you trust!"
214+
echo "❌ This workflow will block until manual approval is granted"
215+
216+
no-challenges-changed:
217+
runs-on: ubuntu-latest
218+
needs: validate-submission-security
219+
if: needs.validate-submission-security.outputs.validation_passed == 'true' && needs.validate-submission-security.outputs.changed_challenges == '[]'
220+
221+
steps:
222+
- name: No Tests Needed
223+
run: |
224+
echo "✅ PR validation passed but no challenge directories were modified"
225+
echo "🎯 This PR doesn't contain any challenge submissions to test"
226+
echo "✨ All security checks completed successfully"
227+
83228
test-submissions:
84229
runs-on: ubuntu-latest
85230
needs: validate-submission-security
86-
if: needs.validate-submission-security.outputs.validation_passed == 'true'
231+
if: needs.validate-submission-security.outputs.validation_passed == 'true' && needs.validate-submission-security.outputs.changed_challenges != '[]'
87232

88233
strategy:
89234
matrix:
90-
challenge:
91-
- challenge-1
92-
- challenge-2
93-
- challenge-3
94-
- challenge-4
95-
- challenge-5
96-
- challenge-6
97-
- challenge-7
98-
- challenge-8
99-
- challenge-9
100-
- challenge-10
101-
- challenge-11
102-
- challenge-12
103-
- challenge-13
104-
- challenge-14
105-
- challenge-15
106-
- challenge-16
107-
- challenge-17
108-
- challenge-18
109-
- challenge-19
110-
- challenge-20
111-
- challenge-21
112-
- challenge-22
113-
- challenge-23
114-
- challenge-24
115-
- challenge-25
116-
- challenge-26
117-
- challenge-27
118-
- challenge-28
119-
- challenge-29
120-
- challenge-30
235+
challenge: ${{ fromJson(needs.validate-submission-security.outputs.changed_challenges) }}
121236

122237
steps:
123238
- name: Checkout repository
@@ -128,24 +243,75 @@ jobs:
128243
- name: Set up Go
129244
uses: actions/setup-go@v4
130245
with:
131-
go-version: '1.21'
246+
go-version: '1.25.0'
132247

133248
- name: Run Tests for ${{ matrix.challenge }}
134249
working-directory: ${{ matrix.challenge }}
135250
run: |
136251
USERNAME="${{ github.event.pull_request.user.login }}"
137-
SUBMISSION_DIR="submissions/$USERNAME"
138-
if [ -d "$SUBMISSION_DIR" ]; then
139-
echo "Testing submission from $USERNAME"
140-
cp "$SUBMISSION_DIR"/*.go .
141-
142-
# Handle dependencies if go.mod exists
143-
if [ -f "go.mod" ]; then
144-
echo "Found go.mod file, downloading dependencies..."
145-
go mod tidy
252+
253+
# Check if this is a package challenge or regular challenge
254+
if [[ "${{ matrix.challenge }}" =~ ^packages/ ]]; then
255+
# Package challenge
256+
SUBMISSION_DIR="submissions/$USERNAME"
257+
if [ -d "$SUBMISSION_DIR" ]; then
258+
echo "Testing package challenge submission from $USERNAME"
259+
260+
# Create a temporary directory for testing
261+
TEMP_DIR=$(mktemp -d)
262+
263+
# Copy the user's solution file and test file to temp directory
264+
cp "$SUBMISSION_DIR/solution.go" "$TEMP_DIR/"
265+
cp "solution-template_test.go" "$TEMP_DIR/"
266+
267+
# Copy go.mod if it exists
268+
if [ -f "go.mod" ]; then
269+
cp "go.mod" "$TEMP_DIR/"
270+
fi
271+
272+
# Rename solution.go to solution-template.go for the test
273+
mv "$TEMP_DIR/solution.go" "$TEMP_DIR/solution-template.go"
274+
275+
# Navigate to temp directory
276+
pushd "$TEMP_DIR" > /dev/null
277+
278+
# Handle dependencies if go.mod exists
279+
if [ -f "go.mod" ]; then
280+
echo "Found go.mod file, downloading dependencies..."
281+
# Update module name to avoid conflicts
282+
sed -i 's/^module .*/module challenge/' go.mod
283+
go mod tidy
284+
else
285+
go mod init challenge
286+
fi
287+
288+
# Run tests
289+
go test -v
290+
TEST_EXIT_CODE=$?
291+
292+
# Return to original directory and cleanup
293+
popd > /dev/null
294+
rm -rf "$TEMP_DIR"
295+
296+
exit $TEST_EXIT_CODE
297+
else
298+
echo "No submission found for $USERNAME in package challenge ${{ matrix.challenge }}"
146299
fi
147-
148-
go test -v
149300
else
150-
echo "No submission found for $USERNAME in ${{ matrix.challenge }}"
301+
# Regular challenge
302+
SUBMISSION_DIR="submissions/$USERNAME"
303+
if [ -d "$SUBMISSION_DIR" ]; then
304+
echo "Testing submission from $USERNAME"
305+
cp "$SUBMISSION_DIR"/*.go .
306+
307+
# Handle dependencies if go.mod exists
308+
if [ -f "go.mod" ]; then
309+
echo "Found go.mod file, downloading dependencies..."
310+
go mod tidy
311+
fi
312+
313+
go test -v
314+
else
315+
echo "No submission found for $USERNAME in ${{ matrix.challenge }}"
316+
fi
151317
fi

0 commit comments

Comments
 (0)