Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6dc9fdf
Create FEATURE-MATRIX.md
felix-homelab Aug 5, 2026
0df3ce9
refactor(frp): isolate frp imports behind internal/frpx
felix-homelab Aug 7, 2026
68c9346
test(frp): add tunnel integration matrix, fork CI and upgrade runbook
felix-homelab Aug 7, 2026
3505c7e
Merge branch 'main' of https://github.com/felix-homelab/frp-panel int…
felix-homelab Aug 7, 2026
eec2a63
updated: frp v0.70.1 - golib v0.8.1 - go 1.25.0
felix-homelab Aug 11, 2026
c26ba8e
updated features
felix-homelab Aug 11, 2026
dd79e50
Create release.yml
felix-homelab Aug 11, 2026
8d64222
fix(proxy): keep every http proxy field when re-deriving the worker L…
felix-homelab Oct 5, 2026
903d7df
fix(client): apply frp's enabled filter on hot config updates
felix-homelab Oct 5, 2026
a0cab83
fix(proxy): show disabled proxies as disabled instead of error
felix-homelab Oct 5, 2026
725d1b8
fix(www): stop list editors' add buttons from submitting the form
felix-homelab Oct 5, 2026
dc07ebb
fix(dao): keep proxy row identity across client rebuilds
felix-homelab Oct 5, 2026
23eeb43
test(www): add a dependency-free frontend unit test runner
felix-homelab Oct 5, 2026
75170f0
feat(www): offer only plugins that work for the proxy type
felix-homelab Oct 5, 2026
1ee4a82
feat(www): request headers and HTTP/2 switch for http(s) client plugins
felix-homelab Oct 5, 2026
a2954af
refactor(conf): share the frps auth plugin injection
felix-homelab Oct 5, 2026
9495449
feat(www): let users add their own frps http plugins
felix-homelab Oct 5, 2026
6b181bf
feat: agents report their frp version
felix-homelab Oct 5, 2026
7820691
feat: offer wireProtocol v2 only when both agents support it
felix-homelab Oct 5, 2026
32bbd9f
feat(hack): add frpdrift, a config field drift check
felix-homelab Oct 5, 2026
8ae0ee0
ci: run frontend checks and the full Go test set in fork checks
felix-homelab Oct 5, 2026
2f4aa29
docs: correct the frp upgrade runbook
felix-homelab Oct 5, 2026
8aea1db
docs(matrix): close finished rows and correct stale facts
felix-homelab Oct 5, 2026
0435c44
docs: add a roadmap that tracks backlog order and progress
felix-homelab Oct 5, 2026
0444337
docs: add a per-tag changelog including the differences to upstream
felix-homelab Oct 5, 2026
10492cf
ci(release): publish the tag's changelog section as release notes
felix-homelab Oct 5, 2026
3479cf1
docs(readme): explain what this fork changes
felix-homelab Oct 5, 2026
75d65f9
ci(release): use actions/setup-go v5
felix-homelab Oct 6, 2026
2fa7f36
ci(release): publish container images to ghcr.io
felix-homelab Oct 6, 2026
a68a6b7
docs: document the fork's container images
felix-homelab Oct 6, 2026
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
35 changes: 35 additions & 0 deletions .github/scripts/changelog-section.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Prints the CHANGELOG.md section for one tag, without its "## [tag] - date" heading.
#
# .github/scripts/changelog-section.sh v0.10.1 [CHANGELOG.md]
#
# Exits 1 when the tag has no section, or the section is empty, so a release cannot go
# out without notes. Used by .github/workflows/release.yml.
set -euo pipefail

tag="${1:?usage: changelog-section.sh <tag> [changelog]}"
file="${2:-CHANGELOG.md}"

section="$(awk -v tag="$tag" '
/^## \[/ {
if (found) exit
# "## [v0.10.1] - 2026-08-11" -> "v0.10.1"
name = $0
sub(/^## \[/, "", name)
sub(/\].*$/, "", name)
if (name == tag) { found = 1; next }
}
# Link reference definitions at the bottom of the file end the last section.
found && /^\[[^]]+\]: / { exit }
found { print }
' "$file")"

# Trim leading and trailing blank lines.
section="$(printf '%s\n' "$section" | sed -e '/./,$!d' | tac | sed -e '/./,$!d' | tac)"

if [ -z "$section" ]; then
echo "CHANGELOG: no section for ${tag} in ${file}. Add '## [${tag}] - YYYY-MM-DD' before tagging." >&2
exit 1
fi

printf '%s\n' "$section"
87 changes: 87 additions & 0 deletions .github/workflows/fork-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Fork-local checks.
#
# This is a NEW workflow file rather than an edit to master-merge-check.yml on purpose:
# this repository is a fork, and a new file can never produce a rebase conflict against
# upstream, whereas editing the shared workflow would conflict on every upstream change.
#
# It also deliberately does NOT enable `go test ./...`. That target is red for a reason
# unrelated to this fork -- services/workerd.TestRunWorker hardcodes /home/coder/... paths
# and only passes in the original author's dev container. Turning it on would make every
# run look broken. The package list below is the subset that actually passes.
name: fork checks

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
# Tracks the go directive in go.mod, so bumping frp (which can raise the
# required Go version) does not also require editing this file.
go-version-file: go.mod
cache: true

# cmd/frpp/main.go has `//go:embed all:out`, and cmd/frpp/out is gitignored because
# it is the Next.js static export. Without it, one broken package aborts the entire
# ./... walk and every Go command fails. The frontend is not needed for these checks.
- name: Satisfy the frontend embed
run: mkdir -p cmd/frpp/out && touch cmd/frpp/out/.gitkeep

- name: Build
run: go build ./...

- name: Vet
run: go vet ./conf/... ./internal/... ./services/... ./utils/... ./biz/...

# services/dao runs against a temporary sqlite file; biz/... holds the handler tests.
- name: Unit tests
run: go test ./conf/... ./models/... ./services/wg/... ./services/dao/... ./utils/... ./internal/... ./biz/... ./hack/...

# The real gate for an frp upgrade: stands up a live frps + frpc in one process and
# pushes bytes through tcp, udp, http, https, stcp, sudp and tcpmux (xtcp is skipped
# on loopback), then checks that `enabled: false` takes effect on a hot update.
- name: Tunnel integration tests
run: go test -tags integration -run TestTunnelMatrix -timeout 5m ./internal/frpx/...

# The frontend gate FEATURE-MATRIX.md always described but this workflow never ran.
www:
runs-on: ubuntu-latest
defaults:
run:
working-directory: www
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 9

- uses: actions/setup-node@v4
with:
# Same major as release.yml.
node-version: 20
cache: pnpm
cache-dependency-path: www/pnpm-lock.yaml

- name: Install
run: pnpm install --frozen-lockfile

- name: Unit tests
run: pnpm test

- name: Lint
run: pnpm lint

# Type-checks and compiles the static export. `pnpm build` would also copy it into
# cmd/frpp/out, which this job does not need.
- name: Build
run: pnpm exec next build
2 changes: 1 addition & 1 deletion .github/workflows/master-merge-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.24.x'
go-version: '1.25.x'

- name: Install npm dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/master.workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.24.x"
go-version: "1.25.x"
- name: npm install and build
run: |
cd www
Expand Down
145 changes: 145 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Fork-local release.
#
# Like fork-checks.yml, this is a NEW file rather than an edit to tag.workflow.yml on
# purpose: this repository is a fork, and a new file can never produce a rebase conflict
# against upstream, whereas editing the shared workflow would conflict on every upstream
# change. The upstream tag workflows are disabled repo-side (Actions -> Workflows ->
# Disable) so the two do not race for the same release.
#
# It differs from upstream's tag.workflow.yml in exactly three ways:
#
# 1. `permissions: contents: write`. This repo's default GITHUB_TOKEN permission is
# read-only, which is why upstream's workflow died with "Resource not accessible by
# integration" on v0.10.0 -- a read-only token cannot create a release or upload
# assets. An explicit permissions block overrides the repo default, so this works
# without depending on a repo setting that a re-fork or a settings reset would lose.
#
# 2. Container images go to GitHub Container Registry (ghcr.io/<owner>/<repo>), not
# Docker Hub. Upstream's ko steps need DOCKERHUB_USERNAME and DOCKERHUB_TOKEN, which
# are not set here, and are hardcoded to docker.io/vaalacat/frp-panel -- upstream's
# namespace. GHCR takes this workflow's own token (`packages: write`), so there is no
# registry secret to manage. The images themselves are built exactly like upstream's:
# ko with .ko.yaml (alpine base, every platform alpine ships) and .ko.workerd.yaml.
#
# 3. Release notes are the tag's section of CHANGELOG.md, and a tag without one is not
# released (.github/scripts/changelog-section.sh).
#
# The build steps below are copied verbatim from upstream's tag.workflow.yml. They are
# known-green on this fork as of v0.10.0 (that run reached the release step before
# failing), so they are deliberately not "cleaned up" -- notably the redundant-looking
# `npm install` before pnpm, which the frontend build in build.sh relies on in practice.
name: fork release

on:
push:
tags:
- "v*"
workflow_dispatch:

permissions:
contents: write
# Pushing container images to ghcr.io.
packages: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

# Release notes come from CHANGELOG.md, one section per tag. A tag without a section
# fails here, before anything is built or published. Relative .md links are made
# absolute, because they would not resolve on the release page.
- name: Release notes from CHANGELOG.md
if: startsWith(github.ref, 'refs/tags/v')
run: |
set -euo pipefail
bash .github/scripts/changelog-section.sh "${GITHUB_REF_NAME}" CHANGELOG.md \
| sed -E "s#\]\(([A-Za-z0-9_./-]+\.md)\)#](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/blob/${GITHUB_REF_NAME}/\1)#g" \
> "${RUNNER_TEMP}/release-notes.md"
cat "${RUNNER_TEMP}/release-notes.md"

- name: npm setup
uses: actions/setup-node@v4
with:
node-version: 20

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.25.x"

- name: npm install and build
run: |
cd www
npm install && npm install -g pnpm

# build.sh stamps conf.gitVersion from GITHUB_REF_NAME when it is set (build.sh:24),
# so the `version` subcommand reports the tag rather than `git describe` output.
- name: Compile server
run: bash ./build.sh

- name: Publish release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
files: dist/*
body_path: ${{ runner.temp }}/release-notes.md
# Appended below the CHANGELOG section: the commit and PR list for the tag.
generate_release_notes: true
# dist/ silently empty would publish a release with no binaries -- the exact
# failure this workflow exists to prevent.
fail_on_unmatched_files: true

# install.sh downloads from the `latest` release tag by name, so upstream keeps a
# rolling `latest` release alongside the versioned one. Preserved here.
- name: Refresh rolling `latest` release
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
git tag -f latest "${GITHUB_SHA}"
git push -f origin latest
if gh release view latest >/dev/null 2>&1; then
gh release edit latest --target "${GITHUB_SHA}" \
--notes "Rolling build of ${GITHUB_REF_NAME}."
else
gh release create latest --target "${GITHUB_SHA}" --title latest \
--notes "Rolling build of ${GITHUB_REF_NAME}."
fi
gh release upload latest dist/* --clobber

# Last on purpose: an image problem must never hold back the binaries above.
# setup-ko logs in to ghcr.io with this job's token and sets
# KO_DOCKER_REPO=ghcr.io/<owner>/<repo>; --bare pushes to exactly that name.
- name: Setup ko
if: startsWith(github.ref, 'refs/tags/v')
uses: ko-build/setup-ko@v0.9
with:
version: v0.19.1

# The build above left the frontend in cmd/frpp/out, which the binary embeds.
# The source label is what links the GHCR package to this repository.
- name: Publish container images
if: startsWith(github.ref, 'refs/tags/v')
run: |
set -euo pipefail
labels=(
--image-label "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}"
--image-label "org.opencontainers.image.version=${GITHUB_REF_NAME}"
--image-label "org.opencontainers.image.revision=${GITHUB_SHA}"
--image-label "org.opencontainers.image.licenses=AGPL-3.0"
)
ko build ./cmd/frpp --sbom=none --bare "${labels[@]}" -t "${GITHUB_REF_NAME}" -t latest

# The workerd variant (Cloudflare workerd as the base, for agents that run
# Workers). KO_CONFIG_PATH takes a directory, hence the copy.
mkdir -p "${RUNNER_TEMP}/ko-workerd"
cp .ko.workerd.yaml "${RUNNER_TEMP}/ko-workerd/.ko.yaml"
KO_CONFIG_PATH="${RUNNER_TEMP}/ko-workerd" \
ko build ./cmd/frpp --sbom=none --bare "${labels[@]}" \
-t "${GITHUB_REF_NAME}-workerd" -t latest-workerd
2 changes: 1 addition & 1 deletion .github/workflows/tag.workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.24.x"
go-version: "1.25.x"
- name: npm install and build
run: |
cd www
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/workerd-docker-tag.workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.24.x"
go-version: "1.25.x"
- name: npm install and build
run: |
cd www
Expand Down
Loading