Daily LeetCode Challenge #41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Daily LeetCode Challenge | |
| on: | |
| schedule: | |
| - cron: '30 0 * * *' # 00:30 UTC, shortly after LeetCode daily reset | |
| workflow_dispatch: | |
| inputs: | |
| problem_url: | |
| description: 'LeetCode problem URL (leave empty for daily challenge)' | |
| required: false | |
| default: '' | |
| jobs: | |
| daily-leetcode: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup Go | |
| uses: actions/setup-go@v4 | |
| with: | |
| go-version: '1.21' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install Python dependencies | |
| run: | | |
| pip3 install html2text pyyaml | |
| - name: Fetch LeetCode problem metadata | |
| id: fetch | |
| uses: ./.github/actions/fetch-leetcode-problem | |
| with: | |
| problem_url: ${{ inputs.problem_url }} | |
| force_refetch: false | |
| # ---------------------------------------------------------------- | |
| # Premium problem: create issue and stop | |
| # ---------------------------------------------------------------- | |
| - name: Handle premium problem | |
| if: steps.fetch.outputs.is_premium == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh issue create \ | |
| --title "Daily Challenge Skipped: Premium Problem - ${{ steps.fetch.outputs.title }}" \ | |
| --body "$(cat <<'EOF' | |
| Problem #${{ steps.fetch.outputs.number }} — **${{ steps.fetch.outputs.title }}** is a premium problem and was skipped. | |
| - **Date:** ${{ steps.fetch.outputs.date }} | |
| - **Link:** https://leetcode.com/problems/${{ steps.fetch.outputs.slug }}/ | |
| EOF | |
| )" | |
| echo "::notice::Skipping premium problem – issue created." | |
| # ---------------------------------------------------------------- | |
| # All remaining steps are skipped when the problem is premium | |
| # ---------------------------------------------------------------- | |
| - name: Check for duplicate problem | |
| if: steps.fetch.outputs.is_premium != 'true' | |
| id: dup | |
| run: | | |
| if [ -d "problems/${{ steps.fetch.outputs.dir_name }}" ]; then | |
| echo "is_duplicate=true" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Problem directory already exists – will use _daily_ suffix" | |
| else | |
| echo "is_duplicate=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Build Claude prompt | |
| if: steps.fetch.outputs.is_premium != 'true' | |
| run: | | |
| # Generate problem.json from problem.md for build-prompt.py | |
| python3 scripts/parse-problem-md.py "problems/${{ steps.fetch.outputs.dir_name }}/problem.md" > problem.json | |
| IS_DUPLICATE="${{ steps.dup.outputs.is_duplicate }}" \ | |
| DATE_COMPACT="${{ steps.fetch.outputs.date_compact }}" \ | |
| python3 scripts/build-prompt.py > prompt.txt | |
| - name: Create working branch | |
| if: steps.fetch.outputs.is_premium != 'true' | |
| run: | | |
| branch="feat/daily-${{ steps.fetch.outputs.date_compact }}-${{ steps.fetch.outputs.slug }}" | |
| git checkout -b "$branch" | |
| echo "BRANCH_NAME=$branch" >> "$GITHUB_ENV" | |
| - name: Generate solution with Claude | |
| if: steps.fetch.outputs.is_premium != 'true' | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| prompt: | | |
| Read the file `prompt.txt` in the repository root. It contains a LeetCode problem | |
| and detailed instructions for generating a DSA-coach analysis with progressive hints, | |
| a Go solution, and Go table-driven tests. | |
| Follow all formatting and naming instructions in the prompt. Write each file directly | |
| to the filesystem using the Write tool: | |
| - `problems/${{ steps.fetch.outputs.dir_name }}/analysis.md` | |
| - `problems/${{ steps.fetch.outputs.dir_name }}/solution.go` | |
| - `problems/${{ steps.fetch.outputs.dir_name }}/solution_test.go` | |
| Do NOT use delimiters or print file contents to stdout. Create the actual files. | |
| Do NOT run any git commands. Do NOT create commits or branches. | |
| claude_args: | | |
| --model opus | |
| --max-turns 20 | |
| --allowedTools "Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash" | |
| - name: Run tests | |
| if: steps.fetch.outputs.is_premium != 'true' | |
| id: test | |
| continue-on-error: true | |
| run: | | |
| dir="problems/${{ steps.fetch.outputs.dir_name }}" | |
| if [ ! -d "$dir" ]; then | |
| echo "::error::Solution directory was not created by Claude" | |
| echo "test_result=❌ No solution files generated" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| fi | |
| set +e | |
| test_output=$(go test -v "./$dir/..." 2>&1) | |
| test_exit=$? | |
| set -e | |
| echo "$test_output" | |
| if [ $test_exit -eq 0 ]; then | |
| echo "test_result=✅ All tests passed" >> "$GITHUB_OUTPUT" | |
| else | |
| passed=$(echo "$test_output" | grep -c "^--- PASS" || true) | |
| failed=$(echo "$test_output" | grep -c "^--- FAIL" || true) | |
| total=$((passed + failed)) | |
| if [ $total -gt 0 ]; then | |
| echo "test_result=❌ ${failed} of ${total} tests failed" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "test_result=❌ Tests failed to compile or run" >> "$GITHUB_OUTPUT" | |
| fi | |
| fi | |
| - name: Check for existing PR | |
| if: steps.fetch.outputs.is_premium != 'true' | |
| id: existing_pr | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| pr_url=$(gh pr list --head "$BRANCH_NAME" --state open --json url --jq '.[0].url // empty') | |
| if [ -n "$pr_url" ]; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| echo "::notice::PR already exists for this branch: $pr_url — skipping commit and PR creation." | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Commit and push solution | |
| if: steps.fetch.outputs.is_premium != 'true' && steps.existing_pr.outputs.exists != 'true' | |
| run: | | |
| dir="problems/${{ steps.fetch.outputs.dir_name }}" | |
| if [ ! -d "$dir" ] || [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then | |
| echo "::error::No solution files to commit" | |
| exit 1 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" | |
| git add "$dir" | |
| git commit -m "feat: add solution for ${{ steps.fetch.outputs.number }}. ${{ steps.fetch.outputs.title }}" | |
| git push -u origin "$BRANCH_NAME" | |
| - name: Create pull request | |
| if: steps.fetch.outputs.is_premium != 'true' && steps.existing_pr.outputs.exists != 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| pr_title="daily-${{ steps.fetch.outputs.date }}-${{ steps.fetch.outputs.number }}-${{ steps.fetch.outputs.slug }}" | |
| gh pr create \ | |
| --head "$BRANCH_NAME" \ | |
| --title "$pr_title" \ | |
| --body "$(cat <<EOF | |
| ## Summary | |
| - **Problem:** ${{ steps.fetch.outputs.number }}. ${{ steps.fetch.outputs.title }} | |
| - **Difficulty:** ${{ steps.fetch.outputs.difficulty }} | |
| - **Topics:** ${{ steps.fetch.outputs.topics }} | |
| - **Link:** https://leetcode.com/problems/${{ steps.fetch.outputs.slug }}/ | |
| ## Analysis Summary | |
| See \`analysis.md\` in the PR files for progressive hints and detailed approach. | |
| ## Test Results | |
| ${{ steps.test.outputs.test_result }} | |
| --- | |
| 🤖 Generated with Claude (Anthropic API) using dsa-coach analysis | |
| EOF | |
| )" |