Skip to content

Cleanup: version 1.0.0, drop binaries, fix scan loop - #2

Merged
hsr88 merged 3 commits into
mainfrom
cursor/cleanup-short-fixes-68ca
Aug 13, 2026
Merged

Cleanup: version 1.0.0, drop binaries, fix scan loop#2
hsr88 merged 3 commits into
mainfrom
cursor/cleanup-short-fixes-68ca

Conversation

@hsr88

@hsr88 hsr88 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Short housekeeping from the repo review.

Changes

  • Version 1.0.0 in package.json, Cargo, Tauri, UI badge, and README (clone URL + envtunnel.exe path).
  • Stop tracking installers (release/) and leftover screenshots/Vite assets. Favicon/public/icon.png shrunk from 1024×1024 (~1.5 MB) to 128×128.
  • Scan loop: selectedPort is kept in a ref so clicking a port no longer resets the 3s timer. Custom ports and path persist in localStorage.
  • LAN IP: skip VPN/WSL/Tailscale/CGNAT interfaces and prefer 192.16810.x172.16-31.
  • CI: npm run lint + npm run build, cargo check + cargo test --lib.
  • Landing page: download cards show v1.0.0 names and real file sizes. hrefs still target the published GitHub Release assets (EnvTunnel_0.1.0_* — those are the actual filenames; renaming the URL to 1.0.0 would 404 until a new release is published). “Build from source” now points at the README installation heading instead of a broken #-installation fragment.

Checks

  • npm run lint — pass
  • npm run build — pass
  • cargo test --lib could not run in this environment (Rust 1.83 vs a lockfile crate that needs edition 2024). GitHub Actions uses current stable.

Not in this PR: macOS/Linux builds, HTTPS QR codes, full settings UI.

Open in Web Open in Cursor 

Summary by cubic

Releases v1.0.0, removes committed installers, and fixes the scan loop so selecting a port no longer resets the 3s timer or retriggers IP lookups; QR codes now prefer reachable LAN IPs.

  • Scan loop: keep selectedPort in a ref; run a stable 3s timer against a fixed IP; fold IP fetch into the scan timer; persist custom ports and path in localStorage; satisfies React hooks lint.
  • LAN IP selection: skip VPN/WSL/Tailscale/CGNAT interfaces and prioritize 192.168 → 10.x → 172.16–31; adds unit tests for heuristics.
  • Versioning/docs: bump package.json, Cargo.toml, tauri.conf.json, UI badge, and README (repo URL and envtunnel.exe path). Landing download cards show v1.0.0 names and sizes; “Build from source” links to the README installation heading.
  • Repo hygiene: delete tracked installers/assets and add release/ to .gitignore; shrink icons.
  • CI: new workflow runs npm run lint, npm run build, cargo check --locked, and cargo test --locked --lib with Tauri dependencies.

Rollout

  • Do not commit installers. Publish via GitHub Releases and update download hrefs only when v1.0.0 assets are available (filenames may remain EnvTunnel_0.1.0_*).

Written for commit 6a5862f. Summary will update on new commits.

Review in cubic

cursoragent and others added 2 commits August 13, 2026 19:57
Align package/Cargo/Tauri/UI versions, stop tracking installers and leftover
assets, persist custom ports, prefer LAN IPs over VPN/WSL, and add CI.

Co-authored-by: Krystian <haser88@gmail.com>
Keep selectedPort in a ref via an effect, and fold IP fetch into the
scan timer so eslint no longer flags render-time ref writes or unused catch bindings.

Co-authored-by: Krystian <haser88@gmail.com>
@hsr88
hsr88 marked this pull request as ready for review August 13, 2026 19:59
Keep hrefs on the published GitHub Release assets (filenames still
EnvTunnel_0.1.0_*), show v1.0.0 names and real file sizes, and point
Build from source at the README installation heading.

Co-authored-by: Krystian <haser88@gmail.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

6 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src-tauri/src/lib.rs">

<violation number="1" location="src-tauri/src/lib.rs:82">
P2: When multiple adapters share the same priority (e.g. Ethernet and Wi-Fi both on 192.168.x.x), the `_ => {}` arm keeps whichever the OS enumerated first, and `list_afinet_netifas()` order is not guaranteed stable. The chosen IP can therefore change between launches without any network change, producing an inconsistent QR/localhost IP. Break ties deterministically rather than relying on enumeration order.</violation>

<violation number="2" location="src-tauri/src/lib.rs:88">
P1: When no eligible RFC1918 interface exists, this new filtered path falls back to `local_ip()` and can return the VPN, Tailscale, or CGNAT address that the code claims to skip. Keep the fallback subject to the same interface/address filtering, or return an error when no reachable LAN address is available.</violation>
</file>

<file name="src/App.tsx">

<violation number="1" location="src/App.tsx:144">
P2: When `customPorts` changes during an in-flight scan, the effect starts a replacement scan but the old `scanPorts` still applies its result after cleanup. A stale result can overwrite the current ports and selection; guard result application with a generation token or serialize scans.</violation>

<violation number="2" location="src/App.tsx:161">
P2: On mount the loop scans twice: the effect's first run fetches the IP, begins an immediate in-flight scan, then setIp re-runs the effect (ip is a dependency) which starts a second immediate scan before the first finishes. Both concurrent scanPorts calls read the still-empty prevPortsRef, so every running dev server emits duplicate "PORT X ACTIVE" toasts and is probed back-to-back at launch. The old code scanned once on init and the interval effect returned early on a null ip, so this is a regression. Drop `ip` from the dependency array (only fetchIp mutates ip, and it is already awaited inside start) so the loop runs once and still re-scans when customPorts changes.</violation>
</file>

<file name=".github/workflows/ci.yml">

<violation number="1" location=".github/workflows/ci.yml:21">
P2: The `rust` job runs `cargo check`/`cargo test` on `ubuntu-latest`, but EnvTunnel is a Windows-only Tauri app (README lists Windows + Visual Studio Build Tools as the only supported build environment and documents a known Windows link failure). Running on Linux never produces or validates the actual shipping artifact and cannot catch Windows-specific breakages such as the documented `LNK1181: legacy_stdio_definitions.lib` failure, so the CI gives false confidence that the app builds. Consider running this job on `windows-latest` (with the LIB workaround for the known issue) and/or adding a `tauri build` step so the release binary is actually compiled in CI.</violation>

<violation number="2" location=".github/workflows/ci.yml:40">
P1: The `rust` job runs `cargo check` / `cargo test` on a fresh runner without a frontend build, so `dist/` never exists. `tauri::generate_context!()` (lib.rs:337) and tauri-build require `frontendDist` (`../dist`) to exist and panic with "frontendDist configuration is set to '../dist' but this path doesn't exist", so both rust steps fail on a clean checkout. The `frontend` job builds `dist/` but on a separate runner with no shared filesystem. Add `actions/setup-node@v4`, `npm ci` and `npm run build` (before `cargo check`) to the rust job, or at minimum create the empty `dist/` directory, so the tauri context macro has assets to load.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src-tauri/src/lib.rs
_ => {}
}
}
if let Some((_, ip)) = best {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: When no eligible RFC1918 interface exists, this new filtered path falls back to local_ip() and can return the VPN, Tailscale, or CGNAT address that the code claims to skip. Keep the fallback subject to the same interface/address filtering, or return an error when no reachable LAN address is available.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src-tauri/src/lib.rs, line 88:

<comment>When no eligible RFC1918 interface exists, this new filtered path falls back to `local_ip()` and can return the VPN, Tailscale, or CGNAT address that the code claims to skip. Keep the fallback subject to the same interface/address filtering, or return an error when no reachable LAN address is available.</comment>

<file context>
@@ -13,14 +13,117 @@ pub struct PortStatus {
+                _ => {}
+            }
+        }
+        if let Some((_, ip)) = best {
+            return Ok(ip);
+        }
</file context>

Comment thread .github/workflows/ci.yml
librsvg2-dev \
patchelf \
libssl-dev
- run: cargo check --locked

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: The rust job runs cargo check / cargo test on a fresh runner without a frontend build, so dist/ never exists. tauri::generate_context!() (lib.rs:337) and tauri-build require frontendDist (../dist) to exist and panic with "frontendDist configuration is set to '../dist' but this path doesn't exist", so both rust steps fail on a clean checkout. The frontend job builds dist/ but on a separate runner with no shared filesystem. Add actions/setup-node@v4, npm ci and npm run build (before cargo check) to the rust job, or at minimum create the empty dist/ directory, so the tauri context macro has assets to load.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/ci.yml, line 40:

<comment>The `rust` job runs `cargo check` / `cargo test` on a fresh runner without a frontend build, so `dist/` never exists. `tauri::generate_context!()` (lib.rs:337) and tauri-build require `frontendDist` (`../dist`) to exist and panic with "frontendDist configuration is set to '../dist' but this path doesn't exist", so both rust steps fail on a clean checkout. The `frontend` job builds `dist/` but on a separate runner with no shared filesystem. Add `actions/setup-node@v4`, `npm ci` and `npm run build` (before `cargo check`) to the rust job, or at minimum create the empty `dist/` directory, so the tauri context macro has assets to load.</comment>

<file context>
@@ -0,0 +1,41 @@
+            librsvg2-dev \
+            patchelf \
+            libssl-dev
+      - run: cargo check --locked
+      - run: cargo test --locked --lib
</file context>

Comment thread src/App.tsx
let timeoutId: number

const tick = async (targetIp: string) => {
await scanPorts(targetIp)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When customPorts changes during an in-flight scan, the effect starts a replacement scan but the old scanPorts still applies its result after cleanup. A stale result can overwrite the current ports and selection; guard result application with a generation token or serialize scans.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/App.tsx, line 144:

<comment>When `customPorts` changes during an in-flight scan, the effect starts a replacement scan but the old `scanPorts` still applies its result after cleanup. A stale result can overwrite the current ports and selection; guard result application with a generation token or serialize scans.</comment>

<file context>
@@ -102,28 +134,47 @@ function App() {
+    let timeoutId: number
+
+    const tick = async (targetIp: string) => {
+      await scanPorts(targetIp)
+      if (!cancelled) {
+        timeoutId = window.setTimeout(() => tick(targetIp), 3000)
</file context>

Comment thread .github/workflows/ci.yml
- run: npm run lint
- run: npm run build

rust:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The rust job runs cargo check/cargo test on ubuntu-latest, but EnvTunnel is a Windows-only Tauri app (README lists Windows + Visual Studio Build Tools as the only supported build environment and documents a known Windows link failure). Running on Linux never produces or validates the actual shipping artifact and cannot catch Windows-specific breakages such as the documented LNK1181: legacy_stdio_definitions.lib failure, so the CI gives false confidence that the app builds. Consider running this job on windows-latest (with the LIB workaround for the known issue) and/or adding a tauri build step so the release binary is actually compiled in CI.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/ci.yml, line 21:

<comment>The `rust` job runs `cargo check`/`cargo test` on `ubuntu-latest`, but EnvTunnel is a Windows-only Tauri app (README lists Windows + Visual Studio Build Tools as the only supported build environment and documents a known Windows link failure). Running on Linux never produces or validates the actual shipping artifact and cannot catch Windows-specific breakages such as the documented `LNK1181: legacy_stdio_definitions.lib` failure, so the CI gives false confidence that the app builds. Consider running this job on `windows-latest` (with the LIB workaround for the known issue) and/or adding a `tauri build` step so the release binary is actually compiled in CI.</comment>

<file context>
@@ -0,0 +1,41 @@
+      - run: npm run lint
+      - run: npm run build
+
+  rust:
+    runs-on: ubuntu-latest
+    defaults:
</file context>

Comment thread src/App.tsx
init()
return () => { isMounted = false }
}, [fetchIp, scanPorts])
}, [ip, scanPorts, fetchIp])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: On mount the loop scans twice: the effect's first run fetches the IP, begins an immediate in-flight scan, then setIp re-runs the effect (ip is a dependency) which starts a second immediate scan before the first finishes. Both concurrent scanPorts calls read the still-empty prevPortsRef, so every running dev server emits duplicate "PORT X ACTIVE" toasts and is probed back-to-back at launch. The old code scanned once on init and the interval effect returned early on a null ip, so this is a regression. Drop ip from the dependency array (only fetchIp mutates ip, and it is already awaited inside start) so the loop runs once and still re-scans when customPorts changes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/App.tsx, line 161:

<comment>On mount the loop scans twice: the effect's first run fetches the IP, begins an immediate in-flight scan, then setIp re-runs the effect (ip is a dependency) which starts a second immediate scan before the first finishes. Both concurrent scanPorts calls read the still-empty prevPortsRef, so every running dev server emits duplicate "PORT X ACTIVE" toasts and is probed back-to-back at launch. The old code scanned once on init and the interval effect returned early on a null ip, so this is a regression. Drop `ip` from the dependency array (only fetchIp mutates ip, and it is already awaited inside start) so the loop runs once and still re-scans when customPorts changes.</comment>

<file context>
@@ -102,28 +134,47 @@ function App() {
-    init()
-    return () => { isMounted = false }
-  }, [fetchIp, scanPorts])
+  }, [ip, scanPorts, fetchIp])
 
   useEffect(() => {
</file context>
Suggested change
}, [ip, scanPorts, fetchIp])
}, [scanPorts, fetchIp])

Comment thread src-tauri/src/lib.rs
};
match &best {
None => best = Some((prio, v4.to_string())),
Some((best_prio, _)) if prio < *best_prio => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When multiple adapters share the same priority (e.g. Ethernet and Wi-Fi both on 192.168.x.x), the _ => {} arm keeps whichever the OS enumerated first, and list_afinet_netifas() order is not guaranteed stable. The chosen IP can therefore change between launches without any network change, producing an inconsistent QR/localhost IP. Break ties deterministically rather than relying on enumeration order.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src-tauri/src/lib.rs, line 82:

<comment>When multiple adapters share the same priority (e.g. Ethernet and Wi-Fi both on 192.168.x.x), the `_ => {}` arm keeps whichever the OS enumerated first, and `list_afinet_netifas()` order is not guaranteed stable. The chosen IP can therefore change between launches without any network change, producing an inconsistent QR/localhost IP. Break ties deterministically rather than relying on enumeration order.</comment>

<file context>
@@ -13,14 +13,117 @@ pub struct PortStatus {
+            };
+            match &best {
+                None => best = Some((prio, v4.to_string())),
+                Some((best_prio, _)) if prio < *best_prio => {
+                    best = Some((prio, v4.to_string()));
+                }
</file context>

@hsr88
hsr88 merged commit e8b5b3b into main Aug 13, 2026
3 checks passed
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