Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create GH Action to clean up old Playwright reports #96

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/delete-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 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.
#
# Even though this action affects only the gh-pages branch,
# it runs _from_ the main branch, due to GH action schedule constraints.
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule

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 }}"
98 changes: 98 additions & 0 deletions removeOldFolders.js
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);
Loading