Files
IPTables-Management/.gitea/workflows/release.yml
T
ahdoawhfo e4ef47c901 Drop apt, pull shellcheck from GitHub releases
act_runner's job containers live on a temporary bridge network that
does not inherit the runner's own egress path, so apt-get against
Canonical mirrors can time out even on a US host. Remove the apt step,
rely on the tools baked into gitea/runner-images, and fetch the
shellcheck static binary over HTTPS when it is missing.

Also add a short network diagnostics step to make future egress
issues obvious at a glance.

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

278 lines
10 KiB
YAML

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: Network diagnostics
run: |
set +e
echo "=== DNS ==="
cat /etc/resolv.conf 2>/dev/null | head -5 || true
echo "=== Route ==="
ip route 2>/dev/null | head -3 || true
echo "=== Egress probe (5s connect timeout) ==="
for url in \
https://github.com \
https://objects.githubusercontent.com \
https://mirrors.aliyun.com \
http://archive.ubuntu.com \
http://security.ubuntu.com \
; do
code=$(curl -sS -o /dev/null -w '%{http_code}' -m 5 --connect-timeout 5 "$url" || echo TIMEOUT)
printf ' %-45s -> %s\n' "$url" "$code"
done
- name: Ensure required tools (shellcheck via GitHub release)
run: |
set -euo pipefail
# 镜像 gitea/runner-images:ubuntu-latest 已自带 curl / jq / tar / sha256sum。
# 唯一通常缺失的是 shellcheck,从 GitHub releases 拉静态二进制即可,不走 apt。
for tool in curl jq tar sha256sum; do
command -v "$tool" >/dev/null || {
echo "::error::基础工具 $tool 不在 PATH 中,镜像异常。请更换 runner 镜像。" >&2
exit 1
}
done
if command -v shellcheck >/dev/null 2>&1; then
echo "shellcheck 已就绪:$(shellcheck --version | awk '/^version:/{print $2}')"
exit 0
fi
SC_VER=v0.10.0
case "$(uname -m)" in
x86_64) SC_ARCH=x86_64 ;;
aarch64) SC_ARCH=aarch64 ;;
*) echo "::error::不支持的架构: $(uname -m)" >&2; exit 1 ;;
esac
URL="https://github.com/koalaman/shellcheck/releases/download/${SC_VER}/shellcheck-${SC_VER}.linux.${SC_ARCH}.tar.xz"
echo "下载 $URL"
curl -fsSL --retry 3 --connect-timeout 15 -o /tmp/shellcheck.tar.xz "$URL"
tar -xJf /tmp/shellcheck.tar.xz -C /tmp
if [[ $EUID -ne 0 ]] && command -v sudo >/dev/null 2>&1; then
SUDO=sudo
else
SUDO=
fi
$SUDO install -m 0755 "/tmp/shellcheck-${SC_VER}/shellcheck" /usr/local/bin/shellcheck
shellcheck --version | awk '/^version:/{print "shellcheck 已安装:" $2}'
- 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