From 665361a4e38d2bb6f6e84384878a94fed0c10ccb Mon Sep 17 00:00:00 2001 From: Shikhar Pandya Date: Tue, 28 Oct 2025 00:05:47 +0530 Subject: [PATCH 1/6] [Added a script for auto syncing forks of parent repository] Beta #1 --- .gitea/workflows/test_image_build_push.yml | 71 +++++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/test_image_build_push.yml b/.gitea/workflows/test_image_build_push.yml index f53e140..edda211 100644 --- a/.gitea/workflows/test_image_build_push.yml +++ b/.gitea/workflows/test_image_build_push.yml @@ -1,8 +1,10 @@ name: sample gradle build and test -run-name: Build started by $ {{gitea.actor}} +run-name: Build started by ${{ gitea.actor }} + on: push: branches: [test] + jobs: tag: runs-on: ubuntu-latest @@ -13,17 +15,20 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Get new version id: new_version run: | VERSION=$(git describe --tags --abbrev=0) - echo ${VERSION} + echo "Current version: ${VERSION}" MAJOR=$(echo ${VERSION} | cut -d "." -f 1) MINOR=$(echo ${VERSION} | cut -d "." -f 2) PATCH=$(echo ${VERSION} | cut -d "." -f 3) - NEW_PATCH=$(( ${PATCH} + 1)) - echo ${NEW_PATCH} - echo "new_version=$(echo "${MAJOR}.${MINOR}.${NEW_PATCH}")" >> $GITHUB_OUTPUT + NEW_PATCH=$((PATCH + 1)) + NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}" + echo "New version: ${NEW_VERSION}" + echo "new_version=${NEW_VERSION}" >> $GITHUB_OUTPUT + build_tag_push: runs-on: ubuntu-latest needs: tag @@ -34,31 +39,79 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: JDK setup uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' + - name: Validate Gradle Wrapper uses: gradle/actions/wrapper-validation@v3 + - name: Create and push tag run: | - echo "NEW_VERSION=${{ needs.tag.outputs.new_version }}" - git config --global user.name "${{gitea.actor}}" + echo "New version: ${{ needs.tag.outputs.new_version }}" + git config --global user.name "${{ gitea.actor }}" git config --global user.email "${{ gitea.actor }}@users.noreply.github.com" - git tag -a ${{ needs.tag.outputs.new_version }} -m "Pushing new version ${{ needs.tag.outputs.new_version }}" - git push origin ${{ needs.tag.outputs.new_version }} + git tag -a "${{ needs.tag.outputs.new_version }}" -m "Pushing new version ${{ needs.tag.outputs.new_version }}" + git push origin "${{ needs.tag.outputs.new_version }}" + - name: Log in to Gitea Docker Registry uses: docker/login-action@v3 with: registry: 'http://192.168.0.100:8928' username: hitanshu password: ${{ secrets.TOKEN }} + - name: Gradle build run: ./gradlew bootBuildImage --imageName=192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} + - name: Tag image as test run: docker tag 192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} 192.168.0.100:8928/hithomelabs/cftunnels:test + - name: Push to Gitea Registry run: | docker push 192.168.0.100:8928/hithomelabs/cftunnels:test docker push 192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} + + sync_forks: + name: Sync All Forks + runs-on: ubuntu-latest + needs: build_tag_push + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: Sync all forks via Gitea API + env: + TOKEN: ${{ secrets.TOKEN }} + BASE_URL: "https://gitea.hithomelabs.com/api/v1" + OWNER: "Hithomelabs" + REPO: "CFTunnels" + run: | + set -e + set -o pipefail + + echo "Fetching forks for $OWNER/$REPO..." + response=$(curl -s -X GET "$BASE_URL/repos/$OWNER/$REPO/forks" -H "Authorization: token $TOKEN") + + # Extract clone URLs of forked repos (excluding the parent repo) + filtered=$(echo "$response" | grep -o '"clone_url":"[^"]*"' | sed 's/"clone_url":"\([^"]*\)"/\1/' | grep -v "/$OWNER") + + echo "Detected forks:" + echo "$filtered" + + readarray -t forks <<< "$filtered" + + for fork_url in "${forks[@]}"; do + echo "🔄 Syncing fork: $fork_url" + git remote add fork "$fork_url" 2>/dev/null || git remote set-url fork "$fork_url" + if git push fork main --force >/dev/null 2>&1; then + echo "✅ Synced $fork_url successfully" + else + echo "❌ Failed to sync $fork_url" + fi + echo + done + From ef7b6545db6de6147135ccfa69fa0db066a598b6 Mon Sep 17 00:00:00 2001 From: Shikhar Pandya Date: Tue, 28 Oct 2025 00:46:32 +0530 Subject: [PATCH 2/6] [Added a script for auto syncing forks of parent repository] Beta #2 --- .gitea/workflows/test_image_build_push.yml | 27 ++++------------------ 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/.gitea/workflows/test_image_build_push.yml b/.gitea/workflows/test_image_build_push.yml index edda211..546f0d5 100644 --- a/.gitea/workflows/test_image_build_push.yml +++ b/.gitea/workflows/test_image_build_push.yml @@ -84,34 +84,15 @@ jobs: uses: actions/checkout@v4 - name: Sync all forks via Gitea API - env: - TOKEN: ${{ secrets.TOKEN }} - BASE_URL: "https://gitea.hithomelabs.com/api/v1" - OWNER: "Hithomelabs" - REPO: "CFTunnels" run: | - set -e - set -o pipefail - - echo "Fetching forks for $OWNER/$REPO..." - response=$(curl -s -X GET "$BASE_URL/repos/$OWNER/$REPO/forks" -H "Authorization: token $TOKEN") - - # Extract clone URLs of forked repos (excluding the parent repo) - filtered=$(echo "$response" | grep -o '"clone_url":"[^"]*"' | sed 's/"clone_url":"\([^"]*\)"/\1/' | grep -v "/$OWNER") - + echo "Fetching forks for Hithomelabs/CFTunnels..." + response=$(curl -s -X GET "https://gitea.hithomelabs.com/api/v1/repos/Hithomelabs/CFTunnels/forks" -H "Authorization: token ${{secrets.TOKEN}}") + filtered=$(echo "$response" | grep -o '"clone_url":"[^"]*"' | sed 's/"clone_url":"\([^"]*\)"/\1/' | grep -v "/Hithomelabs") echo "Detected forks:" echo "$filtered" - readarray -t forks <<< "$filtered" - for fork_url in "${forks[@]}"; do echo "🔄 Syncing fork: $fork_url" - git remote add fork "$fork_url" 2>/dev/null || git remote set-url fork "$fork_url" - if git push fork main --force >/dev/null 2>&1; then - echo "✅ Synced $fork_url successfully" - else - echo "❌ Failed to sync $fork_url" - fi - echo + git push "$fork_url" test & done From 46e8f614a0f192ddc3bd0dea142c0db78b710a90 Mon Sep 17 00:00:00 2001 From: Shikhar Pandya Date: Tue, 28 Oct 2025 01:12:58 +0530 Subject: [PATCH 3/6] [Added a script for auto syncing forks of parent repository] Beta #3 --- .gitea/workflows/test_image_build_push.yml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/test_image_build_push.yml b/.gitea/workflows/test_image_build_push.yml index 546f0d5..a4ed4ea 100644 --- a/.gitea/workflows/test_image_build_push.yml +++ b/.gitea/workflows/test_image_build_push.yml @@ -1,10 +1,8 @@ name: sample gradle build and test run-name: Build started by ${{ gitea.actor }} - on: push: branches: [test] - jobs: tag: runs-on: ubuntu-latest @@ -28,7 +26,6 @@ jobs: NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}" echo "New version: ${NEW_VERSION}" echo "new_version=${NEW_VERSION}" >> $GITHUB_OUTPUT - build_tag_push: runs-on: ubuntu-latest needs: tag @@ -39,16 +36,13 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: JDK setup uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' - - name: Validate Gradle Wrapper uses: gradle/actions/wrapper-validation@v3 - - name: Create and push tag run: | echo "New version: ${{ needs.tag.outputs.new_version }}" @@ -56,25 +50,20 @@ jobs: git config --global user.email "${{ gitea.actor }}@users.noreply.github.com" git tag -a "${{ needs.tag.outputs.new_version }}" -m "Pushing new version ${{ needs.tag.outputs.new_version }}" git push origin "${{ needs.tag.outputs.new_version }}" - - name: Log in to Gitea Docker Registry uses: docker/login-action@v3 with: registry: 'http://192.168.0.100:8928' username: hitanshu password: ${{ secrets.TOKEN }} - - name: Gradle build run: ./gradlew bootBuildImage --imageName=192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} - - name: Tag image as test run: docker tag 192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} 192.168.0.100:8928/hithomelabs/cftunnels:test - - name: Push to Gitea Registry run: | docker push 192.168.0.100:8928/hithomelabs/cftunnels:test docker push 192.168.0.100:8928/hithomelabs/cftunnels:${{ needs.tag.outputs.new_version }} - sync_forks: name: Sync All Forks runs-on: ubuntu-latest @@ -82,7 +71,6 @@ jobs: steps: - name: Check out repository code uses: actions/checkout@v4 - - name: Sync all forks via Gitea API run: | echo "Fetching forks for Hithomelabs/CFTunnels..." @@ -93,6 +81,6 @@ jobs: readarray -t forks <<< "$filtered" for fork_url in "${forks[@]}"; do echo "🔄 Syncing fork: $fork_url" - git push "$fork_url" test & + authed_url=$(echo "$fork_url" | sed "s#https://#https://${{secrets.TOKEN}}@#") + git push "$authed_url" test & done - From 8b220640404e89cb53ed9a5318a0e294a7a44275 Mon Sep 17 00:00:00 2001 From: hitanshu310 Date: Sat, 15 Nov 2025 01:22:29 +0530 Subject: [PATCH 4/6] ISSUE-44: Hithomelabs/HomeLabDocker#44 Executing integration test every 6 hours --- .gitea/workflows/integration_test.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/integration_test.yaml b/.gitea/workflows/integration_test.yaml index 3a4697c..688f37b 100644 --- a/.gitea/workflows/integration_test.yaml +++ b/.gitea/workflows/integration_test.yaml @@ -1,10 +1,8 @@ name: Daily cloudflare API integration test on: - push: - branches: [ test ] -# schedule: -# - cron: '0 * * * *' # Every hour -# workflow_dispatch: + schedule: + - cron: '0 */6 * * *' # Every hour + workflow_dispatch: jobs: cloudflare-api-test: From f36d8077314a7831b5acb0877f22138e73749852 Mon Sep 17 00:00:00 2001 From: hitanshu310 Date: Sat, 15 Nov 2025 01:27:32 +0530 Subject: [PATCH 5/6] ISSUE-44: Hithomelabs/HomeLabDocker#44 making tests more verbose --- build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build.gradle b/build.gradle index c85d1f3..0800341 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,11 @@ test { useJUnitPlatform { excludeTags 'integration' } + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + exceptionFormat "full" // shows full stack trace + showStandardStreams = true // shows println/log output + } } tasks.register('integrationTestOnly', Test) { @@ -26,6 +31,11 @@ tasks.register('integrationTestOnly', Test) { } description = 'Runs only integration tests tagged with @Tag("integration")' group = 'verification' + testLogging { + events "passed", "skipped", "failed" + exceptionFormat "full" + showStandardStreams = true + } } repositories { From 43416d4bc74b36bb3506eba860e746c2bcc5bc87 Mon Sep 17 00:00:00 2001 From: hitanshu310 Date: Sun, 16 Nov 2025 18:59:06 +0530 Subject: [PATCH 6/6] ISSUE-44: Hithomelabs/HomeLabDocker#44 adding more tests --- .../hithomelabs/CFTunnels/Models/Ingress.java | 41 +++------- .../CoudflareApiIntegrationTest.java | 78 +++++++++++++++---- 2 files changed, 74 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/hithomelabs/CFTunnels/Models/Ingress.java b/src/main/java/com/hithomelabs/CFTunnels/Models/Ingress.java index 7c6c7be..005a02b 100644 --- a/src/main/java/com/hithomelabs/CFTunnels/Models/Ingress.java +++ b/src/main/java/com/hithomelabs/CFTunnels/Models/Ingress.java @@ -1,8 +1,17 @@ package com.hithomelabs.CFTunnels.Models; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + import java.util.List; import java.util.Map; +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter public class Ingress { private String service; @@ -10,40 +19,8 @@ public class Ingress { private Map originRequest; private String path; - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - public static boolean deleteByHostName(List ingressList, String toBeDeleted){ return ingressList.removeIf(ingress -> ingress.getHostname() != null && ingress.getHostname().equals(toBeDeleted)); } - public String getService() { - return service; - } - - public void setService(String service) { - this.service = service; - } - - public String getHostname() { - return hostname; - } - - public void setHostname(String hostname) { - this.hostname = hostname; - } - - public Map getOriginRequest() { - return originRequest; - } - - public void setOriginRequest(Map originRequest) { - this.originRequest = originRequest; - } } diff --git a/src/test/java/com/hithomelabs/CFTunnels/Integration/CoudflareApiIntegrationTest.java b/src/test/java/com/hithomelabs/CFTunnels/Integration/CoudflareApiIntegrationTest.java index 0ce0bc0..9553db1 100644 --- a/src/test/java/com/hithomelabs/CFTunnels/Integration/CoudflareApiIntegrationTest.java +++ b/src/test/java/com/hithomelabs/CFTunnels/Integration/CoudflareApiIntegrationTest.java @@ -1,25 +1,25 @@ package com.hithomelabs.CFTunnels.Integration; -import com.fasterxml.jackson.databind.ObjectMapper; import com.hithomelabs.CFTunnels.Config.CloudflareConfig; import com.hithomelabs.CFTunnels.Headers.AuthKeyEmailHeader; +import com.hithomelabs.CFTunnels.Models.Config; +import com.hithomelabs.CFTunnels.Models.Ingress; +import com.hithomelabs.CFTunnels.Models.TunnelResponse; +import com.hithomelabs.CFTunnels.Services.CloudflareAPIService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("integration") @@ -27,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class CoudflareApiIntegrationTest { @Autowired - TestRestTemplate restTemplate; + RestTemplate restTemplate; @Autowired AuthKeyEmailHeader authKeyEmailHeader; @@ -35,21 +35,73 @@ public class CoudflareApiIntegrationTest { @Autowired CloudflareConfig cloudflareConfig; - private static ObjectMapper mapper = new ObjectMapper(); + @Autowired + CloudflareAPIService cloudflareAPIService; + + private static final String DEV_TUNNEL_ID = "50df9101-f625-4618-b7c5-100338a57124"; @Test @DisplayName("Calls cloudflare cfd tunnels API and checks that dev tunnel should be a part of the response") - public void getTunnelsTest(){ + public void testGetTunnelsTest() { + + ResponseEntity response = cloudflareAPIService.getCloudflareTunnels(); - // * * Resource URL to hit get request at - String url = "https://api.cloudflare.com/client/v4/accounts/" + cloudflareConfig.getAccountId() + "/cfd_tunnel"; - HttpEntity httpEntity = new HttpEntity<>("", authKeyEmailHeader.getHttpHeaders()); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Map.class); assertEquals(HttpStatus.OK, response.getStatusCode()); + List> tunnelList = (List>) response.getBody().get("result"); boolean hasName = tunnelList.stream() .anyMatch(tunnel -> "devtunnel".equals(tunnel.get("name"))); + assertTrue(hasName); } + @Test + @DisplayName("Calls cloudflare API to get mappings for devtunnel tunnel") + public void testTunnelConfigurations() { + + ResponseEntity responseEntity = cloudflareAPIService.getCloudflareTunnelConfigurations(DEV_TUNNEL_ID, restTemplate, TunnelResponse.class); + + // * * Check if status code is 200 + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + + // * * Checking if mapping for devdocker exists + TunnelResponse tunnelResponse = responseEntity.getBody(); + boolean hasMatch = tunnelResponse.getResult().getConfig().getIngress().stream() + .anyMatch(ingress -> "devdocker.hithomelabs.com".equals(ingress.getHostname())); + assertTrue(hasMatch); + } + + @Test + @DisplayName("Inserts and deletes a mapping using Cloudflare API") + public void testAddAndDeleteMapping() { + + ResponseEntity beforeMapping = cloudflareAPIService.getCloudflareTunnelConfigurations(DEV_TUNNEL_ID, restTemplate, TunnelResponse.class); + + assertEquals(HttpStatus.OK, beforeMapping.getStatusCode()); + + Ingress ingress = new Ingress(); + ingress.setHostname("random.hithomelabs.com"); + ingress.setService("http://192.168.0.100:3457"); + + Config beforeInsertConfig = beforeMapping.getBody().getResult().getConfig(); + List beforeInsert = beforeInsertConfig.getIngress(); + beforeInsert.add(beforeInsert.size() - 1, ingress); + + ResponseEntity afterInsert = cloudflareAPIService.putCloudflareTunnelConfigurations(DEV_TUNNEL_ID, restTemplate, TunnelResponse.class, beforeInsertConfig); + + assertEquals(HttpStatus.OK, afterInsert.getStatusCode()); + Config afterInsertConfig = afterInsert.getBody().getResult().getConfig(); + List ingressList = afterInsertConfig.getIngress(); + + boolean hasIngress = ingressList.get(ingressList.size() - 2 ).getHostname().equals("random.hithomelabs.com"); + assertTrue(hasIngress); + + Boolean deleteSuccess = Ingress.deleteByHostName(ingressList, ingress.getHostname()); + assertTrue(deleteSuccess); + + ResponseEntity afterDelete = cloudflareAPIService.putCloudflareTunnelConfigurations(DEV_TUNNEL_ID, restTemplate, TunnelResponse.class, afterInsertConfig); + assertEquals(HttpStatus.OK, afterDelete.getStatusCode()); + assertFalse(afterDelete.getBody().getResult().getConfig().getIngress().stream().anyMatch(anyIngress -> "random.hithomelabs.com".equals(anyIngress.getHostname()))); + } + }