revive deploy

This commit is contained in:
André Michelle
2025-08-07 11:57:19 +02:00
parent 81cd48368e
commit e2d4ca15a3
7 changed files with 328 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
# .github/workflows/deploy.yml
name: Deploy openDAW studio
on:
workflow_dispatch:
inputs:
dry:
description: "Dry run (no upload)"
required: false
default: 'true'
type: boolean
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: ⬇️ Checkout main repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: ⬇️ Checkout private submodule
uses: actions/checkout@v4
with:
repository: andremichelle/opendaw-lib
path: lib
ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }}
persist-credentials: false
- name: 🦄 Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: 📦 Install dependencies
run: npm ci
- name: 🏗️ Build project
shell: bash
run: |
bash ./rebuild.sh
- name: 🚚 Run deploy script
env:
TERM: xterm-256color
SFTP_HOST: ${{ secrets.SFTP_HOST }}
SFTP_PORT: ${{ secrets.SFTP_PORT }}
SFTP_USERNAME: ${{ secrets.SFTP_USERNAME }}
SFTP_PASSWORD: ${{ secrets.SFTP_PASSWORD }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
DRY_RUN: ${{ inputs.dry && '1' || '0' }}
run: |
npx ts-node --transpile-only deploy/run.ts ${{ inputs.dry && '--dry' || '' }}
+17
View File
@@ -0,0 +1,17 @@
name: Notify Discord
on:
workflow_dispatch:
jobs:
notify:
runs-on: ubuntu-latest
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: npm ci
- run: npx ts-node --transpile-only deploy/discord.ts
+18
View File
@@ -0,0 +1,18 @@
(async () => {
const webhookUrl = process.env.DISCORD_WEBHOOK
if (!webhookUrl) {
console.error("Missing DISCORD_WEBHOOK")
process.exit(1)
}
const content = "🧪 Discord test from GitHub Actions"
const res = await fetch(webhookUrl, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({content})
})
if (!res.ok) {
console.error(`Failed to post: ${res.status}`, await res.text())
process.exit(1)
}
console.log("✅ Discord message sent")
})()
+85
View File
@@ -0,0 +1,85 @@
import SftpClient from "ssh2-sftp-client"
import * as fs from "fs"
import * as path from "path"
const config = {
host: process.env.SFTP_HOST,
port: Number(process.env.SFTP_PORT),
username: process.env.SFTP_USERNAME,
password: process.env.SFTP_PASSWORD
} as const
const DRY_RUN = process.env.DRY_RUN === "1" || process.argv.includes("--dry")
console.info(`DRY_RUN: ${DRY_RUN}`)
const env = Object.entries({
SFTP_HOST: process.env.SFTP_HOST,
SFTP_PORT: process.env.SFTP_PORT,
SFTP_USERNAME: process.env.SFTP_USERNAME,
SFTP_PASSWORD: process.env.SFTP_PASSWORD,
DISCORD_WEBHOOK: process.env.DISCORD_WEBHOOK
})
const missing = env.filter(([, v]) => !v).map(([k]) => k)
if (missing.length > 0) {
throw new Error(`Missing secrets/vars: ${missing.join(", ")}`)
}
if (DRY_RUN) {
console.log("✅ All secrets & variables are set. Nothing was uploaded (dry-run).")
process.exit(0)
}
const sftp = new SftpClient()
const staticFolders = ["/viscious-speed"]
async function deleteDirectory(remoteDir: string) {
const items = await sftp.list(remoteDir)
for (const item of items) {
const remotePath = path.posix.join(remoteDir, item.name)
if (staticFolders.includes(remotePath)) continue
if (item.type === "d") {
await deleteDirectory(remotePath)
await sftp.rmdir(remotePath, true)
} else {
await sftp.delete(remotePath)
}
}
}
async function uploadDirectory(localDir: string, remoteDir: string) {
for (const file of fs.readdirSync(localDir)) {
const localPath = path.join(localDir, file)
const remotePath = path.posix.join(remoteDir, file)
if (fs.lstatSync(localPath).isDirectory()) {
await sftp.mkdir(remotePath, true).catch(() => {/* exists */})
if (staticFolders.includes(remotePath)) continue
await uploadDirectory(localPath, remotePath)
} else {
console.log(`upload ${remotePath}`)
await sftp.put(localPath, remotePath)
}
}
}
// --------------------- main -------------------------------------------------
(async () => {
console.log(`⏩ upload…`)
await sftp.connect(config)
await deleteDirectory("/")
await uploadDirectory("./packages/app/studio/dist", "/")
await sftp.end()
const webhookUrl = process.env.DISCORD_WEBHOOK
if (webhookUrl) {
console.log("posting to discord...")
const now = Math.floor(Date.now() / 1000) // in seconds
const content = `🚀 **openDAW** has been deployed to <https://opendaw.studio> <t:${now}:R>.`
try {
const response = await fetch(webhookUrl, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({content})
})
console.log(response)
} catch (error) {
console.warn(error)
}
}
console.log("deploy complete")
})()
+15
View File
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "es2016",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"strict": true,
"skipLibCheck": true
},
"files": [
"run.ts"
]
}
+135
View File
@@ -12,12 +12,14 @@
"packages/**/*"
],
"devDependencies": {
"@types/ssh2-sftp-client": "^9.0.5",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"lerna": "^8.2.3",
"prettier": "^3.6.2",
"ssh2-sftp-client": "^12.0.1",
"turbo": "^2.5.5",
"typescript": "^5.9.2",
"vitest": "^3.1.3"
@@ -2797,6 +2799,43 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/ssh2": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz",
"integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "^18.11.18"
}
},
"node_modules/@types/ssh2-sftp-client": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/@types/ssh2-sftp-client/-/ssh2-sftp-client-9.0.5.tgz",
"integrity": "sha512-cpUO6okDusnfLw2hnmaBiomlSchIWNVcCdpywLRsg/h9Q1TTiUSrzhkn5sJeeyTM8h6xRbZEZZjgWtUXFDogHg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/ssh2": "^1.0.0"
}
},
"node_modules/@types/ssh2/node_modules/@types/node": {
"version": "18.19.121",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.121.tgz",
"integrity": "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/ssh2/node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/wicg-file-system-access": {
"version": "2023.10.6",
"resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2023.10.6.tgz",
@@ -3379,6 +3418,16 @@
"node": ">=0.10.0"
}
},
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -3442,6 +3491,16 @@
],
"license": "MIT"
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
@@ -3530,6 +3589,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/buildcheck": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
"dev": true,
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/byte-size": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz",
@@ -4142,6 +4211,21 @@
}
}
},
"node_modules/cpu-features": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"dependencies": {
"buildcheck": "~0.0.6",
"nan": "^2.19.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -7841,6 +7925,14 @@
"dev": true,
"license": "ISC"
},
"node_modules/nan": {
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -9926,6 +10018,42 @@
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/ssh2": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz",
"integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"asn1": "^0.2.6",
"bcrypt-pbkdf": "^1.0.2"
},
"engines": {
"node": ">=10.16.0"
},
"optionalDependencies": {
"cpu-features": "~0.0.10",
"nan": "^2.20.0"
}
},
"node_modules/ssh2-sftp-client": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/ssh2-sftp-client/-/ssh2-sftp-client-12.0.1.tgz",
"integrity": "sha512-ICJ1L2PmBel2Q2ctbyxzTFZCPKSHYYD6s2TFZv7NXmZDrDNGk8lHBb/SK2WgXLMXNANH78qoumeJzxlWZqSqWg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"concat-stream": "^2.0.0",
"ssh2": "^1.16.0"
},
"engines": {
"node": ">=18.20.4"
},
"funding": {
"type": "individual",
"url": "https://square.link/u/4g7sPflL"
}
},
"node_modules/ssri": {
"version": "10.0.6",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz",
@@ -10631,6 +10759,13 @@
"win32"
]
},
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"dev": true,
"license": "Unlicense"
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+2
View File
@@ -15,6 +15,8 @@
"publish-sdk": "lerna publish"
},
"devDependencies": {
"@types/ssh2-sftp-client": "^9.0.5",
"ssh2-sftp-client": "^12.0.1",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"eslint": "^8.57.0",