Skip to content

Refactor Develop Continued#286

Draft
tsmarvin wants to merge 68 commits intodevelopfrom
refactor-develop2
Draft

Refactor Develop Continued#286
tsmarvin wants to merge 68 commits intodevelopfrom
refactor-develop2

Conversation

@tsmarvin
Copy link
Owner

@tsmarvin tsmarvin commented Feb 8, 2026

Description

Continued refactor of the in progress updates to PR#274

dependabot bot and others added 30 commits January 17, 2026 19:57
Bumps caddy from `efb93f7` to `b6424b4`.

---
updated-dependencies:
- dependency-name: caddy
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5.0.1 to 5.1.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](actions/setup-dotnet@2016bd2...baa11fb)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
)

* Remove FluentAssertions and convert assertions to MSTest
* Fix Apple Music link parser to properly handle query parameters
- Run formatter, fix line endings and encodings
In-Progress merge to reduce the number of PRs see the fan out of PRs for more context.
- Add initial ATProto user OAuth configuration
- Add genre caching functionality
- Overhaul ATProto process to add a unified session manager to vastly reduce the number of refreshSession and getSession calls we're making
- Update package lock files for CI with linux-x64 runtime (#256)
- Implement Initial ATProto OAuth token exchange with DPoP and fix base branch issues
- Add genre caching for playlist organization (#258)
- Fix additional bugs
…#259)

* - Add initial ATProto user OAuth configuration
- Add genre caching functionality

* Overhaul ATProto process to add a unified session manager to vastly reduce the number of refreshSession and getSession calls we're making

* Update package lock files for CI with linux-x64 runtime (#256)

* Initial plan

* Address non-resolved review comments: OAuth methods and Dockerfile improvements

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Move ATProtoOAuthResult to its own file in Records directory

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Implement full OAuth token exchange with DPoP support

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Improve redirect URI handling with constants and validation

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Address code review feedback: sealed modifier, error handling, and redirect URI fix

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Refactor: narrow try-catch scope and simplify JWT return

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Address review feedback: validation, error handling, and DPoP improvements

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Refine error handling and nbf timing for clock skew tolerance

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Clarify error messages for token response parsing

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Add comprehensive unit tests for ATProto OAuth service and DPoP JWT creation

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Fix HttpClient anti-pattern: use IHttpClientFactory instead of storing HttpClient

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Run formatter

* Enhance error handling and JWK validation, reorganize utility methods

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* refactor: standardize logging with Serilog and consolidate cache layer

- Migrate all workers to Serilog via ServiceDefaults
- Remove MediaLinkCacheRepository, consolidate into RedisMediaLinkCache
- Enhance SagaCoordinator with comprehensive logging and retry logic
- Add integration tests for cache layer
- Update documentation and deployment scripts

* Add resilience configuration, documentation, and TODOs for OAuth improvements

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* 1. Increase maintainability index scores by
  a. Breaking out shared logic into the BridgeBeats.Worker.Common project
  b. reducing top-level namespaces and breaking code up into methods and classes.

* Implement ATProto OAuth token exchange with DPoP and fix base branch issues (#257)

* Move dependabot file back out of workflows.

* [WIP] Add genre caching for playlist organization (#258)

* Initial plan

* Fix Dockerfile ARG, encrypt DPoP key storage, add OAuth cleanup service

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Replace silent OAuth session failure with explicit NotImplementedException

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Remove old unencrypted DPoP key property and add migration

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Run initial OAuth cleanup on startup and simplify exception message

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Fix Data Protection config and scoped service dependency in cleanup service

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Fix Data Protection wiring and update comments to reflect actual behavior

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Document plain text storage reality and security risks honestly

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Fix ASP0015 issues

* - Resolve CA1041 issues
- Fix tests
- Run formatter
- Prefer string.Empty to ""

* - Resolve CA1822  issues

* - Resolve CA1848 and add event Ids.
- Resolve IDE0305 too because I forgot to hit commit before updating these.

* Additional IDE recommended improvements.

* Adjustments to address MSTEST0049

* Queues seem functional

* Regenerate package lock files for linux-x64 runtime

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

---------

Co-authored-by: Taylor Marvin <taylor@marvin.email>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>
…est forgery token validation

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…est forgery token validation

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…ry test helper, break up TestUtilities file into individual class files.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
tsmarvin and others added 20 commits February 4, 2026 20:16
- services start up and stay running
wip oauth public -> confidential client update
…285)

* Initial plan

* Address review comments: fix path comment, secure Redis password, require persistent internal service key

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Improve Redis password handling: add error checking and prevent command injection

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Use head -n 1 to ensure only first line of password file is used

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Improve error handling and use safer awk print instead of printf

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Fix Redis config generation, healthcheck auth, signal handling, and secret newlines

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Add --no-auth-warning flag and clarify secret handling comment

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

* Use head -n 1 in healthcheck for consistency and security

Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tsmarvin <57049894+tsmarvin@users.noreply.github.com>
Co-authored-by: Taylor Marvin <Taylor@Marvin.Email>
Copilot AI review requested due to automatic review settings February 8, 2026 06:13
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Continues the refactor work from PR#274 by updating deployment/docs for the new worker/Redis-based architecture, modernizing container startup/build tooling, and expanding automated tests for newly introduced components.

Changes:

  • Updated deployment/configuration docs for installation scripts, Redis, ATProto lexicons, and the Discord worker architecture.
  • Refactored container build + entrypoint flow to publish multiple projects, run the AppHost, and add Redis to docker-compose.
  • Added substantial unit/integration test coverage and new test infrastructure (shared Redis Testcontainer, antiforgery helpers, controller/service tests).

Reviewed changes

Copilot reviewed 75 out of 397 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
docs/DISCORD_BOT.md Documents Discord worker architecture separation/scaling concept.
docs/DEPLOYMENT.md Adds install-script quick start and updates sharding/deployment guidance.
docs/CONFIGURATION.md Documents Redis + Discord worker settings and updates appsettings examples.
docs/ATPROTO_LEXICON.md Expands lexicon docs to include playlist lexicon and updated paths.
containers/setup-secrets.sh Removes old secrets setup script in favor of new installation approach.
containers/entrypoint.sh Refactors startup to generate appsettings, configure Aspire dashboard, Redis, and run AppHost.
containers/docker-compose.yml Adds Redis service, new secret mounts, DP key persistence, and dependency wiring.
containers/Test-LockFileChanges.ps1 New lock-file diff validator intended for cross-platform restore/publish consistency.
containers/Dockerfile.caddy Updates pinned Caddy base image digests.
containers/Dockerfile Reworks container build to publish all projects and include Aspire tools/dashboard.
containers/Caddyfile Proxies Aspire Dashboard to in-container dashboard port rather than separate container.
containers/Build-Publish.ps1 New publish orchestrator script to standardize output layout and lock regeneration.
containers/.env.example Updates env example to remove SQLite cache config and document required secrets.
Tests/Unit/WellKnownControllerTests.cs Adds unit tests for ATProto OAuth well-known endpoints.
Tests/Unit/StatisticsControllerTests.cs Adds controller unit tests for stats rendering/refresh behavior.
Tests/Unit/SpotifyArtistGenreServiceTests.cs Adds constructor validation tests for Spotify genre service.
Tests/Unit/RetryAfterExceededExceptionTests.cs Adds tests for rate-limit exception behavior and provider detection.
Tests/Unit/RecordKeyGeneratorTests.cs Updates tests to new namespaces and MSTest assertions, adds XML docs.
Tests/Unit/QrCodeServiceTests.cs Adds unit tests for QR code data URI generation.
Tests/Unit/OpenGraphExtensionsTests.cs Updates namespace and test naming for theme-color expectations.
Tests/Unit/OpenGraphCardServiceTests.cs Updates namespace and expands card service constructor checks.
Tests/Unit/LookupStatisticsTests.cs Adds DTO default/initializer behavior tests.
Tests/Unit/LoggingConfigurationTests.cs Updates logging config tests to LogDirPath and adds documentation.
Tests/Unit/HashUtilityTests.cs Adds tests for hashing utilities and URL normalization.
Tests/Unit/DataProtectionLookupProtectorTests.cs Adds tests for DP-based lookup protection roundtrips and failure modes.
Tests/Unit/DataProtectionKeyRingTests.cs Adds tests for DP key ring behavior.
Tests/Unit/ConnectionStringCompatibilityTests.cs Removes tests tied to the removed SQLite link-cache connection string.
Tests/Unit/ConfigurationValidationTests.cs Updates tests to reflect removed link-cache connection string usage.
Tests/Unit/CacheBootstrapBackgroundServiceTests.cs Adds background service tests for ATProto→cache bootstrap logic.
Tests/Unit/AppleMusicLinkParserTests.cs Adds coverage for Apple Music URL parsing edge cases.
Tests/Unit/AppleJwtHandlerTests.cs Updates namespace and adds more documentation around JWT generation tests.
Tests/Unit/AppSettingsTests.cs Updates binding tests for renamed logging setting and removed settings.
Tests/Unit/ATProtoSigningKeyProviderTests.cs Adds extensive JWKS/JWK key provider tests.
Tests/TestUtilities.cs Removes old custom factory utilities replaced by new infra.
Tests/TestConfiguration.cs Adds shared config loader and helper for Aspire parameter args.
Tests/SharedTestInfrastructure.cs Adds shared Redis Testcontainer lifecycle for integration tests.
Tests/RateLimitTestHelper.cs Adds helper to mark tests inconclusive when rate limited.
Tests/Integration/RedisRequestDeduplicatorTests.cs Adds Redis integration tests for request de-dup + pub/sub completion.
Tests/Integration/RedisRateLimitTrackerTests.cs Adds Redis integration tests for rate limit state/TTL behavior.
Tests/Integration/QueueMetricsIntegrationTests.cs Adds integration tests validating observable gauge metrics from Redis streams.
Tests/Integration/HealthEndpointAuthorizationTests.cs Re-enables health endpoint checks and updates config overrides.
Tests/Integration/DashboardAuthorizationTests.cs Re-enables dashboard auth tests with a test auth scheme/factory.
Tests/EndToEnd/OpenGraphCardControllerTests.cs Updates E2E tests to direct-provider mode + improved rate-limit handling.
Tests/EndToEnd/MusicLookupControllerTests.cs Uses antiforgery helper and adjusts multi-URL test expectations.
Tests/EndToEnd/HomeControllerTests.cs Adds antiforgery handling for POST endpoints and expands docs.
Tests/CustomWebApplicationFactory.cs New factory wiring Redis + direct-mode overrides for WebApplicationFactory tests.
Tests/BridgeBeats.Tests.csproj Adds Testcontainers/Aspire testing deps, removes FluentAssertions, tightens build settings.
Tests/AntiforgeryTestHelper.cs Adds helper for antiforgery token acquisition and POST wrapper.
.github/workflows/tests.yml Switches restore workflow to RID-specific restore + lock validation script.
.github/workflows/publish-caddy-cloudflare.yml Updates pinned docker/login-action version SHA.
.github/workflows/docs.yml Updates setup-dotnet action SHA.
.github/workflows/docker-publish.yml Updates pinned docker/login-action version SHA.
.editorconfig Tightens analyzer severities and adds additional analyzer config.

Comment on lines +46 to +52
RUN chown -R $APP_UID:$APP_UID /src && \
chown -R $APP_UID:$APP_UID /app/ && \
chown $APP_UID:$APP_UID /entrypoint.sh && \
chmod +x /entrypoint.sh && \
chmod +x /app/tools/dcp/dcp

USER $APP_UID
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Dockerfile uses $APP_UID for chown and USER selection, but this diff removed the earlier USER $APP_UID and does not define ARG/ENV APP_UID in the shown stages. If APP_UID is unset at build time, these lines can fail (or run as an empty user). Define APP_UID explicitly (e.g., ARG with a default + matching user creation) or switch to a known existing user.

Copilot uses AI. Check for mistakes.
// Can calculate when to retry
DateTimeOffset now = DateTimeOffset.UtcNow;
DateTimeOffset suggestedRetryTime = now.Add(exception.RetryAfterValue);
Assert.IsGreaterThan(now, suggestedRetryTime);
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion is reversed: suggestedRetryTime should be later than now when retryAfterValue is positive. This test will fail as written; assert that suggestedRetryTime is greater than now.

Suggested change
Assert.IsGreaterThan(now, suggestedRetryTime);
Assert.IsGreaterThan(suggestedRetryTime, now);

Copilot uses AI. Check for mistakes.

# Logging configuration
LOG_FILE_PATH="${LOG_FILE_PATH:-/app/data/logs/bridgebeats-.log}"
LOG_DIR_PATH="${LOG_DIR_PATH:-/app/data/logs}"
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script defines LOG_DIR_PATH but always creates /app/data/logs instead of "$LOG_DIR_PATH". If LOG_DIR_PATH is overridden, the target directory may not exist and file logging can fail. Create the directory using the configured LOG_DIR_PATH.

Suggested change
LOG_DIR_PATH="${LOG_DIR_PATH:-/app/data/logs}"
LOG_DIR_PATH="${LOG_DIR_PATH:-/app/data/logs}"
mkdir -p "$LOG_DIR_PATH"

Copilot uses AI. Check for mistakes.
@@ -151,17 +197,27 @@ BridgeBeats is stateless (except for the optional SQLite cache) and can be horiz

### Discord Bot Sharding

For large Discord deployments, use the `NODE_NUMBER` environment variable:
The Discord integration runs as a separate worker service (`BridgeBeats.Worker.Discord`). For large Discord deployments, run multiple instances with different node numbers:
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section mixes BridgeBeats__NodeNumber (used in the example) with NODE_NUMBER (called out as the uniqueness requirement). Pick one configuration name and use it consistently throughout the sharding docs (including the unique-shard warning) to avoid misconfiguration.

Copilot uses AI. Check for mistakes.
Comment on lines +205 to +207
-e BridgeBeats__DiscordToken=your_token \
-e BridgeBeats__NodeNumber=0 \
-e BridgeBeats__BaseUrl=https://bridgebeats.link \
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section mixes BridgeBeats__NodeNumber (used in the example) with NODE_NUMBER (called out as the uniqueness requirement). Pick one configuration name and use it consistently throughout the sharding docs (including the unique-shard warning) to avoid misconfiguration.

Copilot uses AI. Check for mistakes.
```

Note: If you do not increment this integer you could have multiple instances replying to the same events.
**Important**: Each shard must have a unique `NODE_NUMBER`. Running multiple instances with the same node number will cause duplicate responses to the same messages.
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section mixes BridgeBeats__NodeNumber (used in the example) with NODE_NUMBER (called out as the uniqueness requirement). Pick one configuration name and use it consistently throughout the sharding docs (including the unique-shard warning) to avoid misconfiguration.

Suggested change
**Important**: Each shard must have a unique `NODE_NUMBER`. Running multiple instances with the same node number will cause duplicate responses to the same messages.
**Important**: Each shard must have a unique `BridgeBeats__NodeNumber`. Running multiple instances with the same node number will cause duplicate responses to the same messages.

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +84
$backup = Get-Content $BackupPath -Raw | ConvertFrom-Json -AsHashtable
$current = Get-Content $CurrentPath -Raw | ConvertFrom-Json -AsHashtable
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing ConvertTo-Json output from hashtables can be unstable because hashtable key enumeration order isn’t guaranteed, which can cause false positives when the underlying data is the same. A more robust approach is to compare specific fields (e.g., resolved/type/contentHash) or parse into ordered objects (avoid -AsHashtable) and canonicalize/sort keys before comparison.

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +119
$backupJson = $backupDeps[$package] | ConvertTo-Json -Compress
$currentJson = $currentDeps[$package] | ConvertTo-Json -Compress

if ($backupJson -ne $currentJson) {
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing ConvertTo-Json output from hashtables can be unstable because hashtable key enumeration order isn’t guaranteed, which can cause false positives when the underlying data is the same. A more robust approach is to compare specific fields (e.g., resolved/type/contentHash) or parse into ordered objects (avoid -AsHashtable) and canonicalize/sort keys before comparison.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 8, 2026 08:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 75 out of 397 changed files in this pull request and generated 9 comments.

Comment on lines +16 to +18
RUN DOTNET_RID="linux-x64"; \
if [ "$TARGETARCH" = "arm64" ]; then DOTNET_RID="linux-arm64"; fi; \
pwsh /build/containers/Build-Publish.ps1 -Runtime $DOTNET_RID
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mcr.microsoft.com/dotnet/sdk base image typically doesn't include PowerShell (pwsh). This RUN pwsh ... step will fail during Docker build unless PowerShell is explicitly installed. Consider either installing PowerShell in the build stage (apt-get) or rewriting the publish pipeline in bash/dotnet so it runs without pwsh.

Copilot uses AI. Check for mistakes.

// Assert
Assert.StartsWith( "data:image/png;base64,", result );
Assert.IsGreaterThan( 100, result.Length, "Base64 PNG should be substantial" );
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Assert.IsGreaterThan(...) calls have the expected/actual arguments reversed, which will make the tests fail (e.g., you want result.Length > 100, largeResult.Length > smallResult.Length, and bytes.Length > 8). Swap the argument order (or use a boolean assertion) so the comparisons reflect the intended expectations.

Copilot uses AI. Check for mistakes.

// Assert
// Larger pixels per module should produce a larger base64 string (larger image)
Assert.IsGreaterThan( smallResult.Length, largeResult.Length, "Larger pixelsPerModule should produce larger image" );
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Assert.IsGreaterThan(...) calls have the expected/actual arguments reversed, which will make the tests fail (e.g., you want result.Length > 100, largeResult.Length > smallResult.Length, and bytes.Length > 8). Swap the argument order (or use a boolean assertion) so the comparisons reflect the intended expectations.

Copilot uses AI. Check for mistakes.
byte[] bytes = Convert.FromBase64String( base64Part );

// PNG files start with specific magic bytes: 137 80 78 71 13 10 26 10
Assert.IsGreaterThan( 8, bytes.Length, "PNG file should have more than 8 bytes" );
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Assert.IsGreaterThan(...) calls have the expected/actual arguments reversed, which will make the tests fail (e.g., you want result.Length > 100, largeResult.Length > smallResult.Length, and bytes.Length > 8). Swap the argument order (or use a boolean assertion) so the comparisons reflect the intended expectations.

Suggested change
Assert.IsGreaterThan( 8, bytes.Length, "PNG file should have more than 8 bytes" );
Assert.IsGreaterThan( bytes.Length, 8, "PNG file should have more than 8 bytes" );

Copilot uses AI. Check for mistakes.
Comment on lines +203 to +208
# Instance 1 - shard 0
docker run -p 10001:10000 \
-e BridgeBeats__DiscordToken=your_token \
-e BridgeBeats__NodeNumber=0 \
-e BridgeBeats__BaseUrl=https://bridgebeats.link \
tsmarvin/bridgebeats-discord:latest
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section mixes BridgeBeats__NodeNumber (in the examples) with NODE_NUMBER (in the Important note). Align on a single variable name (and match docs/CONFIGURATION.md) so operators don’t configure the wrong setting.

Copilot uses AI. Check for mistakes.
- internal
environment:
- BASEURL=${BASEURL:-bridgebeats.link}
- NODE_NUMBER=${NODE_NUMBER:-100}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compose default NODE_NUMBER is 100, while the documentation/config references indicate a default of 0. If NODE_NUMBER is still meaningful (e.g., sharding), consider making the compose default consistent with the documented default or add a brief note explaining why 100 is chosen here.

Suggested change
- NODE_NUMBER=${NODE_NUMBER:-100}
- NODE_NUMBER=${NODE_NUMBER:-0}

Copilot uses AI. Check for mistakes.
.Where( kv => kv.Value is not null )
.Where( kv => !kv.Key.EndsWith( "ConnectionString", StringComparison.OrdinalIgnoreCase ) )
.ToDictionary( kv => kv.Key, kv => kv.Value );
.ToDictionary( );
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .ToDictionary() without selectors is unusual and may not compile unless you’re relying on a very specific framework/library overload. For clarity and compatibility, prefer an explicit form like .ToDictionary(kv => kv.Key, kv => kv.Value) (this pattern appears in multiple test files in the PR).

Suggested change
.ToDictionary( );
.ToDictionary( kv => kv.Key, kv => kv.Value );

Copilot uses AI. Check for mistakes.
for lockfile in $(find . -name "packages.lock.json" ! -name "*.backup"); do
backup="${lockfile}.backup"
if [ -f "$backup" ]; then
pwsh containers/Test-LockFileChanges.ps1 -BackupPath "$backup" -CurrentPath "$lockfile" -ToPlatform "linux-x64"
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lock validation script is being called without -FromPlatform, so it assumes the backup lock file was also linux-x64. If a checked-in lock file uses a different RID, this can produce false failures. Consider detecting the original RID per lock file (similar to Build-Publish.ps1) and passing it as -FromPlatform in the workflow.

Suggested change
pwsh containers/Test-LockFileChanges.ps1 -BackupPath "$backup" -CurrentPath "$lockfile" -ToPlatform "linux-x64"
fromRid=$(grep -m1 '"runtimeIdentifier"' "$backup" | sed -E 's/.*"runtimeIdentifier"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/')
if [ -z "$fromRid" ]; then
fromRid="linux-x64"
fi
pwsh containers/Test-LockFileChanges.ps1 -BackupPath "$backup" -CurrentPath "$lockfile" -FromPlatform "$fromRid" -ToPlatform "linux-x64"

Copilot uses AI. Check for mistakes.
# Logging configuration
LOG_FILE_PATH="${LOG_FILE_PATH:-/app/data/logs/bridgebeats-.log}"
LOG_DIR_PATH="${LOG_DIR_PATH:-/app/data/logs}"

Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG_DIR_PATH is configurable, but directory creation is hardcoded to /app/data/logs. Create the directory using $LOG_DIR_PATH (and optionally also create /app/data/logs for backward compatibility) so non-default log paths work reliably.

Suggested change
# Ensure the configured log directory exists (and keep /app/data/logs for backward compatibility)
if [ ! -d "$LOG_DIR_PATH" ]; then
mkdir -p "$LOG_DIR_PATH"
fi
if [ "$LOG_DIR_PATH" != "/app/data/logs" ]; then
mkdir -p "/app/data/logs"
fi

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 8, 2026 10:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 75 out of 397 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

Tests/Unit/HashUtilityTests.cs:1

  • This likely asserts the opposite of what the message says (it reads as “8 is greater than bytes.Length”). To match “PNG file should have more than 8 bytes”, the assertion should validate bytes.Length > 8.
using BridgeBeats.Core.Infrastructure.Utilities;

escape_bs() { printf '%s' "$1" | sed 's/\\/\\\\/g'; }

# 0) Create logs directory if it doesn't exist
# 0) Create required directories
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG_DIR_PATH is configurable, but the script always creates /app/data/logs instead of creating the directory specified by $LOG_DIR_PATH. If LOG_DIR_PATH is set to a different path, log writing can fail. Create "$LOG_DIR_PATH" (and consider also keeping /app/data/logs only if it’s still required elsewhere).

Suggested change
# 0) Create required directories
# 0) Create required directories
mkdir -p "$LOG_DIR_PATH"

Copilot uses AI. Check for mistakes.
| `ALLOWED_HOSTS` | Allowed hosts for the web server | `*` |
| `DEFAULT_LOGLEVEL` | Default logging level | `Information` |
| `HOSTING_DEFAULT_LOGLEVEL` | ASP.NET hosting logging level | `Information` |
| `OTLP_ENDPOINT` | OpenTelemetry OTLP endpoint for Aspire Dashboard | `http://aspire-dashboard:4317` |
| `LOG_FILE_PATH` | File path for log files | `/app/data/logs/bridgebeats-.log` |
| `CACHE_DAYS` | Number of days to cache ATProto PDS lookup results | `7` |
| `BridgeBeats__LinkCacheConnectionString` | SQLite connection string for cache database | `Data Source=bridgebeats.db` |
| `REDIS_CONNECTION_STRING` | Redis connection string for caching and queuing | `localhost:6379` (provided by Aspire) |
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs instruct users to set REDIS_CONNECTION_STRING, but the container configuration/entrypoint wiring in this PR uses REDIS_HOST/REDIS_PORT (+ a Redis password secret) to construct ConnectionStrings:redis. Either document the REDIS_HOST/REDIS_PORT setup (and secret) here, or update the container entrypoint/compose to accept REDIS_CONNECTION_STRING directly so docs and deployment match.

Suggested change
| `REDIS_CONNECTION_STRING` | Redis connection string for caching and queuing | `localhost:6379` (provided by Aspire) |
| `REDIS_HOST` | Redis host for caching and queuing | `redis` (provided by Aspire/container) |
| `REDIS_PORT` | Redis port for caching and queuing | `6379` |
| `REDIS_PASSWORD` | Redis password for caching and queuing (if required) | *(empty – no auth by default)* |

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +113
Redis is used for distributed caching, request queuing, and rate limit tracking. In development, Aspire provides and configures Redis automatically. In production:

1. Deploy a Redis instance (standalone or cluster)
2. Set `REDIS_CONNECTION_STRING` to your Redis endpoint
3. Recommended: Use Redis with persistence (RDB or AOF) for queue durability
4. Recommended: Enable TLS and authentication for production deployments
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs instruct users to set REDIS_CONNECTION_STRING, but the container configuration/entrypoint wiring in this PR uses REDIS_HOST/REDIS_PORT (+ a Redis password secret) to construct ConnectionStrings:redis. Either document the REDIS_HOST/REDIS_PORT setup (and secret) here, or update the container entrypoint/compose to accept REDIS_CONNECTION_STRING directly so docs and deployment match.

Suggested change
Redis is used for distributed caching, request queuing, and rate limit tracking. In development, Aspire provides and configures Redis automatically. In production:
1. Deploy a Redis instance (standalone or cluster)
2. Set `REDIS_CONNECTION_STRING` to your Redis endpoint
3. Recommended: Use Redis with persistence (RDB or AOF) for queue durability
4. Recommended: Enable TLS and authentication for production deployments
Redis is used for distributed caching, request queuing, and rate limit tracking. In development, Aspire provides and configures Redis automatically. In production (containerized deployment):
1. Deploy a Redis instance (standalone or cluster).
2. Configure the BridgeBeats container with:
- `REDIS_HOST` – the hostname or service name of your Redis instance
- `REDIS_PORT` – the TCP port for Redis (usually `6379`)
- A Redis password provided via environment variable or secret (for example, `REDIS_PASSWORD`), which the container entrypoint uses together with `REDIS_HOST`/`REDIS_PORT` to build `ConnectionStrings:redis`
3. Recommended: Use Redis with persistence (RDB or AOF) for queue durability.
4. Recommended: Enable TLS and authentication for production deployments.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +113
2. Set `REDIS_CONNECTION_STRING` to your Redis endpoint
3. Recommended: Use Redis with persistence (RDB or AOF) for queue durability
4. Recommended: Enable TLS and authentication for production deployments
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs instruct users to set REDIS_CONNECTION_STRING, but the container configuration/entrypoint wiring in this PR uses REDIS_HOST/REDIS_PORT (+ a Redis password secret) to construct ConnectionStrings:redis. Either document the REDIS_HOST/REDIS_PORT setup (and secret) here, or update the container entrypoint/compose to accept REDIS_CONNECTION_STRING directly so docs and deployment match.

Suggested change
2. Set `REDIS_CONNECTION_STRING` to your Redis endpoint
3. Recommended: Use Redis with persistence (RDB or AOF) for queue durability
4. Recommended: Enable TLS and authentication for production deployments
2. When running in containers (e.g., via Aspire or Docker), configure Redis using `REDIS_HOST` and `REDIS_PORT` (and the associated Redis password secret defined in your deployment). The container entrypoint combines these into `ConnectionStrings:redis` for the application.
3. When running outside of containers, you may instead set `REDIS_CONNECTION_STRING` directly to a full Redis connection string if your hosting model prefers a single connection string value.
4. Recommended: Use Redis with persistence (RDB or AOF) for queue durability
5. Recommended: Enable TLS and authentication for production deployments

Copilot uses AI. Check for mistakes.
The fastest way to deploy BridgeBeats is with the installation script. No need to clone the repository.

### Linux / macOS

Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deployment guide promotes curl | bash, which is risky because it executes remote code without an integrity check. Consider documenting a safer flow (download to a file, review, optionally verify a checksum/signature, then execute), or at minimum call out the trust/security tradeoff explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines 30 to +33
.AsEnumerable()
.Where( kv => kv.Value is not null )
.Where( kv => !kv.Key.EndsWith( "ConnectionString", StringComparison.OrdinalIgnoreCase ) )
.ToDictionary( kv => kv.Key, kv => kv.Value );
.ToDictionary( );
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enumerable.ToDictionary() does not have a parameterless overload in common LINQ APIs; this is likely a compile error. Use an explicit key/value selector (e.g., ToDictionary(kv => kv.Key, kv => kv.Value)) or construct a Dictionary<string, string?> from the enumeration. The same pattern appears in other updated test files in this PR (e.g., DashboardAuthorizationTests, OpenGraphCardControllerTests, MusicLookupControllerTests, HomeControllerTests) and should be corrected consistently.

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +65
string smallResult = _service.GenerateQrCodeDataUri( url, pixelsPerModule: 5 );
string largeResult = _service.GenerateQrCodeDataUri( url, pixelsPerModule: 20 );
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison assertions appear to have their operands reversed relative to the intent expressed in the messages. For example, “should be substantial” implies result.Length > 100, and “larger image” implies largeResult.Length > smallResult.Length. Fix the operand ordering (or use explicit boolean assertions) so the tests validate the intended behavior.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants