4 Commits
v4.1.0 ... v4

Author SHA1 Message Date
43e095a00a changed embedded mutex to private field (#209)
Some checks failed
coverage / build (push) Failing after 16m47s
test / test (push) Successful in 2m56s
sync / sync (push) Successful in 10s
2025-05-25 01:14:42 +03:00
abd66c79df [v4] update ci (#207)
All checks were successful
sync / sync (push) Successful in 17s
* update ci

* added commit hash check to avoid unnecessary repository cloning
2025-05-05 18:01:36 +03:00
vtolstov
9af830532b Apply Code Coverage Badge
All checks were successful
coverage / build (push) Successful in 7m30s
test / test (push) Successful in 10m29s
sync / sync (push) Successful in 13s
2025-04-27 18:39:33 +00:00
443ea4bdb9 improve sync
Some checks failed
sync / sync (push) Has been cancelled
coverage / build (push) Has been cancelled
test / test (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-04-27 21:38:54 +03:00
13 changed files with 200 additions and 105 deletions

53
.github/workflows/job_coverage.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: coverage
on:
push:
branches: [ main, v3, v4 ]
paths-ignore:
- '.github/**'
- '.gitea/**'
pull_request:
branches: [ main, v3, v4 ]
jobs:
build:
if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: test coverage
run: |
go test -v -cover ./... -covermode=count -coverprofile coverage.out -coverpkg ./...
go tool cover -func coverage.out -o coverage.out
- name: coverage badge
uses: tj-actions/coverage-badge-go@v2
with:
green: 80
filename: coverage.out
- uses: stefanzweifel/git-auto-commit-action@v4
name: autocommit
with:
commit_message: Apply Code Coverage Badge
skip_fetch: false
skip_checkout: false
file_pattern: ./README.md
- name: push
if: steps.auto-commit-action.outputs.changes_detected == 'true'
uses: ad-m/github-push-action@master
with:
github_token: ${{ github.token }}
branch: ${{ github.ref }}

View File

@@ -3,10 +3,10 @@ name: lint
on: on:
pull_request: pull_request:
types: [opened, reopened, synchronize] types: [opened, reopened, synchronize]
branches: branches: [ master, v3, v4 ]
- master paths-ignore:
- v3 - '.github/**'
- v4 - '.gitea/**'
jobs: jobs:
lint: lint:
@@ -24,6 +24,6 @@ jobs:
- name: setup deps - name: setup deps
run: go get -v ./... run: go get -v ./...
- name: run lint - name: run lint
uses: https://github.com/golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
version: 'latest' version: 'latest'

94
.github/workflows/job_sync.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: sync
on:
schedule:
- cron: '*/5 * * * *'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
sync:
if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: init
run: |
git config --global user.email "vtolstov <vtolstov@users.noreply.github.com>"
git config --global user.name "github-actions[bot]"
echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc
echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc
- name: check master
id: check_master
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync master
if: steps.check_master.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream master
git push upstream master --progress
git push origin master --progress
cd ../
rm -rf repo
- name: check v3
id: check_v3
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v3
if: steps.check_v3.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream v3
git push upstream v3 --progress
git push origin v3 --progress
cd ../
rm -rf repo
- name: check v4
id: check_v4
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v4
if: steps.check_v4.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream v4
git push upstream v4 --progress
git push origin v4 --progress
cd ../
rm -rf repo

View File

@@ -3,15 +3,12 @@ name: test
on: on:
pull_request: pull_request:
types: [opened, reopened, synchronize] types: [opened, reopened, synchronize]
branches: branches: [ master, v3, v4 ]
- master
- v3
- v4
push: push:
branches: branches: [ master, v3, v4 ]
- master paths-ignore:
- v3 - '.github/**'
- v4 - '.gitea/**'
jobs: jobs:
test: test:

View File

@@ -3,15 +3,12 @@ name: test
on: on:
pull_request: pull_request:
types: [opened, reopened, synchronize] types: [opened, reopened, synchronize]
branches: branches: [ master, v3, v4 ]
- master
- v3
- v4
push: push:
branches: branches: [ master, v3, v4 ]
- master paths-ignore:
- v3 - '.github/**'
- v4 - '.gitea/**'
jobs: jobs:
test: test:
@@ -35,19 +32,19 @@ jobs:
go-version: 'stable' go-version: 'stable'
- name: setup go work - name: setup go work
env: env:
GOWORK: /workspace/${{ github.repository_owner }}/go.work GOWORK: ${{ github.workspace }}/go.work
run: | run: |
go work init go work init
go work use . go work use .
go work use micro-tests go work use micro-tests
- name: setup deps - name: setup deps
env: env:
GOWORK: /workspace/${{ github.repository_owner }}/go.work GOWORK: ${{ github.workspace }}/go.work
run: go get -v ./... run: go get -v ./...
- name: run tests - name: run tests
env: env:
INTEGRATION_TESTS: yes INTEGRATION_TESTS: yes
GOWORK: /workspace/${{ github.repository_owner }}/go.work GOWORK: ${{ github.workspace }}/go.work
run: | run: |
cd micro-tests cd micro-tests
go test -mod readonly -v ./... || true go test -mod readonly -v ./... || true

View File

@@ -1,44 +1,5 @@
run: run:
concurrency: 4 concurrency: 8
deadline: 5m timeout: 5m
issues-exit-code: 1 issues-exit-code: 1
tests: true tests: true
linters-settings:
govet:
check-shadowing: true
enable:
- fieldalignment
linters:
enable:
- govet
- deadcode
- errcheck
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
- bodyclose
- gci
- goconst
- gocritic
- gosimple
- gofmt
- gofumpt
- goimports
- golint
- gosec
- makezero
- misspell
- nakedret
- nestif
- nilerr
- noctx
- prealloc
- unconvert
- unparam
disable-all: false

View File

@@ -1,4 +1,5 @@
# TCP Server # TCP Server
![Coverage](https://img.shields.io/badge/Coverage-0.0%25-red)
The TCP micro.Server implemtation. It's a partial implementation which strips out codecs, transports, etc but enables you The TCP micro.Server implemtation. It's a partial implementation which strips out codecs, transports, etc but enables you
to create a TCP Server that could potentially be used for some TCP based API services. to create a TCP Server that could potentially be used for some TCP based API services.

62
tcp.go
View File

@@ -10,7 +10,6 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/register" "go.unistack.org/micro/v4/register"
"go.unistack.org/micro/v4/server" "go.unistack.org/micro/v4/server"
@@ -25,7 +24,7 @@ type Server struct {
rsvc *register.Service rsvc *register.Service
exit chan chan error exit chan chan error
opts server.Options opts server.Options
sync.RWMutex mu sync.RWMutex
registered bool registered bool
init bool init bool
stateLive *atomic.Uint32 stateLive *atomic.Uint32
@@ -45,16 +44,9 @@ func (h *Server) Health() bool {
return h.stateHealth.Load() == 1 return h.stateHealth.Load() == 1
} }
func (h *Server) newCodec(ct string) (codec.Codec, error) {
if cf, ok := h.opts.Codecs[ct]; ok {
return cf, nil
}
return nil, codec.ErrUnknownContentType
}
func (h *Server) Options() server.Options { func (h *Server) Options() server.Options {
h.RLock() h.mu.RLock()
defer h.RUnlock() defer h.mu.RUnlock()
return h.opts return h.opts
} }
@@ -65,11 +57,11 @@ func (h *Server) Init(opts ...server.Option) error {
if len(opts) == 0 && h.init { if len(opts) == 0 && h.init {
return nil return nil
} }
h.Lock() h.mu.Lock()
for _, o := range opts { for _, o := range opts {
o(&h.opts) o(&h.opts)
} }
h.Unlock() h.mu.Unlock()
if err := h.opts.Register.Init(); err != nil { if err := h.opts.Register.Init(); err != nil {
return err return err
@@ -91,9 +83,9 @@ func (h *Server) Init(opts ...server.Option) error {
} }
func (h *Server) Handle(handler server.Handler) error { func (h *Server) Handle(handler server.Handler) error {
h.Lock() h.mu.Lock()
h.hd = handler h.hd = handler
h.Unlock() h.mu.Unlock()
return nil return nil
} }
@@ -113,11 +105,11 @@ func (h *Server) NewHandler(handler interface{}, opts ...server.HandlerOption) s
} }
func (h *Server) Register() error { func (h *Server) Register() error {
h.Lock() h.mu.Lock()
config := h.opts config := h.opts
rsvc := h.rsvc rsvc := h.rsvc
h.Unlock() h.mu.Unlock()
// if service already filled, reuse it and return early // if service already filled, reuse it and return early
if rsvc != nil { if rsvc != nil {
@@ -132,9 +124,9 @@ func (h *Server) Register() error {
return err return err
} }
h.RLock() h.mu.RLock()
registered := h.registered registered := h.registered
h.RUnlock() h.mu.RUnlock()
if !registered { if !registered {
if config.Logger.V(logger.InfoLevel) { if config.Logger.V(logger.InfoLevel) {
@@ -152,8 +144,8 @@ func (h *Server) Register() error {
return nil return nil
} }
h.Lock() h.mu.Lock()
defer h.Unlock() defer h.mu.Unlock()
if h.registered { if h.registered {
return nil return nil
@@ -166,9 +158,9 @@ func (h *Server) Register() error {
} }
func (h *Server) Deregister() error { func (h *Server) Deregister() error {
h.Lock() h.mu.Lock()
config := h.opts config := h.opts
h.Unlock() h.mu.Unlock()
service, err := server.NewRegisterService(h) service, err := server.NewRegisterService(h)
if err != nil { if err != nil {
@@ -183,14 +175,14 @@ func (h *Server) Deregister() error {
return err return err
} }
h.Lock() h.mu.Lock()
if !h.registered { if !h.registered {
h.Unlock() h.mu.Unlock()
return nil return nil
} }
h.registered = false h.registered = false
h.Unlock() h.mu.Unlock()
return nil return nil
} }
@@ -208,10 +200,10 @@ func (h *Server) getListener() net.Listener {
} }
func (h *Server) Start() error { func (h *Server) Start() error {
h.RLock() h.mu.RLock()
config := h.opts config := h.opts
hd := h.hd.Handler() hd := h.hd.Handler()
h.RUnlock() h.mu.RUnlock()
var err error var err error
var ts net.Listener var ts net.Listener
@@ -244,9 +236,9 @@ func (h *Server) Start() error {
config.Logger.Info(config.Context, "Listening on "+ts.Addr().String()) config.Logger.Info(config.Context, "Listening on "+ts.Addr().String())
} }
h.Lock() h.mu.Lock()
h.opts.Address = ts.Addr().String() h.opts.Address = ts.Addr().String()
h.Unlock() h.mu.Unlock()
if err = config.Broker.Connect(config.Context); err != nil { if err = config.Broker.Connect(config.Context); err != nil {
return err return err
@@ -283,9 +275,9 @@ func (h *Server) Start() error {
select { select {
// register self on interval // register self on interval
case <-t.C: case <-t.C:
h.RLock() h.mu.RLock()
registered := h.registered registered := h.registered
h.RUnlock() h.mu.RUnlock()
rerr := h.opts.RegisterCheck(h.opts.Context) rerr := h.opts.RegisterCheck(h.opts.Context)
// nolint: nestif // nolint: nestif
if rerr != nil && registered { if rerr != nil && registered {
@@ -359,9 +351,9 @@ func (h *Server) Name() string {
func (h *Server) serve(ln net.Listener, hd Handler) { func (h *Server) serve(ln net.Listener, hd Handler) {
var tempDelay time.Duration // how long to sleep on accept failure var tempDelay time.Duration // how long to sleep on accept failure
h.RLock() h.mu.RLock()
config := h.opts config := h.opts
h.RUnlock() h.mu.RUnlock()
for { for {
c, err := ln.Accept() c, err := ln.Accept()
// nolint: nestif // nolint: nestif
@@ -371,7 +363,7 @@ func (h *Server) serve(ln net.Listener, hd Handler) {
return return
default: default:
} }
if ne, ok := err.(net.Error); ok && ne.Temporary() { if ne, ok := err.(net.Error); ok && ne.Temporary() { // nolint:staticcheck
if tempDelay == 0 { if tempDelay == 0 {
tempDelay = 5 * time.Millisecond tempDelay = 5 * time.Millisecond
} else { } else {