Skip to content

Commit 55a1f50

Browse files
authored
Release (#545)
2 parents a4b6a4a + 3090ffb commit 55a1f50

10 files changed

Lines changed: 238 additions & 32 deletions

File tree

.github/workflows/preview.yml

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,28 @@ jobs:
5252
with:
5353
node-version: 22
5454
cache: "pnpm"
55+
- name: Get PR body for issue_comment events
56+
if: github.event_name == 'issue_comment'
57+
uses: actions/github-script@v7
58+
id: pr-body
59+
with:
60+
script: |
61+
const { data: pr } = await github.rest.pulls.get({
62+
owner: context.repo.owner,
63+
repo: context.repo.repo,
64+
pull_number: context.issue.number
65+
});
66+
core.setOutput('body', pr.body || '');
67+
- name: Parse PR config
68+
id: pr-config
69+
run: node scripts/githubActions.mjs parsePrConfig
70+
env:
71+
PR_BODY: ${{ github.event.pull_request.body || steps.pr-body.outputs.body }}
5572
- name: Update deployAlwaysUpdate packages
73+
if: steps.pr-config.outputs.packages != ''
5674
run: |
57-
if [ -f package.json ]; then
58-
PACKAGES=$(node -e "const pkg = require('./package.json'); if (pkg.deployAlwaysUpdate) console.log(pkg.deployAlwaysUpdate.join(' '))")
59-
if [ ! -z "$PACKAGES" ]; then
60-
echo "Updating packages: $PACKAGES"
61-
pnpm up -L $PACKAGES
62-
else
63-
echo "No deployAlwaysUpdate packages found in package.json"
64-
fi
65-
else
66-
echo "package.json not found"
67-
fi
75+
echo "Updating packages: ${{ steps.pr-config.outputs.packages }}"
76+
pnpm up -L ${{ steps.pr-config.outputs.packages }}
6877
- name: Install Global Dependencies
6978
run: pnpm add -g vercel
7079
- name: Pull Vercel Environment Information
@@ -79,6 +88,7 @@ jobs:
7988
env:
8089
CONFIG_JSON_SOURCE: BUNDLED
8190
LOCAL_CONFIG_FILE: config.mcraft-only.json
91+
CONFIG_JSON: ${{ steps.pr-config.outputs.configJson }}
8292
- name: Write pr redirect index.html
8393
run: |
8494
mkdir -p .vercel/output/static/pr
@@ -98,12 +108,11 @@ jobs:
98108
allow-repeats: true
99109
message: |
100110
Deployed to Vercel Preview: ${{ steps.deploy.outputs.stdout }}
101-
[Playground](${{ steps.deploy.outputs.stdout }}/playground/)
102-
[Storybook](${{ steps.deploy.outputs.stdout }}/storybook/)
111+
[DEPLOY LINK](${{ steps.deploy.outputs.stdout }}/)
103112
# - run: git checkout next scripts/githubActions.mjs
104113
- name: Set deployment alias
105114
if: ${{ steps.alias.outputs.alias != '' && steps.alias.outputs.alias != 'mcraft.fun' && steps.alias.outputs.alias != 's.mcraft.fun' }}
106115
run: |
107116
for alias in $(echo ${{ steps.alias.outputs.alias }} | tr "," "\n"); do
108117
vercel alias set ${{ steps.deploy.outputs.stdout }} $alias --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro
109-
done
118+
done

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"esbuild-plugin-polyfill-node": "^0.3.0",
8080
"express": "^4.18.2",
8181
"filesize": "^10.0.12",
82-
"flying-squid": "npm:@zardoy/flying-squid@^0.0.119",
82+
"flying-squid": "npm:@zardoy/flying-squid@^0.0.122",
8383
"framer-motion": "^12.9.2",
8484
"fs-extra": "^11.1.1",
8585
"google-drive-browserfs": "github:zardoy/browserfs#google-drive",

pnpm-lock.yaml

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rsbuild.config.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { genLargeDataAliases } from './scripts/genLargeDataAliases'
1616
import sharp from 'sharp'
1717
import supportedVersions from './src/supportedVersions.mjs'
1818
import { startWsServer } from './scripts/wsServer'
19+
import { applyWatermarkPackagesToConfig } from './scripts/watermarkLockfilePins'
1920

2021
const SINGLE_FILE_BUILD = process.env.SINGLE_FILE_BUILD === 'true'
2122

@@ -32,6 +33,9 @@ const execAsync = promisify(childProcess.exec)
3233

3334
const buildingVersion = new Date().toISOString().split(':')[0]
3435

36+
const buildTime = new Date()
37+
const buildDisplayDate = `${String(buildTime.getDate()).padStart(2, '0')}.${String(buildTime.getMonth() + 1).padStart(2, '0')}.${String(buildTime.getFullYear()).slice(-2)}`
38+
3539
const dev = process.env.NODE_ENV === 'development'
3640
const disableServiceWorker = process.env.DISABLE_SERVICE_WORKER === 'true'
3741

@@ -49,13 +53,34 @@ if (fs.existsSync('./assets/release.json')) {
4953
}
5054

5155
const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8'))
56+
57+
// Precedence (last write wins): base config.json → LOCAL_CONFIG_FILE → CONFIG_JSON (CI / PR)
5258
try {
53-
Object.assign(configJson, JSON.parse(fs.readFileSync(process.env.LOCAL_CONFIG_FILE || './config.local.json', 'utf8')))
54-
} catch (err) {}
59+
const localConfigFile = process.env.LOCAL_CONFIG_FILE || './config.local.json'
60+
if (fs.existsSync(localConfigFile)) {
61+
const localConfig = JSON.parse(fs.readFileSync(localConfigFile, 'utf8'))
62+
Object.assign(configJson, localConfig)
63+
}
64+
} catch (err) {
65+
console.warn('Failed to parse LOCAL_CONFIG_FILE:', err)
66+
}
67+
68+
if (process.env.CONFIG_JSON) {
69+
try {
70+
const prConfig = JSON.parse(process.env.CONFIG_JSON)
71+
Object.assign(configJson, prConfig)
72+
console.log('Applied config from CONFIG_JSON env var:', Object.keys(prConfig).join(', '))
73+
} catch (err) {
74+
console.warn('Failed to parse CONFIG_JSON env var:', err)
75+
}
76+
}
77+
5578
if (dev) {
5679
configJson.defaultProxy = ':8080'
5780
}
5881

82+
applyWatermarkPackagesToConfig(configJson as Record<string, unknown>, path.resolve('pnpm-lock.yaml'))
83+
5984
const configSource = (SINGLE_FILE_BUILD ? 'BUNDLED' : (process.env.CONFIG_JSON_SOURCE || 'REMOTE')) as 'BUNDLED' | 'REMOTE'
6085

6186
const faviconPath = 'favicon.png'
@@ -155,6 +180,7 @@ const appConfig = defineConfig({
155180
// ],
156181
define: {
157182
'process.env.BUILD_VERSION': JSON.stringify(!dev ? buildingVersion : 'undefined'),
183+
'process.env.BUILD_DISPLAY_DATE': JSON.stringify(buildDisplayDate),
158184
'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS),
159185
'process.env.SINGLE_FILE_BUILD': JSON.stringify(process.env.SINGLE_FILE_BUILD),
160186
'process.env.SINGLE_FILE_BUILD_MODE': JSON.stringify(process.env.SINGLE_FILE_BUILD),

scripts/watermarkLockfilePins.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import fs from 'fs'
2+
import { parse as parseYaml } from 'yaml'
3+
4+
type DepEntry = { specifier?: string; version?: string }
5+
6+
type LockRoot = {
7+
dependencies?: Record<string, DepEntry>
8+
devDependencies?: Record<string, DepEntry>
9+
optionalDependencies?: Record<string, DepEntry>
10+
}
11+
12+
function shortSha (hex: string) {
13+
return hex.slice(0, 7)
14+
}
15+
16+
function pinLine (name: string, value: string) {
17+
return `${name}: ${value}`
18+
}
19+
20+
/** pnpm `version` field for git deps: https://codeload.github.com/org/repo/tar.gz/<40hex>(peer...) */
21+
function displayPin (name: string, specifier: string | undefined, versionField: string | undefined) {
22+
const spec = specifier ?? ''
23+
const ver = versionField ?? ''
24+
25+
const tarballSha = ver.match(/tar\.gz\/([a-f0-9]{40})/i)
26+
if (tarballSha) {
27+
return pinLine(name, shortSha(tarballSha[1]))
28+
}
29+
30+
const plain = ver.replace(/\(.*/, '').trim()
31+
if (plain && !plain.startsWith('http') && !plain.startsWith('link:')) {
32+
return pinLine(name, plain)
33+
}
34+
35+
if (plain.startsWith('link:')) {
36+
return pinLine(name, plain)
37+
}
38+
39+
const spec40 = spec.match(/#([a-f0-9]{40})\s*$/i)
40+
if (spec40) {
41+
return pinLine(name, shortSha(spec40[1]))
42+
}
43+
44+
if (spec.startsWith('github:')) {
45+
const hashPart = spec.split('#')[1]
46+
if (hashPart && /^[a-f0-9]{40}$/i.test(hashPart)) {
47+
return pinLine(name, shortSha(hashPart))
48+
}
49+
const ref = hashPart || spec.replace(/^github:[^/]+\/[^#]+#?/, '')
50+
return pinLine(name, ref || spec)
51+
}
52+
53+
return pinLine(name, spec || ver || '?')
54+
}
55+
56+
export function resolveWatermarkPackagePins (lockfilePath: string, names: string[]): string[] {
57+
const doc = parseYaml(fs.readFileSync(lockfilePath, 'utf8')) as {
58+
importers?: Record<string, LockRoot>
59+
}
60+
const root = doc.importers?.['.']
61+
if (!root) {
62+
return names.map((n) => pinLine(n, '?'))
63+
}
64+
65+
const merged: Record<string, DepEntry> = {
66+
...root.dependencies,
67+
...root.devDependencies,
68+
...root.optionalDependencies,
69+
}
70+
71+
return names.map((name) => {
72+
const entry = merged[name]
73+
if (!entry) return pinLine(name, '?')
74+
return displayPin(name, entry.specifier, entry.version)
75+
})
76+
}
77+
78+
/** Reads `watermarkPackages`, appends resolved lines to `watermark`, removes `watermarkPackages`. */
79+
export function applyWatermarkPackagesToConfig (
80+
configJson: Record<string, unknown>,
81+
lockfilePath: string
82+
) {
83+
const pkgs = configJson.watermarkPackages
84+
if (!Array.isArray(pkgs) || pkgs.length === 0) return
85+
86+
const names = pkgs.filter((x): x is string => typeof x === 'string')
87+
delete configJson.watermarkPackages
88+
89+
if (names.length === 0) return
90+
91+
try {
92+
const lines = resolveWatermarkPackagePins(lockfilePath, names)
93+
const extra = lines.join('\n')
94+
const existing = typeof configJson.watermark === 'string' ? configJson.watermark.trim() : ''
95+
configJson.watermark = [existing, extra].filter(Boolean).join('\n')
96+
} catch (err) {
97+
console.warn('watermarkPackages: failed to read pnpm-lock.yaml:', err)
98+
}
99+
}

src/appConfig.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,43 @@ export type AppConfig = {
5959
alwaysReconnectButton?: boolean
6060
reportBugButtonWithReconnect?: boolean
6161
disabledCommands?: string[] // Array of command IDs to disable (e.g. ['movement.jump', 'general.chat'])
62+
/** Shown as a small fixed label (e.g. preview / branch id). Set via bundled or remote config. */
63+
watermark?: string
64+
/** If true, append a second line `BUILD DD.MM.YY` from compile time. */
65+
watermarkDate?: boolean
66+
/**
67+
* Build-time only: dependency names to resolve from pnpm-lock.yaml into extra `watermark` lines
68+
* (npm → locked version; GitHub tarball → short SHA). Stripped from the shipped config after compose.
69+
*/
70+
watermarkPackages?: string[]
71+
}
72+
73+
let watermarkEl: HTMLDivElement | null = null
74+
75+
function watermarkTextFromConfig (cfg: AppConfig | undefined) {
76+
const parts: string[] = []
77+
const w = cfg?.watermark?.trim()
78+
if (w) parts.push(w)
79+
if (cfg?.watermarkDate && process.env.BUILD_DISPLAY_DATE) {
80+
parts.push(`BUILD ${process.env.BUILD_DISPLAY_DATE}`)
81+
}
82+
return parts.length ? parts.join('\n') : ''
83+
}
84+
85+
function setWatermarkFromConfig (cfg: AppConfig | undefined) {
86+
const text = watermarkTextFromConfig(cfg)
87+
if (!text) {
88+
watermarkEl?.remove()
89+
watermarkEl = null
90+
return
91+
}
92+
if (!watermarkEl) {
93+
watermarkEl = document.createElement('div')
94+
watermarkEl.className = 'app-watermark'
95+
watermarkEl.dataset.appWatermark = ''
96+
document.body.appendChild(watermarkEl)
97+
}
98+
watermarkEl.textContent = text
6299
}
63100

64101
export const loadAppConfig = (appConfig: AppConfig) => {
@@ -92,6 +129,7 @@ export const loadAppConfig = (appConfig: AppConfig) => {
92129
appViewer?.appConfigUdpate()
93130

94131
setStorageDataOnAppConfigLoad(appConfig)
132+
setWatermarkFromConfig(miscUiState.appConfig)
95133
}
96134

97135
export const isBundledConfigUsed = !!process.env.INLINED_APP_CONFIG

src/appParams.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export type AppQsParams = {
1313
lockConnect?: string
1414
autoConnect?: string
1515
alwaysReconnect?: string
16+
chunkTemplate?: string
17+
chunkTemplateBlockMap?: string
1618
// googledrive.ts params
1719
state?: string
1820
// ServersListProvider.tsx params

0 commit comments

Comments
 (0)