Compare commits

...

39 Commits

Author SHA1 Message Date
ruberoid
77d1585ed5 docs: Update CLAUDE.md with prepare-build.sh requirement
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-28 00:33:25 +04:00
ruberoid
e03eae219d chore: Update submodule references after Dockerfile fix
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-28 00:33:07 +04:00
ruberoid
35bb116bfd fix: Remove additional_contexts and revert Kaniko changes
- Removed additional_contexts from docker-compose.yml (not supported by Kaniko)
- Reverted Kaniko image from v1.23.2 back to :debug
- Removed --build-context flags (not supported)
- Added prepare-build.sh script to copy nuget.config before builds
2025-10-28 00:30:17 +04:00
ruberoid
012d81bd66 fix: Update Kaniko to v1.23.2 to support --build-context flag
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
The previous fix added --build-context flag but the old Kaniko version
(executor:debug) doesn't support it. Updated to v1.23.2-debug which
includes support for --build-context flag (added in v1.9.0).
2025-10-28 00:16:39 +04:00
ruberoid
5531a42ed7 fix: Add --build-context=rootconfig flag to Kaniko builds
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
The recent docker-compose.yml change added additional_contexts with 'rootconfig'
reference. Dockerfiles now use 'COPY --from=rootconfig nuget.config' which works
with Docker Compose but breaks Kaniko builds.

Added --build-context=rootconfig=. flag to all Kaniko executor commands to provide
the named build context that Dockerfiles expect. This matches Docker Compose
behavior and fixes the 'rootconfig' image pull error.
2025-10-28 00:04:59 +04:00
ruberoid
36e574ed3a Merge branch 'main' of https://gitea.musk.fun/nocr/flea
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-27 23:57:09 +04:00
ruberoid
7172fe3659 fixed nuget.config copy to additional dockerfile context.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
2025-10-27 23:53:13 +04:00
ruberoid
9a7de46061 Новая модель, новые каноны контекста!
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-27 23:09:02 +04:00
ruberoid
97798df417 docs: Add detailed CI/CD workflow examples to CLAUDE.md
All checks were successful
continuous-integration/drone/push Build is passing
- Add 'How to trigger' sections with exact git commands for each pipeline
- Include practical examples: feature testing, contract publishing, releases
- Add 'Common CI/CD Workflows' section with real-world scenarios
- Document emergency rollback procedure
- Clarify contract-only service names (telegram_listener vs telegram-listener)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 12:44:23 +04:00
ruberoid
029319e7c1 docs: Add NuGet Package Management section to CLAUDE.md
- Document Central Package Management (CPM) with Package Source Mapping
- Explain package source configuration (nuget.org vs musk private feed)
- Describe how nuget.config is used across different environments
- Add troubleshooting context for NU1507 warning resolution

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 12:40:43 +04:00
ruberoid
b8ba3df310 copy nuget.config while deploy to every submodule.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-17 12:26:39 +04:00
ruberoid
e196a79174 Removed echoed package sourcing to nuget.config file attached.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
2025-10-17 12:11:58 +04:00
ruberoid
5407479361 deploy_only: Deploy latest
All checks were successful
continuous-integration/drone/tag Build is passing
2025-10-17 11:05:06 +04:00
ruberoid
af5d46e4e8 fixed for only latest usages to deploy. 2025-10-17 11:04:58 +04:00
ruberoid
718a4ff1c9 deploy_only: Deploy latest
All checks were successful
continuous-integration/drone/tag Build is passing
2025-10-17 11:02:06 +04:00
ruberoid
5e6d3e968d fixed for only latest usages to deploy. 2025-10-17 11:01:57 +04:00
ruberoid
82ade98af3 deploy_only: 0.7.35
Some checks failed
continuous-integration/drone/tag Build is failing
2025-10-17 10:59:05 +04:00
ruberoid
1be50c3eeb deploy_only: 0.7.35
Some checks failed
continuous-integration/drone/tag Build is failing
2025-10-17 10:56:40 +04:00
ruberoid
8a3c6b50e3 deploy_only: fixed deploy to k8s. added secret kube.
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build was killed
2025-10-17 10:49:47 +04:00
ruberoid
ce5540185b Returned back dockerfiles source as echo commands.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
2025-10-16 23:35:48 +04:00
ruberoid
47aaf30de1 Returned back curl to app images.
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/tag Build is failing
2025-10-16 23:12:22 +04:00
ruberoid
4ec038887c Optimized Dockerfiles in submodules. 2025-10-16 23:04:49 +04:00
ruberoid
b8a3814deb Made full release sequental vs parallel.
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build was killed
2025-10-16 22:50:54 +04:00
ruberoid
6ddf2ff0b5 Modified .dockerignore files for more optimizations 2025-10-16 22:47:48 +04:00
ruberoid
182178ebe1 removed branch main for trigger pipeline MAIN.
Some checks reported errors
continuous-integration/drone/tag Build was killed
2025-10-16 22:13:24 +04:00
ruberoid
15519f2377 Обновил документацию. 2025-10-16 22:11:06 +04:00
ruberoid
32b0253981 drone fix 2. validated through python3 yaml.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-16 21:56:40 +04:00
ruberoid
e9d2bcdb7a drone fix
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 21:53:11 +04:00
ruberoid
a9c846d793 fix 9
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 18:17:26 +04:00
ruberoid
59b5226d47 fix 8
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-16 18:12:45 +04:00
ruberoid
e0c7f8f38a fix drone 6
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 18:09:37 +04:00
ruberoid
7b3571cd5b .drone 4 fix
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 18:08:47 +04:00
ruberoid
f6fe2eb54e three more fix drone
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-16 18:07:27 +04:00
ruberoid
45c78ed008 more fix drone
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 18:07:00 +04:00
ruberoid
a2d01c84a9 merged .drone
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 18:06:12 +04:00
ruberoid
223e58c8d2 4/5 cicd build stages. 2025-10-16 18:00:23 +04:00
604ac1f507 issues/7 (#8)
Co-authored-by: ruberoid <dcharushnikov@gmail.com>
Reviewed-on: #8
2025-10-16 13:58:17 +00:00
ruberoid
cc7a5d3285 Just first build cicd left temporaryly.
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-16 17:56:07 +04:00
ruberoid
18d8f59673 Доабвил скрипт автокоммита субмодулей. #7
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2025-10-16 17:52:31 +04:00
14 changed files with 1952 additions and 521 deletions

View File

@ -53,7 +53,8 @@ steps:
path: /root/.nuget/packages path: /root/.nuget/packages
commands: commands:
- cd flea - cd flea
- dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json - mkdir -p /root/.nuget/NuGet
- cp nuget.config /root/.nuget/NuGet/NuGet.Config
- echo "🔄 Restoring all projects..." - echo "🔄 Restoring all projects..."
- dotnet restore telegram-listener/Nocr.TelegramListener.sln - dotnet restore telegram-listener/Nocr.TelegramListener.sln
- dotnet restore telegram-client/Nocr.TelegramClient.sln - dotnet restore telegram-client/Nocr.TelegramClient.sln
@ -94,6 +95,7 @@ steps:
depends_on: depends_on:
- dotnet-build - dotnet-build
- docker - docker
--- ---
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 📝 Pipeline 2: Main Branch Validation # 📝 Pipeline 2: Main Branch Validation
@ -146,7 +148,8 @@ steps:
path: /root/.nuget/packages path: /root/.nuget/packages
commands: commands:
- cd flea - cd flea
- dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json - mkdir -p /root/.nuget/NuGet
- cp nuget.config /root/.nuget/NuGet/NuGet.Config
- dotnet restore telegram-listener/Nocr.TelegramListener.sln - dotnet restore telegram-listener/Nocr.TelegramListener.sln
- dotnet restore telegram-client/Nocr.TelegramClient.sln - dotnet restore telegram-client/Nocr.TelegramClient.sln
- dotnet restore text-matcher/Nocr.TextMatcher.sln - dotnet restore text-matcher/Nocr.TextMatcher.sln
@ -201,8 +204,6 @@ trigger:
- refs/tags/* - refs/tags/*
event: event:
- tag - tag
branch:
- issues/*
clone: clone:
disable: true disable: true
@ -223,10 +224,10 @@ steps:
- name: check-trigger - name: check-trigger
image: alpine/git image: alpine/git
commands: commands:
- cd flea
- COMMIT_MSG=$(git log -1 --pretty=%B)
- echo "Commit message - $COMMIT_MSG"
- | - |
cd flea
COMMIT_MSG=$(git log -1 --pretty=%B)
echo "Commit message - $COMMIT_MSG"
if echo "$COMMIT_MSG" | grep -q "contracts_only:"; then if echo "$COMMIT_MSG" | grep -q "contracts_only:"; then
echo "✅ contracts_only detected, proceeding..." echo "✅ contracts_only detected, proceeding..."
exit 0 exit 0
@ -246,12 +247,13 @@ steps:
NUGETAPIKEY: NUGETAPIKEY:
from_secret: nuget_musk_api_key from_secret: nuget_musk_api_key
commands: commands:
- cd flea
- COMMIT_MSG=$(git log -1 --pretty=%B)
- | - |
cd flea
COMMIT_MSG=$(git log -1 --pretty=%B)
if echo "$COMMIT_MSG" | grep -q "contracts_only:telegram_listener"; then if echo "$COMMIT_MSG" | grep -q "contracts_only:telegram_listener"; then
echo "📦 Publishing telegram-listener contracts..." echo "📦 Publishing telegram-listener contracts..."
dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json mkdir -p /root/.nuget/NuGet
cp nuget.config /root/.nuget/NuGet/NuGet.Config
dotnet pack telegram-listener/Nocr.TelegramListener.sln -o ./bin -p:PackageVersion=${DRONE_TAG} dotnet pack telegram-listener/Nocr.TelegramListener.sln -o ./bin -p:PackageVersion=${DRONE_TAG}
dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate
else else
@ -269,12 +271,13 @@ steps:
NUGETAPIKEY: NUGETAPIKEY:
from_secret: nuget_musk_api_key from_secret: nuget_musk_api_key
commands: commands:
- cd flea
- COMMIT_MSG=$(git log -1 --pretty=%B)
- | - |
cd flea
COMMIT_MSG=$(git log -1 --pretty=%B)
if echo "$COMMIT_MSG" | grep -q "contracts_only:text_matcher"; then if echo "$COMMIT_MSG" | grep -q "contracts_only:text_matcher"; then
echo "📦 Publishing text-matcher contracts..." echo "📦 Publishing text-matcher contracts..."
dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json mkdir -p /root/.nuget/NuGet
cp nuget.config /root/.nuget/NuGet/NuGet.Config
dotnet pack text-matcher/Nocr.TextMatcher.sln -o ./bin -p:PackageVersion=${DRONE_TAG} dotnet pack text-matcher/Nocr.TextMatcher.sln -o ./bin -p:PackageVersion=${DRONE_TAG}
dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate
else else
@ -282,7 +285,6 @@ steps:
fi fi
depends_on: depends_on:
- check-trigger - check-trigger
- telegram-listener-contracts
- name: users-contracts - name: users-contracts
image: mcr.microsoft.com/dotnet/sdk:8.0 image: mcr.microsoft.com/dotnet/sdk:8.0
@ -293,12 +295,13 @@ steps:
NUGETAPIKEY: NUGETAPIKEY:
from_secret: nuget_musk_api_key from_secret: nuget_musk_api_key
commands: commands:
- cd flea
- COMMIT_MSG=$(git log -1 --pretty=%B)
- | - |
cd flea
COMMIT_MSG=$(git log -1 --pretty=%B)
if echo "$COMMIT_MSG" | grep -q "contracts_only:users"; then if echo "$COMMIT_MSG" | grep -q "contracts_only:users"; then
echo "📦 Publishing users contracts..." echo "📦 Publishing users contracts..."
dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json mkdir -p /root/.nuget/NuGet
cp nuget.config /root/.nuget/NuGet/NuGet.Config
dotnet pack users/Nocr.Users.sln -o ./bin -p:PackageVersion=${DRONE_TAG} dotnet pack users/Nocr.Users.sln -o ./bin -p:PackageVersion=${DRONE_TAG}
dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate
else else
@ -306,7 +309,7 @@ steps:
fi fi
depends_on: depends_on:
- check-trigger - check-trigger
- text-matcher-contracts
--- ---
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 🚀 Pipeline 4: Full Release # 🚀 Pipeline 4: Full Release
@ -322,11 +325,9 @@ metadata:
trigger: trigger:
ref: ref:
- refs/tags/v* - refs/tags/*
event: event:
- tag - tag
branch:
- release/*
clone: clone:
disable: true disable: true
@ -347,10 +348,10 @@ steps:
- name: check-trigger - name: check-trigger
image: alpine/git image: alpine/git
commands: commands:
- cd flea
- COMMIT_MSG=$(git log -1 --pretty=%B)
- echo "Commit message - $COMMIT_MSG"
- | - |
cd flea
COMMIT_MSG=$(git log -1 --pretty=%B)
echo "Commit message - $COMMIT_MSG"
if echo "$COMMIT_MSG" | grep -qE "contracts_only:|deploy_only:"; then if echo "$COMMIT_MSG" | grep -qE "contracts_only:|deploy_only:"; then
echo "⏭️ contracts_only or deploy_only detected, skipping full release..." echo "⏭️ contracts_only or deploy_only detected, skipping full release..."
exit 78 exit 78
@ -375,7 +376,8 @@ steps:
from_secret: nuget_musk_api_key from_secret: nuget_musk_api_key
commands: commands:
- cd flea - cd flea
- dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json - mkdir -p /root/.nuget/NuGet
- cp nuget.config /root/.nuget/NuGet/NuGet.Config
- dotnet pack telegram-listener/Nocr.TelegramListener.sln -o ./bin -p:PackageVersion=${DRONE_TAG} - dotnet pack telegram-listener/Nocr.TelegramListener.sln -o ./bin -p:PackageVersion=${DRONE_TAG}
- dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate - dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate
depends_on: depends_on:
@ -391,7 +393,8 @@ steps:
from_secret: nuget_musk_api_key from_secret: nuget_musk_api_key
commands: commands:
- cd flea - cd flea
- dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json - mkdir -p /root/.nuget/NuGet
- cp nuget.config /root/.nuget/NuGet/NuGet.Config
- dotnet pack text-matcher/Nocr.TextMatcher.sln -o ./bin -p:PackageVersion=${DRONE_TAG} - dotnet pack text-matcher/Nocr.TextMatcher.sln -o ./bin -p:PackageVersion=${DRONE_TAG}
- dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate - dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate
depends_on: depends_on:
@ -408,7 +411,8 @@ steps:
from_secret: nuget_musk_api_key from_secret: nuget_musk_api_key
commands: commands:
- cd flea - cd flea
- dotnet nuget add source --name musk https://gitea.musk.fun/api/packages/nocr/nuget/index.json - mkdir -p /root/.nuget/NuGet
- cp nuget.config /root/.nuget/NuGet/NuGet.Config
- dotnet pack users/Nocr.Users.sln -o ./bin -p:PackageVersion=${DRONE_TAG} - dotnet pack users/Nocr.Users.sln -o ./bin -p:PackageVersion=${DRONE_TAG}
- dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate - dotnet nuget push ./bin/*Contract*.nupkg --api-key $NUGETAPIKEY --source musk --skip-duplicate
depends_on: depends_on:
@ -416,7 +420,7 @@ steps:
- text-matcher-nuget - text-matcher-nuget
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# STAGE 2: Build Docker Images with Kaniko (3 parallel streams) # STAGE 2: Build Docker Images with Kaniko (sequential)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- name: telegram-listener-build-push - name: telegram-listener-build-push
@ -429,19 +433,20 @@ steps:
commands: commands:
- mkdir -p /kaniko/.docker - mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json
- cd flea/telegram-listener - cd flea
- /kaniko/executor - cp nuget.config telegram-listener/
--context=. - cd telegram-listener
--dockerfile=src/Nocr.TelegramListener.Host/Dockerfile - |
--destination=hub.musk.fun/k8s/nocr/telegram_listener:${DRONE_COMMIT_SHA:0:7} /kaniko/executor \
--destination=hub.musk.fun/k8s/nocr/telegram_listener:${DRONE_TAG} --context=. \
--destination=hub.musk.fun/k8s/nocr/telegram_listener:latest --dockerfile=src/Nocr.TelegramListener.Host/Dockerfile \
--cache=true --destination=hub.musk.fun/k8s/nocr/telegram_listener:${DRONE_COMMIT_SHA:0:7} \
--cache-repo=hub.musk.fun/k8s/cache/nocr-telegram-listener --destination=hub.musk.fun/k8s/nocr/telegram_listener:${DRONE_TAG} \
--compressed-caching=true --destination=hub.musk.fun/k8s/nocr/telegram_listener:latest \
--cache=true \
--cache-repo=hub.musk.fun/k8s/cache/nocr-telegram-listener \
--compressed-caching=true
depends_on: depends_on:
- telegram-listener-nuget
- text-matcher-nuget
- users-nuget - users-nuget
- name: telegram-client-build-push - name: telegram-client-build-push
@ -454,16 +459,19 @@ steps:
commands: commands:
- mkdir -p /kaniko/.docker - mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json
- cd flea/telegram-client - cd flea
- /kaniko/executor - cp nuget.config telegram-client/
--context=. - cd telegram-client
--dockerfile=src/Nocr.TelegramClient.Host/Dockerfile - |
--destination=hub.musk.fun/k8s/nocr/telegram_client:${DRONE_COMMIT_SHA:0:7} /kaniko/executor \
--destination=hub.musk.fun/k8s/nocr/telegram_client:${DRONE_TAG} --context=. \
--destination=hub.musk.fun/k8s/nocr/telegram_client:latest --dockerfile=src/Nocr.TelegramClient.Host/Dockerfile \
--cache=true --destination=hub.musk.fun/k8s/nocr/telegram_client:${DRONE_COMMIT_SHA:0:7} \
--cache-repo=hub.musk.fun/k8s/cache/nocr-telegram-client --destination=hub.musk.fun/k8s/nocr/telegram_client:${DRONE_TAG} \
--compressed-caching=true --destination=hub.musk.fun/k8s/nocr/telegram_client:latest \
--cache=true \
--cache-repo=hub.musk.fun/k8s/cache/nocr-telegram-client \
--compressed-caching=true
depends_on: depends_on:
- telegram-listener-build-push - telegram-listener-build-push
@ -477,20 +485,21 @@ steps:
commands: commands:
- mkdir -p /kaniko/.docker - mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json
- cd flea/text-matcher - cd flea
- /kaniko/executor - cp nuget.config text-matcher/
--context=. - cd text-matcher
--dockerfile=src/Nocr.TextMatcher.Host/Dockerfile - |
--destination=hub.musk.fun/k8s/nocr/text_matcher:${DRONE_COMMIT_SHA:0:7} /kaniko/executor \
--destination=hub.musk.fun/k8s/nocr/text_matcher:${DRONE_TAG} --context=. \
--destination=hub.musk.fun/k8s/nocr/text_matcher:latest --dockerfile=src/Nocr.TextMatcher.Host/Dockerfile \
--cache=true --destination=hub.musk.fun/k8s/nocr/text_matcher:${DRONE_COMMIT_SHA:0:7} \
--cache-repo=hub.musk.fun/k8s/cache/nocr-text-matcher --destination=hub.musk.fun/k8s/nocr/text_matcher:${DRONE_TAG} \
--compressed-caching=true --destination=hub.musk.fun/k8s/nocr/text_matcher:latest \
--cache=true \
--cache-repo=hub.musk.fun/k8s/cache/nocr-text-matcher \
--compressed-caching=true
depends_on: depends_on:
- telegram-listener-nuget - telegram-client-build-push
- text-matcher-nuget
- users-nuget
- name: text-matcher-migrator-build-push - name: text-matcher-migrator-build-push
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
@ -502,16 +511,19 @@ steps:
commands: commands:
- mkdir -p /kaniko/.docker - mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json
- cd flea/text-matcher - cd flea
- /kaniko/executor - cp nuget.config text-matcher/
--context=. - cd text-matcher
--dockerfile=src/Nocr.TextMatcher.Migrator/Dockerfile - |
--destination=hub.musk.fun/k8s/nocr/text_matcher_migrator:${DRONE_COMMIT_SHA:0:7} /kaniko/executor \
--destination=hub.musk.fun/k8s/nocr/text_matcher_migrator:${DRONE_TAG} --context=. \
--destination=hub.musk.fun/k8s/nocr/text_matcher_migrator:latest --dockerfile=src/Nocr.TextMatcher.Migrator/Dockerfile \
--cache=true --destination=hub.musk.fun/k8s/nocr/text_matcher_migrator:${DRONE_COMMIT_SHA:0:7} \
--cache-repo=hub.musk.fun/k8s/cache/nocr-text-matcher-migrator --destination=hub.musk.fun/k8s/nocr/text_matcher_migrator:${DRONE_TAG} \
--compressed-caching=true --destination=hub.musk.fun/k8s/nocr/text_matcher_migrator:latest \
--cache=true \
--cache-repo=hub.musk.fun/k8s/cache/nocr-text-matcher-migrator \
--compressed-caching=true
depends_on: depends_on:
- text-matcher-build-push - text-matcher-build-push
@ -525,20 +537,21 @@ steps:
commands: commands:
- mkdir -p /kaniko/.docker - mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json
- cd flea/users - cd flea
- /kaniko/executor - cp nuget.config users/
--context=. - cd users
--dockerfile=src/Nocr.Users.Host/Dockerfile - |
--destination=hub.musk.fun/k8s/nocr/users:${DRONE_COMMIT_SHA:0:7} /kaniko/executor \
--destination=hub.musk.fun/k8s/nocr/users:${DRONE_TAG} --context=. \
--destination=hub.musk.fun/k8s/nocr/users:latest --dockerfile=src/Nocr.Users.Host/Dockerfile \
--cache=true --destination=hub.musk.fun/k8s/nocr/users:${DRONE_COMMIT_SHA:0:7} \
--cache-repo=hub.musk.fun/k8s/cache/nocr-users --destination=hub.musk.fun/k8s/nocr/users:${DRONE_TAG} \
--compressed-caching=true --destination=hub.musk.fun/k8s/nocr/users:latest \
--cache=true \
--cache-repo=hub.musk.fun/k8s/cache/nocr-users \
--compressed-caching=true
depends_on: depends_on:
- telegram-listener-nuget - text-matcher-migrator-build-push
- text-matcher-nuget
- users-nuget
- name: users-migrator-build-push - name: users-migrator-build-push
image: gcr.io/kaniko-project/executor:debug image: gcr.io/kaniko-project/executor:debug
@ -550,16 +563,19 @@ steps:
commands: commands:
- mkdir -p /kaniko/.docker - mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"hub.musk.fun\":{\"username\":\"$HUB_USERNAME\",\"password\":\"$HUB_PASSWORD\"}}}" > /kaniko/.docker/config.json
- cd flea/users - cd flea
- /kaniko/executor - cp nuget.config users/
--context=. - cd users
--dockerfile=src/Nocr.Users.Migrator/Dockerfile - |
--destination=hub.musk.fun/k8s/nocr/users_migrator:${DRONE_COMMIT_SHA:0:7} /kaniko/executor \
--destination=hub.musk.fun/k8s/nocr/users_migrator:${DRONE_TAG} --context=. \
--destination=hub.musk.fun/k8s/nocr/users_migrator:latest --dockerfile=src/Nocr.Users.Migrator/Dockerfile \
--cache=true --destination=hub.musk.fun/k8s/nocr/users_migrator:${DRONE_COMMIT_SHA:0:7} \
--cache-repo=hub.musk.fun/k8s/cache/nocr-users-migrator --destination=hub.musk.fun/k8s/nocr/users_migrator:${DRONE_TAG} \
--compressed-caching=true --destination=hub.musk.fun/k8s/nocr/users_migrator:latest \
--cache=true \
--cache-repo=hub.musk.fun/k8s/cache/nocr-users-migrator \
--compressed-caching=true
depends_on: depends_on:
- users-build-push - users-build-push
@ -569,14 +585,79 @@ steps:
- name: deploy-to-k8s - name: deploy-to-k8s
image: bitnami/kubectl:latest image: bitnami/kubectl:latest
environment:
KUBECONFIG_CONTENT:
from_secret: kubeconfig
commands: commands:
- mkdir -p $HOME/.kube
- echo "$KUBECONFIG_CONTENT" > $HOME/.kube/config
- chmod 600 $HOME/.kube/config
- cd flea/_deploy/scripts - cd flea/_deploy/scripts
- chmod +x deploy.sh - sh deploy.sh "${DRONE_TAG}" "${DRONE_COMMIT_SHA:0:7}"
- ./deploy.sh ${DRONE_TAG} ${DRONE_COMMIT_SHA:0:7}
depends_on: depends_on:
- telegram-client-build-push
- text-matcher-migrator-build-push
- users-migrator-build-push - users-migrator-build-push
when: when:
ref: ref:
- refs/tags/* - refs/tags/*
---
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 🚀 Pipeline 5: Deploy-Only
# Trigger: Tag with commit message containing "deploy_only:"
# Purpose: Fast deploy of already-built images
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
kind: pipeline
type: kubernetes
name: deploy-only
metadata:
namespace: musk-drone
trigger:
ref:
- refs/tags/*
event:
- tag
clone:
disable: true
steps:
- name: clone
image: alpine/git
commands:
- git clone https://gitea.musk.fun/nocr/flea
- cd flea
- git checkout $DRONE_TAG
- git submodule update --init --recursive
- name: check-trigger
image: alpine/git
commands:
- |
cd flea
COMMIT_MSG=$(git log -1 --pretty=%B)
echo "Commit message - $COMMIT_MSG"
if echo "$COMMIT_MSG" | grep -q "deploy_only:"; then
echo "✅ deploy_only detected, proceeding..."
exit 0
else
echo "⏭️ No deploy_only marker, skipping..."
exit 78
fi
depends_on:
- clone
- name: deploy
image: bitnami/kubectl:latest
environment:
KUBECONFIG_CONTENT:
from_secret: kubeconfig
commands:
- mkdir -p $HOME/.kube
- echo "$KUBECONFIG_CONTENT" > $HOME/.kube/config
- chmod 600 $HOME/.kube/config
- cd flea/_deploy/scripts
- sh deploy.sh "${DRONE_TAG}" "" "latest"
depends_on:
- check-trigger

433
CLAUDE.md
View File

@ -27,28 +27,39 @@ The services follow a message bus pattern using RabbitMQ for async communication
**Note:** WTelegram sends both `UpdateNewChannelMessage` and `UpdateEditChannelMessage` for the same message. TelegramListener publishes separate events to avoid duplicate notifications downstream. **Note:** WTelegram sends both `UpdateNewChannelMessage` and `UpdateEditChannelMessage` for the same message. TelegramListener publishes separate events to avoid duplicate notifications downstream.
Each service follows Clean Architecture with separate projects for: Each service follows Clean Architecture with separate projects for:
- Host (API/entry point) - **Host** (API/entry point) - ASP.NET Core application, controllers, startup configuration
- AppServices (business logic) - **AppServices** (business logic) - Use cases, business logic, command/query handlers
- Core (shared utilities) - **Core** (shared utilities) - Domain entities, interfaces, shared logic
- Api.Contracts (REST API contracts) - **Api.Contracts** (REST API contracts) - DTOs, request/response models for REST endpoints (published as NuGet)
- Async.Api.Contracts (event contracts) - **Async.Api.Contracts** (event contracts) - Event DTOs for RabbitMQ messaging (published as NuGet)
- Persistence (database layer, where applicable) - **Persistence** (database layer) - EF Core DbContext, repositories, data access (text-matcher, users only)
- Migrator (database migrations, where applicable) - **Migrator** (database migrations) - EF Core migrations, migration scripts (text-matcher, users only)
**Project Structure Examples:**
- telegram-listener: Host, AppServices, Core, Async.Api.Contracts (no database)
- telegram-client: Host, AppServices, Core (no database, no published contracts)
- text-matcher: Host, AppServices, Core, Api.Contracts, Async.Api.Contracts, Persistence, Migrator
- users: Host, AppServices, Core, Api.Contracts, Persistence, Migrator
## Development Commands ## Development Commands
### Running the System ### Running the System
```bash ```bash
# IMPORTANT: Before building with Docker Compose, prepare the build environment
./prepare-build.sh
# Start all services with Docker Compose # Start all services with Docker Compose
docker-compose up docker-compose up
# Start individual services for development # Start individual services for development (no preparation needed)
cd telegram-client && dotnet run --project src/Nocr.TelegramClient.Host cd telegram-client && dotnet run --project src/Nocr.TelegramClient.Host
cd telegram-listener && dotnet run --project src/Nocr.TelegramListener.Host cd telegram-listener && dotnet run --project src/Nocr.TelegramListener.Host
cd text-matcher && dotnet run --project src/Nocr.TextMatcher.Host cd text-matcher && dotnet run --project src/Nocr.TextMatcher.Host
cd users && dotnet run --project src/Nocr.Users.Host cd users && dotnet run --project src/Nocr.Users.Host
``` ```
**Note:** The `prepare-build.sh` script copies `nuget.config` to all submodule roots, which is required for Docker builds. This step is automatic in CI/CD but must be run manually for local Docker Compose builds.
### Building ### Building
```bash ```bash
# Build individual services # Build individual services
@ -60,8 +71,17 @@ dotnet build src/Nocr.<ServiceName>.Host
### Testing ### Testing
```bash ```bash
# Run unit tests (only text-matcher has tests currently) # Run all tests in text-matcher
cd text-matcher && dotnet test cd text-matcher && dotnet test
# Run tests for a specific project
cd text-matcher && dotnet test tests/Nocr.TextMatcher.AppServices.UnitTests/Nocr.TextMatcher.AppServices.UnitTests.csproj
# Run tests with verbose output
cd text-matcher && dotnet test --verbosity detailed
# Run tests with code coverage (if configured)
cd text-matcher && dotnet test --collect:"XPlat Code Coverage"
``` ```
### Database Migrations ### Database Migrations
@ -75,6 +95,29 @@ cd users && ./src/Nocr.Users.Migrator/AddMigration.sh MyMigrationName
# Apply migrations (handled automatically by migrator containers in docker-compose) # Apply migrations (handled automatically by migrator containers in docker-compose)
``` ```
### Working with Git Submodules
```bash
# Initialize submodules after cloning the repository
git submodule update --init --recursive
# Update all submodules to their latest commits on main
./update-submodules.sh
# Commit and push changes to all submodules at once
./commit-all.sh "Your commit message"
# Update a single submodule
cd telegram-listener
git pull origin main
cd ..
git add telegram-listener
git commit -m "Update telegram-listener submodule"
# Check status of all submodules
git submodule status
```
## Configuration ## Configuration
**📖 See [CONFIGURATION.md](CONFIGURATION.md) for detailed configuration guide.** **📖 See [CONFIGURATION.md](CONFIGURATION.md) for detailed configuration guide.**
@ -133,13 +176,47 @@ When running with docker-compose:
## Key Technologies ## Key Technologies
- .NET 8 - **.NET 8** - All services built on .NET 8
- ASP.NET Core Web APIs - **ASP.NET Core Web APIs** - REST endpoints and hosting
- Entity Framework Core with MariaDB - **Entity Framework Core with MariaDB** - ORM for text-matcher and users databases
- WTelegramClient for Telegram API - **WTelegramClient** - MTProto API client for telegram-listener
- Rebus for message bus (RabbitMQ) - **Telegram.Bot** - Bot API client for telegram-client
- Docker & Docker Compose for containerization - **Rebus** - Message bus abstraction over RabbitMQ
- Drone CI/CD on Kubernetes - **RabbitMQ** - Message broker for async event-driven communication
- **Docker & Docker Compose** - Local development and containerization
- **Kaniko** - Container image building in CI/CD
- **Drone CI** - CI/CD pipeline on Kubernetes
## NuGet Package Management
The project uses **Central Package Management (CPM)** with **Package Source Mapping** to manage NuGet dependencies:
### Package Sources
- **nuget.org**: All public packages (Microsoft.*, Serilog.*, etc.)
- **musk** (private): Internal `Nocr.*` contract packages
### Configuration
The `nuget.config` file in the project root defines package source mapping:
```xml
<packageSourceMapping>
<packageSource key="musk">
<package pattern="Nocr.*" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
```
### How It Works
1. **Local Development**: Copy `nuget.config` to each submodule root when needed
2. **CI/CD**: Drone automatically copies `nuget.config` to `/root/.nuget/NuGet/NuGet.Config`
3. **Docker Builds**: Kaniko copies `nuget.config` to submodule root before building
This eliminates NuGet warning NU1507 and ensures consistent package resolution across all environments.
## CI/CD Pipeline ## CI/CD Pipeline
@ -148,39 +225,240 @@ When running with docker-compose:
The project uses Drone CI with 5 specialized pipelines: The project uses Drone CI with 5 specialized pipelines:
### 1. Feature Validation (`feature/*`, `fix/*` branches) ### 1. Feature Validation (`feature/*`, `fix/*` branches)
- Triggered on push to feature/fix branches **Purpose**: Test feature branches before merging to main
**How to trigger**:
```bash
# Create feature branch and push
git checkout -b feature/my-new-feature
# ... make changes ...
git add .
git commit -m "Add new feature"
git push origin feature/my-new-feature
```
**What happens**:
- Runs build + tests with Testcontainers support - Runs build + tests with Testcontainers support
- Provides fast feedback before merge - Provides fast feedback before merge
- Duration: ~3-5 minutes - Duration: ~3-5 minutes
### 2. Main Validation (`main` branch) ### 2. Main Validation (`main` branch)
- Triggered on push to main **Purpose**: Ensure main branch stays healthy after merge
**How to trigger**:
```bash
# Merge feature to main
git checkout main
git merge feature/my-new-feature
git push origin main
```
**What happens**:
- Same checks as feature validation - Same checks as feature validation
- Ensures main branch stays healthy - Ensures main branch builds and tests pass
- Duration: ~3-5 minutes - Duration: ~3-5 minutes
### 3. Contracts-Only Publish (tags with `contracts_only:`) ### 3. Contracts-Only Publish
- Triggered by tag + commit message containing `contracts_only:<service>` **Purpose**: Publish NuGet contracts without building Docker images (fast iteration)
- Publishes NuGet contracts without building Docker images
- Fast iteration on contract changes **How to trigger**:
```bash
# Update contracts in a submodule
cd telegram-listener
# ... update Async.Api.Contracts ...
git add . && git commit -m "Add MessageEdited event"
git push
# From parent repo, tag with contracts_only marker
cd ..
git add telegram-listener
git commit -m "contracts_only:telegram_listener - Add MessageEdited event"
git tag 0.7.41
git push && git push --tags
```
**What happens**:
- Publishes only the specified service's NuGet contracts
- Skips Docker image builds
- Duration: ~2 minutes - Duration: ~2 minutes
- Example: `git commit -m "contracts_only:telegram_listener - Add MessageEdited event"`
### 4. Full Release (clean tags) **Services**: `telegram_listener`, `text_matcher`, `users`
- Triggered by tag without special markers
- Complete release cycle: ### 4. Full Release
1. Publish all NuGet contracts (parallel) **Purpose**: Complete release - contracts + images + deployment
2. Build all Docker images with Kaniko (parallel, after contracts)
3. Deploy to Kubernetes (automatic for `v*` tags) **How to trigger**:
```bash
# Make your changes, commit to main
git add .
git commit -m "Implement new feature"
git push
# Tag for full release (no special markers in commit message)
git tag v1.3.0
git push --tags
```
**What happens**:
1. Publishes all NuGet contracts (parallel)
2. Builds all Docker images with Kaniko (parallel, after contracts)
3. Deploys to Kubernetes (automatic for `v*` tags)
- Duration: ~8-10 minutes - Duration: ~8-10 minutes
- Example: `git tag v1.3.0`
### 5. Deploy-Only (tags with `deploy_only:`) ### 5. Deploy-Only
- Triggered by tag + commit message containing `deploy_only:` **Purpose**: Deploy existing images without rebuilding (rollbacks, hotfixes)
- Skips building, only deploys existing images
- Useful for rollbacks and hotfixes **How to trigger**:
```bash
# Deploy already-built images (e.g., rollback to previous version)
git commit --allow-empty -m "deploy_only: Rollback to v1.2.9"
git tag deploy-v1.2.9
git push && git push --tags
# Or deploy latest images
git commit --allow-empty -m "deploy_only: Deploy latest"
git tag deploy-latest
git push && git push --tags
```
**What happens**:
- Skips building entirely
- Deploys specified tag's images (or `latest`)
- Duration: ~1 minute - Duration: ~1 minute
- Example: `git commit --allow-empty -m "deploy_only: Deploy v1.2.9"`
## Development Workflows
### Making Changes to a Single Service
When working on a specific service (e.g., telegram-listener):
```bash
# 1. Navigate to the submodule
cd telegram-listener
# 2. Create a feature branch in the submodule
git checkout -b feature/new-feature
# 3. Make your changes to the code
# ... edit files ...
# 4. Test locally (if tests exist)
dotnet test
# 5. Commit and push in the submodule
git add .
git commit -m "Add new feature"
git push origin feature/new-feature
# 6. Return to parent repo and update submodule reference
cd ..
git add telegram-listener
git commit -m "Update telegram-listener: Add new feature"
git push origin main
```
### Updating Contract Packages
When you change event contracts (Async.Api.Contracts) or REST contracts (Api.Contracts):
```bash
# 1. Make changes to contracts in the submodule
cd text-matcher
# ... edit Nocr.TextMatcher.Async.Api.Contracts ...
git add . && git commit -m "Add new event contract"
git push origin main
# 2. Return to parent repo and publish contracts only
cd ..
git add text-matcher
git commit -m "contracts_only:text_matcher - Add new event contract"
git tag 0.8.6
git push && git push --tags
# 3. Other services can now reference the new contract version
# Update their .csproj or Directory.Packages.props to use version 0.8.6
```
### Working Across Multiple Services
When implementing a feature that spans multiple services:
```bash
# 1. Update each submodule in sequence
cd telegram-listener
git checkout -b feature/cross-service-feature
# ... make changes ...
git commit -m "Part 1: Update listener"
git push origin feature/cross-service-feature
cd ../text-matcher
git checkout -b feature/cross-service-feature
# ... make changes ...
git commit -m "Part 2: Update matcher"
git push origin feature/cross-service-feature
# 2. Update parent repo to reference all changes
cd ..
git add telegram-listener text-matcher
git commit -m "Implement cross-service feature"
git push origin main
# 3. Tag for full release
git tag v1.5.0
git push --tags
```
## Common CI/CD Workflows
### Test a Feature Branch
```bash
git checkout -b feature/add-logging
# ... make changes ...
git add . && git commit -m "Add structured logging"
git push origin feature/add-logging
# ✅ Drone runs feature-validation pipeline
```
### Publish Contracts After API Changes
```bash
cd text-matcher
# Update Nocr.TextMatcher.Async.Api.Contracts
git add . && git commit -m "Add UserDeleted event"
git push
cd ..
git add text-matcher
git commit -m "contracts_only:text_matcher - Add UserDeleted event"
git tag 0.8.5
git push && git push --tags
# ✅ Drone publishes text-matcher contracts to NuGet
```
### Full Release Workflow
```bash
# Work on main branch
git add .
git commit -m "Implement payment processing"
git push
# ✅ Drone runs main-validation
# Tag for release
git tag v2.0.0
git push --tags
# ✅ Drone runs full-release pipeline:
# 1. Publishes all contracts
# 2. Builds all Docker images
# 3. Deploys to Kubernetes
```
### Emergency Rollback
```bash
# Rollback to last known good version
git commit --allow-empty -m "deploy_only: Emergency rollback to v1.9.5"
git tag rollback-v1.9.5
git push && git push --tags
# ✅ Drone deploys v1.9.5 images immediately (~1 minute)
```
### Deployment Scripts ### Deployment Scripts
@ -195,4 +473,83 @@ Located in `_deploy/scripts/`:
- **Parallel execution** for independent operations - **Parallel execution** for independent operations
- **Proper dependency order**: Contracts → Images → Deploy - **Proper dependency order**: Contracts → Images → Deploy
- **Kaniko layer caching** for faster Docker builds - **Kaniko layer caching** for faster Docker builds
- **Docker-in-Docker** support for Testcontainers in tests - **Docker-in-Docker** support for Testcontainers in tests
## Troubleshooting
### Submodule Issues
**Problem:** Submodule directories are empty after cloning
```bash
# Solution: Initialize submodules
git submodule update --init --recursive
```
**Problem:** Submodule is in detached HEAD state
```bash
# Solution: Check out the main branch in the submodule
cd <submodule-name>
git checkout main
git pull origin main
cd ..
```
**Problem:** Changes in submodule not reflected in parent repo
```bash
# Solution: Update submodule reference in parent
cd .. # Return to parent repo
git add <submodule-name>
git commit -m "Update <submodule-name> reference"
```
### Build Issues
**Problem:** NuGet package restore fails with NU1507 warning
```bash
# Solution: Copy nuget.config to submodule root
cp nuget.config telegram-listener/
cd telegram-listener && dotnet restore
```
**Problem:** Cannot find Nocr.* contract packages
```bash
# Solution: Ensure you have access to the private NuGet feed
# Check nuget.config has the correct source configuration
# Verify credentials for the "musk" package source
```
### Docker Compose Issues
**Problem:** Service cannot connect to RabbitMQ
```bash
# Solution: Use the Docker network hostname, not localhost
# In docker-compose environment: nocr-rabbitmq:5672
# In local development: localhost:5672
```
**Problem:** Database connection fails
```bash
# Solution: Wait for database health checks to pass
# Check docker-compose logs for database container
docker-compose logs nocr-text-matcher-db
# Database takes 10-30 seconds to be ready on first start
```
### CI/CD Issues
**Problem:** Pipeline doesn't trigger on tag
```bash
# Solution: Ensure tag matches expected patterns
# Feature validation: feature/*, fix/*, issues/*
# Main validation: main branch
# Contracts: Tag with commit message containing "contracts_only:<service>"
# Full release: Any tag without special markers
# Deploy-only: Tag with commit message containing "deploy_only:"
```
**Problem:** Docker image build fails in Kaniko
```bash
# Solution: Check if nuget.config is correctly copied
# Verify _deploy/deploy.sh contains nuget.config copy step
# Check Drone logs for nuget.config presence in build context
```

View File

@ -1,48 +1,48 @@
# Configuration Guide # Руководство по конфигурации
This document describes how to configure the NOCR microservices system for different deployment scenarios. Этот документ описывает, как настроить микросервисную систему NOCR для различных сценариев развертывания.
## Table of Contents ## Содержание
1. [Configuration System Overview](#configuration-system-overview) 1. [Обзор системы конфигурации](#обзор-системы-конфигурации)
2. [Deployment Scenarios](#deployment-scenarios) 2. [Сценарии развертывания](#сценарии-развертывания)
- [Local Development (VS Code)](#local-development-vs-code) - [Локальная разработка (VS Code)](#локальная-разработка-vs-code)
- [Docker Compose](#docker-compose) - [Docker Compose](#docker-compose)
- [Kubernetes](#kubernetes) - [Kubernetes](#kubernetes)
3. [Configuration Priority](#configuration-priority) 3. [Приоритет конфигурации](#приоритет-конфигурации)
4. [Service-Specific Settings](#service-specific-settings) 4. [Настройки для конкретных сервисов](#настройки-для-конкретных-сервисов)
5. [Debug Mode](#debug-mode) 5. [Режим отладки](#режим-отладки)
6. [Troubleshooting](#troubleshooting) 6. [Устранение неполадок](#устранение-неполадок)
--- ---
## Configuration System Overview ## Обзор системы конфигурации
The system uses ASP.NET Core's layered configuration approach with the following sources (listed from lowest to highest priority): Система использует многоуровневый подход конфигурации ASP.NET Core со следующими источниками (от низшего к высшему приоритету):
1. `appsettings.json` - Base configuration (committed to git) 1. `appsettings.json` - Базовая конфигурация (коммитится в git)
2. `appsettings.{Environment}.json` - Environment-specific settings (some committed, some not) 2. `appsettings.{Environment}.json` - Настройки для конкретного окружения (некоторые коммитятся, некоторые нет)
3. User Secrets - Development-only secrets (stored locally, not in git) 3. User Secrets - Секреты только для разработки (хранятся локально, не в git)
4. **Environment Variables** - **Highest priority** (overrides everything) 4. **Переменные окружения** - **Наивысший приоритет** (переопределяют всё)
### Important Notes ### Важные примечания
- **Never commit secrets to git** - **Никогда не коммитьте секреты в git**
- Use `.example` files as templates - Используйте файлы `.example` как шаблоны
- Environment Variables always win in priority - Переменные окружения всегда имеют наивысший приоритет
- Each service has its own configuration requirements - Каждый сервис имеет свои требования к конфигурации
--- ---
## Deployment Scenarios ## Сценарии развертывания
### Local Development (VS Code) ### Локальная разработка (VS Code)
For debugging services individually in VS Code: Для отладки отдельных сервисов в VS Code:
#### Step 1: Create Environment-Specific Configuration #### Шаг 1: Создание конфигурации для окружения
For each service you want to run, create `appsettings.Development.json` from the example: Для каждого сервиса, который вы хотите запустить, создайте `appsettings.Development.json` из примера:
```bash ```bash
# Telegram Listener # Telegram Listener
@ -62,11 +62,11 @@ cd telegram-client/src/Nocr.TelegramClient.Host
cp appsettings.Development.json.example appsettings.Development.json cp appsettings.Development.json.example appsettings.Development.json
``` ```
#### Step 2: Fill in Your Values #### Шаг 2: Заполните ваши значения
Edit each `appsettings.Development.json` file and replace placeholder values with your actual credentials. Отредактируйте каждый файл `appsettings.Development.json` и замените placeholder-значения на ваши реальные учетные данные.
**Example for Telegram Listener:** **Пример для Telegram Listener:**
```json ```json
{ {
"RebusRabbitMqOptions": { "RebusRabbitMqOptions": {
@ -80,13 +80,13 @@ Edit each `appsettings.Development.json` file and replace placeholder values wit
} }
``` ```
#### Step 3: Alternative - Use Environment Variables #### Шаг 3: Альтернатива - использование переменных окружения
Instead of creating `appsettings.Development.json`, you can use environment variables in `.vscode/launch.json`: Вместо создания `appsettings.Development.json`, вы можете использовать переменные окружения в `.vscode/launch.json`:
```json ```json
{ {
"name": "Launch Service", "name": "Запуск сервиса",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"env": { "env": {
@ -99,57 +99,57 @@ Instead of creating `appsettings.Development.json`, you can use environment vari
} }
``` ```
**Note:** Use double underscores `__` to represent nested configuration sections in environment variables. **Примечание:** Используйте двойное подчеркивание `__` для представления вложенных секций конфигурации в переменных окружения.
#### Step 4: Start Infrastructure #### Шаг 4: Запуск инфраструктуры
You need RabbitMQ and databases running: Вам нужны работающие RabbitMQ и базы данных:
```bash ```bash
# Start only infrastructure services # Запустить только инфраструктурные сервисы
docker-compose up nocr-rabbitmq nocr-text-matcher-db nocr-users-db -d docker-compose up nocr-rabbitmq nocr-text-matcher-db nocr-users-db -d
``` ```
#### Step 5: Debug in VS Code #### Шаг 5: Отладка в VS Code
Press F5 or use the Run and Debug panel to start your service. Нажмите F5 или используйте панель Run and Debug для запуска вашего сервиса.
--- ---
### Docker Compose ### Docker Compose
For running the entire system locally with Docker: Для запуска всей системы локально с Docker:
#### Step 1: Create .nocr.env File #### Шаг 1: Создание файла .nocr.env
```bash ```bash
cd /path/to/project/root cd /путь/к/корню/проекта
cp .nocr.env.example .nocr.env cp .nocr.env.example .nocr.env
``` ```
#### Step 2: Edit .nocr.env #### Шаг 2: Редактирование .nocr.env
Open `.nocr.env` and fill in your actual credentials: Откройте `.nocr.env` и заполните ваши реальные учетные данные:
```bash ```bash
# Telegram Listener - get from https://my.telegram.org/apps # Telegram Listener - получить на https://my.telegram.org/apps
WTelegramClientOptions__ApiId=22101230 WTelegramClientOptions__ApiId=22101230
WTelegramClientOptions__ApiHash=c72f884d8eb84cb7134a14362bff060b WTelegramClientOptions__ApiHash=c72f884d8eb84cb7134a14362bff060b
WTelegramClientOptions__PhoneNumber=79167310711 WTelegramClientOptions__PhoneNumber=79167310711
# Telegram Client Bot - get from @BotFather # Telegram Client Bot - получить у @BotFather
TelegramBotOptions__Token=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz TelegramBotOptions__Token=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
# Optional: Enable debug logging # Опционально: включить отладочное логирование
# NOCR_DEBUG_MODE=true # NOCR_DEBUG_MODE=true
``` ```
#### Step 3: Create DockerCompose Configuration Files #### Шаг 3: Создание конфигурационных файлов для DockerCompose
Each service needs `appsettings.DockerCompose.json`: Каждому сервису нужен `appsettings.DockerCompose.json`:
```bash ```bash
# For each service # Для каждого сервиса
cd telegram-listener/src/Nocr.TelegramListener.Host cd telegram-listener/src/Nocr.TelegramListener.Host
cp appsettings.DockerCompose.json.example appsettings.DockerCompose.json cp appsettings.DockerCompose.json.example appsettings.DockerCompose.json
@ -163,21 +163,21 @@ cd telegram-client/src/Nocr.TelegramClient.Host
cp appsettings.DockerCompose.json.example appsettings.DockerCompose.json cp appsettings.DockerCompose.json.example appsettings.DockerCompose.json
``` ```
**Note:** These files contain Docker network hostnames (e.g., `nocr-rabbitmq:5672` instead of `localhost:5672`). **Примечание:** Эти файлы содержат имена хостов Docker-сети (например, `nocr-rabbitmq:5672` вместо `localhost:5672`).
#### Step 4: Start All Services #### Шаг 4: Запуск всех сервисов
```bash ```bash
docker-compose up docker-compose up
``` ```
Or build and start: Или со сборкой:
```bash ```bash
docker-compose up --build docker-compose up --build
``` ```
#### Step 5: Verify Services are Running #### Шаг 5: Проверка работоспособности сервисов
- Telegram Listener: http://localhost:5040/health - Telegram Listener: http://localhost:5040/health
- Text Matcher: http://localhost:5041/health - Text Matcher: http://localhost:5041/health
@ -189,32 +189,32 @@ docker-compose up --build
### Kubernetes ### Kubernetes
For production deployment on Kubernetes: Для production-развертывания на Kubernetes:
#### Step 1: Create Kubernetes Secrets #### Шаг 1: Создание Kubernetes Secrets
**Do NOT use `.nocr.env` file in K8s.** Instead, create Secrets: **НЕ используйте файл `.nocr.env` в K8s.** Вместо этого создайте Secrets:
```bash ```bash
# Create namespace # Создать namespace
kubectl create namespace nocr kubectl create namespace nocr
# Create secrets for Telegram Listener # Создать secrets для Telegram Listener
kubectl create secret generic telegram-listener-secrets \ kubectl create secret generic telegram-listener-secrets \
--from-literal=WTelegramClientOptions__ApiId=22101230 \ --from-literal=WTelegramClientOptions__ApiId=22101230 \
--from-literal=WTelegramClientOptions__ApiHash=c72f884d8eb84cb7134a14362bff060b \ --from-literal=WTelegramClientOptions__ApiHash=c72f884d8eb84cb7134a14362bff060b \
--from-literal=WTelegramClientOptions__PhoneNumber=79167310711 \ --from-literal=WTelegramClientOptions__PhoneNumber=79167310711 \
-n nocr -n nocr
# Create secrets for Telegram Client # Создать secrets для Telegram Client
kubectl create secret generic telegram-client-secrets \ kubectl create secret generic telegram-client-secrets \
--from-literal=TelegramBotOptions__Token=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz \ --from-literal=TelegramBotOptions__Token=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz \
-n nocr -n nocr
``` ```
#### Step 2: Reference Secrets in Deployment #### Шаг 2: Ссылка на Secrets в Deployment
Example Kubernetes deployment manifest: Пример манифеста Kubernetes deployment:
```yaml ```yaml
apiVersion: apps/v1 apiVersion: apps/v1
@ -236,9 +236,9 @@ spec:
name: telegram-listener-secrets name: telegram-listener-secrets
``` ```
#### Step 3: Create appsettings.Production.json #### Шаг 3: Создание appsettings.Production.json
Create production config files with K8s service names: Создайте production-конфигурационные файлы с именами сервисов K8s:
```json ```json
{ {
@ -248,9 +248,9 @@ Create production config files with K8s service names:
} }
``` ```
**Note:** Secrets from environment variables will override these values. **Примечание:** Секреты из переменных окружения переопределят эти значения.
#### Step 4: Deploy #### Шаг 4: Деплой
```bash ```bash
kubectl apply -f deployment/ kubectl apply -f deployment/
@ -258,36 +258,36 @@ kubectl apply -f deployment/
--- ---
## Configuration Priority ## Приоритет конфигурации
Understanding priority is crucial when troubleshooting configuration issues. Понимание приоритета критически важно при устранении проблем с конфигурацией.
### Priority Order (Lowest → Highest) ### Порядок приоритета (от низшего к высшему)
1. ⬇️ `appsettings.json` (base) 1. ⬇️ `appsettings.json` (базовый)
2. ⬆️ `appsettings.{Environment}.json` (e.g., Development, DockerCompose, Production) 2. ⬆️ `appsettings.{Environment}.json` (например, Development, DockerCompose, Production)
3. ⬆️ User Secrets (Development only) 3. ⬆️ User Secrets (только Development)
4. ⬆️⬆️ **Environment Variables (ALWAYS WINS)** 4. ⬆️⬆️ **Переменные окружения (ВСЕГДА ВЫИГРЫВАЮТ)**
### Example Scenario ### Пример сценария
If you have: Если у вас есть:
- `appsettings.json`: `"ConnectionString": ""` - `appsettings.json`: `"ConnectionString": ""`
- `appsettings.DockerCompose.json`: `"ConnectionString": "amqp://admin:admin@nocr-rabbitmq:5672/"` - `appsettings.DockerCompose.json`: `"ConnectionString": "amqp://admin:admin@nocr-rabbitmq:5672/"`
- Environment Variable: `RebusRabbitMqOptions__ConnectionString=amqp://admin:admin@localhost:5672/` - Переменная окружения: `RebusRabbitMqOptions__ConnectionString=amqp://admin:admin@localhost:5672/`
**Result:** Environment variable wins! Connection will use `localhost:5672`. **Результат:** Переменная окружения побеждает! Подключение будет использовать `localhost:5672`.
This is why we removed `appsettings.protected.json` - it was overriding Docker Compose settings incorrectly. Именно поэтому мы удалили `appsettings.protected.json` - он неправильно переопределял настройки Docker Compose.
--- ---
## Service-Specific Settings ## Настройки для конкретных сервисов
### Telegram Listener ### Telegram Listener
**Required Configuration:** **Обязательная конфигурация:**
```json ```json
{ {
@ -302,17 +302,17 @@ This is why we removed `appsettings.protected.json` - it was overriding Docker C
} }
``` ```
**How to obtain Telegram credentials:** **Как получить учетные данные Telegram:**
1. Visit https://my.telegram.org/apps 1. Посетите https://my.telegram.org/apps
2. Log in with your phone number 2. Войдите с вашим номером телефона
3. Create a new application 3. Создайте новое приложение
4. Copy `api_id` and `api_hash` 4. Скопируйте `api_id` и `api_hash`
--- ---
### Text Matcher ### Text Matcher
**Required Configuration:** **Обязательная конфигурация:**
```json ```json
{ {
@ -325,7 +325,7 @@ This is why we removed `appsettings.protected.json` - it was overriding Docker C
} }
``` ```
**Ports by environment:** **Порты по окружениям:**
- Development: `localhost:3316` - Development: `localhost:3316`
- Docker Compose: `nocr-text-matcher-db:3306` - Docker Compose: `nocr-text-matcher-db:3306`
- Kubernetes: `text-matcher-db-service:3306` - Kubernetes: `text-matcher-db-service:3306`
@ -334,7 +334,7 @@ This is why we removed `appsettings.protected.json` - it was overriding Docker C
### Users ### Users
**Required Configuration:** **Обязательная конфигурация:**
```json ```json
{ {
@ -344,7 +344,7 @@ This is why we removed `appsettings.protected.json` - it was overriding Docker C
} }
``` ```
**Ports by environment:** **Порты по окружениям:**
- Development: `localhost:3326` - Development: `localhost:3326`
- Docker Compose: `nocr-users-db:3306` - Docker Compose: `nocr-users-db:3306`
- Kubernetes: `users-db-service:3306` - Kubernetes: `users-db-service:3306`
@ -353,7 +353,7 @@ This is why we removed `appsettings.protected.json` - it was overriding Docker C
### Telegram Client ### Telegram Client
**Required Configuration:** **Обязательная конфигурация:**
```json ```json
{ {
@ -372,45 +372,45 @@ This is why we removed `appsettings.protected.json` - it was overriding Docker C
} }
``` ```
**How to obtain Bot Token:** **Как получить Bot Token:**
1. Open Telegram and find @BotFather 1. Откройте Telegram и найдите @BotFather
2. Send `/newbot` command 2. Отправьте команду `/newbot`
3. Follow instructions to create your bot 3. Следуйте инструкциям для создания вашего бота
4. Copy the token provided 4. Скопируйте предоставленный токен
--- ---
## Debug Mode ## Режим отладки
Enable debug mode to print masked configuration values on startup. Включите режим отладки для вывода маскированных значений конфигурации при запуске.
### How to Enable ### Как включить
Set environment variable: Установите переменную окружения:
```bash ```bash
export NOCR_DEBUG_MODE=true export NOCR_DEBUG_MODE=true
``` ```
Or in `.nocr.env`: Или в `.nocr.env`:
```bash ```bash
NOCR_DEBUG_MODE=true NOCR_DEBUG_MODE=true
``` ```
Or in `docker-compose.yml`: Или в `docker-compose.yml`:
```yaml ```yaml
environment: environment:
NOCR_DEBUG_MODE: "true" NOCR_DEBUG_MODE: "true"
``` ```
### Example Output ### Пример вывода
When debug mode is enabled, you'll see masked configuration on startup: Когда режим отладки включен, вы увидите маскированную конфигурацию при запуске:
``` ```
=== [NOCR_DEBUG] Configuration Values === === [NOCR_DEBUG] Значения конфигурации ===
[NOCR_DEBUG] RebusRabbitMqOptions: [NOCR_DEBUG] RebusRabbitMqOptions:
[NOCR_DEBUG] ConnectionString: amqp://admin:***@nocr-rabbitmq:5672/ [NOCR_DEBUG] ConnectionString: amqp://admin:***@nocr-rabbitmq:5672/
[NOCR_DEBUG] InputQueueName: nocr.telegram.listener.queue [NOCR_DEBUG] InputQueueName: nocr.telegram.listener.queue
@ -420,144 +420,144 @@ When debug mode is enabled, you'll see masked configuration on startup:
[NOCR_DEBUG] ApiId: 22101230 [NOCR_DEBUG] ApiId: 22101230
[NOCR_DEBUG] ApiHash: c7...0b [NOCR_DEBUG] ApiHash: c7...0b
[NOCR_DEBUG] PhoneNumber: 79...11 [NOCR_DEBUG] PhoneNumber: 79...11
=== [NOCR_DEBUG] End Configuration === === [NOCR_DEBUG] Конец конфигурации ===
``` ```
**Security:** Passwords and secrets are automatically masked to protect sensitive data. **Безопасность:** Пароли и секреты автоматически маскируются для защиты конфиденциальных данных.
--- ---
## Troubleshooting ## Устранение неполадок
### Problem: Service can't connect to RabbitMQ ### Проблема: Сервис не может подключиться к RabbitMQ
**Symptoms:** **Симптомы:**
``` ```
Connection refused to nocr-rabbitmq:5672 Connection refused to nocr-rabbitmq:5672
``` ```
**Solution:** **Решение:**
1. Check if RabbitMQ is running: `docker-compose ps nocr-rabbitmq` 1. Проверьте, работает ли RabbitMQ: `docker-compose ps nocr-rabbitmq`
2. Enable debug mode: `NOCR_DEBUG_MODE=true` 2. Включите режим отладки: `NOCR_DEBUG_MODE=true`
3. Verify ConnectionString in debug output 3. Проверьте ConnectionString в выводе отладки
4. For Docker Compose, ensure hostname is `nocr-rabbitmq:5672`, NOT `localhost:5672` 4. Для Docker Compose убедитесь, что hostname - `nocr-rabbitmq:5672`, НЕ `localhost:5672`
5. Check if environment variable is overriding appsettings 5. Проверьте, не переопределяет ли переменная окружения appsettings
--- ---
### Problem: Service can't connect to database ### Проблема: Сервис не может подключиться к базе данных
**Symptoms:** **Симптомы:**
``` ```
Unable to connect to any of the specified MySQL hosts Unable to connect to any of the specified MySQL hosts
``` ```
**Solution:** **Решение:**
1. Check database is running: `docker-compose ps nocr-text-matcher-db` 1. Проверьте, что база данных запущена: `docker-compose ps nocr-text-matcher-db`
2. Enable debug mode to see masked connection string 2. Включите режим отладки для просмотра маскированной строки подключения
3. Verify hostname in connection string: 3. Проверьте hostname в строке подключения:
- Docker Compose: `nocr-text-matcher-db:3306` - Docker Compose: `nocr-text-matcher-db:3306`
- Local dev: `localhost:3316` - Локальная разработка: `localhost:3316`
4. Wait for database healthcheck to pass (may take 10-30 seconds on first start) 4. Подождите прохождения healthcheck (может занять 10-30 секунд при первом запуске)
--- ---
### Problem: Configuration values not applied ### Проблема: Значения конфигурации не применяются
**Symptoms:** **Симптомы:**
- Settings in `appsettings.Development.json` are ignored - Настройки в `appsettings.Development.json` игнорируются
- Service uses wrong configuration - Сервис использует неправильную конфигурацию
**Solution:** **Решение:**
1. Check `ASPNETCORE_ENVIRONMENT` is set correctly: 1. Проверьте, что `ASPNETCORE_ENVIRONMENT` установлен правильно:
- VS Code: `Development` - VS Code: `Development`
- Docker Compose: `DockerCompose` - Docker Compose: `DockerCompose`
- K8s: `Production` - K8s: `Production`
2. Enable debug mode to see which values are loaded 2. Включите режим отладки для просмотра загруженных значений
3. Check for environment variables overriding your settings 3. Проверьте переменные окружения, переопределяющие ваши настройки
4. Remember: **Environment Variables always win!** 4. Помните: **Переменные окружения всегда выигрывают!**
--- ---
### Problem: Secrets exposed in logs ### Проблема: Секреты раскрыты в логах
**Solution:** **Решение:**
- Debug mode automatically masks sensitive values - Режим отладки автоматически маскирует конфиденциальные значения
- Never log configuration in production without masking - Никогда не логируйте конфигурацию в production без маскировки
- Review debug output format in `Startup.cs`: - Проверьте формат вывода отладки в `Startup.cs`:
- Passwords: `amqp://user:***@host` - Пароли: `amqp://user:***@host`
- Secrets: `ab...yz` (first 2 + last 2 chars only) - Секреты: `ab...yz` (только первые 2 + последние 2 символа)
--- ---
### Problem: Missing .nocr.env file ### Проблема: Отсутствует файл .nocr.env
**Symptoms:** **Симптомы:**
``` ```
docker-compose up fails with missing environment variables docker-compose up падает с ошибкой отсутствующих переменных окружения
``` ```
**Solution:** **Решение:**
1. Copy example file: `cp .nocr.env.example .nocr.env` 1. Скопируйте файл-пример: `cp .nocr.env.example .nocr.env`
2. Fill in your actual values 2. Заполните ваши реальные значения
3. Make sure `.nocr.env` is in the project root (same level as `docker-compose.yml`) 3. Убедитесь, что `.nocr.env` находится в корне проекта (на том же уровне, что и `docker-compose.yml`)
--- ---
## Quick Reference ## Краткая справка
### Environment Variable Naming Convention ### Соглашение об именовании переменных окружения
ASP.NET Core uses double underscores `__` to represent nested JSON structure: ASP.NET Core использует двойное подчеркивание `__` для представления вложенной JSON-структуры:
```bash ```bash
# JSON: { "Section": { "Key": "Value" } } # JSON: { "Section": { "Key": "Value" } }
# Environment Variable: # Переменная окружения:
Section__Key=Value Section__Key=Value
# Example: # Примеры:
WTelegramClientOptions__ApiId=12345 WTelegramClientOptions__ApiId=12345
RebusRabbitMqOptions__ConnectionString=amqp://localhost RebusRabbitMqOptions__ConnectionString=amqp://localhost
``` ```
### File Locations ### Расположение файлов
``` ```
flea/ # Project root flea/ # Корень проекта
├── .nocr.env # Your secrets (gitignored) ├── .nocr.env # Ваши секреты (gitignored)
├── .nocr.env.example # Template (committed) ├── .nocr.env.example # Шаблон (коммитится)
├── docker-compose.yml # References .nocr.env ├── docker-compose.yml # Ссылается на .nocr.env
├── telegram-listener/ ├── telegram-listener/
│ └── src/Nocr.TelegramListener.Host/ │ └── src/Nocr.TelegramListener.Host/
│ ├── appsettings.json # Base (committed) │ ├── appsettings.json # Базовый (коммитится)
│ ├── appsettings.Development.json # Local dev (gitignored) │ ├── appsettings.Development.json # Локальная разработка (gitignored)
│ ├── appsettings.Development.json.example # Template (committed) │ ├── appsettings.Development.json.example # Шаблон (коммитится)
│ ├── appsettings.DockerCompose.json # Docker (gitignored) │ ├── appsettings.DockerCompose.json # Docker (gitignored)
│ └── appsettings.DockerCompose.json.example # Template (committed) │ └── appsettings.DockerCompose.json.example # Шаблон (коммитится)
└── [same structure for text-matcher, users, telegram-client] └── [та же структура для text-matcher, users, telegram-client]
``` ```
--- ---
## Security Best Practices ## Рекомендации по безопасности
1. ✅ **Never commit secrets** - Use `.example` files as templates 1. ✅ **Никогда не коммитьте секреты** - используйте файлы `.example` как шаблоны
2. ✅ **Use environment variables** for sensitive data in production 2. ✅ **Используйте переменные окружения** для конфиденциальных данных в production
3. ✅ **Use K8s Secrets** for Kubernetes deployments 3. ✅ **Используйте K8s Secrets** для развертывания в Kubernetes
4. ✅ **Enable debug mode** only when troubleshooting 4. ✅ **Включайте режим отладки** только при устранении неполадок
5. ✅ **Rotate credentials** regularly 5. ✅ **Ротируйте учетные данные** регулярно
6. ✅ **Use User Secrets** for local development (optional) 6. ✅ **Используйте User Secrets** для локальной разработки (опционально)
7. ❌ **Don't commit** `.nocr.env` or `appsettings.*.json` (except `.example` files) 7. ❌ **Не коммитьте** `.nocr.env` или `appsettings.*.json` (кроме файлов `.example`)
8. ❌ **Don't use** `appsettings.protected.json` (removed from code) 8. ❌ **Не используйте** `appsettings.protected.json` (удален из кода)
--- ---
## Additional Resources ## Дополнительные ресурсы
- [ASP.NET Core Configuration Docs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) - [Документация конфигурации ASP.NET Core](https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/configuration/)
- [Docker Compose Environment Variables](https://docs.docker.com/compose/environment-variables/) - [Переменные окружения Docker Compose](https://docs.docker.com/compose/environment-variables/)
- [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) - [Kubernetes Secrets](https://kubernetes.io/ru/docs/concepts/configuration/secret/)
- [Telegram API Documentation](https://core.telegram.org/api) - [Документация Telegram API](https://core.telegram.org/api)
- [Telegram Bot API](https://core.telegram.org/bots/api) - [Telegram Bot API](https://core.telegram.org/bots/api)

450
README.md
View File

@ -1,22 +1,446 @@
# Спец проект для работы с зависимостями, внешними и внутренними сервисами и приложениями. # NOCR - Система мониторинга Telegram-каналов
Аккумуляция всех вложенных проектов. [![Build Status](https://drone.musk.fun/api/badges/nocr/flea/status.svg?ref=refs/heads/main)](https://drone.musk.fun/nocr/flea)
[telegram-listener](https://gitea.musk.fun/nocr/telegram-listener) Микросервисная система на базе Telegram Bot для мониторинга и уведомлений о текстовых совпадениях в открытых каналах и чатах.
Модуль, отвечающий за скан открытых каналов и чатов.
[telegram-client](https://gitea.musk.fun/nocr/telegram-client) ## 📋 Обзор проекта
Модуль, отвечающий за клиентский интерфейс самого бота взаимодействия с пользователем.
[users](https://gitea.musk.fun/nocr/users) Это родительский проект, содержащий все сервисы как **git submodules**. Каждый сервис является отдельным репозиторием, что обеспечивает независимую разработку и версионирование.
Модуль, отвечающий за управление пользователями сервиса и их предпочтениями.
[text-matcher](https://gitea.musk.fun/nocr/text-matcher) ### Архитектура сервисов
Модуль, отвечающий за сравнивание всего объема подписанных сообщений через listener по пересечению с пользовательскими интересами.
Система состоит из 4 основных микросервисов, взаимодействующих через RabbitMQ:
#### 1. [telegram-listener](https://gitea.musk.fun/nocr/telegram-listener)
**Назначение:** Сканирование открытых Telegram-каналов и чатов
- Подписка на каналы через Telegram MTProto API
- Публикация событий о новых и отредактированных сообщениях в message bus
- Хранение сессий пользователей
#### 2. [text-matcher](https://gitea.musk.fun/nocr/text-matcher)
**Назначение:** Сопоставление сообщений с пользовательскими подписками
- Обработка событий от telegram-listener
- Проверка совпадений по регулярным выражениям и ключевым словам
- Хранение истории совпадений в БД (MariaDB)
- Публикация событий о найденных совпадениях
#### 3. [telegram-client](https://gitea.musk.fun/nocr/telegram-client)
**Назначение:** Клиентский интерфейс бота для взаимодействия с пользователями
- Прием команд от пользователей через Telegram Bot API
- Управление подписками на каналы
- Отправка уведомлений о найденных совпадениях
- Интеграция с users и text-matcher через REST API
#### 4. [users](https://gitea.musk.fun/nocr/users)
**Назначение:** Управление пользователями и их настройками
- CRUD операции для пользователей
- Хранение предпочтений и подписок
- REST API для других сервисов
--- ---
docker-compose.yml файл используется для старта проектов и зависисмостей внутри докера. Конфигурация хостовых сервисов опирается на конфигурационный файл общего назначения `appsettings.json` и специфичный для докера `appsettings.DockerCompose.json` (частный файл). Однако общие чувствительные для всех видов запуска секреты хранятся в `appsettings.protected.json`. Для личного пользования и отдельного запуска проектов необходимо двигаться по пути `appsettings.Development.json`. ## 🚀 Быстрый старт
# Статус сборки drone: ### Вариант 1: Docker Compose (рекомендуется)
[![Build Status](https://drone.musk.fun/api/badges/nocr/flea/status.svg?ref=refs/heads/main)](https://drone.musk.fun/nocr/flea)
```bash
# 1. Создайте файл с секретами
cp .nocr.env.example .nocr.env
# 2. Заполните ваши Telegram API ключи в .nocr.env
# - API ID и Hash: https://my.telegram.org/apps
# - Bot Token: @BotFather в Telegram
# 3. Запустите все сервисы
docker-compose up
```
**Проверка работоспособности:**
- Telegram Listener: http://localhost:5040/health
- Text Matcher: http://localhost:5041/health
- Users: http://localhost:5042/health
- Telegram Client: http://localhost:5050/health
- RabbitMQ UI: http://localhost:15672 (admin/admin)
### Вариант 2: Локальная разработка (VS Code)
```bash
# 1. Запустите инфраструктуру (RabbitMQ + базы данных)
docker-compose up nocr-rabbitmq nocr-text-matcher-db nocr-users-db -d
# 2. Создайте конфигурационные файлы для каждого сервиса
cd telegram-listener/src/Nocr.TelegramListener.Host
cp appsettings.Development.json.example appsettings.Development.json
# Повторите для всех сервисов
# 3. Заполните ваши секреты в appsettings.Development.json
# 4. Запустите сервисы через VS Code (F5)
```
**📖 Подробная документация:** См. [CONFIGURATION.md](CONFIGURATION.md)
---
## 🏗️ Архитектура взаимодействия
```
┌──────────────────┐ ┌───────────────┐ ┌──────────────────┐
│ Telegram Channel │────────▶│ Listener │────────▶│ Text Matcher │
│ (MTProto API) │ │ (Scan msgs) │ │ (Match rules) │
└──────────────────┘ └───────┬───────┘ └────────┬─────────┘
│ │
│ │
│ RabbitMQ │
│ Message Bus │
│ │
▼ ▼
┌───────────────┐ ┌──────────────────┐
│ Event Topics │ │ Telegram Bot │
& Queues │◀────────│ Client │
└───────────────┘ │ (Notifications) │
└──────────────────┘
┌──────────────────┐
│ Users Service │
│ (User Management)│
└──────────────────┘
```
**Паттерн взаимодействия:**
- **Event-driven**: Асинхронная обработка через RabbitMQ
- **REST API**: Синхронные запросы между telegram-client и другими сервисами
- **Clean Architecture**: Разделение на слои Host/AppServices/Core/Persistence
**📖 Подробная диаграмма:** См. [architecture.md](architecture.md)
---
## 🔧 Технологический стек
- **.NET 8** - Фреймворк для всех сервисов
- **ASP.NET Core** - Web API
- **Entity Framework Core** - ORM для MariaDB
- **WTelegramClient** - MTProto API для telegram-listener
- **Telegram.Bot** - Bot API для telegram-client
- **Rebus** - Message bus поверх RabbitMQ
- **MariaDB** - Базы данных для text-matcher и users
- **RabbitMQ** - Брокер сообщений
- **Docker & Docker Compose** - Контейнеризация
- **Drone CI** - CI/CD на Kubernetes
---
## 📦 CI/CD Pipeline
Проект использует **Drone CI** с 5 специализированными пайплайнами:
### 1. 🧪 Feature Validation
**Триггер:** Push в ветки `feature/*`, `fix/*`, `issues/*`
**Длительность:** ~3-5 минут
- Сборка всех сервисов
- Запуск тестов с Testcontainers
- Быстрая обратная связь перед PR
### 2. ✅ Main Validation
**Триггер:** Push в ветку `main`
**Длительность:** ~3-5 минут
- Валидация после мерджа в main
- Гарантия стабильности основной ветки
### 3. 📝 Contracts-Only Publish
**Триггер:** Tag с сообщением `contracts_only:<service>`
**Длительность:** ~2 минуты
- Публикация NuGet-пакетов контрактов
- Без сборки Docker-образов
```bash
git commit -m "contracts_only:telegram_listener - Add MessageEdited event"
git tag v1.2.4-contracts && git push origin v1.2.4-contracts
```
### 4. 🚀 Full Release
**Триггер:** Tag без специальных маркеров
**Длительность:** ~8-10 минут
1. Публикация всех контрактов (параллельно)
2. Сборка Docker-образов с Kaniko (параллельно)
3. Деплой в Kubernetes (для тегов `v*`)
```bash
git tag v1.3.0 && git push origin v1.3.0
```
### 5. ⚡ Deploy-Only
**Триггер:** Tag с сообщением `deploy_only:`
**Длительность:** ~1 минута
- Деплой существующих образов без пересборки
- Откат к предыдущей версии
```bash
git commit --allow-empty -m "deploy_only: Rollback to v1.2.9"
git tag v1.2.9-deploy && git push origin v1.2.9-deploy
```
**📖 Полная документация CI/CD:** См. [_deploy/README.md](_deploy/README.md)
---
## 🛠️ Команды разработки
### Сборка
```bash
# Сборка конкретного сервиса
cd telegram-listener && dotnet build
# Сборка всех сервисов
dotnet build telegram-listener/Nocr.TelegramListener.sln
dotnet build telegram-client/Nocr.TelegramClient.sln
dotnet build text-matcher/Nocr.TextMatcher.sln
dotnet build users/Nocr.Users.sln
```
### Тестирование
```bash
# Запуск тестов (text-matcher)
cd text-matcher && dotnet test
```
### Миграции БД
```bash
# Добавить новую миграцию
cd text-matcher
./src/Nocr.TextMatcher.Migrator/AddMigration.sh MyMigrationName
cd users
./src/Nocr.Users.Migrator/AddMigration.sh MyMigrationName
# Применение миграций автоматически при запуске через docker-compose
```
### Работа с субмодулями
```bash
# Инициализация субмодулей после клонирования
git submodule update --init --recursive
# Обновление всех субмодулей до последних коммитов
git submodule update --remote --merge
# Автокоммит изменений в субмодулях (скрипт)
./commit-submodules.sh "Update submodules"
```
---
## 📊 Порты сервисов
При запуске через Docker Compose:
| Сервис | Порт | Health Check |
|--------|------|--------------|
| telegram-listener | 5040 | http://localhost:5040/health |
| text-matcher | 5041 | http://localhost:5041/health |
| users | 5042 | http://localhost:5042/health |
| telegram-client | 5050 | http://localhost:5050/health |
| RabbitMQ AMQP | 5672 | - |
| RabbitMQ Management | 15672 | http://localhost:15672 |
| MariaDB (text-matcher) | 3316 | - |
| MariaDB (users) | 3326 | - |
---
## 🔐 Конфигурация
### Приоритет источников конфигурации
1. **appsettings.json** (базовые настройки)
2. **appsettings.{Environment}.json** (Development/DockerCompose/Production)
3. **User Secrets** (только для Development)
4. **Environment Variables** ⬆️ **НАИВЫСШИЙ ПРИОРИТЕТ**
### Важные правила
- ❌ **Никогда не коммитьте секреты** в git
- ✅ Используйте файлы `.example` как шаблоны
- ✅ Environment Variables переопределяют все остальное
- ✅ Docker Compose использует `.nocr.env` (gitignored)
- ✅ Kubernetes использует Secrets
### Режим отладки
Для вывода маскированных значений конфигурации:
```bash
export NOCR_DEBUG_MODE=true
```
**📖 Полное руководство:** См. [CONFIGURATION.md](CONFIGURATION.md)
---
## 📚 Документация
- [CONFIGURATION.md](CONFIGURATION.md) - Подробное руководство по конфигурации
- [_deploy/README.md](_deploy/README.md) - CI/CD пайплайны и деплой
- [architecture.md](architecture.md) - Архитектурная диаграмма
- [CLAUDE.md](CLAUDE.md) - Инструкции для Claude Code AI
### Документация сервисов
- [telegram-listener/README.md](telegram-listener/README.md)
- [telegram-client/README.md](telegram-client/README.md)
- [text-matcher/README.md](text-matcher/README.md)
- [users/README.md](users/README.md)
---
## 🤝 Рабочий процесс разработки
### Создание новой функции
```bash
# 1. Создайте feature-ветку
git checkout -b feature/add-filter
# 2. Работайте в субмодуле
cd telegram-listener
git checkout -b feature/add-filter
# Внесите изменения...
git commit -m "Add new filter"
git push origin feature/add-filter
# 3. Обновите родительский проект
cd ..
git add telegram-listener
git commit -m "Update telegram-listener: Add new filter"
git push origin feature/add-filter
# 4. Создайте PR и дождитесь прохождения CI
```
### Релиз новой версии
```bash
# 1. Убедитесь, что все изменения в main
git checkout main && git pull
# 2. Создайте тег
git tag v1.4.0 -m "Release v1.4.0: New features"
git push origin v1.4.0
# 3. Drone автоматически:
# - Опубликует NuGet-пакеты
# - Соберет Docker-образы
# - Задеплоит в Kubernetes (для v* тегов)
```
---
## 🐛 Troubleshooting
### Сервис не подключается к RabbitMQ
```bash
# 1. Проверьте, что RabbitMQ запущен
docker-compose ps nocr-rabbitmq
# 2. Включите режим отладки
export NOCR_DEBUG_MODE=true
# 3. Проверьте ConnectionString в логах
# Для Docker: должно быть nocr-rabbitmq:5672, НЕ localhost:5672
```
### Ошибка подключения к базе данных
```bash
# 1. Проверьте статус БД
docker-compose ps nocr-text-matcher-db
# 2. Подождите прохождения healthcheck (10-30 сек при первом запуске)
# 3. Проверьте порт в connection string:
# - Docker: nocr-text-matcher-db:3306
# - Local: localhost:3316
```
### Pipeline падает на тестах
```bash
# 1. Проверьте, что Testcontainers может подключиться к Docker
# В pipeline должна быть переменная DOCKER_HOST
# 2. Запустите тесты локально
cd text-matcher
dotnet test
# 3. Проверьте логи в Drone UI
```
---
## 📈 Мониторинг
### Проверка здоровья сервисов
```bash
# Локально
curl http://localhost:5040/health # telegram-listener
curl http://localhost:5041/health # text-matcher
curl http://localhost:5042/health # users
curl http://localhost:5050/health # telegram-client
# В Kubernetes (из deploy pod)
cd _deploy/scripts
./health-check.sh
```
### Просмотр логов
```bash
# Docker Compose
docker-compose logs -f telegram-listener
docker-compose logs -f text-matcher
# Kubernetes
kubectl logs -f deployment/telegram-listener -n nocr
kubectl logs -f deployment/text-matcher -n nocr
```
---
## 📝 История изменений (15-16 октября 2025)
### Основные улучшения
- ✨ **Новый CI/CD**: 5 специализированных пайплайнов Drone
- 🔧 **Исправления .drone.yml**: Корректная обработка YAML с многострочными командами
- 📦 **Contracts-only пайплайн**: Быстрая публикация контрактов без пересборки образов
- 🚀 **Deploy-only пайплайн**: Быстрый откат и деплой существующих образов
- 🔄 **MessageEdited события**: Отдельная обработка отредактированных сообщений
- 📝 **Автокоммит субмодулей**: Скрипт для упрощения работы с субмодулями
- ⚡ **Оптимизации**: Shared NuGet cache, параллельная сборка, Kaniko caching
### Исправленные проблемы
- 🐛 Исправлен парсинг YAML в `.drone.yml` (строка 126)
- 🐛 Корректная обработка `${DRONE_COMMIT_SHA:0:7}` в командах
- 🐛 Правильные отступы в многострочных bash-скриптах
- 🐛 Зависимости между этапами pipeline
---
## 📄 Лицензия
[Укажите лицензию вашего проекта]
## 👥 Авторы
[Укажите авторов проекта]
---
**Дополнительные ссылки:**
- [Drone CI](https://drone.musk.fun/nocr/flea)
- [Gitea Repository](https://gitea.musk.fun/nocr/flea)

View File

@ -1,208 +1,228 @@
# 🚀 Nocr CI/CD Pipeline Documentation # 🚀 Документация CI/CD Pipeline NOCR
## 📋 Overview ## 📋 Обзор
The Nocr project uses a modern, multi-pipeline CI/CD setup powered by Drone CI on Kubernetes. This document describes the 5 specialized pipelines and how to use them. Проект NOCR использует современную многопайплайновую CI/CD систему на базе Drone CI, работающую на Kubernetes. Этот документ описывает 5 специализированных пайплайнов и способы их использования.
--- ---
## 🎯 Pipeline Architecture ## 🎯 Архитектура пайплайнов
### Pipeline 1: **Feature Validation** ### Пайплайн 1: **Feature Validation (Валидация функциональных веток)**
**Trigger:** Push to `feature/*` or `fix/*` branches **Триггер:** Push в ветки `feature/*`, `fix/*` или `issues/*`
**Purpose:** Fast feedback for developers **Назначение:** Быстрая обратная связь для разработчиков
**Duration:** ~3-5 minutes **Длительность:** ~3-5 минут
**What it does:** **Что делает:**
- Clones repo with submodules - Клонирует репозиторий с субмодулями
- Restores all NuGet packages (shared cache) - Восстанавливает все NuGet-пакеты (общий кэш)
- Builds all 4 services in Release mode - Собирает все 4 сервиса в Debug-режиме
- Runs unit and integration tests with Testcontainers - Запускает unit и integration тесты с Testcontainers
**Example workflow:** **Пример рабочего процесса:**
```bash ```bash
git checkout -b feature/add-new-filter git checkout -b feature/add-new-filter
# Make changes... # Внесите изменения...
git add . git add .
git commit -m "Add new filter functionality" git commit -m "Add new filter functionality"
git push origin feature/add-new-filter git push origin feature/add-new-filter
``` ```
Drone automatically runs tests. Check results before creating PR. Drone автоматически запустит тесты. Проверьте результаты перед созданием PR.
--- ---
### Pipeline 2: **Main Validation** ### Пайплайн 2: **Main Validation (Валидация основной ветки)**
**Trigger:** Push to `main` branch **Триггер:** Push в ветку `main`
**Purpose:** Validate main branch after merge **Назначение:** Валидация основной ветки после мерджа
**Duration:** ~3-5 minutes **Длительность:** ~3-5 минут
**What it does:** **Что делает:**
- Same as Feature Validation - То же, что и Feature Validation
- Ensures main branch is always in working state - Гарантирует, что ветка main всегда в рабочем состоянии
**Example workflow:** **Пример рабочего процесса:**
```bash ```bash
# After PR is merged to main # После мерджа PR в main
# Pipeline runs automatically # Пайплайн запускается автоматически
``` ```
--- ---
### Pipeline 3: **Contracts-Only Publish** ### Пайплайн 3: **Contracts-Only Publish (Публикация только контрактов)**
**Trigger:** Tag with commit message containing `contracts_only:<service>` **Триггер:** Тег с сообщением коммита, содержащим `contracts_only:<service>`
**Purpose:** Fast publish of contract packages without building images **Назначение:** Быстрая публикация NuGet-пакетов контрактов без сборки Docker-образов
**Duration:** ~2 minutes **Длительность:** ~2 минуты
**What it does:** **Что делает:**
- Packs specified service contracts into NuGet packages - Упаковывает контракты указанного сервиса в NuGet-пакеты
- Publishes to internal NuGet feed - Публикует во внутреннем NuGet-фиде
- Skips Docker image builds - Пропускает сборку Docker-образов
**Example workflow:** **Пример рабочего процесса:**
```bash ```bash
# Update telegram-listener contracts # Обновите контракты telegram-listener
cd telegram-listener cd telegram-listener
# Make changes to Async.Api.Contracts... # Внесите изменения в Async.Api.Contracts...
git add . git add .
git commit -m "contracts_only:telegram_listener - Add MessageEdited event" git commit -m "contracts_only:telegram_listener - Add MessageEdited event"
git push origin main git push origin main
# Create tag # Создайте тег
git tag v1.2.4-contracts git tag v1.2.4-contracts
git push origin v1.2.4-contracts git push origin v1.2.4-contracts
``` ```
**Supported markers:** **Поддерживаемые маркеры:**
- `contracts_only:telegram_listener` - `contracts_only:telegram_listener`
- `contracts_only:text_matcher` - `contracts_only:text_matcher`
- `contracts_only:users` - `contracts_only:users`
--- ---
### Pipeline 4: **Full Release** ### Пайплайн 4: **Full Release (Полный релиз)**
**Trigger:** Tag on main WITHOUT `contracts_only` or `deploy_only` in commit message **Триггер:** Тег на main БЕЗ `contracts_only` или `deploy_only` в сообщении коммита
**Purpose:** Complete release cycle **Назначение:** Полный цикл релиза
**Duration:** ~8-10 minutes **Длительность:** ~8-10 минут
**What it does:** **Что делает:**
1. **Stage 1:** Publish all contracts to NuGet (parallel) 1. **Этап 1:** Публикация всех контрактов в NuGet (параллельно)
2. **Stage 2:** Build all Docker images with Kaniko (3 parallel streams) 2. **Этап 2:** Сборка всех Docker-образов с Kaniko (3 параллельных потока)
3. **Stage 3:** Deploy to Kubernetes (only for tags matching `v*`) 3. **Этап 3:** Деплой в Kubernetes (только для тегов, начинающихся с `v`)
**Example workflow:** **Пример рабочего процесса:**
```bash ```bash
# Ready to release # Готовы к релизу
git tag v1.3.0 git tag v1.3.0
git push origin v1.3.0 git push origin v1.3.0
# Drone will: # Drone выполнит:
# 1. Publish contracts # 1. Публикацию контрактов
# 2. Build images (tagged with v1.3.0, commit SHA, and latest) # 2. Сборку образов (с тегами v1.3.0, commit SHA, и latest)
# 3. Deploy to k8s (if tag starts with 'v') # 3. Деплой в k8s (если тег начинается с 'v')
``` ```
**Image tags created:** **Создаваемые теги образов:**
- `hub.musk.fun/k8s/nocr/telegram_listener:v1.3.0` - `hub.musk.fun/k8s/nocr/telegram_listener:v1.3.0`
- `hub.musk.fun/k8s/nocr/telegram_listener:abc1234` (commit SHA) - `hub.musk.fun/k8s/nocr/telegram_listener:abc1234` (commit SHA)
- `hub.musk.fun/k8s/nocr/telegram_listener:latest` - `hub.musk.fun/k8s/nocr/telegram_listener:latest`
--- ---
### Pipeline 5: **Deploy-Only** ### Пайплайн 5: **Deploy-Only (Только деплой)**
**Trigger:** Tag with commit message containing `deploy_only:` **Триггер:** Тег с сообщением коммита, содержащим `deploy_only: <version>`
**Purpose:** Fast deploy of already-built images **Назначение:** Быстрый деплой уже собранных образов
**Duration:** ~1 minute **Длительность:** ~1 минута
**What it does:** **Что делает:**
- Skips building - Пропускает сборку
- Deploys specified images to Kubernetes - Деплоит указанные образы в Kubernetes
- Useful for rolling back or promoting existing images - Полезно для отката или продвижения существующих образов
**Example workflow:** **ВАЖНО:** В сообщении коммита нужно указать версию образов для деплоя:
**Пример рабочего процесса:**
```bash ```bash
# Deploy existing images # Деплой существующих образов v1.2.9
git commit --allow-empty -m "deploy_only: Deploy v1.2.9" git commit --allow-empty -m "deploy_only: v1.2.9"
git tag v1.2.9-deploy git tag deploy-v1.2.9
git push origin v1.2.9-deploy git push origin deploy-v1.2.9
# Откат на предыдущую версию v1.2.8
git commit --allow-empty -m "deploy_only: v1.2.8"
git tag rollback-v1.2.8
git push origin rollback-v1.2.8
``` ```
**Формат commit message:**
- `deploy_only: v1.2.9` - деплоит образы с тегом `v1.2.9`
- Скрипт извлекает версию из сообщения и использует соответствующие образы
- Образы должны существовать в registry (собраны ранее через full-release)
--- ---
## 🛠️ Deployment Scripts ## 🛠️ Скрипты деплоя
All deployment scripts are located in `_deploy/scripts/`: Все скрипты деплоя находятся в `_deploy/scripts/`:
### `deploy.sh` ### `deploy.sh`
**Purpose:** Deploy services to Kubernetes **Назначение:** Деплой сервисов в Kubernetes
**Usage:** **Использование:**
```bash ```bash
./deploy.sh <tag> <commit-sha> # Full release (использует TAG для образов)
./deploy.sh v1.3.0 abc1234 ./deploy.sh v1.3.0 abc1234
# Deploy only (переопределяет тег образа)
./deploy.sh deploy-v1.2.9 def5678 v1.2.9
``` ```
**Features:** **Параметры:**
- Updates deployment manifests with new image tags 1. `tag` - Git тег (обязательный)
- Applies manifests to cluster 2. `commit-sha` - SHA коммита (опциональный)
- Waits for rollouts to complete with timeout 3. `image-tag-override` - Переопределение тега образа (опциональный, для deploy_only)
- Runs health checks after deployment
- Shows pod status **Возможности:**
- Обновляет deployment-манифесты новыми тегами образов
- Применяет манифесты в кластер
- Ожидает завершения rollout с таймаутом
- Запускает проверки здоровья после деплоя
- Показывает статус подов
### `rollback.sh` ### `rollback.sh`
**Purpose:** Rollback deployments to previous version **Назначение:** Откат деплоев к предыдущей версии
**Usage:** **Использование:**
```bash ```bash
# Rollback single service # Откат одного сервиса
./rollback.sh telegram-listener ./rollback.sh telegram-listener
# Rollback all services # Откат всех сервисов
./rollback.sh all ./rollback.sh all
``` ```
**Features:** **Возможности:**
- Shows revision history - Показывает историю ревизий
- Performs kubectl rollout undo - Выполняет kubectl rollout undo
- Waits for rollback to complete - Ожидает завершения отката
- Runs health checks after rollback - Запускает проверки здоровья после отката
### `health-check.sh` ### `health-check.sh`
**Purpose:** Check health of all Nocr services **Назначение:** Проверка здоровья всех сервисов NOCR
**Usage:** **Использование:**
```bash ```bash
./health-check.sh ./health-check.sh
``` ```
**Checks:** **Проверяет:**
- Pod status (Running/Ready) - Статус подов (Running/Ready)
- Health endpoints (/health) - Health-эндпоинты (/health)
- Recent events for failed pods - Последние события для упавших подов
--- ---
## 📦 Optimizations ## 📦 Оптимизации
### Shared NuGet Cache ### Общий NuGet-кэш
All pipelines use a shared temp volume for NuGet packages: Все пайплайны используют общий временный volume для NuGet-пакетов:
- First `dotnet restore` downloads packages - Первый `dotnet restore` скачивает пакеты
- Subsequent builds reuse cached packages - Последующие сборки переиспользуют кэшированные пакеты
- **~60% faster** than individual restores per service - **~60% быстрее**, чем индивидуальное восстановление для каждого сервиса
### Parallel Execution ### Параллельное выполнение
- Contract publishing: 3 services in parallel - Публикация контрактов: 3 сервиса параллельно
- Docker builds: 3 parallel streams - Сборка Docker: 3 параллельных потока
- Independent operations never block each other - Независимые операции никогда не блокируют друг друга
### Kaniko Caching ### Кэширование Kaniko
All Kaniko builds use: Все сборки Kaniko используют:
- `--cache=true` - Layer caching enabled - `--cache=true` - Включено кэширование слоев
- `--cache-repo=hub.musk.fun/k8s/cache/*` - Shared cache repo - `--cache-repo=hub.musk.fun/k8s/cache/*` - Общий репозиторий кэша
- `--compressed-caching=true` - Faster cache transfer - `--compressed-caching=true` - Быстрая передача кэша
--- ---
## 🧪 Testcontainers Support ## 🧪 Поддержка Testcontainers
Feature and Main validation pipelines include Docker-in-Docker service for Testcontainers: Пайплайны валидации Feature и Main включают Docker-in-Docker сервис для Testcontainers:
```yaml ```yaml
services: services:
@ -211,91 +231,149 @@ services:
privileged: true privileged: true
``` ```
Tests can use Testcontainers to spin up real databases, message queues, etc. Тесты могут использовать Testcontainers для запуска реальных баз данных, очередей сообщений и т.д.
--- ---
## 🔒 Required Secrets ## 🔒 Необходимые секреты
Configure these in Drone: Настройте эти секреты в Drone:
- `hub_username` - Docker registry username - `hub_username` - Имя пользователя Docker registry
- `hub_password` - Docker registry password - `hub_password` - Пароль Docker registry
- `nuget_musk_api_key` - NuGet feed API key - `nuget_musk_api_key` - API-ключ NuGet-фида
--- ---
## 📊 Pipeline Decision Tree ## 📊 Дерево решений пайплайна
``` ```
Push to feature/* → Feature Validation (build + test) Push в feature/* → Feature Validation (сборка + тесты)
Push to main → Main Validation (build + test) Push в main → Main Validation (сборка + тесты)
Tag + "contracts_only:" → Contracts Publish Тег + "contracts_only:" → Contracts Publish
Tag + "deploy_only:" → Deploy Only Тег + "deploy_only:" → Deploy Only
Tag (no markers) → Full Release (contracts → images → deploy) Тег (без маркеров) → Full Release (контракты → образы → деплой)
``` ```
--- ---
## 🎓 Best Practices ## 🎓 Лучшие практики
1. **Feature Branches** 1. **Функциональные ветки**
- Always create feature branches for new work - Всегда создавайте функциональные ветки для новой работы
- Let CI validate before merging to main - Дайте CI валидировать перед мерджем в main
2. **Contracts Changes** 2. **Изменения контрактов**
- Use `contracts_only:` for quick contract updates - Используйте `contracts_only:` для быстрого обновления контрактов
- Other services can update references immediately - Другие сервисы могут сразу обновить ссылки
3. **Release Process** 3. **Процесс релиза**
- Tag only from main branch - Создавайте теги только из ветки main
- Use semantic versioning (v1.2.3) - Используйте семантическое версионирование (v1.2.3)
- Tags starting with `v` auto-deploy to k8s - Теги, начинающиеся с `v`, автоматически деплоятся в k8s
4. **Emergency Rollback** 4. **Экстренный откат**
```bash ```bash
# Quick rollback via deploy-only # Быстрый откат через deploy-only
git commit --allow-empty -m "deploy_only: Rollback to v1.2.8" git commit --allow-empty -m "deploy_only: v1.2.8"
git tag v1.2.8-rollback git tag rollback-v1.2.8
git push origin v1.2.8-rollback git push origin rollback-v1.2.8
# Or use rollback script directly on the cluster # Или используйте скрипт отката напрямую в кластере
kubectl exec -it deploy-pod -- bash kubectl exec -it deploy-pod -- bash
cd /flea/_deploy/scripts cd /flea/_deploy/scripts
./rollback.sh all ./rollback.sh all
``` ```
5. **Monitoring Deployments** 5. **Мониторинг деплоев**
- Watch Drone UI for pipeline progress - Следите за UI Drone для прогресса пайплайна
- Check pod logs: `kubectl logs -f deployment/telegram-listener -n nocr` - Проверяйте логи подов: `kubectl logs -f deployment/telegram-listener -n nocr`
- Run health checks: `./_deploy/scripts/health-check.sh` - Запускайте проверки здоровья: `./_deploy/scripts/health-check.sh`
--- ---
## 🐛 Troubleshooting ## 🐛 Устранение неполадок
### Pipeline stuck on "Waiting for contracts" ### Пайплайн завис на "Waiting for contracts"
**Cause:** Contract publish failed **Причина:** Публикация контракта упала
**Solution:** Check NuGet feed, verify API key **Решение:** Проверьте NuGet-фид, проверьте API-ключ
### Docker build fails with "unauthorized" ### Сборка Docker падает с ошибкой "unauthorized"
**Cause:** Invalid registry credentials **Причина:** Неверные учетные данные registry
**Solution:** Update `hub_username` and `hub_password` secrets **Решение:** Обновите секреты `hub_username` и `hub_password`
### Tests fail with "Cannot connect to Docker daemon" ### Тесты падают с ошибкой "Cannot connect to Docker daemon"
**Cause:** Testcontainers can't reach Docker-in-Docker service **Причина:** Testcontainers не может достичь Docker-in-Docker сервиса
**Solution:** Check `DOCKER_HOST` environment variable is set correctly **Решение:** Проверьте, что переменная окружения `DOCKER_HOST` установлена правильно
### Deployment fails with "ImagePullBackOff" ### Деплой падает с ошибкой "ImagePullBackOff"
**Cause:** Image not found in registry **Причина:** Образ не найден в registry
**Solution:** Verify image was built and pushed successfully in previous step **Решение:** Проверьте, что образ был собран и запушен успешно на предыдущем этапе
### YAML-ошибки в .drone.yml
**Причина:** Неправильное форматирование многострочных команд или двоеточие в переменных
**Решение:**
- Используйте блоки `|` для многострочных команд
- Экранируйте переменные с двоеточиями (например, `"${VAR:0:7}"`)
- Проверьте YAML: `python3 -c "import yaml; list(yaml.safe_load_all(open('.drone.yml')))"`
--- ---
## 📚 Additional Resources ## 📚 Дополнительные ресурсы
- [Drone CI Documentation](https://docs.drone.io/) - [Документация Drone CI](https://docs.drone.io/)
- [Kaniko Documentation](https://github.com/GoogleContainerTools/kaniko) - [Документация Kaniko](https://github.com/GoogleContainerTools/kaniko)
- [Testcontainers for .NET](https://dotnet.testcontainers.org/) - [Testcontainers для .NET](https://dotnet.testcontainers.org/)
- [Kubernetes Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) - [Kubernetes Deployments](https://kubernetes.io/ru/docs/concepts/workloads/controllers/deployment/)
---
## 🔄 История изменений (15-16 октября 2025)
### Исправлена критическая проблема с YAML
**Проблема:**
- Ошибка `yaml: line 126: mapping values are not allowed in this context`
- Многострочные команды `/kaniko/executor` содержали переменные с двоеточием (`${DRONE_COMMIT_SHA:0:7}`)
- YAML парсер интерпретировал двоеточие как разделитель ключ:значение
**Решение:**
1. Обернули все команды `/kaniko/executor` в многострочные блоки с `|`
2. Добавили обратные слэши `\` для продолжения команды
3. Экранировали аргументы с двоеточиями в кавычках
**Пример:**
```yaml
# До (неправильно):
commands:
- /kaniko/executor
--destination=hub.musk.fun/image:${DRONE_COMMIT_SHA:0:7}
# После (правильно):
commands:
- |
/kaniko/executor \
--destination=hub.musk.fun/image:${DRONE_COMMIT_SHA:0:7}
```
### Добавлены новые функции
- ✨ Контракты-только пайплайн для быстрой итерации
- 🚀 Деплой-только пайплайн для откатов
- 🔄 Обработка MessageEdited событий
- 📝 Скрипт автокоммита субмодулей
- ⚡ Оптимизированный общий NuGet-кэш
- 🔧 Параллельная сборка и деплой
---
## 📞 Поддержка
Если у вас возникли проблемы с CI/CD:
1. Проверьте логи в Drone UI: https://drone.musk.fun/nocr/flea
2. Проверьте статус подов: `kubectl get pods -n nocr`
3. Запустите health-check: `./health-check.sh`
4. Проверьте логи контейнеров: `kubectl logs -f <pod-name> -n nocr`
Для вопросов по инфраструктуре обращайтесь к DevOps-команде.

View File

@ -4,20 +4,34 @@ set -e
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 🚀 Nocr Services Deployment Script # 🚀 Nocr Services Deployment Script
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Usage: ./deploy.sh <tag> <commit-sha> # Usage: ./deploy.sh <tag> [commit-sha] [image-tag-override]
# Example: ./deploy.sh v1.2.3 abc1234 # Examples:
# Full release: ./deploy.sh v1.2.3 abc1234
# Deploy only: ./deploy.sh deploy-v1.2.3 def5678 v1.2.3
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TAG=${1:-latest} TAG=${1:-latest}
COMMIT_SHA=${2:-latest} COMMIT_SHA=${2:-}
NAMESPACE="nocr" NAMESPACE="nocr"
DEPLOYMENT_FILE="../k8s/deployment.yaml" DEPLOYMENT_FILE="../k8s/deployment.yaml"
IMAGE_TAG_OVERRIDE=${3:-}
# Determine image tag to use
# Priority: 1) IMAGE_TAG_OVERRIDE (from command line), 2) TAG
if [ -n "$IMAGE_TAG_OVERRIDE" ]; then
IMAGE_TAG=$IMAGE_TAG_OVERRIDE
else
IMAGE_TAG=${TAG}
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 Starting deployment of Nocr services" echo "🚀 Starting deployment of Nocr services"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Tag: $TAG" echo "📦 Tag: $TAG"
echo "📝 Commit SHA: $COMMIT_SHA" echo "🏷️ Image Tag: $IMAGE_TAG"
if [ -n "$COMMIT_SHA" ]; then
echo "📝 Commit SHA: $COMMIT_SHA"
fi
echo "🎯 Namespace: $NAMESPACE" echo "🎯 Namespace: $NAMESPACE"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@ -42,12 +56,12 @@ cp "$DEPLOYMENT_FILE" "$TEMP_DEPLOYMENT"
echo "🔧 Updating image tags in deployment manifests..." echo "🔧 Updating image tags in deployment manifests..."
# Update image tags for all services # Update image tags for all services
sed -i "s|hub.musk.fun/k8s/nocr/telegram_listener:.*|hub.musk.fun/k8s/nocr/telegram_listener:${COMMIT_SHA}|g" "$TEMP_DEPLOYMENT" sed -i "s|hub.musk.fun/k8s/nocr/telegram_listener:.*|hub.musk.fun/k8s/nocr/telegram_listener:${IMAGE_TAG}|g" "$TEMP_DEPLOYMENT"
sed -i "s|hub.musk.fun/k8s/nocr/telegram_client:.*|hub.musk.fun/k8s/nocr/telegram_client:${COMMIT_SHA}|g" "$TEMP_DEPLOYMENT" sed -i "s|hub.musk.fun/k8s/nocr/telegram_client:.*|hub.musk.fun/k8s/nocr/telegram_client:${IMAGE_TAG}|g" "$TEMP_DEPLOYMENT"
sed -i "s|hub.musk.fun/k8s/nocr/text_matcher:.*|hub.musk.fun/k8s/nocr/text_matcher:${COMMIT_SHA}|g" "$TEMP_DEPLOYMENT" sed -i "s|hub.musk.fun/k8s/nocr/text_matcher:.*|hub.musk.fun/k8s/nocr/text_matcher:${IMAGE_TAG}|g" "$TEMP_DEPLOYMENT"
sed -i "s|hub.musk.fun/k8s/nocr/text_matcher_migrator:.*|hub.musk.fun/k8s/nocr/text_matcher_migrator:${COMMIT_SHA}|g" "$TEMP_DEPLOYMENT" sed -i "s|hub.musk.fun/k8s/nocr/text_matcher_migrator:.*|hub.musk.fun/k8s/nocr/text_matcher_migrator:${IMAGE_TAG}|g" "$TEMP_DEPLOYMENT"
sed -i "s|hub.musk.fun/k8s/nocr/users:.*|hub.musk.fun/k8s/nocr/users:${COMMIT_SHA}|g" "$TEMP_DEPLOYMENT" sed -i "s|hub.musk.fun/k8s/nocr/users:.*|hub.musk.fun/k8s/nocr/users:${IMAGE_TAG}|g" "$TEMP_DEPLOYMENT"
sed -i "s|hub.musk.fun/k8s/nocr/users_migrator:.*|hub.musk.fun/k8s/nocr/users_migrator:${COMMIT_SHA}|g" "$TEMP_DEPLOYMENT" sed -i "s|hub.musk.fun/k8s/nocr/users_migrator:.*|hub.musk.fun/k8s/nocr/users_migrator:${IMAGE_TAG}|g" "$TEMP_DEPLOYMENT"
echo "✅ Image tags updated" echo "✅ Image tags updated"
@ -60,11 +74,18 @@ rm "$TEMP_DEPLOYMENT"
echo "✅ Manifests applied" echo "✅ Manifests applied"
echo "" echo ""
echo "⏳ Waiting for rollouts to complete..."
# Force restart deployments to pull latest images
echo "🔄 Restarting deployments to pull new images..."
DEPLOYMENTS=("telegram-listener" "text-matcher" "users" "telegram-client")
for deployment in "${DEPLOYMENTS[@]}"; do
kubectl rollout restart deployment/"$deployment" -n "$NAMESPACE"
echo " ↻ Restarted $deployment"
done
echo "" echo ""
# Wait for each deployment to roll out echo "⏳ Waiting for rollouts to complete..."
DEPLOYMENTS=("telegram-listener" "text-matcher" "users" "telegram-client") echo ""
TIMEOUT="300s" TIMEOUT="300s"
for deployment in "${DEPLOYMENTS[@]}"; do for deployment in "${DEPLOYMENTS[@]}"; do

View File

@ -1,47 +1,389 @@
# Архитектура системы NOCR
Диаграмма взаимодействия между сервисами системы мониторинга Telegram-каналов.
```mermaid ```mermaid
classDiagram classDiagram
TelegramListener <|-- Bus TelegramListener --|> Bus : publishes
TextMatcher <|-- TelegramClient Bus --|> TextMatcher : subscribes
TextMatcher <|-- Bus TextMatcher --|> Bus : publishes
Bus <|-- TextMatcher Bus --|> TelegramClient : subscribes
TelegramClient <|-- Bus TelegramClient --|> Users : REST API
Users <|-- TelegramClient TelegramClient --|> TextMatcher : REST API
Bus <|-- TelegramListener
class Bus{ class Bus{
+2024-03-28 18:27:50.551 [info] Installing honnef.co/go/tools/cmd/staticcheck@latest (/Users/nazarovsa/go/bin/staticcheck) SUCCEEDED <<RabbitMQ Message Broker>>
+MessageReceived events
+MessageEdited events
+TextSubscriptionMatched events
+TextSubscriptionUpdated events
} }
class TelegramListener { class TelegramListener {
+MessageReceived <<Сканер каналов>>
-TextMatcher.TextMatchCreated +publishes: MessageReceived
+publishes: MessageEdited
---
WTelegramClient (MTProto API)
Хранение сессий пользователей
} }
class TextMatcher { class TextMatcher {
+TextMatchCreated <<Обработчик совпадений>>
+TextMatchMatched +publishes: TextSubscriptionMatched
-TelegramListener.MessageReceived +publishes: TextSubscriptionUpdated
+long Create(CreateTextMatchRequest request) -subscribes: MessageReceived
-subscribes: MessageEdited
---
REST API:
+long Create(CreateTextMatchRequest)
+TextMatchData? GetById(long id) +TextMatchData? GetById(long id)
+TextMatchData[] GetByUserId(long userId) +TextMatchData[] GetByUserId(long userId)
+void Delete(long id) +void Delete(long id)
+void Activate(long id) +void Activate(long id)
+void Disable(long id) +void Disable(long id)
---
БД: История совпадений (MariaDB)
Проверка регулярных выражений
} }
class Users { class Users {
+long Create(CreateUserRequest request) <<Управление пользователями>>
---
REST API:
+long Create(CreateUserRequest)
+UserData? GetById(long id) +UserData? GetById(long id)
+UserData? GetByIdentity(UserIdentityType identityType, string identity) +UserData? GetByIdentity(type, identity)
---
БД: Пользователи и настройки (MariaDB)
} }
class TelegramClient { class TelegramClient {
-TextMatcher.TextMatchMatched <<Telegram Bot интерфейс>>
-subscribes: TextSubscriptionMatched
-subscribes: TextSubscriptionUpdated
---
Telegram.Bot (Bot API)
Обработка команд пользователей
Отправка уведомлений
---
REST клиенты:
-Users.Create() -Users.Create()
-Users.GetById() -Users.GetById()
-Users.GetByIdentity() -Users.GetByIdentity()
-TextMatcher.GetByUserId() -TextMatcher.GetByUserId()
-TextMatcher.Create() -TextMatcher.Create()
} }
```
``` ## Описание компонентов
### 1. TelegramListener (Сканер каналов)
**Технологии:** .NET 8, WTelegramClient, Rebus
**Ответственность:**
- Подключение к Telegram через MTProto API
- Подписка на открытые каналы и чаты
- Получение новых и отредактированных сообщений
- Публикация событий в RabbitMQ
**Публикуемые события:**
- `MessageReceived` - новое сообщение получено
- `MessageEdited` - сообщение отредактировано
**Примечание:** WTelegram отправляет оба события (`UpdateNewChannelMessage` и `UpdateEditChannelMessage`) для одного и того же сообщения. TelegramListener публикует отдельные события, чтобы избежать дублирования уведомлений.
---
### 2. TextMatcher (Обработчик совпадений)
**Технологии:** .NET 8, Entity Framework Core, MariaDB, Rebus
**Ответственность:**
- Подписка на события сообщений от TelegramListener
- Проверка совпадений с пользовательскими подписками (regex, ключевые слова)
- Хранение истории совпадений в БД
- Публикация событий о найденных совпадениях
- REST API для управления подписками
**Подписывается на события:**
- `MessageReceived` - проверка нового сообщения
- `MessageEdited` - обновление ранее найденного совпадения
**Публикуемые события:**
- `TextSubscriptionMatched` - новое совпадение найдено
- `TextSubscriptionUpdated` - обновление ранее найденного совпадения
**REST API эндпоинты:**
- `POST /api/textmatch` - Создать новую подписку
- `GET /api/textmatch/{id}` - Получить подписку по ID
- `GET /api/textmatch/user/{userId}` - Получить все подписки пользователя
- `DELETE /api/textmatch/{id}` - Удалить подписку
- `POST /api/textmatch/{id}/activate` - Активировать подписку
- `POST /api/textmatch/{id}/disable` - Отключить подписку
---
### 3. TelegramClient (Telegram Bot)
**Технологии:** .NET 8, Telegram.Bot, Rebus, RestEase
**Ответственность:**
- Интерфейс бота для взаимодействия с пользователями
- Обработка команд бота (/start, /subscribe, /list и т.д.)
- Подписка на события совпадений
- Отправка уведомлений пользователям
- Интеграция с Users и TextMatcher через REST API
**Подписывается на события:**
- `TextSubscriptionMatched` - отправка уведомления о новом совпадении
- `TextSubscriptionUpdated` - уведомление об обновлении
**REST клиенты:**
- **Users API:** управление пользователями
- **TextMatcher API:** управление подписками
---
### 4. Users (Управление пользователями)
**Технологии:** .NET 8, Entity Framework Core, MariaDB
**Ответственность:**
- CRUD операции для пользователей
- Хранение предпочтений и настроек
- REST API для других сервисов
**REST API эндпоинты:**
- `POST /api/users` - Создать нового пользователя
- `GET /api/users/{id}` - Получить пользователя по ID
- `GET /api/users/identity/{type}/{identity}` - Найти пользователя по Telegram ID
---
### 5. RabbitMQ (Message Bus)
**Технологии:** RabbitMQ 3.x, Rebus
**Ответственность:**
- Асинхронная доставка событий между сервисами
- Гарантия доставки сообщений
- Очереди и обмены (exchanges)
**Настройка:**
- **Direct Exchange:** `nocr.direct` - для точечной маршрутизации
- **Topic Exchange:** `nocr.topics` - для публикации/подписки по топикам
- **Очереди:** Каждый сервис имеет свою input queue
---
## Паттерны взаимодействия
### Event-Driven Architecture (Асинхронная)
```
TelegramListener → RabbitMQ → TextMatcher → RabbitMQ → TelegramClient
```
**Преимущества:**
- Слабая связанность сервисов
- Масштабируемость (можно добавить несколько обработчиков)
- Отказоустойчивость (сообщения не теряются при падении сервиса)
### Request-Response (Синхронная)
```
TelegramClient → REST API → Users
TelegramClient → REST API → TextMatcher
```
**Использование:**
- Создание/получение данных пользователей
- Управление подписками
- Запросы, требующие немедленного ответа
---
## Потоки данных
### Основной поток (Мониторинг и уведомления)
1. **Сканирование:**
- TelegramListener подключается к Telegram MTProto
- Получает новые сообщения из каналов
- Публикует `MessageReceived` в RabbitMQ
2. **Обработка:**
- TextMatcher получает `MessageReceived`
- Проверяет сообщение на совпадение с подписками
- Сохраняет историю в БД
- Публикует `TextSubscriptionMatched` в RabbitMQ
3. **Уведомление:**
- TelegramClient получает `TextSubscriptionMatched`
- Получает данные пользователя из Users API
- Отправляет уведомление через Telegram Bot API
### Поток редактирования сообщений
1. **Обновление:**
- TelegramListener получает `UpdateEditChannelMessage`
- Публикует `MessageEdited` в RabbitMQ
2. **Повторная проверка:**
- TextMatcher получает `MessageEdited`
- Проверяет, было ли это сообщение ранее найдено
- Если было, публикует `TextSubscriptionUpdated`
3. **Уведомление об обновлении:**
- TelegramClient получает `TextSubscriptionUpdated`
- Отправляет обновленное уведомление пользователю
---
## Clean Architecture
Каждый сервис следует принципам Clean Architecture:
```
┌─────────────────────────────────────────┐
│ Host (API / Entry Point) │ ← ASP.NET Core, Controllers
├─────────────────────────────────────────┤
│ AppServices (Business Logic) │ ← Use Cases, Handlers
├─────────────────────────────────────────┤
│ Core (Domain / Shared) │ ← Entities, Interfaces
├─────────────────────────────────────────┤
│ Persistence (Data Access) │ ← EF Core, Repositories
├─────────────────────────────────────────┤
│ Async.Api.Contracts (Event Contracts) │ ← RabbitMQ Events (NuGet)
├─────────────────────────────────────────┤
│ Api.Contracts (REST Contracts) │ ← DTOs, Requests (NuGet)
├─────────────────────────────────────────┤
│ Migrator (Database Migrations) │ ← EF Core Migrations
└─────────────────────────────────────────┘
```
**Преимущества:**
- Независимость от фреймворков
- Тестируемость
- Независимость от UI/Database
- Переиспользование контрактов через NuGet
---
## Базы данных
### TextMatcher Database (MariaDB)
**Порт:** 3316 (локально), 3306 (Docker/K8s)
**Таблицы:**
- `TextMatches` - Пользовательские подписки
- `MatchHistory` - История найденных совпадений
- `__EFMigrationsHistory` - История миграций EF Core
### Users Database (MariaDB)
**Порт:** 3326 (локально), 3306 (Docker/K8s)
**Таблицы:**
- `Users` - Пользователи системы
- `UserPreferences` - Настройки пользователей
- `__EFMigrationsHistory` - История миграций EF Core
---
## Развертывание
### Docker Compose (Локальная разработка)
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ telegram- │ │ telegram- │ │ text- │ │ users │
│ listener │ │ client │ │ matcher │ │ │
│ :5040 │ │ :5050 │ │ :5041 │ │ :5042 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
└────────────────┴────────────────┴────────────────┘
┌─────────┴─────────┐
│ RabbitMQ │
│ :5672, :15672 │
└───────────────────┘
┌───────────────┴───────────────┐
│ │
┌───────┴────────┐ ┌────────┴────────┐
│ text-matcher- │ │ users-db │
│ db :3316 │ │ :3326 │
└────────────────┘ └─────────────────┘
```
### Kubernetes (Production)
- **Namespace:** nocr
- **Services:** ClusterIP для внутренней коммуникации
- **Deployments:** С health checks и resource limits
- **Secrets:** Для Telegram API credentials и Bot tokens
- **ConfigMaps:** Для конфигурации окружения
---
## Мониторинг и наблюдаемость
### Health Checks
Каждый сервис предоставляет `/health` эндпоинт:
- **telegram-listener:** http://localhost:5040/health
- **text-matcher:** http://localhost:5041/health
- **users:** http://localhost:5042/health
- **telegram-client:** http://localhost:5050/health
### Логирование
- Структурированное логирование через Serilog
- Логи выводятся в stdout для Docker/K8s
- Централизованное логирование через kubectl logs
### Метрики
- RabbitMQ Management UI: http://localhost:15672
- Мониторинг очередей и exchanges
- Просмотр активных подключений
---
## Масштабирование
### Горизонтальное масштабирование
**Можно масштабировать:**
- ✅ **TextMatcher** - несколько инстансов обрабатывают сообщения параллельно
- ✅ **TelegramClient** - несколько инстансов отправляют уведомления параллельно
- ✅ **Users** - stateless API, можно масштабировать без ограничений
**Ограничения:**
- ⚠️ **TelegramListener** - один инстанс на одну Telegram сессию (ограничение WTelegram)
### Вертикальное масштабирование
- Увеличение CPU/RAM для сервисов
- Оптимизация запросов к БД
- Настройка connection pools
---
## Безопасность
### Секреты
- Telegram API credentials хранятся в K8s Secrets
- Bot tokens хранятся в K8s Secrets
- Database credentials в environment variables
### Сетевая безопасность
- Все сервисы работают в приватной сети
- Только TelegramClient имеет доступ к внешним API
- RabbitMQ защищен credentials
### Валидация
- Входящие данные валидируются в AppServices
- REST API использует Data Annotations
- Event contracts строго типизированы
---
## Дополнительная информация
- **[README.md](README.md)** - Общая документация проекта
- **[CONFIGURATION.md](CONFIGURATION.md)** - Руководство по конфигурации
- **[_deploy/README.md](_deploy/README.md)** - CI/CD и деплой
- **[CLAUDE.md](CLAUDE.md)** - Инструкции для AI-ассистента
---
оследнее обновление: 16 октября 2025_

91
commit-all.sh Executable file
View File

@ -0,0 +1,91 @@
#!/bin/bash
# Check if commit message is provided
if [ -z "$1" ]; then
echo "Error: Commit message is required"
echo "Usage: ./commit-all.sh \"Your commit message\""
exit 1
fi
COMMIT_MSG="$1"
# Get list of submodules
SUBMODULES=$(git config --file .gitmodules --get-regexp path | awk '{ print $2 }')
if [ -z "$SUBMODULES" ]; then
echo "No submodules found in this repository"
exit 0
fi
echo "🚀 Starting commit and push for all submodules..."
echo "📝 Commit message: $COMMIT_MSG"
echo ""
# Counter for tracking results
SUCCESS_COUNT=0
FAILED_COUNT=0
SKIPPED_COUNT=0
# Iterate through each submodule
for submodule in $SUBMODULES; do
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Processing submodule: $submodule"
if [ ! -d "$submodule" ]; then
echo "⚠️ Warning: Submodule directory not found, skipping..."
SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
continue
fi
cd "$submodule" || {
echo "❌ Failed to enter directory $submodule"
FAILED_COUNT=$((FAILED_COUNT + 1))
cd - > /dev/null
continue
}
# Check if there are any changes
if [ -z "$(git status --porcelain)" ]; then
echo " No changes to commit, skipping..."
SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
cd - > /dev/null
continue
fi
# Add all changes
echo " → git add -A"
git add -A
# Commit changes
echo " → git commit"
if git commit -m "$COMMIT_MSG"; then
# Push to main branch
echo " → git push origin main"
if git push origin main; then
echo "✅ Successfully committed and pushed"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo "❌ Failed to push"
FAILED_COUNT=$((FAILED_COUNT + 1))
fi
else
echo "❌ Failed to commit"
FAILED_COUNT=$((FAILED_COUNT + 1))
fi
cd - > /dev/null
echo ""
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Summary:"
echo " ✅ Success: $SUCCESS_COUNT"
echo " ❌ Failed: $FAILED_COUNT"
echo " ⏭️ Skipped: $SKIPPED_COUNT"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ $FAILED_COUNT -gt 0 ]; then
exit 1
fi
exit 0

21
nuget.config Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="musk" value="https://gitea.musk.fun/api/packages/nocr/nuget/index.json" />
</packageSources>
<!-- Package Source Mapping for Central Package Management -->
<packageSourceMapping>
<!-- All Nocr.* packages come from the private 'musk' feed -->
<packageSource key="musk">
<package pattern="Nocr.*" />
</packageSource>
<!-- All other packages come from nuget.org -->
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>

16
prepare-build.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# Prepares the build environment by copying nuget.config to all submodules
# This is required for both local docker-compose builds and CI/CD
set -e
echo "📦 Copying nuget.config to all submodules..."
for submodule in telegram-client telegram-listener text-matcher users; do
if [ -d "$submodule" ]; then
cp nuget.config "$submodule/"
echo "✓ Copied to $submodule/"
fi
done
echo "✅ Build preparation complete!"

@ -1 +1 @@
Subproject commit b754ae1c5b50feb757237c2036e0e25432cdd1e3 Subproject commit e8ee01cbe809867305ff02611155cddffccbb401

@ -1 +1 @@
Subproject commit 7030e7bce2004ea342ceedf9eb5126077e267b72 Subproject commit 9ad504753550414edd96ef4194a3e0ee7e604e28

@ -1 +1 @@
Subproject commit e0b6beab7e6ccdf3d89b56d07abef3b619cbfa32 Subproject commit 1a55061e4b1b125d1b911f479fc830fb94cd93cc

2
users

@ -1 +1 @@
Subproject commit 3a28060108887845256c5b0a6dd3f189665cd1cd Subproject commit 30ae0c043ca2f8958223d6fb343b8b3e3561d727