Add GPG key file (#9608)

* Add files via upload

* Update build-all.yml

* Update build-linux.yml

* Update build-osx.yml

* Update build-windows-desktop.yml

* Update build-windows-x86.yml

* Update build-windows.yml

* Update package-zip.yml

* Update build-linux.yml

* Update build-osx.yml

* Update build-windows-x86.yml

* Update package-zip.yml

* Update upload-sign.yml

* Update pub-key.yml

* Update README.md

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
JieXu
2026-06-22 09:48:06 +08:00
committed by GitHub
parent ce380b861c
commit f1b88ccf2a
10 changed files with 278 additions and 74 deletions

View File

@@ -9,8 +9,17 @@ on:
permissions:
actions: write
contents: write
jobs:
public-key:
if: inputs.release_tag != ''
uses: ./.github/workflows/pub-key.yml
with:
release_tag: ${{ inputs.release_tag }}
secrets:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
update:
runs-on: ubuntu-latest
steps:

View File

@@ -12,6 +12,7 @@ on:
permissions:
contents: write
actions: read
jobs:
build:
@@ -26,6 +27,7 @@ jobs:
with:
target: linux
release_tag: ${{ inputs.release_tag }}
secrets: inherit
deb:
name: build and release deb x64 & arm64
@@ -56,9 +58,9 @@ jobs:
find "$GITHUB_WORKSPACE/dist/deb" -name "v2rayn_*_arm64.deb" -exec mv {} "$GITHUB_WORKSPACE/dist/deb/v2rayN-linux-arm64.deb" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/deb" || true
- name: Upload DEBs to release
uses: svenstaro/upload-release-action@v2
with: { file: "dist/deb/**/*.deb", tag: "${{ env.RELEASE_TAG }}", file_glob: true, prerelease: true }
- name: Upload DEB artifact
uses: actions/upload-artifact@v7
with: { name: release-deb, path: "dist/deb/**/*.deb", if-no-files-found: error }
rpm:
name: build and release rpm x64 & arm64
@@ -115,9 +117,9 @@ jobs:
find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.aarch64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
- name: Upload RPMs to release
uses: svenstaro/upload-release-action@v2
with: { file: "dist/rpm/**/*.rpm", tag: "${{ env.RELEASE_TAG }}", file_glob: true, prerelease: true }
- name: Upload RPM artifact
uses: actions/upload-artifact@v7
with: { name: release-rpm, path: "dist/rpm/**/*.rpm", if-no-files-found: error }
rpm-riscv64:
name: build and release rpm riscv64
@@ -155,25 +157,9 @@ jobs:
find "$GITHUB_WORKSPACE/dist/rpm-riscv64" -name "*.riscv64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm-riscv64/v2rayN-linux-rhel-riscv64.rpm" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/rpm-riscv64" || true
- name: Upload RPMs to release
shell: bash
env: { GITHUB_TOKEN: "${{ github.token }}" }
run: |
set -euo pipefail
shopt -s globstar nullglob
files=(dist/rpm-riscv64/**/*.rpm)
(( ${#files[@]} )) || { echo "No RPMs found."; exit 1; }
api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}"
upload_url="$(curl -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" "$api" | jq -r '.upload_url // empty' | sed 's/{?name,label}//')"
[[ "$upload_url" ]] || { echo "Release upload URL not found: ${RELEASE_TAG}"; exit 1; }
for f in "${files[@]}"; do
echo "Uploading ${f##*/}"
curl -fsSL -X POST \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Content-Type: application/x-rpm" \
--data-binary @"$f" \
"${upload_url}?name=${f##*/}"
done
- name: Upload RPM artifact
uses: actions/upload-artifact@v7
with: { name: release-rpm-riscv64, path: "dist/rpm-riscv64/**/*.rpm", if-no-files-found: error }
deb-riscv64:
name: build and release deb riscv64
@@ -212,25 +198,9 @@ jobs:
find "$GITHUB_WORKSPACE/dist/deb-riscv64" -name "*.deb" -exec mv {} "$GITHUB_WORKSPACE/dist/deb-riscv64/v2rayN-linux-riscv64.deb" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/deb-riscv64" || true
- name: Upload DEBs to release
shell: bash
env: { GITHUB_TOKEN: "${{ github.token }}" }
run: |
set -euo pipefail
shopt -s globstar nullglob
files=(dist/deb-riscv64/**/*.deb)
(( ${#files[@]} )) || { echo "No DEBs found."; exit 1; }
api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}"
upload_url="$(curl -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" "$api" | jq -r '.upload_url // empty' | sed 's/{?name,label}//')"
[[ "$upload_url" ]] || { echo "Release upload URL not found: ${RELEASE_TAG}"; exit 1; }
for f in "${files[@]}"; do
echo "Uploading ${f##*/}"
curl -fsSL -X POST \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Content-Type: application/vnd.debian.binary-package" \
--data-binary @"$f" \
"${upload_url}?name=${f##*/}"
done
- name: Upload DEB artifact
uses: actions/upload-artifact@v7
with: { name: release-deb-riscv64, path: "dist/deb-riscv64/**/*.deb", if-no-files-found: error }
deb-loong64:
name: build and release deb loong64
@@ -378,9 +348,9 @@ jobs:
find "$GITHUB_WORKSPACE/dist/deb-loong64" -name "*.deb" -exec mv {} "$GITHUB_WORKSPACE/dist/deb-loong64/v2rayN-linux-loong64.deb" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/deb-loong64"
- name: Upload DEBs to release
uses: svenstaro/upload-release-action@v2
with: { file: "dist/deb-loong64/**/*.deb", tag: "${{ env.RELEASE_TAG }}", file_glob: true, prerelease: true }
- name: Upload DEB artifact
uses: actions/upload-artifact@v7
with: { name: release-deb-loong64, path: "dist/deb-loong64/**/*.deb", if-no-files-found: error }
rpm-loong64:
name: build and release rpm loong64
@@ -528,6 +498,48 @@ jobs:
find "$GITHUB_WORKSPACE/dist/rpm-loong64" -name "*.loongarch64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm-loong64/v2rayN-linux-rhel-loong64.rpm" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/rpm-loong64"
- name: Upload RPMs to release
uses: svenstaro/upload-release-action@v2
with: { file: "dist/rpm-loong64/**/*.rpm", tag: "${{ env.RELEASE_TAG }}", file_glob: true, prerelease: true }
- name: Upload RPM artifact
uses: actions/upload-artifact@v7
with: { name: release-rpm-loong64, path: "dist/rpm-loong64/**/*.rpm", if-no-files-found: error }
release-deb:
name: sign and upload deb x64 arm64
needs: deb
uses: ./.github/workflows/upload-sign.yml
with: { release_tag: "${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}", artifact_name: release-deb, file_pattern: "*.deb" }
secrets: { GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" }
release-rpm:
name: sign and upload rpm x64 arm64
needs: rpm
uses: ./.github/workflows/upload-sign.yml
with: { release_tag: "${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}", artifact_name: release-rpm, file_pattern: "*.rpm" }
secrets: { GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" }
release-rpm-riscv64:
name: sign and upload rpm riscv64
needs: rpm-riscv64
uses: ./.github/workflows/upload-sign.yml
with: { release_tag: "${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}", artifact_name: release-rpm-riscv64, file_pattern: "*.rpm" }
secrets: { GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" }
release-deb-riscv64:
name: sign and upload deb riscv64
needs: deb-riscv64
uses: ./.github/workflows/upload-sign.yml
with: { release_tag: "${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}", artifact_name: release-deb-riscv64, file_pattern: "*.deb" }
secrets: { GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" }
release-deb-loong64:
name: sign and upload deb loong64
needs: deb-loong64
uses: ./.github/workflows/upload-sign.yml
with: { release_tag: "${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}", artifact_name: release-deb-loong64, file_pattern: "*.deb" }
secrets: { GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" }
release-rpm-loong64:
name: sign and upload rpm loong64
needs: rpm-loong64
uses: ./.github/workflows/upload-sign.yml
with: { release_tag: "${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }}", artifact_name: release-rpm-loong64, file_pattern: "*.rpm" }
secrets: { GPG_PRIVATE_KEY: "${{ secrets.GPG_PRIVATE_KEY }}" }

View File

@@ -12,6 +12,7 @@ on:
permissions:
contents: write
actions: read
jobs:
build:
@@ -26,6 +27,7 @@ jobs:
with:
target: macos
release_tag: ${{ inputs.release_tag }}
secrets: inherit
dmg:
name: package and release macOS dmg
@@ -62,13 +64,24 @@ jobs:
- name: Package dmg
run: ./package-osx.sh macos-$Arch v2rayN-macos-$Arch ${{ inputs.release_tag }}
- name: Sleep for race condition between matrix jobs
run: sleep "$(od -An -N2 -tu2 /dev/urandom | awk 'NR==1{printf "%.2f", $1/5461}')"
- name: Upload dmg to release
uses: svenstaro/upload-release-action@v2
- name: Upload dmg artifact
uses: actions/upload-artifact@v7
with:
file: ${{ github.workspace }}/v2rayN*.dmg
tag: ${{ inputs.release_tag }}
file_glob: true
prerelease: true
name: release-dmg-${{ matrix.arch }}
path: v2rayN*.dmg
if-no-files-found: error
release-dmg:
name: sign and upload macOS dmg
if: inputs.release_tag != ''
needs: dmg
strategy:
matrix:
arch: [ x64, arm64 ]
uses: ./.github/workflows/upload-sign.yml
with:
release_tag: ${{ inputs.release_tag }}
artifact_name: release-dmg-${{ matrix.arch }}
file_pattern: '*.dmg'
secrets:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}

View File

@@ -12,6 +12,7 @@ on:
permissions:
contents: write
actions: read
jobs:
build:
@@ -26,3 +27,4 @@ jobs:
with:
target: windows-desktop
release_tag: ${{ inputs.release_tag }}
secrets: inherit

View File

@@ -9,6 +9,7 @@ on:
permissions:
contents: write
actions: read
jobs:
package-x86:
@@ -222,10 +223,25 @@ jobs:
-DestinationPath "${{ matrix.zip_name }}" `
-Force
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
- name: Upload zip artifact
uses: actions/upload-artifact@v7
with:
file: ${{ matrix.zip_name }}
tag: ${{ inputs.release_tag }}
prerelease: true
overwrite: true
name: release-windows-x86-${{ matrix.flavor }}
path: ${{ matrix.zip_name }}
if-no-files-found: error
release-x86:
name: sign and upload windows x86
if: inputs.release_tag != ''
needs: package-x86
strategy:
fail-fast: false
matrix:
flavor: [ wpf, desktop ]
uses: ./.github/workflows/upload-sign.yml
with:
release_tag: ${{ inputs.release_tag }}
artifact_name: release-windows-x86-${{ matrix.flavor }}
file_pattern: '*.zip'
secrets:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}

View File

@@ -12,6 +12,7 @@ on:
permissions:
contents: write
actions: read
jobs:
build:
@@ -27,3 +28,4 @@ jobs:
with:
target: windows
release_tag: ${{ inputs.release_tag }}
secrets: inherit

View File

@@ -9,9 +9,13 @@ on:
target: # windows linux macos windows-desktop
required: true
type: string
secrets:
GPG_PRIVATE_KEY:
required: true
permissions:
contents: write
actions: read
jobs:
package:
@@ -55,13 +59,23 @@ jobs:
if: inputs.target == 'windows-desktop'
run: mv "v2rayN-$Target-$Arch.zip" "v2rayN-$Target-$Arch-desktop.zip"
- name: Sleep for race condition between matrix jobs
run: sleep "$(od -An -N2 -tu2 /dev/urandom | awk 'NR==1{printf "%.2f", $1/5461}')"
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
- name: Upload zip artifact
uses: actions/upload-artifact@v7
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ inputs.release_tag }}
file_glob: true
prerelease: true
name: release-${{ inputs.target }}-${{ matrix.arch }}
path: v2rayN*.zip
if-no-files-found: error
release:
name: sign and upload zip x64 arm64
needs: package
strategy:
matrix:
arch: [ x64, arm64 ]
uses: ./.github/workflows/upload-sign.yml
with:
release_tag: ${{ inputs.release_tag }}
artifact_name: release-${{ inputs.target }}-${{ matrix.arch }}
file_pattern: '*.zip'
secrets:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}

48
.github/workflows/pub-key.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: publish GPG public key
on:
workflow_call:
inputs:
release_tag:
required: true
type: string
secrets:
GPG_PRIVATE_KEY:
required: true
workflow_dispatch:
inputs:
release_tag:
required: true
type: string
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-26.04
steps:
- name: Import GPG private key
shell: bash
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
set -euo pipefail
install -m 700 -d ~/.gnupg
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
- name: Export GPG public key
shell: bash
run: |
set -euo pipefail
fingerprint="$(gpg --batch --list-secret-keys --with-colons | awk -F: '$1 == "fpr" { print $10; exit }')"
[[ -n "$fingerprint" ]] || { echo "No GPG secret key found."; exit 1; }
gpg --batch --armor --export "$fingerprint" > v2rayN-public-key.asc
- name: Upload GPG public key to release
uses: svenstaro/upload-release-action@v2
with:
file: v2rayN-public-key.asc
tag: ${{ inputs.release_tag }}
prerelease: true
overwrite: true

76
.github/workflows/upload-sign.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: sign and upload release files
on:
workflow_call:
inputs:
release_tag:
required: true
type: string
artifact_name:
required: true
type: string
file_pattern:
required: false
type: string
default: '**/*'
content_type:
required: false
type: string
default: application/octet-stream
secrets:
GPG_PRIVATE_KEY:
required: true
permissions:
contents: write
actions: read
jobs:
upload:
runs-on: ubuntu-26.04
steps:
- name: Download release artifact
uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact_name }}
path: dist
- name: Import GPG private key
shell: bash
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
set -euo pipefail
install -m 700 -d ~/.gnupg
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
- name: Sign release files
shell: bash
run: |
set -euo pipefail
cd dist
shopt -s globstar nullglob
files=( ${{ inputs.file_pattern }} )
(( ${#files[@]} )) || { echo "No files matched: ${{ inputs.file_pattern }}"; exit 1; }
for f in "${files[@]}"; do
[[ -f "$f" ]] || continue
gpg --batch --yes --armor --detach-sign --output "$f.sig" "$f"
done
- name: Upload release files
uses: svenstaro/upload-release-action@v2
with:
file: dist/${{ inputs.file_pattern }}
tag: ${{ inputs.release_tag }}
file_glob: true
prerelease: true
overwrite: true
- name: Upload release signatures
uses: svenstaro/upload-release-action@v2
with:
file: dist/${{ inputs.file_pattern }}.sig
tag: ${{ inputs.release_tag }}
file_glob: true
prerelease: true
overwrite: true

View File

@@ -16,3 +16,15 @@ Read the [Wiki](https://github.com/2dust/v2rayN/wiki) for details.
## Telegram Channel
[github_2dust](https://t.me/github_2dust)
## GPG 签名校验 (GPG Verification)
为了预防镜像站、运营商或 CDN 劫持,本项目发布的 Release 文件已使用 GPG 进行签名,以供用户校验文件的真实性与完整性。
To prevent hijacking by mirror sites, ISPs, or CDNs, the Release files of this project are signed with GPG for users to verify their authenticity and integrity.
### 公钥指纹 (Key Fingerprint)
```text
76945E9F3E9A168F8070F195805D661C134DFAF68903C199463C31E5AE903AE0
```