Skip to content

Repository Backup

Repository Backup #17

Workflow file for this run

name: Repository Backup
on:
schedule:
# Run weekly on Sunday at 2 AM UTC
- cron: '0 2 * * 0'
workflow_dispatch:
inputs:
organization:
description: 'GitHub organization to backup'
required: false
default: 'quantecon'
force:
description: 'Force backup even if already exists today'
required: false
default: false
type: boolean
jobs:
backup:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC authentication
contents: read
issues: write # Required for creating/updating issues
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
# Recommended: Use OIDC authentication (no long-lived credentials)
# Requires AWS IAM Identity Provider and Role configured for GitHub Actions
# See README.md for setup instructions
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION || 'us-east-1' }}
- name: Run backup
env:
GITHUB_TOKEN: ${{ secrets.REPO_BACKUP_TOKEN || secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event.inputs.force }}" = "true" ]; then
python -m src.main --config config.yml --task backup --force
else
python -m src.main --config config.yml --task backup
fi
- name: Generate backup report
id: report
if: always()
env:
GITHUB_TOKEN: ${{ secrets.REPO_BACKUP_TOKEN || secrets.GITHUB_TOKEN }}
run: |
python -m src.main --config config.yml --task report 2>&1 | tee /tmp/report.txt
echo "report<<EOF" >> $GITHUB_OUTPUT
cat /tmp/report.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create or update monthly backup report issue
if: always()
uses: actions/github-script@v7
with:
script: |
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
const now = new Date();
const date = now.toISOString().split('T')[0];
const dayOfMonth = now.getUTCDate();
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'];
const monthName = monthNames[now.getUTCMonth()];
const year = now.getUTCFullYear();
const issueTitle = `Backup Reports - ${monthName} ${year}`;
const jobStatus = '${{ job.status }}';
const isFailure = jobStatus === 'failure';
// Check if this is the last week of the month (day >= 25)
const isEndOfMonth = dayOfMonth >= 25;
// Build comment with collapsed report details
let comment = `### ${date} - ${jobStatus === 'success' ? '✅ Success' : '❌ Failed'}\n\n`;
comment += `**Workflow Run:** [View Details](${runUrl})\n\n`;
comment += `<details>\n<summary>📋 Report Output</summary>\n\n`;
comment += '```\n';
comment += `${{ steps.report.outputs.report }}`;
comment += '\n```\n\n';
comment += `</details>\n`;
if (isFailure) {
comment += `\n⚠️ @mmcky - backup failed, please investigate.\n`;
}
// Add end-of-month review reminder
if (isEndOfMonth && !isFailure) {
comment += `\n---\n📅 **End of month reminder:** @mmcky please review this month's backups and close this issue when done.\n`;
}
// Find existing issue for this month
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'backup-report'
});
const existingIssue = issues.data.find(i => i.title === issueTitle);
if (existingIssue) {
// Add comment to existing monthly issue
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existingIssue.number,
body: comment
});
console.log(`Added comment to issue #${existingIssue.number}: ${issueTitle}`);
} else {
// Create new monthly issue
let issueBody = `# Backup Reports - ${monthName} ${year}\n\n`;
issueBody += `This issue tracks all backup runs for **${monthName} ${year}**.\n\n`;
issueBody += `## Summary\n\n`;
issueBody += `- **Schedule:** Weekly on Sunday at 2 AM UTC\n`;
issueBody += `- **Organization:** QuantEcon\n`;
issueBody += `- **Storage:** AWS S3\n\n`;
issueBody += `Each backup run will be posted as a comment below with a collapsed report.\n`;
const newIssue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: issueTitle,
body: issueBody,
labels: ['backup-report']
});
// Add first run as comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: newIssue.data.number,
body: comment
});
console.log(`Created issue #${newIssue.data.number}: ${issueTitle}`);
}