Admittedly, I've gotten addicted to Cloudflare Workers

In my earlier post "Updating RPM Spec Files for Packaging with GitHub Actions", I used GitHub Actions to automate spec file version bumps. Combined with Fedora Copr's webhook support, this enables fully automated package builds. It seemed perfect — except there was no build status monitoring, meaning I had no way to get timely notifications when a build failed (whether due to a bug in the spec itself or a network issue during the build).

Nishikino Carbonyl suggested notifications.fedoraproject.org, which supports configuring notifications — there's even a Copr option under Filters > Applications. Unfortunately, it didn't work in practice. The notification settings there appear to only configure email filtering rules — if Copr never intended to send you an email on build failure in the first place, no filter rule will make one appear.

Fortunately, Fedora Copr has a well-documented API. The /monitor endpoint can be used to fetch the latest build status for packages.

So the plan is: use a Cloudflare Workers cron job to periodically call this endpoint and check for any failed builds.

Let's start with the fetch logic:

async function fetchCopr() {
    const ownername = "zhullyb";
    const projectname = "v2rayA";
    const url = new URL("https://copr.fedorainfracloud.org/api_3/monitor")
    url.searchParams.set("ownername", ownername)
    url.searchParams.set("projectname", projectname)
    const response = await fetch(url)
    const data = await response.json()
    if (data.output !== "ok") {
        throw new Error("Failed to fetch COPR data")
    }
    return data
}

Next, the notification logic. I'm using a Feishu (Lark) webhook bot here:

async function notify(text) {
    const webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/ffffffff-ffff-ffff-ffff-ffffffffffff"
    const body = {
        msg_type: "text",
        content: {
            text: text
        }
    }
    const response = await fetch(webhook, {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(body)
    })
    console.log(response)
}

Finally, the cron handler and build status parsing:

export default {
    async fetch(request, env, ctx) {
      return new Response('Hello World!');
    },
    async scheduled(event, env, ctx) {
        const data = await fetchCopr()
        const errorPackages = new Array()
        for (const pkg of data.packages) {
            for (const chroot of Object.values(pkg.chroots)) {
                if (chroot.state == "failed") {
                    errorPackages.push(pkg.name)
                    break
                }
            }
        }
        if (errorPackages.length > 0) {
            await notify(`COPR build failures detected for the following packages:\n${errorPackages.join("\n")}`)
        } else {
            console.log("All COPR packages built successfully")
        }
    }
};

Then go to the Cloudflare Workers Settings page and configure a Cron expression. I set mine to trigger at minute 55 of every hour — that's only 24 Workers invocations per day, basically nothing.

Drawback: I didn't bother setting up a persistent database to track which packages have already been flagged. This means once a package fails, you'll get a notification every hour until it's fixed. Feels like spam calls from an unknown number. I'm not planning to fix this for now — maybe I'll just lower the cron frequency instead.