Repository navigation
Server Stats Update and Deploy #449
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: Server Stats Update and Deploy | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| # Intended trigger time: 00:00 Beijing Time (UTC+8). | |
| # - Target UTC time: 16:00 (00:00 Beijing - 8 hours). | |
| # Note: cron syntax uses UTC time. | |
| - cron: "0 16 * * *" | |
| permissions: | |
| contents: write | |
| pages: write | |
| id-token: write | |
| pull-requests: write | |
| concurrency: | |
| group: "pages" | |
| cancel-in-progress: false | |
| jobs: | |
| update-stats: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| stats_updated: ${{ steps.check-changes.outputs.stats_changed }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Needed for git operations later | |
| - name: Set up Python | |
| uses: actions/setup-python@v3 | |
| with: | |
| python-version: "3.10" | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| if [ -f requirements.txt ]; then pip install -r requirements.txt; fi | |
| - name: Add SSH key and configure known hosts | |
| run: | | |
| mkdir -p ~/.ssh | |
| echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa | |
| chmod 600 ~/.ssh/id_rsa | |
| echo "Host server-stats-host | |
| HostName ${{ secrets.SERVER_HOST }} | |
| User ${{ secrets.SSH_USERNAME }} | |
| Port ${{ secrets.SSH_PORT || '22' }} | |
| IdentityFile ~/.ssh/id_rsa" > ~/.ssh/config | |
| ssh-keyscan -p ${{ secrets.SSH_PORT || '22' }} ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts | |
| - name: Copy files to server | |
| run: | | |
| rsync -avz -e "ssh -p ${{ secrets.SSH_PORT || '22' }}" --delete \ | |
| --exclude '.git/' \ | |
| --exclude '.github/' \ | |
| ./ ${{ secrets.SSH_USERNAME }}@${{ secrets.SERVER_HOST }}:/tmp/server-stats/ | |
| - name: Execute script on server | |
| run: | | |
| ssh -p ${{ secrets.SSH_PORT || '22' }} ${{ secrets.SSH_USERNAME }}@${{ secrets.SERVER_HOST }} \ | |
| "cd /tmp/server-stats/scripts && python3 count-user-login-minutes-every-day.py && \ | |
| python3 transfer-csv-to-readme.py && python3 dashboard_html_gen.py" | |
| - name: Retrieve updated file | |
| run: | | |
| rsync -avz -e "ssh -p ${{ secrets.SSH_PORT || '22' }}" \ | |
| ${{ secrets.SSH_USERNAME }}@${{ secrets.SERVER_HOST }}:/tmp/server-stats/ssh_login_minutes.csv \ | |
| ./ | |
| rsync -avz -e "ssh -p ${{ secrets.SSH_PORT || '22' }}" \ | |
| ${{ secrets.SSH_USERNAME }}@${{ secrets.SERVER_HOST }}:/tmp/server-stats/README.md \ | |
| ./ | |
| rsync -avz -e "ssh -p ${{ secrets.SSH_PORT || '22' }}" \ | |
| ${{ secrets.SSH_USERNAME }}@${{ secrets.SERVER_HOST }}:/tmp/server-stats/dashboard/index.html \ | |
| ./dashboard/index.html | |
| ssh -p ${{ secrets.SSH_PORT || '22' }} \ | |
| ${{ secrets.SSH_USERNAME }}@${{ secrets.SERVER_HOST }} \ | |
| "rm -rf /tmp/server-stats" | |
| - name: Check for changes | |
| id: check-changes | |
| run: | | |
| git status | |
| git diff | |
| if git diff --quiet; then | |
| echo "No changes detected" | |
| echo "stats_changed=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "Changes detected" | |
| echo "stats_changed=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload updated files as artifact | |
| if: steps.check-changes.outputs.stats_changed == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: stats-updates | |
| path: | | |
| ssh_login_minutes.csv | |
| README.md | |
| dashboard/index.html | |
| retention-days: 1 | |
| create-pr: | |
| needs: update-stats | |
| if: | | |
| (needs.update-stats.result == 'success' && needs.update-stats.outputs.stats_updated == 'true') || | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'push' && github.ref == 'refs/heads/main') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| ref: ${{ github.ref }} | |
| - name: Download updated files | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: stats-updates | |
| path: ./ | |
| - name: Verify downloaded files | |
| run: | | |
| echo "Downloaded files:" | |
| ls -la ./ssh_login_minutes.csv ./README.md ./dashboard/index.html | |
| echo "File contents:" | |
| head -5 ./ssh_login_minutes.csv | |
| head -10 ./README.md | tail -5 | |
| - name: Create new branch | |
| run: | | |
| timestamp=$(date +%Y%m%d%H%M%S) | |
| branch_name="update/stats-${timestamp}" | |
| echo "BRANCH_NAME=${branch_name}" >> $GITHUB_ENV | |
| git checkout -b "${branch_name}" | |
| - name: Add and commit changes | |
| env: | |
| GIT_USER_NAME: ${{ secrets.GIT_PUSH_USER_NAME }} | |
| GIT_USER_EMAIL: ${{ secrets.GIT_PUSH_USER_EMAIL }} | |
| run: | | |
| git config user.name "$GIT_USER_NAME" | |
| git config user.email "$GIT_USER_EMAIL" | |
| git add ./ssh_login_minutes.csv ./README.md ./dashboard/index.html | |
| commit_message="[Bot] Update server stats at $(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M UTC+8')" | |
| git commit -m "${commit_message}" | |
| git push origin "${BRANCH_NAME}" | |
| - name: Create Pull Request and Merge | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| try { | |
| const { data: pr } = await github.rest.pulls.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `🤖 Bot: Update server stats (${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })})`, | |
| body: `This is an automated PR to update server statistics.\n\n**Changes:**\n- Updated login minutes data\n- Updated README\n- Updated dashboard\n\n*Auto-generated by GitHub Actions*`, | |
| head: process.env.BRANCH_NAME, | |
| base: 'main' | |
| }); | |
| console.log(`Created PR #${pr.number}`); | |
| // Add automated label | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: ['automated', 'server-stats'] | |
| }); | |
| // Output PR number for next step | |
| core.setOutput('pr_number', pr.number.toString()); | |
| console.log(`Attempting to merge PR #${pr.number}...`); | |
| try { | |
| await github.rest.pulls.merge({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| merge_method: 'squash', | |
| commit_title: `Merge PR #${pr.number}: Update server stats`, | |
| commit_message: 'Automated merge by GitHub Actions' | |
| }); | |
| console.log(`Successfully merged PR #${pr.number}`); | |
| } catch (mergeError) { | |
| console.error(`Failed to merge: ${mergeError.message}`); | |
| } | |
| } catch (error) { | |
| console.error(`Failed to create PR: ${error.message}`); | |
| } | |
| - name: Delete branch | |
| uses: actions/github-script@v7 | |
| env: | |
| BRANCH_NAME: ${{ env.BRANCH_NAME }} | |
| with: | |
| script: | | |
| try { | |
| await github.rest.git.deleteRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: `heads/${process.env.BRANCH_NAME}` | |
| }); | |
| console.log(`Deleted branch ${process.env.BRANCH_NAME}`); | |
| } catch (error) { | |
| console.log(`Failed to delete branch: ${error.message}`); | |
| } | |
| deploy: | |
| needs: create-pr | |
| if: | | |
| needs.create-pr.result == 'success' || | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'push' && github.ref == 'refs/heads/main') | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository with latest changes | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| ref: "main" | |
| - name: Setup GitHub Pages | |
| uses: actions/configure-pages@v5 | |
| - name: Verify dashboard files exist | |
| run: | | |
| echo "Checking dashboard files..." | |
| if [ -d "./dashboard" ]; then | |
| echo "Dashboard directory found" | |
| ls -la ./dashboard/ | |
| if [ -z "$(ls -A ./dashboard/)" ]; then | |
| echo "Warning: Dashboard directory is empty" | |
| echo "<html><body><h1>Dashboard is being generated...</h1></body></html>" > ./dashboard/index.html | |
| fi | |
| else | |
| echo "Dashboard directory not found, creating with placeholder..." | |
| mkdir -p ./dashboard | |
| echo "<html><body><h1>Dashboard is being generated...</h1><p>Last update: $(date)</p></body></html>" > ./dashboard/index.html | |
| fi | |
| - name: Upload dashboard as artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: "./dashboard" | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 |