Enhance development workflow and deployment practices for Archipelago

- Updated the Development-Workflow documentation to clarify deployment strategy, emphasizing direct deployment to the live system for testing.
- Added detailed instructions for the deployment command, including syncing code, building frontend and backend, and restarting services.
- Improved SSH key management section to assist with authentication issues.
- Expanded the testing workflow to include steps for checking logs and syncing changes back to the ISO build.
- Updated the ISO build integration section to ensure system-level changes are captured for future builds.
- Refactored various sections for clarity and completeness, including deployment paths and system configuration files.
This commit is contained in:
Dorian
2026-02-01 13:24:03 +00:00
parent 00d1af12f0
commit 34fc06726e
28 changed files with 1248 additions and 285 deletions
+58 -36
View File
@@ -81,16 +81,38 @@
<button
v-if="pkg.state === 'stopped'"
@click.stop="startApp(id as string)"
class="flex-1 px-4 py-2 bg-green-500/20 border border-green-500/40 rounded-lg text-green-200 text-sm font-medium hover:bg-green-500/30 transition-colors"
:disabled="loadingActions[id as string]"
class="flex-1 px-4 py-2 bg-green-500/20 border border-green-500/40 rounded-lg text-green-200 text-sm font-medium hover:bg-green-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
>
Start
<svg
v-if="loadingActions[id as string]"
class="animate-spin h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>{{ loadingActions[id as string] ? 'Starting...' : 'Start' }}</span>
</button>
<button
v-if="pkg.state === 'running'"
@click.stop="stopApp(id as string)"
class="flex-1 px-4 py-2 bg-yellow-500/20 border border-yellow-500/40 rounded-lg text-yellow-200 text-sm font-medium hover:bg-yellow-500/30 transition-colors"
:disabled="loadingActions[id as string]"
class="flex-1 px-4 py-2 bg-yellow-500/20 border border-yellow-500/40 rounded-lg text-yellow-200 text-sm font-medium hover:bg-yellow-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
>
Stop
<svg
v-if="loadingActions[id as string]"
class="animate-spin h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>{{ loadingActions[id as string] ? 'Stopping...' : 'Stop' }}</span>
</button>
</div>
</div>
@@ -152,6 +174,9 @@ import { PackageState } from '../types/api'
const router = useRouter()
const store = useAppStore()
// Track loading states for each app action
const loadingActions = ref<Record<string, boolean>>({})
// Use real packages from store - no more dummy apps
const packages = computed(() => {
const realPackages = store.packages
@@ -185,38 +210,14 @@ function launchApp(id: string) {
const isDev = import.meta.env.DEV
const pkg = packages.value[id]
// Special handling for Bitcoin Core - open in new tab on port 18445
if (id === 'bitcoin') {
window.open('http://localhost:18445', '_blank', 'noopener,noreferrer')
return
}
// Special handling for LND - open in new tab on port 8085
if (id === 'lnd') {
window.open('http://localhost:8085', '_blank', 'noopener,noreferrer')
return
}
// Special handling for Penpot - open in new tab on port 9001
if (id === 'penpot' || id === 'penpot-frontend') {
window.open('http://localhost:9001', '_blank', 'noopener,noreferrer')
return
}
// Special handling for Morphos - open in new tab on port 8081
if (id === 'morphos' || id === 'morphos-server') {
window.open('http://localhost:8081', '_blank', 'noopener,noreferrer')
return
}
// Special handling for Nextcloud - open in new tab on port 8086
if (id === 'nextcloud') {
window.open('http://localhost:8086', '_blank', 'noopener,noreferrer')
return
}
// Get the LAN address from the package manifest
const lanAddress = pkg?.installed?.['interface-addresses']?.main?.['lan-address']
let lanAddress = pkg?.installed?.['interface-addresses']?.main?.['lan-address']
// Replace localhost with the current hostname (for remote access)
if (lanAddress && lanAddress.includes('localhost')) {
const currentHost = window.location.hostname
lanAddress = lanAddress.replace('localhost', currentHost)
}
if (lanAddress) {
window.open(lanAddress, '_blank', 'noopener,noreferrer')
@@ -236,7 +237,12 @@ function launchApp(id: string) {
}
if (appUrls[id]) {
const url = isDev ? appUrls[id].dev : appUrls[id].prod
let url = isDev ? appUrls[id].dev : appUrls[id].prod
// Replace localhost with current hostname for remote access
if (url.includes('localhost')) {
const currentHost = window.location.hostname
url = url.replace('localhost', currentHost)
}
window.open(url, '_blank', 'noopener,noreferrer')
return
}
@@ -267,18 +273,34 @@ function goToApp(id: string) {
}
async function startApp(id: string) {
loadingActions.value[id] = true
try {
await store.startPackage(id)
// Wait for state update from WebSocket
// The loader will be cleared when we receive the updated state
// For now, keep a max timeout as fallback
setTimeout(() => {
loadingActions.value[id] = false
}, 5000)
} catch (err) {
console.error('Failed to start app:', err)
loadingActions.value[id] = false
}
}
async function stopApp(id: string) {
loadingActions.value[id] = true
try {
await store.stopPackage(id)
// Wait for state update from WebSocket
// The loader will be cleared when we receive the updated state
// For now, keep a max timeout as fallback
setTimeout(() => {
loadingActions.value[id] = false
}, 5000)
} catch (err) {
console.error('Failed to stop app:', err)
loadingActions.value[id] = false
}
}
+6 -3
View File
@@ -24,7 +24,7 @@
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Bundled Apps -->
<div
v-for="app in BUNDLED_APPS"
v-for="app in bundledApps"
:key="app.id"
class="glass-card p-6 hover:bg-white/5 transition-colors"
>
@@ -189,6 +189,9 @@ import ContainerStatus from '@/components/ContainerStatus.vue'
const store = useContainerStore()
// Expose BUNDLED_APPS to the template (prevents tree-shaking)
const bundledApps = BUNDLED_APPS
// Get current host for launch URLs
const currentHost = computed(() => window.location.hostname)
@@ -205,14 +208,14 @@ onMounted(async () => {
// Containers that aren't bundled apps
const otherContainers = computed(() => {
const bundledIds = BUNDLED_APPS.map(a => a.id)
const bundledIds = bundledApps.map(a => a.id)
return store.containers.filter(c => {
const name = c.name.toLowerCase()
return !bundledIds.some(id => name.includes(id))
})
})
const hasAnyApps = computed(() => BUNDLED_APPS.length > 0 || store.containers.length > 0)
const hasAnyApps = computed(() => bundledApps.length > 0 || store.containers.length > 0)
function extractAppName(containerName: string): string {
return containerName