diff --git a/.github/workflows/api-image.yaml b/.github/workflows/api-image.yaml new file mode 100644 index 0000000..df6dade --- /dev/null +++ b/.github/workflows/api-image.yaml @@ -0,0 +1,81 @@ +name: Build and push API image + +on: + push: + tags: + - "api/v*.*.*" + branches: + - main + paths: + - "api/**" + - "shared/**" + - "go.work" + - "go.work.sum" + - ".github/workflows/api-image.yml" + pull_request: + branches: + - main + paths: + - "api/**" + - "shared/**" + - "go.work" + - "go.work.sum" + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/control-api + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup docker buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to ghcr + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=match,pattern=api/v(.*),group=1 + type=sha,prefix=sha- + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: api/Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=api + cache-to: type=gha,mode=max,scope=api + + notify: + needs: [build-and-push] + if: always() + uses: ./.github/workflows/notify-discord.yml + with: + status: ${{ contains(needs.*.result, 'failure') && 'failure' || 'success' }} + pipeline: "api-image" + secrets: inherit diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..882a223 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,55 @@ +name: ci + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + GO_VERSION: "1.26.4" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v6 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: | + go.work.sum + api/go.sum + tui/go.sum + shared/go.sum + + - name: build + run: go build -v ./api/... ./tui/... ./shared/... + + lint: + runs-on: ubuntu-latest + strategy: + matrix: + module: [api, tui, shared] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v6 + with: + go-version: ${{ env.GO_VERSION }} + + - name: lint (${{ matrix.module }}) + uses: golangci/golangci-lint-action@v8 + with: + version: latest + working-directory: ${{ matrix.module }} + + notify: + needs: [build, lint] + if: always() + uses: ./.github/workflows/notify-discord.yml + with: + status: ${{ contains(needs.*.result, 'failure') && 'failure' || 'success' }} + pipeline: "ci" + secrets: inherit diff --git a/.github/workflows/discord-notify.yaml b/.github/workflows/discord-notify.yaml new file mode 100644 index 0000000..f12bc3a --- /dev/null +++ b/.github/workflows/discord-notify.yaml @@ -0,0 +1,38 @@ +name: notify-discord + +on: + workflow_call: + inputs: + status: + required: true + type: string + pipeline: + required: true + type: string + secrets: + DISCORD_WEBHOOK_URL: + required: true + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Discord Notify + env: + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + STATUS: ${{ inputs.status }} + run: | + COLOR=$([[ "$STATUS" == "success" ]] && echo "3066993" || echo "15158332") + curl -X POST "$DISCORD_WEBHOOK_URL" \ + -H "Content-Type: application/json" \ + -d '{ + "embeds": [{ + "title": "'"${{ inputs.pipeline }}"' — '"$STATUS"'", + "color": '"$COLOR"', + "fields": [ + { "name": "Repo", "value": "${{ github.repository }}", "inline": true }, + { "name": "Branch", "value": "${{ github.ref_name }}", "inline": true }, + { "name": "Commit", "value": "${{ github.sha }}", "inline": false } + ] + }] + }' diff --git a/.github/workflows/tui-image.yaml b/.github/workflows/tui-image.yaml new file mode 100644 index 0000000..7c7208f --- /dev/null +++ b/.github/workflows/tui-image.yaml @@ -0,0 +1,81 @@ +name: Build and push TUI image + +on: + push: + tags: + - "tui/v*.*.*" + branches: + - main + paths: + - "tui/**" + - "shared/**" + - "go.work" + - "go.work.sum" + - ".github/workflows/tui-image.yml" + pull_request: + branches: + - main + paths: + - "tui/**" + - "shared/**" + - "go.work" + - "go.work.sum" + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/control-tui + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup docker buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to ghcr + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=match,pattern=tui/v(.*),group=1 + type=sha,prefix=sha- + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: tui/Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=tui + cache-to: type=gha,mode=max,scope=tui + + notify: + needs: [build-and-push] + if: always() + uses: ./.github/workflows/notify-discord.yml + with: + status: ${{ contains(needs.*.result, 'failure') && 'failure' || 'success' }} + pipeline: "tui-image" + secrets: inherit diff --git a/api/internal/hub/hub.go b/api/internal/hub/hub.go index 595ff4e..a78e691 100644 --- a/api/internal/hub/hub.go +++ b/api/internal/hub/hub.go @@ -136,6 +136,10 @@ func (h *Hub) Run(ctx context.Context) { } func (h *Hub) broadcastToProject(ctx context.Context, msg *model.WSMessage) { + if h.redis != nil { + h.redis.Publish(ctx, "ws:broadcast", msg) + } + data, err := json.Marshal(msg) if err != nil { return @@ -153,9 +157,6 @@ func (h *Hub) broadcastToProject(ctx context.Context, msg *model.WSMessage) { fmt.Printf("[HUB] client %s send buffer full, dropped\n", client.ID) } } - if h.redis != nil { - h.redis.Publish(ctx, "ws:broadcast", msg) - } } // listenRedis subscribe to redis db to access cached content on change