Skip to content

Server Stats Update and Deploy #449

Server Stats Update and Deploy

Server Stats Update and Deploy #449

Workflow file for this run

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