Cloudflare Pages: Auto Delete Old Deployments
Maintain a clean deployment history by automating the cleanup of old Cloudflare Pages deployments using a simple Worker script.
Cloudflare Pages automatically creates new deployments whenever you push changes to your repository. While this continuous deployment feature is incredibly convenient, it can lead to an ever-growing list of deployment histories that you might never need again.
Solution: Create and deploy a Cloudflare Worker script that periodically deletes deployments older than a specified time by calling the Cloudflare API.
package.json
{
"private": true,
"type": "module",
"scripts": {
"build": "cross-env ENVIRONMENT=production esbuild workers/pages-cleanup/src/index.ts --bundle --format=esm --minify --outfile=workers/pages-cleanup/dist/index.js --legal-comments=none",
"dev": "npm run watch && npm run start",
"publish": "wrangler publish --config workers/pages-cleanup/wrangler.toml",
"start": "miniflare workers/pages-cleanup/dist/index.js --wrangler-config workers/pages-cleanup/wrangler.toml --watch",
"watch": "cross-env ENVIRONMENT=development esbuild workers/pages-cleanup/src/index.ts --bundle --format=esm --minify --outfile=workers/pages-cleanup/dist/index.js --sourcemap --watch --legal-comments=none"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20230801.0",
"wrangler": "^3.4.0"
}
}types.d.ts
/// <reference types="@cloudflare/workers-types" />src/index.ts
interface ScheduleEnv {
readonly ACCOUNT_ID: string;
readonly PROJECT_NAME: string;
readonly API_TOKEN: string;
readonly EXPIRATION_DAYS: string;
}
function debugLog(message: string): void {
console.debug(`[${new Date().toISOString()}] ${message}`);
}
const scheduled: ExportedHandlerScheduledHandler<ScheduleEnv> = async (
event,
env
) => {
debugLog(
`Account "${env.ACCOUNT_ID}", project "${
env.PROJECT_NAME
}". Deleting all deployments ${
env.EXPIRATION_DAYS
} days older than ${new Date(event.scheduledTime).toISOString()}...`
);
const endpoint = `https://api.cloudflare.com/client/v4/accounts/${env.ACCOUNT_ID}/pages/projects/${env.PROJECT_NAME}/deployments`;
const init = {
headers: {
"Content-Type": "application/json;charset=UTF-8",
Authorization: `Bearer ${env.API_TOKEN}`,
},
};
const deployments: { id: string; created_on: string }[] = [];
let count = 0;
let total: number | null = null;
let page = 1;
do {
const response: any = await (
await fetch(`${endpoint}?page=${page}`, init)
).json();
if (!total) {
total = response.result_info.total_count as number;
debugLog(`Found ${total} deployments in project.`);
}
count += response.result_info.count;
debugLog(`Retrieved ${count} deployments, ${total - count} remaining.`);
deployments.push(...response.result);
page++;
} while (count < total);
debugLog(`All ${deployments.length} deployments retrieved.`);
let deleted = 0;
await Promise.all(
deployments.map(async (deployment) => {
if (
(event.scheduledTime - new Date(deployment.created_on).getTime()) /
86400000 >
parseInt(env.EXPIRATION_DAYS)
) {
await fetch(`${endpoint}/${deployment.id}`, {
method: "DELETE",
...init,
});
debugLog(
`Deleted deployment ${deployment.id} (created on ${deployment.created_on}).`
);
deleted++;
}
})
);
debugLog(`Done. Deleted ${deleted} deployments.`);
};
export default { scheduled };