DEV Community

Cover image for Cloudflare pages direct upload with stable preview urls
Adam
Adam

Posted on • Originally published at urbanisierung.dev

Cloudflare pages direct upload with stable preview urls

I switched all my projects to Monorepos this year, and I use Cloudlfare Pages intensively for hosting static websites. There is one small problem with Cloudlfare's Github integration: you can only connect one project per repository. In a monorepo where I provide pages like a landing page, documentation and an app, this is a problem.

It's good that you can also upload the assets directly. The problem with that: you lose some nice benefits:

  • Stable preview URL
  • PR comment with the links

And those are already quite nice benefits ;) So I started to rebuild the benefits myself. With the help of Wrangler and the Cloudflare API it is not difficult to achieve everything.

To get a stable URL I originally assumed that I would just get an updated stable URL with the help of the branch name.

npm i -g wrangler
cd ${{ env.ROOT_DIRECTORY }}
CF_PUBLISH_OUTPUT=$(wrangler pages deploy ${{ env.DIST_DIRECTORY }} --project-name=${{ env.CLOUDFLARE_PAGES_PROJECT_NAME }} --branch="${{ steps.extract_branch.outputs.branch }}" --commit-dirty=true --commit-hash=${{ steps.meta.outputs.sha_short }} | grep complete)
echo "cf_deployments=$CF_PUBLISH_OUTPUT" >> "$GITHUB_OUTPUT"
Enter fullscreen mode Exit fullscreen mode

Unfortunately, after a few test runs, I found that this is not the case. I didn't deal with it further at this point, but tried to take an alternative approach:

  • Search all deployments to a branch on every run.
  • Delete all deployments
  • Upload new assets

For reading and deleting deployments I wrote a small TypesScript program that I run in the CI pipeline.

Read out all previous branch deployments:

public async getDeployments(options?: { branch?: string }) {
    const { branch } = options || {}
    const { accountId, projectName, apiToken } = this.config

    const response = await fetch(
      `https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/${projectName}/deployments`,
      {
        headers: {
          Authorization: `Bearer ${apiToken}`,
        },
      },
    ).then((res) => res.json())

    let deployments = response.result

    if (branch) {
      deployments = deployments.filter(
        (deployment) =>
          deployment.deployment_trigger?.metadata?.branch === branch,
      )
    }

    return deployments
}
Enter fullscreen mode Exit fullscreen mode

Deleting a deployment:

public async deleteDeployment(id: string) {
    const { accountId, projectName, apiToken } = this.config
    await fetch(
      `https://api.cloudflare.com/client/v4/accounts/${accountId}/pages/projects/${projectName}/deployments/${id}?force=true`,
      {
        method: 'DELETE',
        headers: {
          Authorization: `Bearer ${apiToken}`,
        },
      },
    ).then((res) => res.json())
}
Enter fullscreen mode Exit fullscreen mode

The approach has another advantage: deployments that are no longer current are always cleaned up, since I am no longer interested in them anyway.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Heroku

This site is powered by Heroku

Heroku was created by developers, for developers. Get started today and find out why Heroku has been the platform of choice for brands like DEV for over a decade.

Sign Up

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay