Files
IPTables-Management/.gitea/workflows/release.yml
ahdoawhfo a741fa1e16 Make toolchain install resilient to apt outages
Only install tools that are actually missing (gitea/runner-images
already ships curl/jq/tar), and point apt at mirrors.aliyun.com when
we do need to install something so archive.ubuntu.com timeouts stop
blocking the build. Handles both Noble's DEB822 ubuntu.sources and
the legacy sources.list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 13:51:08 +08:00

271 lines
9.5 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Build and Release
on:
workflow_dispatch:
inputs:
version:
description: '版本号 (留空则自动生成 v<YYYY.MM.DD>-<short-sha>)'
required: false
default: ''
prerelease:
description: '标记为预发布 (Pre-release)'
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'
draft:
description: '以草稿方式创建 Release (不对外公开)'
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'
skip_tests:
description: '跳过单元测试 (紧急构建时使用)'
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'
jobs:
build-release:
runs-on: ubuntu-latest
env:
# 仓库使用 SHA-256 对象格式actions/checkout 内部 `git init` 需在此模式下运行,否则 fetch 会报
# `mismatched algorithms: client sha1; server sha256`。
GIT_DEFAULT_HASH: sha256
steps:
- name: Checkout (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install toolchain (only what's missing)
run: |
set -euo pipefail
MISSING=()
for tool in shellcheck jq curl tar sha256sum; do
command -v "$tool" >/dev/null 2>&1 || MISSING+=("$tool")
done
if [[ ${#MISSING[@]} -eq 0 ]]; then
echo "所有工具已就绪,跳过安装。"
exit 0
fi
echo "需要安装: ${MISSING[*]}"
if [[ $EUID -ne 0 ]] && command -v sudo >/dev/null 2>&1; then
SUDO=sudo
else
SUDO=
fi
# 切换到阿里云镜像以避开 archive.ubuntu.com / security.ubuntu.com 出境超时。
if [[ -f /etc/apt/sources.list.d/ubuntu.sources ]]; then
$SUDO sed -i \
-e 's|http://archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' \
-e 's|http://security.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' \
/etc/apt/sources.list.d/ubuntu.sources
fi
if [[ -f /etc/apt/sources.list ]]; then
$SUDO sed -i \
-e 's|http://archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' \
-e 's|http://security.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' \
/etc/apt/sources.list
fi
# 实际安装的包名映射coreutils 提供 sha256sum
PKGS=()
for tool in "${MISSING[@]}"; do
case "$tool" in
sha256sum) PKGS+=(coreutils) ;;
*) PKGS+=("$tool") ;;
esac
done
$SUDO apt-get update -qq
DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y -qq --no-install-recommends \
"${PKGS[@]}" ca-certificates
- name: Run shellcheck
run: |
set -euo pipefail
shellcheck iptables-forward.sh install.sh lib/*.sh tests/run_all.sh
- name: Run unit tests
if: ${{ inputs.skip_tests != 'true' }}
run: |
set -euo pipefail
bash tests/run_all.sh --skip-integration
- name: Compute version metadata
id: ver
run: |
set -euo pipefail
SHORT_SHA=$(git rev-parse --short=7 HEAD)
FULL_SHA=$(git rev-parse HEAD)
DATE=$(date -u +%Y.%m.%d)
BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
if [[ -n "${{ inputs.version }}" ]]; then
VERSION="${{ inputs.version }}"
else
VERSION="v${DATE}-${SHORT_SHA}"
fi
TAG="${VERSION}"
TITLE="IPTables 端口转发管理工具 ${VERSION}"
ARTIFACT_BASE="iptables-forward-${VERSION}"
{
echo "version=${VERSION}"
echo "tag=${TAG}"
echo "title=${TITLE}"
echo "artifact_base=${ARTIFACT_BASE}"
echo "short_sha=${SHORT_SHA}"
echo "full_sha=${FULL_SHA}"
echo "build_time=${BUILD_TIME}"
} >> "$GITHUB_OUTPUT"
echo "准备构建: ${VERSION}"
- name: Ensure tag does not already exist
run: |
set -euo pipefail
TAG='${{ steps.ver.outputs.tag }}'
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "::error::Tag ${TAG} 已存在于远端。请手动传入新的 version 输入。"
exit 1
fi
- name: Build release tarball
id: build
run: |
set -euo pipefail
NAME='${{ steps.ver.outputs.artifact_base }}'
mkdir -p "dist/${NAME}"
cp iptables-forward.sh install.sh README.md "dist/${NAME}/"
cp -r lib "dist/${NAME}/"
[[ -f LICENSE ]] && cp LICENSE "dist/${NAME}/" || true
chmod 755 "dist/${NAME}/iptables-forward.sh" "dist/${NAME}/install.sh"
tar -czf "dist/${NAME}.tar.gz" -C dist "${NAME}"
( cd dist && sha256sum "${NAME}.tar.gz" > "${NAME}.tar.gz.sha256" )
ls -la dist/
- name: Generate changelog
id: changelog
run: |
set -euo pipefail
PREV_TAG=$(git tag --list 'v*' --sort=-creatordate | head -n 1 || true)
{
echo "## 构建信息"
echo ""
echo "- **版本**: \`${{ steps.ver.outputs.version }}\`"
echo "- **提交**: \`${{ steps.ver.outputs.full_sha }}\`"
echo "- **构建时间 (UTC)**: ${{ steps.ver.outputs.build_time }}"
echo "- **触发者**: @${{ github.actor }}"
echo ""
echo "## 变更记录"
echo ""
if [[ -n "${PREV_TAG}" ]]; then
echo "自 \`${PREV_TAG}\` 以来的提交:"
echo ""
git log "${PREV_TAG}..HEAD" --pretty='- %s (`%h`)' --no-merges || true
else
echo "首次发布,近 30 条提交:"
echo ""
git log -30 --pretty='- %s (`%h`)' --no-merges
fi
echo ""
echo "## 安装"
echo ""
echo '```bash'
echo "# 下载"
echo "wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.ver.outputs.tag }}/${{ steps.ver.outputs.artifact_base }}.tar.gz"
echo "wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.ver.outputs.tag }}/${{ steps.ver.outputs.artifact_base }}.tar.gz.sha256"
echo ""
echo "# 校验"
echo "sha256sum -c ${{ steps.ver.outputs.artifact_base }}.tar.gz.sha256"
echo ""
echo "# 解压并安装 (可选)"
echo "tar -xzf ${{ steps.ver.outputs.artifact_base }}.tar.gz"
echo "cd ${{ steps.ver.outputs.artifact_base }}"
echo "sudo ./install.sh # 链接到 /usr/local/bin/iptables-forward"
echo ""
echo "# 直接运行"
echo "sudo ./iptables-forward.sh"
echo '```'
} > dist/RELEASE_NOTES.md
cat dist/RELEASE_NOTES.md
- name: Create tag and Release via Gitea API
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITEA_URL: ${{ github.server_url }}
REPO: ${{ github.repository }}
TAG: ${{ steps.ver.outputs.tag }}
TITLE: ${{ steps.ver.outputs.title }}
SHA: ${{ steps.ver.outputs.full_sha }}
ARTIFACT_BASE: ${{ steps.ver.outputs.artifact_base }}
PRERELEASE: ${{ inputs.prerelease }}
DRAFT: ${{ inputs.draft }}
run: |
set -euo pipefail
BODY=$(jq -Rs . < dist/RELEASE_NOTES.md)
PAYLOAD=$(jq -nc \
--arg tag "$TAG" \
--arg sha "$SHA" \
--arg name "$TITLE" \
--argjson body "$BODY" \
--argjson prerelease "$PRERELEASE" \
--argjson draft "$DRAFT" \
'{tag_name: $tag, target_commitish: $sha, name: $name, body: $body, prerelease: $prerelease, draft: $draft}')
echo "Creating release: $TAG"
RESP=$(curl -sSfL -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "${PAYLOAD}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases")
RELEASE_ID=$(echo "$RESP" | jq -r '.id')
if [[ -z "${RELEASE_ID}" || "${RELEASE_ID}" == "null" ]]; then
echo "::error::创建 Release 失败。响应: ${RESP}"
exit 1
fi
echo "Release ID: ${RELEASE_ID}"
for asset in "dist/${ARTIFACT_BASE}.tar.gz" "dist/${ARTIFACT_BASE}.tar.gz.sha256"; do
fname=$(basename "$asset")
echo "Uploading $fname"
curl -sSfL -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${asset};filename=${fname}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${fname}" \
> /dev/null
done
echo "完成: ${GITEA_URL}/${REPO}/releases/tag/${TAG}"
- name: Upload artifacts to workflow run (备份)
uses: actions/upload-artifact@v3
with:
name: ${{ steps.ver.outputs.artifact_base }}
path: |
dist/${{ steps.ver.outputs.artifact_base }}.tar.gz
dist/${{ steps.ver.outputs.artifact_base }}.tar.gz.sha256
dist/RELEASE_NOTES.md
retention-days: 30