Delete Course Data #2
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: Delete Course Data | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| course_ids: | |
| description: "Course ID(s) to delete, comma-separated (e.g. 30004 or 30004,30005)" | |
| required: true | |
| type: string | |
| sub_ids: | |
| description: "Optional lecture sub_id(s) to delete within the course (comma-separated). If omitted, deletes the entire course." | |
| required: false | |
| type: string | |
| default: "" | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: db-reset | |
| cancel-in-progress: false | |
| jobs: | |
| delete: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies | |
| run: | | |
| pip install -r requirements.txt | |
| pip install pycryptodome | |
| - name: Fetch and decrypt database | |
| env: | |
| STUID: ${{ secrets.STUID }} | |
| UISPSW: ${{ secrets.UISPSW }} | |
| run: | | |
| git fetch origin data --depth=1 | |
| mkdir -p data/sharded/shards | |
| if git cat-file -e origin/data:data/icourse-index.enc 2>/dev/null; then | |
| git show origin/data:data/icourse-index.enc > data/sharded/icourse-index.enc | |
| shard_paths=$(git ls-tree -r --name-only origin/data | grep '^data/shards/' || true) | |
| for path in $shard_paths; do | |
| base=$(basename "$path") | |
| git show "origin/data:$path" > "data/sharded/shards/$base" | |
| done | |
| python scripts/db_shard.py reassemble data/sharded data/icourse.db | |
| else | |
| echo "::error::No sharded database found on data branch." | |
| exit 1 | |
| fi | |
| - name: Delete lecture(s) or course(s) | |
| run: | | |
| SUB_IDS="${{ inputs.sub_ids }}" | |
| IFS=',' read -ra CIDS <<< "${{ inputs.course_ids }}" | |
| for cid in "${CIDS[@]}"; do | |
| cid=$(echo "$cid" | xargs) | |
| if [ -n "$SUB_IDS" ]; then | |
| echo "Deleting specific lecture(s) from course $cid..." | |
| IFS=',' read -ra SIDS <<< "$SUB_IDS" | |
| for sid in "${SIDS[@]}"; do | |
| sid=$(echo "$sid" | xargs) | |
| echo " Deleting lecture $sid..." | |
| sqlite3 data/icourse.db "DELETE FROM ppt_pages WHERE sub_id='$sid'" | |
| sqlite3 data/icourse.db "DELETE FROM lectures WHERE sub_id='$sid'" | |
| done | |
| remaining=$(sqlite3 data/icourse.db "SELECT COUNT(*) FROM lectures WHERE course_id='$cid'") | |
| echo " $remaining lecture(s) remaining for course $cid" | |
| if [ "$remaining" -eq 0 ]; then | |
| sqlite3 data/icourse.db "DELETE FROM courses WHERE course_id='$cid'" | |
| echo " Course $cid removed (no lectures left)" | |
| fi | |
| else | |
| echo "Deleting entire course $cid..." | |
| count=$(sqlite3 data/icourse.db "SELECT COUNT(*) FROM lectures WHERE course_id='$cid'") | |
| sqlite3 data/icourse.db "DELETE FROM ppt_pages WHERE sub_id IN (SELECT sub_id FROM lectures WHERE course_id='$cid')" | |
| sqlite3 data/icourse.db "DELETE FROM lectures WHERE course_id='$cid'" | |
| sqlite3 data/icourse.db "DELETE FROM courses WHERE course_id='$cid'" | |
| echo " Removed $count lecture(s) for course $cid" | |
| fi | |
| done | |
| # NOTE: COURSE_IDS is deliberately NOT touched here. GitHub's | |
| # Secrets API is write-only (values can never be read back), so a | |
| # workflow cannot remove one ID from the list. The frontend's | |
| # subscription editor owns that secret — it re-encrypts and PUTs | |
| # the full list whenever the user changes their subscription. | |
| - name: Re-shard and push | |
| env: | |
| STUID: ${{ secrets.STUID }} | |
| UISPSW: ${{ secrets.UISPSW }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Shard into a fresh directory — data/sharded still holds the | |
| # fetched pre-delete shard files, and a deleted course's shard | |
| # must not survive into the push. | |
| rm -rf data/sharded_out | |
| mkdir -p data/sharded_out | |
| python scripts/db_shard.py shard data/icourse.db data/sharded_out | |
| # data/ is gitignored in the source tree and the data branch has | |
| # unrelated history, so stage to a clean tree and force-push — | |
| # the same pattern check.yml / single_run.yml use. | |
| rm -rf /tmp/db_deploy | |
| mkdir -p /tmp/db_deploy/data/shards | |
| cp data/sharded_out/icourse-index.enc /tmp/db_deploy/data/ | |
| cp data/sharded_out/shards/*.db.gz.enc /tmp/db_deploy/data/shards/ | |
| cd /tmp/db_deploy | |
| git init -q | |
| git checkout -q -b data | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add data/ | |
| git commit -q -m "chore: delete ${{ inputs.sub_ids != '' && 'lecture(s)' || 'course(s)' }} ${{ inputs.sub_ids || inputs.course_ids }}" | |
| git remote add origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" | |
| git push --force origin data |