CI/CD-driven static page server for Forgejo and Gitea. Uses OAuth2 to enforce repository permissions on pages.
Find a file
Leon Schmidt 6ba035e0a7
All checks were successful
/ docker-build-and-push (push) Successful in 2m2s
/ create-release (push) Successful in 1m19s
chore(devcontainers): added todo-highlight extension
2025-12-27 18:45:37 +00:00
.devcontainer chore(devcontainers): added todo-highlight extension 2025-12-27 18:45:37 +00:00
.forgejo/workflows chore(ci): fixed uppercase image slug 2025-11-29 17:05:01 +01:00
.gitignore Added testing scripts 2025-11-28 15:13:51 +01:00
compose.yml chore: Added README and example compose.yml, closes #5 2025-11-29 16:25:14 +01:00
config.example.yml chore: renamed .yaml to .yml and oidc field to oauth 2025-11-29 16:23:27 +01:00
config.go feat: full clickable URL is now visible in response and logs 2025-11-29 23:50:03 +01:00
deploy_api.go feat: Set additional base path, closes #12 2025-12-27 17:17:57 +00:00
deploy_ops.go feat: Set additional base path, closes #12 2025-12-27 17:17:57 +00:00
Dockerfile chore(ci): added workflows 2025-11-29 16:25:33 +01:00
etc-hosts_TESTING fix: closes #9 2025-12-05 18:24:16 +01:00
forgejo.go fix: wrong url for checking repo permissions 2025-11-29 20:44:13 +01:00
go.mod chore: go mod tidy 2025-11-28 18:46:51 +01:00
go.sum chore: go mod tidy 2025-11-28 18:46:51 +01:00
main.go chore: change bind address to all interfaces 2025-11-29 18:02:57 +01:00
oauth2.go fix(oauth): fixed missing state in callback issue 2025-12-02 17:34:57 +01:00
page_handler.go feat: Set additional base path, closes #12 2025-12-27 17:17:57 +00:00
README.md chore: added docs for additional base path feature 2025-12-27 17:27:47 +00:00
test-delete.sh chore: updated test scripts 2025-12-27 17:18:13 +00:00
test-deploy.sh chore: updated test scripts 2025-12-27 17:18:13 +00:00
url_path_helper.go fix: missing seperator for path remainder 2025-12-27 18:45:08 +00:00
utils.go feat: full clickable URL is now visible in response and logs 2025-11-29 23:50:03 +01:00

Forge Pages

Forge Pages is a GitHub/GitLab Pages pendant for Forge Servers (Forgejo/Gitea) to deploy static webpages.

Unlike other pages services such as the Codeberg Page Server, static assets do not have to be committed into a separate branch or registry, but can be built and deployed directly from within a workflow. There is also a suitable action that simplifies the deployment.

Deployed pages can be secured by enforcing the user permissions configured in the repository, the page was deployed from. This makes this tool a great fit for deploying private pages as well!

Features

  • Deploy static pages using the Forge Pages Action or manually using tar and curl
  • Uses the worflow token (e.g. ${{ forgejo.token }}) to ensure write permissions to the repository before deployment
  • Can protect pages with the Forgejo/Gitea OAuth2 provider
  • Same URL-layout as with GitHub/GitLab Pages: https://<owner>.<base-url>/<repo>/*
    • Can optionally add an additional base path after the <repo> part that can be seperatly protected (useful when deploying multiple versions in one repo or for previewing contents of a pull request)

How does it work?

The Forge Pages Server is a Go application that provides a POST /deploy endpoint. Pages must be TAR'ed and GZIP'ed and posted to this endpoint, together with the following query parameters:

  • repo: Repository slug. Used to construct the URL where the page will be deployed to. To prevent confusion, the repo name is always lowercased!
  • access_token: The workflow token (e.g. ${{ forgejo.token }}) to verify permissions to deploy a page to the target specified by repo.
    • This can also be a PAT, as long as it has the appropriate permissions
  • protect: If existent, the page will be protected using the Forgejo/Gitea OAuth2 provider. Only users with at least read/pull permissions can view this page.
    • Alternativly, you can add an empty file called .protect to the root of the page to enable protection
  • additional_base_path: Can be used to set an additional base path suffix to allow for multiple deployments per repo

When visiting a protected page, you will get redirected to the configured OAuth2 provider, where you must log in. If you have the correct permission, you will get redirected to the page.

Deployments can be deleted using the DELETE /deploy endpoint or by uploading an empty page to the same location. The delete endpoint also requires the repo and access_token parameters (and additional_base_path if set during deployment).

Installation

You can use the provided compose.yml. Copy the config.example.yml to config.yml and adjust it for your needs:

  • forge_url: URL to your Forgejo/Gitea instance. Used to make API calls to verify permissions.
  • pages_url: Public URL where you intend to deploy the Forge Pages server. Keey in mind that you need to have a wildcard DNS entry for subdomains on this URL as well!
  • serve_path: Base path where pages will be stored and served from. To persist deployments across container restarts, create a bind/volume mount for this path (already contained in the compose.yml)
  • oauth.*: OAuth2 settings as returned by the OAuth2 provider (see here for how to set up a new OAuth2 application with Forgejo)

This application does not support serving HTTPS directly so you will need to configure a reverse proxy as well.

After setting everything up, you can start Forge Pages with docker compose up -d.

Deploy using the Forge Pages Action

See here for documentation and an example.

Local development

You can use the CLI flag -skip_deploy_checks to skip checking the access_token when deploying new pages. This way, you don't need a valid access token.

It is recommended to add the Pages URL and some subdomains you intend to deploy to as static local hosts. See etc-hosts_TESTING to see some examples.

Create a basic config.yml and start the application with go run . -skip_deploy_checks.