From 8c51cee2a57ebe6b76080efd82a3dd950c21bf8a Mon Sep 17 00:00:00 2001 From: Jamie Banks Date: Tue, 9 Jun 2026 13:55:11 +0100 Subject: [PATCH] feat: expose application version and update CI release workflow --- .gitea/workflows/build.yml | 48 ----------------- .gitea/workflows/release.yml | 81 ++++++++++++++++++++++++++++ app.go | 4 ++ frontend/index.html | 1 + frontend/src/main.js | 90 +++++++++++++++++-------------- frontend/wailsjs/go/main/App.d.ts | 2 + frontend/wailsjs/go/main/App.js | 4 ++ main.go | 2 + wails.json | 3 +- 9 files changed, 145 insertions(+), 90 deletions(-) delete mode 100644 .gitea/workflows/build.yml create mode 100644 .gitea/workflows/release.yml diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml deleted file mode 100644 index 4e844ef..0000000 --- a/.gitea/workflows/build.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Build and Upload Binaries - -on: - push: - branches: - - main - -jobs: - build-and-upload: - runs-on: build-htz-01 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Build Windows and Linux AMD64 binaries - run: | - mkdir -p dist - - # Build Linux amd64 and Windows amd64 binaries - docker run --rm -v ${{ github.workspace }}:/workspace -w /workspace cr.jdbnet.co.uk/jdbnet/wails-builder:latest bash -c " - # Install frontend dependencies - cd frontend && npm install && cd .. - - # Linux amd64 - wails build -platform linux/amd64 -tags webkit2_41 -clean - cp build/bin/goexplore dist/goexplore-linux-amd64 - - # Windows amd64 - wails build -platform windows/amd64 -clean -nsis - cp build/bin/GoExplore-amd64-installer.exe dist/goexplore-windows-installer.exe - " - - - name: Upload to Cloudflare R2 - env: - AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: auto - R2_ENDPOINT: ${{ secrets.R2_ENDPOINT_URL }} - run: | - aws s3 cp dist/goexplore-linux-amd64 s3://apps/goexplore/goexplore-linux-amd64 --endpoint-url "$R2_ENDPOINT" - aws s3 cp dist/goexplore-windows-installer.exe s3://apps/goexplore/goexplore-windows-installer.exe --endpoint-url "$R2_ENDPOINT" - - - name: Clean up - run: sudo rm -rf dist build/bin/* diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..a5e6b6e --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,81 @@ +name: Release + +on: + pull_request: + branches: + - main + types: [closed] + +jobs: + release: + if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'v') + runs-on: build-htz-01 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract Version + id: get_version + run: | + # Remove 'v' prefix for some contexts if needed, but we'll use it raw + RAW_VERSION=${{ github.head_ref }} + CLEAN_VERSION=${RAW_VERSION#v} + echo "VERSION=${RAW_VERSION}" >> $GITHUB_OUTPUT + echo "CLEAN_VERSION=${CLEAN_VERSION}" >> $GITHUB_OUTPUT + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Build Binaries + run: | + mkdir -p dist + + # Build Linux amd64 and Windows amd64 binaries using custom docker image + docker run --rm -v ${{ github.workspace }}:/workspace -w /workspace cr.jdbnet.co.uk/jdbnet/wails-builder:latest bash -c " + # Inject version into wails.json for NSIS & executable properties + sed -i 's/\"productVersion\": \".*\"/\"productVersion\": \"${{ steps.get_version.outputs.CLEAN_VERSION }}\"/' wails.json + + # Install frontend dependencies + cd frontend && npm install && cd .. + + # Linux amd64 + wails build -platform linux/amd64 -tags webkit2_41 -clean -ldflags \"-X main.Version=${{ steps.get_version.outputs.VERSION }}\" + cp build/bin/goexplore dist/goexplore-linux-amd64 + + # Windows amd64 + wails build -platform windows/amd64 -clean -nsis -ldflags \"-X main.Version=${{ steps.get_version.outputs.VERSION }}\" + cp build/bin/GoExplore-amd64-installer.exe dist/goexplore-windows-installer.exe + " + + # - name: Generate Changelog + # id: changelog + # uses: https://github.com/metcalfc/changelog-generator@v4.6.2 + # with: + # myToken: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Gitea Release + uses: https://gitea.com/actions/gitea-release-action@v1 + with: + tag_name: ${{ steps.get_version.outputs.VERSION }} + name: GoExplore ${{ steps.get_version.outputs.VERSION }} + body: ${{ steps.changelog.outputs.changelog }} + draft: false + prerelease: false + files: | + dist/goexplore-linux-amd64 + dist/goexplore-windows-installer.exe + + - name: Upload to Cloudflare R2 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: auto + R2_ENDPOINT: ${{ secrets.R2_ENDPOINT_URL }} + run: | + aws s3 cp dist/goexplore-linux-amd64 s3://apps/goexplore/goexplore-linux-amd64 --endpoint-url "$R2_ENDPOINT" + aws s3 cp dist/goexplore-windows-installer.exe s3://apps/goexplore/goexplore-windows-installer.exe --endpoint-url "$R2_ENDPOINT" + + - name: Clean up + run: sudo rm -rf dist build/bin/* diff --git a/app.go b/app.go index 764e901..7788abf 100644 --- a/app.go +++ b/app.go @@ -44,6 +44,10 @@ func (a *App) startup(ctx context.Context) { a.ctx = ctx } +func (a *App) GetVersion() string { + return Version +} + func (a *App) GetConnections() []config.ConnectionConfig { if a.cfg == nil { return nil diff --git a/frontend/index.html b/frontend/index.html index 77c94c6..0e2d7dd 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -11,6 +11,7 @@
diff --git a/frontend/src/main.js b/frontend/src/main.js index 2c95fdd..5f5bd0e 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,4 +1,4 @@ -import { ListDir, GetConnections, SaveConnection, DeleteConnection, Delete, Rename, PromptUploadFiles, PromptUploadDirectory, PromptDownload, TransferItems, GetTransfers, ClearTransfers } from '../wailsjs/go/main/App.js'; +import { GetVersion, ListDir, GetConnections, SaveConnection, DeleteConnection, Delete, Rename, PromptUploadFiles, PromptUploadDirectory, PromptDownload, TransferItems, GetTransfers, ClearTransfers } from '../wailsjs/go/main/App.js'; let currentConn = 'local'; let currentPath = ''; @@ -10,9 +10,9 @@ let sortField = 'name'; let sortAsc = true; function uuidv4() { - return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) - ); + return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => + (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) + ); } async function init() { @@ -24,9 +24,17 @@ async function init() { } } + try { + const version = await GetVersion(); + const vSpan = document.getElementById('app-version'); + if (vSpan && version) { + vSpan.innerText = version; + } + } catch (e) { } + await loadConnections(); await loadDirectory(currentConn, currentPath); - + document.getElementById('conn-form').addEventListener('submit', async (e) => { e.preventDefault(); const conn = { @@ -41,12 +49,12 @@ async function init() { username: document.getElementById('conn-username').value }; const secret = document.getElementById('conn-secret').value; - + try { await SaveConnection(conn, secret); closeConnModal(); loadConnections(); - } catch(err) { + } catch (err) { alert("Failed to save connection: " + err); } }); @@ -57,7 +65,7 @@ async function loadConnections() { list.innerHTML = `
OS Local Filesystem
`; - + try { const conns = await GetConnections(); connectionsCache = conns || []; @@ -70,7 +78,7 @@ async function loadConnections() { list.appendChild(el); }); } - } catch(e) { + } catch (e) { console.error(e); } } @@ -95,9 +103,9 @@ window.editConnection = (id) => { document.getElementById('conn-region').value = c.region || ''; document.getElementById('conn-pathstyle').checked = c.path_style || false; document.getElementById('conn-username').value = c.username || ''; - document.getElementById('conn-secret').value = ''; + document.getElementById('conn-secret').value = ''; document.getElementById('conn-delete-btn').style.display = 'block'; - + updateProtocolFields(); document.getElementById('conn-modal').style.display = 'flex'; }; @@ -117,10 +125,10 @@ window.closeConnModal = () => { window.updateProtocolFields = () => { const protocol = document.getElementById('conn-protocol').value; - + const showBucket = protocol === 's3' || protocol === 'smb'; document.getElementById('bucket-fields').style.display = showBucket ? 'block' : 'none'; - + // Only show S3 specific fields when S3 is selected const showS3Specific = protocol === 's3'; document.getElementById('region-group').style.display = showS3Specific ? 'flex' : 'none'; @@ -128,17 +136,17 @@ window.updateProtocolFields = () => { }; window.deleteConnection = async () => { - if(!confirm("Are you sure you want to delete this connection?")) return; + if (!confirm("Are you sure you want to delete this connection?")) return; const id = document.getElementById('conn-id').value; try { await DeleteConnection(id); closeConnModal(); - if(currentConn === id) { + if (currentConn === id) { switchConn('local'); } else { loadConnections(); } - } catch(err) { + } catch (err) { alert("Failed to delete connection: " + err); } }; @@ -147,13 +155,13 @@ async function loadDirectory(connId, path) { const tbody = document.getElementById('file-list'); tbody.innerHTML = 'Loading...'; document.getElementById('breadcrumb').innerText = path || '/'; - + try { const files = await ListDir(connId, path); tbody.innerHTML = ''; selectedItems = []; lastSelectedPath = null; - + let displayFiles = files || []; if (!showHiddenFiles) { displayFiles = displayFiles.filter(f => !f.name.startsWith('.')); @@ -163,12 +171,12 @@ async function loadDirectory(connId, path) { tbody.innerHTML = 'Empty directory'; return; } - + displayFiles.sort((a, b) => { if (a.is_dir !== b.is_dir) return a.is_dir ? -1 : 1; - + let cmp = 0; - switch(sortField) { + switch (sortField) { case 'name': cmp = a.name.localeCompare(b.name); break; @@ -203,7 +211,7 @@ async function loadDirectory(connId, path) { tr.onclick = (e) => { const path = f.path; const isSelected = selectedItems.some(i => i.path === path); - + if (e.ctrlKey || e.metaKey) { if (isSelected) { selectedItems = selectedItems.filter(i => i.path !== path); @@ -219,13 +227,13 @@ async function loadDirectory(connId, path) { const currIdx = allRows.findIndex(r => r.dataset.path === path); const startIdx = Math.min(lastIdx, currIdx); const endIdx = Math.max(lastIdx, currIdx); - + selectedItems = []; allRows.forEach(r => r.classList.remove('selected')); - - for(let i=startIdx; i<=endIdx; i++) { + + for (let i = startIdx; i <= endIdx; i++) { const row = allRows[i]; - if(row.dataset.path) { + if (row.dataset.path) { row.classList.add('selected'); selectedItems.push({ path: row.dataset.path, @@ -251,7 +259,7 @@ async function loadDirectory(connId, path) { } tbody.appendChild(tr); }); - } catch(e) { + } catch (e) { tbody.innerHTML = `Error: ${e}`; } } @@ -279,7 +287,7 @@ function formatDate(dateString) { if (!dateString) return '-'; const d = new Date(dateString); if (isNaN(d.getTime())) return dateString; - return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth()+1).padStart(2, '0')}/${d.getFullYear()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; + return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; } window.setSort = (field) => { @@ -300,7 +308,7 @@ window.clearTransfers = async () => { try { await ClearTransfers(); pollTransfers(); - } catch(e) { + } catch (e) { console.error(e); } }; @@ -311,7 +319,7 @@ async function pollTransfers() { const list = document.getElementById('transfer-list'); const btn = document.getElementById('transfers-btn'); btn.innerText = `Transfers (${transfers ? transfers.length : 0})`; - + if (!transfers || transfers.length === 0) { list.innerHTML = '
No active transfers
'; return; @@ -323,7 +331,7 @@ async function pollTransfers() { const speed = t.status === 'active' ? `${t.speed_mbps.toFixed(2)} MB/s` : t.status; const eta = t.status === 'active' ? `${t.eta_seconds}s remaining` : ''; const color = t.status === 'failed' ? 'var(--error)' : (t.status === 'complete' ? 'var(--success)' : 'var(--accent)'); - + html += `
${t.filename}
@@ -338,7 +346,7 @@ async function pollTransfers() {
`; }); list.innerHTML = html; - } catch(e) { + } catch (e) { console.error(e); } } @@ -359,7 +367,7 @@ document.addEventListener('contextmenu', (e) => { if (row && row.dataset.path && !row.closest('#transfer-dest-list')) { e.preventDefault(); - + if (!selectedItems.some(i => i.path === row.dataset.path)) { selectedItems = [{ path: row.dataset.path, @@ -384,7 +392,7 @@ document.addEventListener('contextmenu', (e) => { menu.style.top = `${e.pageY}px`; } else if (isBrowserPane && !e.target.closest('#transfer-dest-list')) { e.preventDefault(); - + document.getElementById('ctx-rename').style.display = 'none'; document.getElementById('ctx-transfer').style.display = 'none'; document.getElementById('ctx-download').style.display = 'none'; @@ -421,7 +429,7 @@ window.handleContextMenu = async (action) => { } if (selectedItems.length === 0) return; - + try { if (action === 'delete') { if (confirm(`Are you sure you want to delete ${selectedItems.length} items?`)) { @@ -441,7 +449,7 @@ window.handleContextMenu = async (action) => { const pathParts = item.path.split('/'); pathParts[pathParts.length - 1] = newName; const newPath = pathParts.join('/'); - + await Rename(currentConn, item.path, newPath); refreshCurrentDir(); } @@ -494,11 +502,11 @@ window.loadTransferDestDirectory = async (connId, path) => { const tbody = document.getElementById('transfer-dest-list'); tbody.innerHTML = 'Loading...'; document.getElementById('transfer-dest-breadcrumb').innerText = path || '/'; - + try { const files = await ListDir(connId, path); tbody.innerHTML = ''; - + if (path && path !== '/') { const tr = document.createElement('tr'); tr.innerHTML = `📁 ..`; @@ -515,7 +523,7 @@ window.loadTransferDestDirectory = async (connId, path) => { tbody.innerHTML += 'Empty directory'; return; } - + files.forEach(f => { const tr = document.createElement('tr'); tr.style.cursor = 'pointer'; @@ -527,7 +535,7 @@ window.loadTransferDestDirectory = async (connId, path) => { } tbody.appendChild(tr); }); - } catch(e) { + } catch (e) { tbody.innerHTML = `Error: ${e}`; } }; @@ -540,7 +548,7 @@ window.executeTransfer = async () => { showTransfers(); selectedItems = []; Array.from(document.querySelectorAll('#file-list tr')).forEach(r => r.classList.remove('selected')); - } catch(e) { + } catch (e) { alert("Transfer failed: " + e); } }; diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 90d039c..c3f43c3 100755 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -15,6 +15,8 @@ export function GetConnections():Promise>; export function GetTransfers():Promise>; +export function GetVersion():Promise; + export function ListDir(arg1:string,arg2:string):Promise>; export function MkDir(arg1:string,arg2:string):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index c0c70e3..1e0950e 100755 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -22,6 +22,10 @@ export function GetTransfers() { return window['go']['main']['App']['GetTransfers'](); } +export function GetVersion() { + return window['go']['main']['App']['GetVersion'](); +} + export function ListDir(arg1, arg2) { return window['go']['main']['App']['ListDir'](arg1, arg2); } diff --git a/main.go b/main.go index c86cbff..7d90f1d 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,8 @@ var assets embed.FS //go:embed build/appicon.png var icon []byte +var Version string = "dev" + func installLinux() { if runtime.GOOS != "linux" { return diff --git a/wails.json b/wails.json index d71e659..ac2ddf6 100644 --- a/wails.json +++ b/wails.json @@ -11,6 +11,7 @@ }, "info": { "companyName": "JDB-NET Limited", - "productName": "GoExplore" + "productName": "GoExplore", + "productVersion": "0.0.0" } } \ No newline at end of file -- 2.47.3