Skip to content

Release

Release #15

Workflow file for this run

name: Release
on:
schedule:
- cron: "0 3 * * 1" # every Monday — check for new core binaries
workflow_dispatch:
inputs:
bump:
description: 'Version bump type'
type: choice
options: [patch, minor, major]
default: patch
skip_bump:
description: 'Skip version bump (use current version)'
type: boolean
default: false
permissions:
contents: write
# Never run two releases at once (they both bump module.prop).
concurrency:
group: release
cancel-in-progress: false
# Third-party actions are pinned to commit SHAs (with the human-readable tag in a
# trailing comment) so a moved tag cannot silently change what runs with the
# `contents: write` token. Bump the SHA + comment together when upgrading.
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
# Push the version-bump commit under a PAT so it bypasses the `main`
# ruleset (the default GITHUB_TOKEN / github-actions app is not in the
# bypass list and cannot be added on a user-owned repo). Set the
# RELEASE_TOKEN secret to a PAT of a user holding the bypass role; it
# falls back to GITHUB_TOKEN, with which the push step would still be
# rejected by the ruleset.
token: ${{ secrets.RELEASE_TOKEN || github.token }}
persist-credentials: true
- name: Check for changes since last release
id: changes
env:
GH_TOKEN: ${{ github.token }}
run: |
skip=true
# 1. New code commits (docs/ci/chore-only changes do not trigger release)
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
has_code_commits=false
if [ -z "$last_tag" ]; then
has_code_commits=true
else
code_commits=$(git log "$last_tag"..HEAD --oneline \
-- \
'module/*.sh' \
'module/bin/kasumi-proxyctl' \
'module/bin/utils.sh' \
'module/webroot/cgi-bin/' \
'control-center/src/' \
'scripts/fetch-bin.sh' \
'scripts/package-release.sh' \
2>/dev/null | wc -l)
[ "$code_commits" -gt 0 ] && has_code_commits=true
fi
[ "$has_code_commits" = "true" ] && skip=false
# 2. Upstream binary updates
pinned_xray=$(grep -m1 'XRAY_VERSION=.*:-' scripts/fetch-bin.sh | sed 's/.*:-\([^}"]*\)}.*/\1/')
pinned_t2s=$(grep -m1 'TUN2SOCKS_VERSION=.*:-' scripts/fetch-bin.sh | sed 's/.*:-\([^}"]*\)}.*/\1/')
pinned_sb=$(grep -m1 'SINGBOX_VERSION=.*:-' scripts/fetch-bin.sh | sed 's/.*:-\([^}"]*\)}.*/\1/')
latest_xray=$(gh release view --repo XTLS/Xray-core --json tagName -q .tagName)
latest_t2s=$(gh release view --repo xjasonlyu/tun2socks --json tagName -q .tagName)
latest_sb=$(gh release view --repo SagerNet/sing-box --json tagName -q .tagName)
bins_changed=false
[ "$pinned_xray" != "$latest_xray" ] && { skip=false; bins_changed=true; }
[ "$pinned_t2s" != "$latest_t2s" ] && { skip=false; bins_changed=true; }
[ "$pinned_sb" != "$latest_sb" ] && { skip=false; bins_changed=true; }
if [ "$bins_changed" = "true" ]; then
echo "bump=minor" >> "$GITHUB_OUTPUT"
else
echo "bump=${{ inputs.bump || 'patch' }}" >> "$GITHUB_OUTPUT"
fi
echo "xray_old=$pinned_xray" >> "$GITHUB_OUTPUT"
echo "t2s_old=$pinned_t2s" >> "$GITHUB_OUTPUT"
echo "sb_old=$pinned_sb" >> "$GITHUB_OUTPUT"
echo "skip=$skip" >> "$GITHUB_OUTPUT"
echo "xray_version=$latest_xray" >> "$GITHUB_OUTPUT"
echo "t2s_version=$latest_t2s" >> "$GITHUB_OUTPUT"
echo "sb_version=$latest_sb" >> "$GITHUB_OUTPUT"
- name: Setup Bun
if: steps.changes.outputs.skip != 'true'
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: latest
- name: Cache Bun dependencies
if: steps.changes.outputs.skip != 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('control-center/bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: Setup Go
if: steps.changes.outputs.skip != 'true'
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: '1.26'
cache: false
- name: Cache Go build & modules
if: steps.changes.outputs.skip != 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: go-${{ runner.os }}-geodat2srs
restore-keys: go-${{ runner.os }}-
- name: Setup NDK
if: steps.changes.outputs.skip != 'true'
id: setup-ndk
uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1
with:
# Mirror flake.nix NDK 28.0.13004108 (== r28).
ndk-version: r28
- name: Clone geodat2srs
if: steps.changes.outputs.skip != 'true'
run: git clone https://github.com/loss-and-quick/geodat2srs.git $HOME/geodat2srs
- name: Install UI dependencies
if: steps.changes.outputs.skip != 'true'
working-directory: control-center
run: bun install --frozen-lockfile
- name: Lint & test
if: steps.changes.outputs.skip != 'true'
working-directory: control-center
run: |
bun run check
bun run check:i18n
bun run test
- name: Bump version
if: steps.changes.outputs.skip != 'true'
id: bump
run: |
if [ "${{ inputs.skip_bump }}" = "true" ]; then
version=$(grep -m1 '^version=' module/module.prop | cut -d= -f2)
else
version=$(scripts/bump-version.sh ${{ inputs.bump || 'patch' }})
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Build release zip
if: steps.changes.outputs.skip != 'true'
env:
NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
XRAY_VERSION: ${{ steps.changes.outputs.xray_version }}
TUN2SOCKS_VERSION: ${{ steps.changes.outputs.t2s_version }}
SINGBOX_VERSION: ${{ steps.changes.outputs.sb_version }}
run: scripts/package-release.sh
- name: Generate update.json
if: steps.changes.outputs.skip != 'true'
run: scripts/gen-update-json.sh
- name: Generate changelog
if: steps.changes.outputs.skip != 'true'
env:
VERSION: ${{ steps.bump.outputs.version }}
XRAY_OLD: ${{ steps.changes.outputs.xray_old }}
XRAY_NEW: ${{ steps.changes.outputs.xray_version }}
T2S_OLD: ${{ steps.changes.outputs.t2s_old }}
T2S_NEW: ${{ steps.changes.outputs.t2s_version }}
SB_OLD: ${{ steps.changes.outputs.sb_old }}
SB_NEW: ${{ steps.changes.outputs.sb_version }}
run: |
scripts/gen-changelog.sh \
"$VERSION" "$XRAY_OLD" "$XRAY_NEW" "$T2S_OLD" "$T2S_NEW" "$SB_OLD" "$SB_NEW"
- name: Commit version bump
if: steps.changes.outputs.skip != 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add module/module.prop update.json CHANGELOG.md
# The version/changelog may already be committed (a hand-prepared
# skip_bump release, or a re-run) — don't fail when there is nothing new.
if git diff --cached --quiet; then
echo "nothing to commit — version, update.json and changelog already current"
else
git commit -m "chore(release): ${{ steps.bump.outputs.version }}"
git push
fi
- name: Create GitHub Release
if: steps.changes.outputs.skip != 'true'
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ steps.bump.outputs.version }}
target_commitish: ${{ github.ref_name }}
name: Kasumi Proxy ${{ steps.bump.outputs.version }}
files: build/kasumi-proxy-${{ steps.bump.outputs.version }}.zip
generate_release_notes: true