diff --git a/.gitea/workflows/dependency-release.yml b/.gitea/workflows/dependency-release.yml new file mode 100644 index 0000000..b75e87a --- /dev/null +++ b/.gitea/workflows/dependency-release.yml @@ -0,0 +1,116 @@ +name: Update dependencies and release + +on: + schedule: + # Every Sunday at 03:17 UTC (11:17 Asia/Shanghai). + - cron: '17 3 * * 0' + workflow_dispatch: + +permissions: + contents: write + +env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GITEA_SERVER_URL: ${{ github.server_url }} + GITEA_REPOSITORY: ${{ github.repository }} + +jobs: + update-and-release: + # The act_runner on the Raspberry Pi must expose the self-hosted label. + runs-on: self-hosted + timeout-minutes: 60 + + steps: + - name: Check out the default branch + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITEA_TOKEN }} + ref: ${{ env.DEFAULT_BRANCH }} + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Update modules + shell: bash + run: | + set -euo pipefail + go get -u ./... + go mod tidy + + - name: Run tests + shell: bash + run: go test -timeout=20m ./... + + - name: Detect dependency changes + id: changes + shell: bash + run: | + if git diff --quiet -- go.mod go.sum; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Commit and push dependency update + if: steps.changes.outputs.changed == 'true' + shell: bash + run: | + set -euo pipefail + git config user.name "gitea-actions[bot]" + git config user.email "gitea-actions[bot]@users.noreply.gitea.local" + git add go.mod go.sum + git commit -m "chore: update Go dependencies" + git push origin "HEAD:$DEFAULT_BRANCH" + + - name: Calculate next patch version + if: steps.changes.outputs.changed == 'true' + id: version + shell: bash + run: | + set -euo pipefail + latest="$(git tag --list 'v[0-9]*' --sort=-version:refname | head -n 1)" + if [[ -z "$latest" ]]; then + next="v0.1.0" + elif [[ "$latest" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + next="v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.$((${BASH_REMATCH[3]} + 1))" + else + echo "Latest tag '$latest' is not a vMAJOR.MINOR.PATCH tag" >&2 + exit 1 + fi + echo "tag=$next" >> "$GITHUB_OUTPUT" + echo "Releasing $next" + + - name: Tag the release + if: steps.changes.outputs.changed == 'true' + shell: bash + env: + RELEASE_TAG: ${{ steps.version.outputs.tag }} + run: | + set -euo pipefail + git tag -a "$RELEASE_TAG" -m "Release $RELEASE_TAG" + git push origin "$RELEASE_TAG" + + - name: Create Gitea release + if: steps.changes.outputs.changed == 'true' + shell: bash + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + RELEASE_TAG: ${{ steps.version.outputs.tag }} + run: | + set -euo pipefail + server_url="${GITEA_SERVER_URL:-${GITHUB_SERVER_URL:-}}" + repository="${GITEA_REPOSITORY:-${GITHUB_REPOSITORY:-}}" + if [[ -z "$server_url" || -z "$repository" ]]; then + echo "GITEA_SERVER_URL and GITEA_REPOSITORY are required" >&2 + exit 1 + fi + curl --fail-with-body --silent --show-error \ + --request POST \ + --header "Authorization: token ${GITEA_TOKEN}" \ + --header "Content-Type: application/json" \ + "${server_url%/}/api/v1/repos/${repository}/releases" \ + --data "$(printf '{"tag_name":"%s","name":"%s","body":"Automated Go dependency update."}' "$RELEASE_TAG" "$RELEASE_TAG")" diff --git a/README.md b/README.md index 092e0e2..fecb689 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,23 @@ * pb协议文件生成命令 `protoc --proto_path=pkg/websocket/codec/protobuf/protocol --go_out=pkg/websocket/codec/protobuf/protocol --go_opt=paths=source_relative base.proto` + +## 自动更新依赖并发布版本 + +仓库包含 `.gitea/workflows/dependency-release.yml`。启用 Gitea Actions 后,它会在每周日 03:17 UTC(北京时间 11:17)运行,也可以在 Actions 页面手动运行: + +1. 使用 `go get -u ./...` 和 `go mod tidy` 更新依赖。 +2. 执行 `go test ./...`,测试失败时不会提交或发布。 +3. 有依赖变更时提交 `go.mod`、`go.sum`,创建下一个 `vMAJOR.MINOR.PATCH` 补丁版本标签,并创建 Gitea Release。 + +### 树莓派上的 Runner 配置 + +在树莓派上安装并注册 Gitea `act_runner`,注册标签中包含 `self-hosted`,并确保 Runner 环境有 Git、Go、Node.js、`curl`。工作流使用 `runs-on: self-hosted`,因此不会依赖 GitHub-hosted Runner。 + +在仓库的 **Settings -> Secrets -> Actions** 中新增: + +- `GITEA_TOKEN`:一个能读写仓库内容、创建标签和 Release 的 Gitea Personal Access Token。 + +如果默认分支启用了保护规则,还需要允许该 Token 所属账号直接推送。 + +首次使用时建议先在仓库中手动创建一个 `v0.1.0` 以后的 SemVer 标签;如果没有任何 `vMAJOR.MINOR.PATCH` 标签,工作流会从 `v0.1.0` 开始。 diff --git a/pkg/captcha/base64.go b/pkg/captcha/base64.go index 043096a..58b565c 100644 --- a/pkg/captcha/base64.go +++ b/pkg/captcha/base64.go @@ -1,6 +1,7 @@ package captcha import ( + "context" "fmt" "gitea.bvbej.com/bvbej/base-golang/pkg/cache" "github.com/mojocn/base64Captcha" @@ -12,7 +13,7 @@ import ( var _ base64Captcha.Store = (*store)(nil) type store struct { - cache cache.Repo + cache cache.RedisRepo ttl time.Duration logger *zap.Logger ns string @@ -20,7 +21,7 @@ type store struct { } func (s *store) Set(id string, value string) error { - err := s.cache.Set(fmt.Sprintf("%s%s%s", s.ns, s.prefix, id), value, s.ttl) + err := s.cache.Set(context.Background(), fmt.Sprintf("%s%s%s", s.ns, s.prefix, id), value, s.ttl) if err != nil { return err } @@ -28,9 +29,9 @@ func (s *store) Set(id string, value string) error { } func (s *store) Get(id string, clear bool) string { - value, err := s.cache.Get(fmt.Sprintf("%s%s%s", s.ns, s.prefix, id)) + value, err := s.cache.Get(context.Background(), fmt.Sprintf("%s%s%s", s.ns, s.prefix, id)) if err == nil && clear { - s.cache.Del(fmt.Sprintf("%s%s%s", s.ns, s.prefix, id)) + _, _ = s.cache.Del(context.Background(), fmt.Sprintf("%s%s%s", s.ns, s.prefix, id)) } return value } @@ -43,7 +44,7 @@ func (s *store) Verify(id, answer string, clear bool) bool { return strings.ToLower(value) == strings.ToLower(answer) } -func NewStore(cache cache.Repo, ttl time.Duration, namespace string) base64Captcha.Store { +func NewStore(cache cache.RedisRepo, ttl time.Duration, namespace string) base64Captcha.Store { return &store{ cache: cache, ttl: ttl,