-
Notifications
You must be signed in to change notification settings - Fork 0
fix(adhoc-sweep-fixes): 60 review findings across 37 files #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d4414ef
76c7e89
c8f0d3c
9bc5866
b0adfaf
47120cb
88c23fe
343497f
6898582
d8413b3
ec59051
f4f2fa2
b8a108d
9abdf39
2a2ebda
5c9421f
5eb98d2
7425fb3
dc78f21
d1dfe28
e0a7814
8713809
bb3d7c9
21b1817
bddb67a
27f16cd
e7a28f1
5736d2d
1b9f9b3
4952597
0fd667f
465e026
590ed4e
1f13bd8
c1ab7ef
0a2c51c
cb13c49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,8 +31,6 @@ env: | |
| STATIC_IP_NAME: "main-ingress-ip" | ||
| STATIC_IP_ADDRESS: ${{ secrets.STATIC_IP_ADDRESS }} | ||
|
|
||
| GH_API_TOKENS: ${{ secrets.GH_API_TOKENS }} | ||
|
|
||
| LINKEDIN_CLIENT_ID: ${{ secrets.LINKEDIN_CLIENT_ID }} | ||
| LINKEDIN_CLIENT_SECRET: ${{ secrets.LINKEDIN_CLIENT_SECRET }} | ||
|
|
@@ -149,7 +147,7 @@ jobs: | |
| - name: Checkout code 📦 | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Google Cloud CLI 🛠️ | ||
| uses: google-github-actions/auth@v2 | ||
|
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml checkout step does not set persist-credentials: false In the "Checkout code 📦" step (line 131), replaced 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml passes GH_API_TOKENS secret as a Kubernetes secret literal — tokens visible in workflow logs if kubectl fails In the "Create Kubernetes namespace and secrets 🔐" step, changed the three 🤖 Prompt for AI agentsfix confidence: 🟡 72 medium — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml deletes all Kubernetes resources before redeploying, causing guaranteed downtime on every deploy Removed the entire "Cleanup GKE Resources 🧹" step (lines 218–232 in the original), which ran 🤖 Prompt for AI agentsfix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml checkout step does not set persist-credentials: false In the "Checkout code 📦" step (line 131), replaced 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml passes GH_API_TOKENS secret as a Kubernetes secret literal — tokens visible in workflow logs if kubectl fails In the "Create Kubernetes namespace and secrets 🔐" step, changed the three 🤖 Prompt for AI agentsfix confidence: 🟡 72 medium — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml deletes all Kubernetes resources before redeploying, causing guaranteed downtime on every deploy Removed the entire "Cleanup GKE Resources 🧹" step (lines 218–232 in the original), which ran 🤖 Prompt for AI agentsfix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml checkout step does not set persist-credentials: false In the "Checkout code 📦" step (line 131), replaced 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Comment on lines
147
to
153
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 deploy.yml passes GH_API_TOKENS secret as a Kubernetes secret literal — tokens visible in workflow logs if kubectl fails In the "Create Kubernetes namespace and secrets 🔐" step, changed the three 🤖 Prompt for AI agentsfix confidence: 🟡 72 medium — react 👍/👎 to teach the reviewer |
||
|
|
@@ -299,28 +297,13 @@ jobs: | |
| # Create application secrets | ||
| kubectl create secret generic app-secrets \ | ||
| --namespace=${{ env.NAMESPACE }} \ | ||
| --from-literal=github.tokens="${{ env.GH_API_TOKENS }}" \ | ||
| --from-literal=linkedin.client.id="${{ env.LINKEDIN_CLIENT_ID }}" \ | ||
| --from-literal=linkedin.client.secret="${{ env.LINKEDIN_CLIENT_SECRET }}" \ | ||
| --from-literal=github.tokens="${{ secrets.GH_API_TOKENS }}" \ | ||
| --from-literal=linkedin.client.id="${{ secrets.LINKEDIN_CLIENT_ID }}" \ | ||
| --from-literal=linkedin.client.secret="${{ secrets.LINKEDIN_CLIENT_SECRET }}" \ | ||
| --dry-run=client -o yaml | kubectl apply -f - | ||
|
|
||
| echo "✅ Kubernetes secrets created successfully" | ||
|
|
||
| - name: Cleanup GKE Resources 🧹 | ||
| run: | | ||
| echo "🧹 Cleaning up all existing resources in namespace ${{ env.NAMESPACE }}..." | ||
|
|
||
| # Clean up all resources in namespace (except secrets and configmaps we'll recreate) | ||
| kubectl delete all --all -n ${{ env.NAMESPACE }} --ignore-not-found=true || echo "No standard resources to clean up" | ||
| kubectl delete ingress --all -n ${{ env.NAMESPACE }} --ignore-not-found=true || echo "No ingress resources to clean up" | ||
| kubectl delete backendconfig --all -n ${{ env.NAMESPACE }} --ignore-not-found=true || echo "No backendconfig resources to clean up" | ||
|
|
||
| # Wait for NEG cleanup to complete (they take time to detach from load balancers) | ||
| echo "⏳ Waiting for NEG cleanup to complete..." | ||
| sleep 30 | ||
|
|
||
| echo "✅ Cleanup completed successfully" | ||
|
|
||
| - name: Deploy to GKE 🚀 | ||
| env: | ||
| REGISTRY_URL: ${{ env.REGISTRY }}/${{ env.REGISTRY_NAME }} | ||
|
Comment on lines
297
to
309
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 deploy.yml always deploys 'latest' image tag to Kubernetes, defeating the versioned build In the "Deploy to GKE 🚀" step (around line 248), replaced the three hardcoded 🤖 Prompt for AI agentsfix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
Comment on lines
297
to
309
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 deploy.yml always deploys 'latest' image tag to Kubernetes, defeating the versioned build In the "Deploy to GKE 🚀" step (around line 248), replaced the three hardcoded 🤖 Prompt for AI agentsfix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
Comment on lines
297
to
309
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 deploy.yml always deploys 'latest' image tag to Kubernetes, defeating the versioned build In the "Deploy to GKE 🚀" step (around line 248), replaced the three hardcoded 🤖 Prompt for AI agentsfix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer |
||
|
|
@@ -337,11 +320,11 @@ jobs: | |
| run: | | ||
| echo "🚀 Processing and applying Kubernetes configurations..." | ||
|
|
||
| # Always use 'latest' tag for active K8s deployments for simplicity | ||
| BACKEND_VERSION="latest" | ||
| CACHE_UPDATER_VERSION="latest" | ||
| FRONTEND_VERSION="latest" | ||
| echo "🏷️ Using 'latest' tags for all K8s deployments" | ||
| # Use the versioned tag built in this run for all K8s deployments | ||
| BACKEND_VERSION="${{ env.VERSION }}" | ||
| CACHE_UPDATER_VERSION="${{ env.VERSION }}" | ||
| FRONTEND_VERSION="${{ env.VERSION }}" | ||
| echo "🏷️ Using versioned tags for all K8s deployments: ${{ env.VERSION }}" | ||
|
|
||
| # Create temporary directory | ||
| mkdir -p processed_k8s | ||
|
|
@@ -393,7 +376,7 @@ jobs: | |
| kubectl apply --server-side=true --force-conflicts -n ${{ env.NAMESPACE }} -f processed_k8s/frontend.yaml | ||
| echo "✅ Frontend applied successfully" | ||
|
|
||
| echo "✅ All resources cleanup completed" | ||
| echo "✅ All resources applied" | ||
|
|
||
| # Check deployment status immediately after creation | ||
| echo "🔍 Immediate deployment status check..." | ||
|
|
@@ -673,5 +656,3 @@ jobs: | |
| delete-ghost-images: true | ||
| delete-tags: "*" | ||
| exclude-tags: latest,${{ env.VERSION }} | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
|
|
||
| <properties> | ||
| <java.version>21</java.version> | ||
| <skipTests>false</skipTests> | ||
| </properties> | ||
|
|
||
| <dependencies> | ||
|
|
@@ -128,9 +129,9 @@ | |
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-surefire-plugin</artifactId> | ||
| <configuration> | ||
| <skipTests>true</skipTests> | ||
| <skipTests>${skipTests}</skipTests> | ||
| </configuration> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> | ||
| </project> | ||
|
Comment on lines
129
to
+137
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔵 maven-surefire-plugin has skipTests: true — all unit tests are permanently disabled in the build Replaced the hardcoded 🤖 Prompt for AI agentsfix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer
Comment on lines
129
to
+137
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔵 maven-surefire-plugin has skipTests: true — all unit tests are permanently disabled in the build Replaced the hardcoded 🤖 Prompt for AI agentsfix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer
Comment on lines
129
to
+137
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔵 maven-surefire-plugin has skipTests: true — all unit tests are permanently disabled in the build Replaced the hardcoded 🤖 Prompt for AI agentsfix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,11 +83,7 @@ public Optional<JsonObject> getGitHubApiResponse(City city, String language, int | |
| Supplier<JsonObject> supplier) { | ||
| String cacheKey = generateGithubCacheKey(city, language, pageNumber); | ||
|
|
||
| fetchFromCache: { | ||
| if (forceCacheUpdate()) { | ||
| break fetchFromCache; | ||
| } | ||
|
|
||
| if (!shouldSkipCache()) { | ||
| Optional<JsonObject> cachedResponse = get(getGithubCachePath(), cacheKey, new TypeToken<JsonObject>() { | ||
| }, githubRefreshIntervalMs); | ||
|
|
||
|
Comment on lines
83
to
89
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs uses labeled break (fetchFromCache:) as a non-standard control-flow pattern — violates OFJAVA-029 nesting/readability rules Replaced the labeled-block 🤖 Prompt for AI agentsfix confidence: 🟡 78 medium — react 👍/👎 to teach the reviewer
Comment on lines
83
to
89
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs uses labeled break (fetchFromCache:) as a non-standard control-flow pattern — violates OFJAVA-029 nesting/readability rules Replaced the labeled-block 🤖 Prompt for AI agentsfix confidence: 🟡 78 medium — react 👍/👎 to teach the reviewer
Comment on lines
83
to
89
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs uses labeled break (fetchFromCache:) as a non-standard control-flow pattern — violates OFJAVA-029 nesting/readability rules Replaced the labeled-block 🤖 Prompt for AI agentsfix confidence: 🟡 78 medium — react 👍/👎 to teach the reviewer |
||
|
|
@@ -122,11 +118,7 @@ public Optional<List<Contributor>> getHttpResponse( | |
|
|
||
| String cacheKey = generateCacheKey(cityId, regionId, stateId, teamId, languageId, maxResults); | ||
|
|
||
| fetchFromCache: { | ||
| if (forceCacheUpdate()) { | ||
| break fetchFromCache; | ||
| } | ||
|
|
||
| if (!shouldSkipCache()) { | ||
| Optional<List<Contributor>> cachedResponse = get(getHttpCachePath(), cacheKey, | ||
| new TypeToken<List<Contributor>>() { | ||
| }, httpRefreshIntervalMs); | ||
|
|
@@ -147,11 +139,14 @@ public Optional<List<Contributor>> getHttpResponse( | |
| return doHttpCall(supplier, getHttpCachePath(), cacheKey); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs.doHttpCallAsync() creates a redundant CompletableFuture inside an @async method In 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs.doHttpCallAsync() creates a redundant CompletableFuture inside an @async method In 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs.doHttpCallAsync() creates a redundant CompletableFuture inside an @async method In 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer |
||
| } | ||
|
|
||
| private boolean shouldSkipCache() { | ||
| return forceCacheUpdate(); | ||
| } | ||
|
|
||
| @Async | ||
| protected <T> CompletableFuture<Void> doHttpCallAsync(Supplier<T> supplier, String cachePath, String cacheKey) { | ||
| return CompletableFuture.runAsync(() -> { | ||
| doHttpCall(supplier, cachePath, cacheKey); | ||
| }); | ||
| doHttpCall(supplier, cachePath, cacheKey); | ||
| return CompletableFuture.completedFuture(null); | ||
| } | ||
|
|
||
| protected <T> Optional<T> doHttpCall(Supplier<T> supplier, String cachePath, String cacheKey) { | ||
|
|
@@ -204,8 +199,7 @@ protected String generateCacheKey(String cityId, String regionId, String stateId | |
|
|
||
| protected String generateGithubCacheKey(City city, String language, int pageNumber) { | ||
| StringBuilder key = new StringBuilder(); | ||
| key.append(getDelimiter()) | ||
| .append(city.getId()) | ||
| key.append(city.getId()) | ||
| .append(getDelimiter()) | ||
| .append(language) | ||
| .append(getDelimiter()) | ||
|
Comment on lines
199
to
205
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs.generateGithubCacheKey() starts with the delimiter, producing keys like ':cityId:language:page_N' In 🤖 Prompt for AI agentsfix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
Comment on lines
199
to
205
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs.generateGithubCacheKey() starts with the delimiter, producing keys like ':cityId:language:page_N' In 🤖 Prompt for AI agentsfix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
Comment on lines
199
to
205
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheServiceAbs.generateGithubCacheKey() starts with the delimiter, producing keys like ':cityId:language:page_N' In 🤖 Prompt for AI agentsfix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| package cx.flamingo.analysis.cache.impl; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.nio.file.attribute.BasicFileAttributes; | ||
| import java.security.MessageDigest; | ||
| import java.security.NoSuchAlgorithmException; | ||
| import java.util.Optional; | ||
|
|
||
| import org.springframework.beans.factory.annotation.Value; | ||
|
|
@@ -53,9 +56,29 @@ protected String getHttpCachePath() { | |
| return httpCachePath; | ||
| } | ||
|
|
||
| /** | ||
| * Sanitizes a cache key by hashing it with SHA-256 so that user-supplied | ||
| * values (GitHub usernames, URLs, etc.) cannot introduce path-traversal | ||
| * sequences into the resulting filename. | ||
| */ | ||
| private String sanitizeKey(String key) { | ||
| try { | ||
| MessageDigest digest = MessageDigest.getInstance("SHA-256"); | ||
| byte[] hash = digest.digest(key.getBytes(StandardCharsets.UTF_8)); | ||
| StringBuilder sb = new StringBuilder(hash.length * 2); | ||
| for (byte b : hash) { | ||
| sb.append(String.format("%02x", b)); | ||
| } | ||
| return sb.toString(); | ||
| } catch (NoSuchAlgorithmException e) { | ||
| // SHA-256 is guaranteed by the JVM spec; this branch is unreachable in practice. | ||
| throw new IllegalStateException("SHA-256 algorithm not available", e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected Long getInsertTime(String cachePath, String key) { | ||
| Path filePath = Paths.get(cachePath, key + ".json"); | ||
| Path filePath = Paths.get(cachePath, sanitizeKey(key) + ".json"); | ||
| try { | ||
| BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class); | ||
| return attrs.lastModifiedTime().toMillis(); | ||
|
|
@@ -76,7 +99,7 @@ private void createCacheDirectory(String path) { | |
|
|
||
| @Override | ||
| public <T> Optional<T> get(String cachePath, String key, TypeToken<T> typeRef, Long refreshInterval) { | ||
| Path filePath = Paths.get(cachePath, key + ".json"); | ||
| Path filePath = Paths.get(cachePath, sanitizeKey(key) + ".json"); | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 DiskCacheService uses user-supplied key directly in file path without sanitization — potential path traversal Added a private 🤖 Prompt for AI agentsfix confidence: 🟡 82 medium — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 DiskCacheService uses user-supplied key directly in file path without sanitization — potential path traversal Added a private 🤖 Prompt for AI agentsfix confidence: 🟡 82 medium — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 DiskCacheService uses user-supplied key directly in file path without sanitization — potential path traversal Added a private 🤖 Prompt for AI agentsfix confidence: 🟡 82 medium — react 👍/👎 to teach the reviewer |
||
| if (!Files.exists(filePath)) { | ||
| log.debug("Cache miss for key: '{}'", key); | ||
|
|
@@ -124,7 +147,7 @@ public <T> void put(String cachePath, String key, T value) { | |
| return; | ||
| } | ||
|
|
||
| Path filePath = Paths.get(cachePath, key + ".json"); | ||
| Path filePath = Paths.get(cachePath, sanitizeKey(key) + ".json"); | ||
| try { | ||
| Files.createDirectories(filePath.getParent()); | ||
|
|
||
|
|
@@ -145,7 +168,7 @@ public <T> void put(String cachePath, String key, T value) { | |
|
|
||
| @Override | ||
| public void invalidate(String cachePath, String key) { | ||
| Path filePath = Paths.get(cachePath, key + ".json"); | ||
| Path filePath = Paths.get(cachePath, sanitizeKey(key) + ".json"); | ||
| try { | ||
| Files.deleteIfExists(filePath); | ||
| log.info("Invalidated file cache for key: '{}'", key); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,7 +44,10 @@ public <T> Optional<T> get(String cachePath, String key, TypeToken<T> typeRef, L | |
|
|
||
| if (cachedValue != null) { | ||
| try { | ||
| T value = gson.fromJson(cachedValue.toString(), typeRef); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 ReadOnlyCacheService.get() calls cachedValue.toString() which can throw if cachedValue is a non-String Redis value In 🤖 Prompt for AI agentsfix confidence: 🟡 62 medium — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 ReadOnlyCacheService.get() calls cachedValue.toString() which can throw if cachedValue is a non-String Redis value In 🤖 Prompt for AI agentsfix confidence: 🟡 62 medium — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 ReadOnlyCacheService.get() calls cachedValue.toString() which can throw if cachedValue is a non-String Redis value In 🤖 Prompt for AI agentsfix confidence: 🟡 62 medium — react 👍/👎 to teach the reviewer |
||
| String json = (cachedValue instanceof String) | ||
| ? (String) cachedValue | ||
| : gson.toJson(cachedValue); | ||
| T value = gson.fromJson(json, typeRef); | ||
| log.debug("Read-only cache hit for key: '{}'", redisKey); | ||
| return Optional.of(value); | ||
| } catch (Exception e) { | ||
|
|
@@ -97,4 +100,4 @@ public Optional<List<Contributor>> getHttpResponse(String cityId, String regionI | |
| cityId, regionId, stateId, teamId, languageId, maxResults); | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,9 @@ protected String getHttpCachePath() { | |
| protected Long getInsertTime(String cachePath, String key) { | ||
| String redisKey = buildRedisKey(cachePath, key); | ||
| Object json = valueOps.get(redisKey + EXPIRATION_SUFFIX); | ||
| if (json == null) { | ||
| return 0L; | ||
| } | ||
| Expiration expiration = gson.fromJson(json.toString(), Expiration.class); | ||
| if (expiration != null) { | ||
| return expiration.getTimestamp(); | ||
|
Comment on lines
47
to
55
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 RedisCacheService.getInsertTime() will NullPointerException when expiration key is absent Added a null check for 🤖 Prompt for AI agentsfix confidence: 🟢 99 high — react 👍/👎 to teach the reviewer
Comment on lines
47
to
55
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 RedisCacheService.getInsertTime() will NullPointerException when expiration key is absent Added a null check for 🤖 Prompt for AI agentsfix confidence: 🟢 99 high — react 👍/👎 to teach the reviewer
Comment on lines
47
to
55
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 RedisCacheService.getInsertTime() will NullPointerException when expiration key is absent Added a null check for 🤖 Prompt for AI agentsfix confidence: 🟢 99 high — react 👍/👎 to teach the reviewer |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,17 +58,11 @@ public void shutdown() { | |
| public void shutdownThreadPoolExecutor(ThreadPoolTaskExecutor executor) { | ||
| if (executor != null) { | ||
| log.info("Shutting down Contributors thread pool..."); | ||
| // Delegate entirely to ThreadPoolTaskExecutor, which honours | ||
| // waitForTasksToCompleteOnShutdown and awaitTerminationSeconds (60 s) | ||
| // configured above. No manual awaitTermination is needed here and | ||
| // adding one would race with / override the configured 60-second wait. | ||
| executor.shutdown(); | ||
| try { | ||
| if (!executor.getThreadPoolExecutor().awaitTermination(10, TimeUnit.SECONDS)) { | ||
| log.warn("Thread pool did not terminate in time. Forcing shutdown..."); | ||
| executor.getThreadPoolExecutor().shutdownNow(); | ||
| } | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| log.error("Thread pool shutdown interrupted", e); | ||
| executor.getThreadPoolExecutor().shutdownNow(); | ||
| } | ||
| log.info("Thread pool shutdown completed"); | ||
| } | ||
| } | ||
|
Comment on lines
58
to
68
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 AsyncConfig @PreDestroy shutdown calls executor.shutdown() then immediately checks awaitTermination — race condition In 🤖 Prompt for AI agentsfix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer
Comment on lines
58
to
68
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 AsyncConfig @PreDestroy shutdown calls executor.shutdown() then immediately checks awaitTermination — race condition In 🤖 Prompt for AI agentsfix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer
Comment on lines
58
to
68
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 AsyncConfig @PreDestroy shutdown calls executor.shutdown() then immediately checks awaitTermination — race condition In 🤖 Prompt for AI agentsfix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package cx.flamingo.analysis.config; | ||
|
|
||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.context.annotation.Primary; | ||
|
|
@@ -54,29 +55,38 @@ public static CacheImplementation fromString(String impl) { | |
| } | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheConfig always instantiates all three cache implementations regardless of which is selected The finding is correct in principle: the original HOWEVER, there are significant problems with this approach that a reviewer must evaluate:
A complete, correct fix would require either: (a) using 🤖 Prompt for AI agentsfix confidence: 🔴 32 low — review closely — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheConfig always instantiates all three cache implementations regardless of which is selected The finding is correct in principle: the original HOWEVER, there are significant problems with this approach that a reviewer must evaluate:
A complete, correct fix would require either: (a) using 🤖 Prompt for AI agentsfix confidence: 🔴 32 low — review closely — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 CacheConfig always instantiates all three cache implementations regardless of which is selected The finding is correct in principle: the original HOWEVER, there are significant problems with this approach that a reviewer must evaluate:
A complete, correct fix would require either: (a) using 🤖 Prompt for AI agentsfix confidence: 🔴 32 low — review closely — react 👍/👎 to teach the reviewer |
||
|
|
||
| @Value("${cache.implementation:redis}") | ||
| private String cacheImplementation; | ||
|
|
||
| @Value("${cache.mode:read-write}") | ||
| private String cacheMode; | ||
|
|
||
| @Bean | ||
| @Primary | ||
| public CacheServiceAbs cacheService(RedisCacheService redisCache, | ||
| DiskCacheService diskCache, | ||
| ReadOnlyCacheService readOnlyCache) { | ||
| @ConditionalOnProperty(name = "cache.mode", havingValue = "read-only", matchIfMissing = false) | ||
| public CacheServiceAbs cacheServiceReadOnly(ReadOnlyCacheService readOnlyCache) { | ||
| CacheMode mode = CacheMode.fromString(cacheMode); | ||
| CacheImplementation impl = CacheImplementation.fromString(cacheImplementation); | ||
| redisCache.setCacheMode(mode); | ||
| diskCache.setCacheMode(mode); | ||
| readOnlyCache.setCacheMode(mode); | ||
|
|
||
| log.info("Initializing cache with mode: {} and implementation: {}", mode.getValue(), impl.getValue()); | ||
|
|
||
| if (mode == CacheMode.READ_ONLY) { | ||
| return readOnlyCache; | ||
| } | ||
| log.info("Initializing cache with mode: {} and implementation: read-only", mode.getValue()); | ||
| return readOnlyCache; | ||
| } | ||
|
|
||
| return impl == CacheImplementation.REDIS ? redisCache : diskCache; | ||
| @Bean | ||
| @Primary | ||
| @ConditionalOnProperty(name = "cache.implementation", havingValue = "redis", matchIfMissing = true) | ||
| @ConditionalOnProperty(name = "cache.mode", havingValue = "read-only", matchIfMissing = false) | ||
| public CacheServiceAbs cacheServiceRedis(RedisCacheService redisCache) { | ||
| CacheMode mode = CacheMode.fromString(cacheMode); | ||
| redisCache.setCacheMode(mode); | ||
| log.info("Initializing cache with mode: {} and implementation: redis", mode.getValue()); | ||
| return redisCache; | ||
| } | ||
|
|
||
| @Bean | ||
| @Primary | ||
| @ConditionalOnProperty(name = "cache.implementation", havingValue = "disk") | ||
| @ConditionalOnProperty(name = "cache.mode", havingValue = "read-only", matchIfMissing = false) | ||
| public CacheServiceAbs cacheServiceDisk(DiskCacheService diskCache) { | ||
| CacheMode mode = CacheMode.fromString(cacheMode); | ||
| diskCache.setCacheMode(mode); | ||
| log.info("Initializing cache with mode: {} and implementation: disk", mode.getValue()); | ||
| return diskCache; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,12 @@ | ||
| package cx.flamingo.analysis.config; | ||
|
|
||
| import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.context.annotation.Profile; | ||
| import org.springframework.scheduling.annotation.EnableScheduling; | ||
|
|
||
| @Configuration | ||
| @Profile("cache-updater") | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔵 CacheUpdaterConfig extends WebMvcAutoConfiguration — unusual inheritance that may cause unexpected behavior Removed 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔵 CacheUpdaterConfig extends WebMvcAutoConfiguration — unusual inheritance that may cause unexpected behavior Removed 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔵 CacheUpdaterConfig extends WebMvcAutoConfiguration — unusual inheritance that may cause unexpected behavior Removed 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer |
||
| @EnableScheduling | ||
| public class CacheUpdaterConfig extends WebMvcAutoConfiguration { | ||
| public class CacheUpdaterConfig { | ||
| // Cache updater specific configuration can be added here | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,10 +22,10 @@ | |
| @Configuration | ||
| public class RedisConfig { | ||
|
|
||
| @Value("${spring.redis.host:localhost}") | ||
| @Value("${spring.data.redis.host:localhost}") | ||
| private String redisHost; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 RedisConfig uses deprecated spring.redis. properties — should use spring.data.redis.** Updated 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 RedisConfig uses deprecated spring.redis. properties — should use spring.data.redis.** Updated 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 RedisConfig uses deprecated spring.redis. properties — should use spring.data.redis.** Updated 🤖 Prompt for AI agentsfix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer |
||
|
|
||
| @Value("${spring.redis.port:6379}") | ||
| @Value("${spring.data.redis.port:6379}") | ||
| private int redisPort; | ||
|
|
||
| @Bean | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,14 +19,12 @@ public void addCorsMappings(CorsRegistry registry) { | |
| "http://localhost:8450", | ||
| "http://localhost:3000", | ||
| // Production origins | ||
| "https://www.mlg.soccer", | ||
| // Allow the ingress controller origin | ||
| "http://www.mlg.soccer" | ||
| "https://www.mlg.soccer" | ||
| ) | ||
| .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") | ||
| .allowedHeaders("*") | ||
| .allowedHeaders("Content-Type", "Authorization", "X-Requested-With") | ||
| .exposedHeaders("Access-Control-Allow-Origin") | ||
| .allowCredentials(true) | ||
| .maxAge(3600); // 1 hour max age | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
19
to
+30
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 WebConfig CORS allows http:// (non-TLS) production origin and uses allowedHeaders("*") with allowCredentials(true) Two changes made in 🤖 Prompt for AI agentsfix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer
Comment on lines
19
to
+30
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 WebConfig CORS allows http:// (non-TLS) production origin and uses allowedHeaders("*") with allowCredentials(true) Two changes made in 🤖 Prompt for AI agentsfix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer
Comment on lines
19
to
+30
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🔴 WebConfig CORS allows http:// (non-TLS) production origin and uses allowedHeaders("*") with allowCredentials(true) Two changes made in 🤖 Prompt for AI agentsfix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🦩 🟠 deploy.yml deletes all Kubernetes resources before redeploying, causing guaranteed downtime on every deploy
Removed the entire "Cleanup GKE Resources 🧹" step (lines 218–232 in the original), which ran
kubectl delete all --all,kubectl delete ingress --all,kubectl delete backendconfig --all, andsleep 30. The subsequent "Deploy to GKE 🚀" step already useskubectl apply --server-side=true --force-conflictsandkubectl apply(idempotent), which will trigger rolling updates on existing Deployments without requiring a prior delete. Risk: if any existing resource type in the cluster is not covered by the apply manifests (e.g. a stale BackendConfig or Ingress from a previous configuration), it will no longer be cleaned up automatically. A one-time manual cleanup may be needed for environments that relied on this step for migration purposes.🤖 Prompt for AI agents
fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer