-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create GH Action to clean up old Playwright reports
- Loading branch information
1 parent
4e5238c
commit 55dec53
Showing
2 changed files
with
155 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# This job deletes all folders in the root directory | ||
# which are named in a certain way, and are older than a certain cutoff. | ||
# | ||
# It is a part of our Playwright setup: | ||
# We save test run output as files in the repo, in the gh-pages branch. | ||
# After a month, we consider it safe to delete those old test results. | ||
|
||
name: Delete old folders from GitHub Pages | ||
|
||
on: | ||
push: | ||
branches: | ||
- "gh-pages" | ||
schedule: | ||
- cron: '0 0 * * *' # This will run the workflow daily at midnight UTC | ||
|
||
jobs: | ||
delete_old_folders: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
with: | ||
ref: gh-pages | ||
|
||
- name: Set up Node | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version-file: ".nvmrc" | ||
|
||
- name: Get current directory | ||
run: echo "CURRENT_DIR=$(pwd)" >> $GITHUB_ENV | ||
|
||
- name: Run the script | ||
run: node removeOldFolders.js "${{ env.CURRENT_DIR }}" | ||
|
||
- name: Commit all changed files back to the repository | ||
uses: stefanzweifel/git-auto-commit-action@v5 | ||
with: | ||
branch: gh-pages | ||
commit_message: Delete folders older than 30 days | ||
|
||
notify_on_delete_pages_failure: | ||
runs-on: ubuntu-latest | ||
needs: | ||
- delete_old_folders | ||
if: failure() | ||
steps: | ||
- name: Slack Notification | ||
uses: rtCamp/action-slack-notify@v2 | ||
env: | ||
SLACK_TITLE: ":boom: The nightly delete of expired Playwright reports job has failed in ${{ github.repository }}." | ||
MSG_MINIMAL: true | ||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}" |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* eslint-disable no-console */ | ||
import { parseArgs } from "node:util"; | ||
import * as fs from "node:fs"; | ||
import { join } from "node:path"; | ||
|
||
/* | ||
* This program will delete all folders in the specified directory | ||
* which are named in a certain way, and older than a certain cutoff. | ||
* | ||
* It is a part of our Playwright setup: | ||
* We save test run output as files in the repo, in the gh-pages branch. | ||
* After a month, we consider it safe to delete those old test results. | ||
* | ||
* Since this program runs in a github action, | ||
* we cannot rely on file system timestamps to make the age determination; | ||
* every file and folder in the repo will appear to be less than a minute old. | ||
* Instead, we embed the test run timestamp in the folder name, and parse that. | ||
*/ | ||
|
||
/** The threshold folder age for deletion. */ | ||
const MAX_AGE_DAYS = 30; | ||
|
||
/** | ||
* Is this filesystem entry a folder, | ||
* with a timestamp-formatted name, | ||
* indicating that it is old enough to delete? | ||
* @param {fs.Dirent} entry | ||
*/ | ||
function shouldDelete(entry) { | ||
const name = entry.name; | ||
if (!entry.isDirectory()) { | ||
console.info( | ||
`SKIPPED --- File ${name} is not a folder. It will not be deleted.` | ||
); | ||
return false; | ||
} | ||
|
||
const folderNameRegex = /^(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})Z$/; | ||
const regexMatch = folderNameRegex.exec(name); | ||
if (!regexMatch) { | ||
console.info( | ||
`SKIPPED --- Folder ${name} does not match the expected regex. It will not be deleted.` | ||
); | ||
return false; | ||
} | ||
|
||
let folderTimestamp; | ||
try { | ||
const [yyyy, MM, dd, hh, mm, ss] = regexMatch | ||
.slice(1) | ||
.map((n) => parseInt(n, 10)); | ||
// JS Dates have 0-based months | ||
folderTimestamp = new Date(yyyy, MM - 1, dd, hh, mm, ss); | ||
} catch { | ||
console.error( | ||
`SKIPPED --- Error parsing timestamp for folder ${entry.name}. It will not be deleted.` | ||
); | ||
return false; | ||
} | ||
|
||
const ageMS = new Date().valueOf() - folderTimestamp.valueOf(); | ||
const ageDays = ageMS / 1000 / 60 / 60 / 24; | ||
if (ageDays < MAX_AGE_DAYS) { | ||
console.debug( | ||
`SKIPPED --- Folder ${entry.name} is not older than ${MAX_AGE_DAYS} days. It will not be deleted.` | ||
); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @param {string} directory - The directory to search for deletable folders | ||
*/ | ||
function deleteOldFolders(directory) { | ||
const foldersToDelete = fs | ||
.readdirSync(directory, { withFileTypes: true }) | ||
.filter(shouldDelete); | ||
for (let folder of foldersToDelete) { | ||
try { | ||
fs.rmSync(join(directory, folder.name), { recursive: true, force: true }); | ||
console.debug( | ||
`DELETED --- Folder ${folder.name} and its contents have been deleted.` | ||
); | ||
} catch (err) { | ||
console.error(`SKIPPED --- Error deleting folder ${folder.name}`, err); | ||
} | ||
} | ||
} | ||
|
||
const directory = parseArgs({ allowPositionals: true }).positionals[0]; | ||
if (!directory) { | ||
throw new Error( | ||
"The directory to search must be provided via the command line." | ||
); | ||
} | ||
deleteOldFolders(directory); |