Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Binance API credentials
BINANCE_API_KEY=your_api_key_here
BINANCE_SECRET=your_secret_key_here
93 changes: 93 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: BinanceBot Build Pipeline

on:
workflow_dispatch:
push:
pull_request:
branches: [ "master" ]

permissions:
contents: read

concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
working-directory: ./src

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
configuration: [Debug, Release]
dotnet-version: ["9.0.x"]

steps:
- uses: actions/checkout@v4

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}
cache: true
cache-dependency-path: |
src/**/*.csproj
src/**/global.json
src/**/NuGet.Config

- name: .NET info
run: dotnet --info

- name: Restore dependencies
run: dotnet restore --verbosity minimal

- name: Build
run: dotnet build --no-restore --configuration ${{ matrix.configuration }} --nologo

- name: Test
run: dotnet test --no-build --configuration ${{ matrix.configuration }} --collect:"XPlat Code Coverage" --logger "trx;LogFileName=test_results.trx" || echo "No tests found"

- name: Upload test results (TRX)
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.configuration }}
path: |
**/TestResults/**/*.trx
if-no-files-found: warn

- name: Upload code coverage (Cobertura)
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-cobertura-${{ matrix.configuration }}
path: |
**/TestResults/**/coverage.cobertura.xml
if-no-files-found: warn

- name: Publish artifacts (Release only)
if: matrix.configuration == 'Release'
run: |
dotnet publish BinanceBot.MarketBot.Console/BinanceBot.MarketBot.Console.csproj \
-c Release \
-o ./publish/MarketBot \
--no-build
dotnet publish BinanceBot.MarketViewer.Console/BinanceBot.MarketViewer.Console.csproj \
-c Release \
-o ./publish/MarketViewer \
--no-build

- name: Upload published artifacts
if: matrix.configuration == 'Release'
uses: actions/upload-artifact@v4
with:
name: binance-bot-binaries
path: |
src/publish/MarketBot/
src/publish/MarketViewer/
retention-days: 7
92 changes: 92 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: BinanceBot Release Pipeline

on:
workflow_dispatch:
push:
branches: [ "master" ]
tags:
- 'v*.*.*'

permissions:
contents: read
packages: write
id-token: write

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-push-images:

name: Build and push Docker images

runs-on: ubuntu-latest

env:
DOCKER_BUILDKIT: 1

strategy:
matrix:
app:
- name: marketbot
dockerfile: Dockerfile.marketbot
project: BinanceBot.MarketBot.Console
- name: marketviewer
dockerfile: Dockerfile.marketviewer
project: BinanceBot.MarketViewer.Console

steps:
- name: Check out the repo
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}

- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ github.repository_owner }}/binancebot-${{ matrix.app.name }}
tags: |
type=raw,value=latest
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
labels: |
org.opencontainers.image.source=${{ github.repository }}
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.title=BinanceBot ${{ matrix.app.name }}
org.opencontainers.image.description=BinanceBot ${{ matrix.app.project }}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./${{ matrix.app.dockerfile }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.app.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.app.name }}
build-args: |
PROJECT_NAME=${{ matrix.app.project }}

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.31.0
with:
image-ref: ghcr.io/${{ github.repository_owner }}/binancebot-${{ matrix.app.name }}:latest
format: 'table'
ignore-unfixed: true
vuln-type: 'library'
severity: 'CRITICAL,HIGH'
51 changes: 51 additions & 0 deletions Dockerfile.marketbot
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
ARG BUILD_CONFIGURATION=Release
ARG PROJECT_NAME=BinanceBot.MarketBot.Console
WORKDIR /src

# Copy solution and project files for better layer caching
COPY src/*.sln ./
COPY src/BinanceBot.Market/*.csproj ./BinanceBot.Market/
COPY src/BinanceBot.MarketBot.Console/*.csproj ./BinanceBot.MarketBot.Console/
COPY src/BinanceBot.MarketViewer.Console/*.csproj ./BinanceBot.MarketViewer.Console/

# Restore dependencies as a separate layer
RUN dotnet restore "${PROJECT_NAME}/${PROJECT_NAME}.csproj" \
--runtime linux-musl-x64

# Copy remaining source files
COPY src/. ./

# Build and publish
WORKDIR /src/${PROJECT_NAME}
RUN dotnet publish "${PROJECT_NAME}.csproj" \
-c $BUILD_CONFIGURATION \
-o /app/publish \
--no-restore \
--runtime linux-musl-x64 \
--self-contained false \
/p:UseAppHost=false

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
ARG PROJECT_NAME=BinanceBot.MarketBot.Console
WORKDIR /app

# Create non-root user
RUN addgroup -g 1000 appuser && \
adduser -u 1000 -G appuser -s /bin/sh -D appuser && \
chown -R appuser:appuser /app

# Copy published output
COPY --from=build --chown=appuser:appuser /app/publish .

# Security: Run as non-root
USER appuser

# Set environment variables
ENV DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

# Note: Set BINANCE_API_KEY and BINANCE_SECRET via environment variables or .env file at runtime
ENTRYPOINT dotnet "${PROJECT_NAME}.dll"
51 changes: 51 additions & 0 deletions Dockerfile.marketviewer
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
ARG BUILD_CONFIGURATION=Release
ARG PROJECT_NAME=BinanceBot.MarketViewer.Console
WORKDIR /src

# Copy solution and project files for better layer caching
COPY src/*.sln ./
COPY src/BinanceBot.Market/*.csproj ./BinanceBot.Market/
COPY src/BinanceBot.MarketBot.Console/*.csproj ./BinanceBot.MarketBot.Console/
COPY src/BinanceBot.MarketViewer.Console/*.csproj ./BinanceBot.MarketViewer.Console/

# Restore dependencies as a separate layer
RUN dotnet restore "${PROJECT_NAME}/${PROJECT_NAME}.csproj" \
--runtime linux-musl-x64

# Copy remaining source files
COPY src/. ./

# Build and publish
WORKDIR /src/${PROJECT_NAME}
RUN dotnet publish "${PROJECT_NAME}.csproj" \
-c $BUILD_CONFIGURATION \
-o /app/publish \
--no-restore \
--runtime linux-musl-x64 \
--self-contained false \
/p:UseAppHost=false

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
ARG PROJECT_NAME=BinanceBot.MarketViewer.Console
WORKDIR /app

# Create non-root user
RUN addgroup -g 1000 appuser && \
adduser -u 1000 -G appuser -s /bin/sh -D appuser && \
chown -R appuser:appuser /app

# Copy published output
COPY --from=build --chown=appuser:appuser /app/publish .

# Security: Run as non-root
USER appuser

# Set environment variables
ENV DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

# Note: Set BINANCE_API_KEY and BINANCE_SECRET via environment variables or .env file at runtime
ENTRYPOINT dotnet "${PROJECT_NAME}.dll"
28 changes: 28 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
services:
marketbot:
image: ghcr.io/${GITHUB_REPOSITORY_OWNER:-codez0mb1e}/binancebot-marketbot:latest
container_name: binancebot-marketbot
restart: unless-stopped
environment:
- BINANCE_API_KEY=${BINANCE_API_KEY}
- BINANCE_SECRET=${BINANCE_SECRET}
env_file:
- .env
networks:
- binancebot-network

marketviewer:
image: ghcr.io/${GITHUB_REPOSITORY_OWNER:-codez0mb1e}/binancebot-marketviewer:latest
container_name: binancebot-marketviewer
restart: unless-stopped
environment:
- BINANCE_API_KEY=${BINANCE_API_KEY}
- BINANCE_SECRET=${BINANCE_SECRET}
env_file:
- .env
networks:
- binancebot-network

networks:
binancebot-network:
driver: bridge
5 changes: 0 additions & 5 deletions src/BinanceBot.MarketBot.Console/.env.example

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<Target Name="CheckEnvFileExists" BeforeTargets="Build">
<Error Condition="!Exists('.env')" Text="The .env file is missing. Please create it from .env.example before building." />
</Target>
</Project>
5 changes: 0 additions & 5 deletions src/BinanceBot.MarketViewer.Console/.env.example

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="CheckEnvFileExists" BeforeTargets="Build">
<Error Condition="!Exists('.env')" Text="Missing .env file. Please create .env from .env.example before building." />
</Target>
</Project>
Loading