ci(demo): publish release branches to demo.datacontroller.io
Pushes to release/** now build the mocks as a browser-hosted site (SASjs CLI 6.1.0, streamConfig.mockWeb) and publish it to the Surfer app behind demo.datacontroller.io, so the demo always matches the release branch. The browser build seeds its drive from the compiled services, which carries no DC data, so the job runs makedata on a throwaway SASjs Server and ships that drive as mock-snapshot.json.
This commit is contained in:
1 parent
37abc3adb7
commit
a128b609f8
5 files changed
+254
-9
No files matched your search
@@ -0,0 +1,114 @@
|
||||
name: Demo preview
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'release/**'
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.15.0'
|
||||
APP_LOC: /Public/app/dc
|
||||
DEMO_HOST: https://demo.datacontroller.io
|
||||
|
||||
# One demo site, so two pushes must not upload over each other. A newer push
|
||||
# supersedes the deploy in flight rather than interleaving with it.
|
||||
concurrency:
|
||||
group: demo-preview
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Write .npmrc file
|
||||
run: |
|
||||
touch client/.npmrc
|
||||
echo '${{ secrets.NPMRC}}' > client/.npmrc
|
||||
|
||||
- name: Install client dependencies
|
||||
run: |
|
||||
cd client
|
||||
# Decrypt and Install sheet
|
||||
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
|
||||
npm ci
|
||||
|
||||
- name: Build frontend
|
||||
# The site serves this build. `sasjs build` rewrites its index.html to
|
||||
# point the adapter at the in-browser backend.
|
||||
run: |
|
||||
cd client
|
||||
npm run postinstall
|
||||
npm run build
|
||||
|
||||
- name: Install the SASjs CLI
|
||||
# Pinned in sas/package.json - the browser build needs CLI >= 6.1.0
|
||||
run: |
|
||||
cd sas
|
||||
npm ci
|
||||
|
||||
- name: Start SASjs server
|
||||
# Needed only to run makedata: the drive the site ships with is captured
|
||||
# from a real server (see the snapshot step below).
|
||||
run: |
|
||||
curl -L https://github.com/sasjs/server/releases/latest/download/linux.zip > linux.zip
|
||||
unzip -q linux.zip
|
||||
touch .env
|
||||
echo RUN_TIMES=js >> .env
|
||||
echo NODE_PATH=node >> .env
|
||||
# The server refuses to run as root unless RUN_AS names an account
|
||||
echo RUN_AS=root >> .env
|
||||
nohup ./api-linux > server.log 2>&1 &
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf -o /dev/null http://localhost:5000/; then
|
||||
echo "SASjs server answered on attempt ${i}"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "SASjs server did not come up within 120s"
|
||||
cat server.log
|
||||
exit 1
|
||||
|
||||
- name: Deploy the mocked services and seed the database
|
||||
run: |
|
||||
cd sas/mocks/sasjs
|
||||
# Start from a clean appLoc so the snapshot below is this build alone,
|
||||
# not a merge with whatever a previous run left on the drive.
|
||||
curl -sS -X DELETE "http://localhost:5000/SASjsApi/drive/folder/?_folderPath=$APP_LOC"
|
||||
../../node_modules/.bin/sasjs cbd -t server-ci
|
||||
# makedata replies with HTML (it is normally called as a URL redirect),
|
||||
# which the CLI reports as "invalid Json string" - ignore that.
|
||||
../../node_modules/.bin/sasjs request services/admin/makedata -t server-ci -d deploy/makedata.json -o ./makedata_out.json || true
|
||||
|
||||
- name: Build the browser-hosted site
|
||||
run: |
|
||||
# DC's data files exist only after makedata has run, so the drive is
|
||||
# captured first and shipped as the site's starting drive. services/web9
|
||||
# is the streamed frontend, which is already the site itself.
|
||||
node sas/mocks/snapshot.js sasjs_root/drive "$APP_LOC" sas/mocks/sasjsbuild/mock-snapshot.json services/web9
|
||||
cd sas/mocks/sasjs
|
||||
../../node_modules/.bin/sasjs b -t server-ci
|
||||
|
||||
- name: Publish to demo.datacontroller.io
|
||||
env:
|
||||
SURFER_USER: allan
|
||||
SURFER_PASSWORD: ${{ secrets.DEMO_DATACONTROLLER_IO }}
|
||||
SURFER_HOST: ${{ env.DEMO_HOST }}
|
||||
SURFER_MESSAGE: Build from ${{ gitea.ref_name }} @ ${{ gitea.sha }}
|
||||
run: |
|
||||
# `deploy` uploads the folder as an archive and swaps the site in one
|
||||
# step, so a failed upload leaves the current site serving.
|
||||
npx --yes @cloudron/surfer@7.2.0 deploy \
|
||||
--server "$SURFER_HOST" \
|
||||
--username "$SURFER_USER" \
|
||||
--password "$SURFER_PASSWORD" \
|
||||
--message "$SURFER_MESSAGE" \
|
||||
sas/mocks/sasjsbuild/mock
|
||||
@@ -133,6 +133,10 @@ There is also a SAS 9 mock variant under `sas/mocks/sas9/` (target `sas9-mocks`)
|
||||
|
||||
After changing mock services, re-run `sasjs cbd -t server-ci` from `sas/mocks/sasjs/`. After changing the frontend, rebuild (`cd client && npm run build`) then re-deploy so the streaming app picks up the new `client/dist/`. If you are using `ng serve` (step 3), only the mock services need re-deploying; the frontend hot-reloads automatically.
|
||||
|
||||
### Demo site
|
||||
|
||||
A push to a `release/**` branch builds the mocks as a browser-hosted site (SASjs CLI >= 6.1, `streamConfig.mockWeb`) and publishes it to [demo.datacontroller.io](https://demo.datacontroller.io) - workflow file `.gitea/workflows/demo.yaml`, using the `DEMO_DATACONTROLLER_IO` Surfer app password (user `allan`). The services run in a service worker and the drive lives in the visitor's IndexedDB, so there is no server. The build runs `makedata` on a throwaway SASjs Server first and ships that drive as `sasjsbuild/mock-snapshot.json`; without it the site opens with no data. The mock licence is minted for HTTP only, so the HTTPS demo shows the free-tier banner.
|
||||
|
||||
## GUI Elements
|
||||
|
||||
For documentation on the Clarity Design System, including a list of components and example usage, see [our website](https://vmware.github.io/clarity).
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// Collect the SASjs Server drive for an app into a mock-snapshot.json, which
|
||||
// `sasjs build` ships as the browser build's starting drive (the alternative is
|
||||
// a drive seeded from the compiled services alone, i.e. no DC data).
|
||||
//
|
||||
// Usage: node sas/mocks/snapshot.js <drive-root> <appLoc> <out-file>
|
||||
// drive-root the SASjs Server drive folder (the `drive` folder of a server install)
|
||||
// appLoc the deployed app location, e.g. /Public/app/dc
|
||||
// out-file where to write the snapshot, e.g. sas/mocks/sasjsbuild/mock-snapshot.json
|
||||
//
|
||||
// Every file is written as a string because the browser runtime's drive holds
|
||||
// text only; a file that does not survive a UTF-8 round trip is skipped rather
|
||||
// than mangled.
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const [driveRoot, appLoc, outFile, excludeArg] = process.argv.slice(2)
|
||||
|
||||
if (!driveRoot || !appLoc || !outFile) {
|
||||
console.error(
|
||||
'Usage: node sas/mocks/snapshot.js <drive-root> <appLoc> <out-file> [exclude,exclude]'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const app = '/' + appLoc.replace(/^\/+|\/+$/g, '')
|
||||
const driveFolder = path.join(
|
||||
driveRoot,
|
||||
'files',
|
||||
...app.split('/').filter(Boolean)
|
||||
)
|
||||
const driveKey = `/sasjs/drive/files${app}`
|
||||
|
||||
// Comma-separated paths relative to the app, e.g. "services/web9" - the
|
||||
// streamed frontend is copied into the drive as a service folder, is already the
|
||||
// site itself, and would otherwise duplicate the whole app inside the snapshot.
|
||||
const excludes = (excludeArg || '')
|
||||
.split(',')
|
||||
.map((value) => value.trim().replace(/^\/+|\/+$/g, ''))
|
||||
.filter(Boolean)
|
||||
|
||||
if (!fs.existsSync(driveFolder)) {
|
||||
console.error('No drive folder at ' + driveFolder)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const files = {}
|
||||
const skipped = []
|
||||
|
||||
const walk = (folder) => {
|
||||
for (const entry of fs.readdirSync(folder, { withFileTypes: true })) {
|
||||
const absolute = path.join(folder, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
walk(absolute)
|
||||
continue
|
||||
}
|
||||
|
||||
const relative = path
|
||||
.relative(driveFolder, absolute)
|
||||
.split(path.sep)
|
||||
.join('/')
|
||||
|
||||
if (
|
||||
excludes.some(
|
||||
(prefix) => relative === prefix || relative.startsWith(prefix + '/')
|
||||
)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
const buffer = fs.readFileSync(absolute)
|
||||
const text = buffer.toString('utf8')
|
||||
|
||||
if (!Buffer.from(text, 'utf8').equals(buffer)) {
|
||||
skipped.push(relative)
|
||||
continue
|
||||
}
|
||||
|
||||
files[`${driveKey}/${relative}`] = text
|
||||
}
|
||||
}
|
||||
|
||||
walk(driveFolder)
|
||||
|
||||
fs.mkdirSync(path.dirname(path.resolve(outFile)), { recursive: true })
|
||||
fs.writeFileSync(path.resolve(outFile), JSON.stringify({ files }, null, 2))
|
||||
|
||||
console.log(
|
||||
`Snapshot: ${Object.keys(files).length} file(s) from ${driveFolder} -> ${outFile}` +
|
||||
(skipped.length
|
||||
? `, skipped ${skipped.length} non-text file(s): ${skipped.slice(0, 3).join(', ')}`
|
||||
: '')
|
||||
)
|
||||
Generated
+43
-8
@@ -6,7 +6,7 @@
|
||||
"": {
|
||||
"name": "dc-sas",
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "6.0.0",
|
||||
"@sasjs/cli": "6.1.0",
|
||||
"@sasjs/core": "6.0.0"
|
||||
}
|
||||
},
|
||||
@@ -165,14 +165,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/cli": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-6.0.0.tgz",
|
||||
"integrity": "sha512-nsdL5J+Y5FHZL4pieqQHjsNmY2DZ4gQcGAupR0dv8aGlweEHqdVErtx3B7+EmIiyfRt+oE3YEofzF41WBWL7tg==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-6.1.0.tgz",
|
||||
"integrity": "sha512-i2CdGS69EM6Sn7XfR1ltBk9Bxz6gEV9xTGSWUmk4eVe+XnJBlYHyg93VgMcx7sWCtzRF9Dv7N2p7eXEc2DHfbQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@sasjs/adapter": "5.1.1",
|
||||
"@sasjs/core": "6.0.0",
|
||||
"@sasjs/utils": "4.0.1",
|
||||
"@sasjs/adapter": "^5.1.1",
|
||||
"@sasjs/core": "^6.0.0",
|
||||
"@sasjs/utils": "^4.1.0",
|
||||
"axios": "1.20.0",
|
||||
"chalk": "4.1.2",
|
||||
"dotenv": "18.0.8",
|
||||
@@ -196,7 +196,42 @@
|
||||
"node": ">=22"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@sasjs/lint": "4.3.0"
|
||||
"@sasjs/lint": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/cli/node_modules/@sasjs/utils": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-4.1.0.tgz",
|
||||
"integrity": "sha512-aC5pOugg6/2jKQR7ZaYsLtbsFy/iVacsrN3/Kzh6BouJUszuVrr13HMrQUSKcWL+rJzxYof7ymmmCl+vE6YwAw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fast-csv/format": "5.0.7",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/prompts": "2.4.9",
|
||||
"chalk": "4.1.1",
|
||||
"consola": "3.4.2",
|
||||
"fs-extra": "11.4.1",
|
||||
"jwt-decode": "4.0.0",
|
||||
"prompts": "2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/cli/node_modules/@sasjs/utils/node_modules/chalk": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
|
||||
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "6.0.0",
|
||||
"@sasjs/cli": "6.1.0",
|
||||
"@sasjs/core": "6.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
Reference in new issue
Block a user