From 501d353ac09c99c82c6fa082b77b0f16e2671979 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 25 Mar 2024 08:19:56 +0300 Subject: [PATCH 1/8] fix vercel preview deploy --- .github/workflows/preview.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index ef1d7773..2a5f5665 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -21,6 +21,7 @@ jobs: uses: actions/checkout@v2 with: ref: refs/pull/${{ github.event.issue.number }}/head + - run: npm i -g pnpm - uses: actions/setup-node@v4 with: node-version: 18 From 6997e78cf1fb47eb6abb9bd65304731205ece2b6 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Mon, 25 Mar 2024 09:57:26 +0300 Subject: [PATCH 2/8] fix warps display for greenfield & click action --- src/loadSave.ts | 6 ++++-- src/react/Notification.tsx | 4 ++-- src/react/NotificationProvider.tsx | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/loadSave.ts b/src/loadSave.ts index 965b1a7c..a707da4d 100644 --- a/src/loadSave.ts +++ b/src/loadSave.ts @@ -6,7 +6,7 @@ import { proxy } from 'valtio' import { gzip } from 'node-gzip' import { options } from './optionsStorage' import { nameToMcOfflineUUID, disconnect } from './flyingSquidUtils' -import { forceCachedDataPaths, forceRedirectPaths, mkdirRecursive } from './browserfs' +import { existsViaStats, forceCachedDataPaths, forceRedirectPaths, mkdirRecursive } from './browserfs' import { isMajorVersionGreater } from './utils' import { activeModalStacks, insertActiveModalStack, miscUiState } from './globalState' @@ -154,7 +154,9 @@ export const loadSave = async (root = '/world') => { // improve compatibility with community saves const rootRemapFiles = ['Warp files'] for (const rootRemapFile of rootRemapFiles) { - forceRedirectPaths[path.join(root, rootRemapFile)] = path.join(root, '..', rootRemapFile) + if (await existsViaStats(path.join(root, '..', rootRemapFile))) { + forceRedirectPaths[path.join(root, rootRemapFile)] = path.join(root, '..', rootRemapFile) + } } // todo reimplement diff --git a/src/react/Notification.tsx b/src/react/Notification.tsx index 6ebd49e2..0f3a6325 100644 --- a/src/react/Notification.tsx +++ b/src/react/Notification.tsx @@ -36,7 +36,7 @@ export default ({ type = 'message', message, subMessage = '', open, icon = '', a position: 'fixed', top: 0, right: 0, - width: '160px', + width: '155px', whiteSpace: 'nowrap', fontSize: '9px', display: 'flex', @@ -45,7 +45,7 @@ export default ({ type = 'message', message, subMessage = '', open, icon = '', a padding: '3px 8px', background: isError ? 'rgba(255, 0, 0, 0.7)' : 'rgba(0, 0, 0, 0.7)', borderRadius: '0 0 0 5px', - pointerEvents: action ? 'auto' : 'none', + pointerEvents: action ? '' : 'none', zIndex: 1200, // even above stats ...addStyles }}> diff --git a/src/react/NotificationProvider.tsx b/src/react/NotificationProvider.tsx index 2f27a595..e5ceb0b3 100644 --- a/src/react/NotificationProvider.tsx +++ b/src/react/NotificationProvider.tsx @@ -40,7 +40,7 @@ export const hideNotification = () => { } export default () => { - const { autoHide, message, open, icon, type, subMessage } = useSnapshot(notificationProxy) + const { autoHide, message, open, icon, type, subMessage, action } = useSnapshot(notificationProxy) useEffect(() => { if (autoHide && open) { @@ -58,6 +58,7 @@ export default () => { // }, []) return Date: Mon, 25 Mar 2024 13:47:01 +0300 Subject: [PATCH 3/8] three shake three.js (#94) --- scripts/esbuildPlugins.mjs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/esbuildPlugins.mjs b/scripts/esbuildPlugins.mjs index 07938c6e..95d88b8e 100644 --- a/scripts/esbuildPlugins.mjs +++ b/scripts/esbuildPlugins.mjs @@ -27,7 +27,8 @@ const plugins = [ build.onLoad({ filter: /minecraft-data[\/\\]data.js$/, }, (args) => { - const version = supportedVersions.at(-1); + const version = supportedVersions.at(-1) + if (!version) throw new Error('unreachable') const data = MCData(version) const defaultVersionsObj = { // default protocol data, needed for auto-version @@ -47,6 +48,14 @@ const plugins = [ }, () => { throw new Error('hit banned package') }) + + build.onResolve({ + filter: /^three$/, + }, async ({ kind, resolveDir }) => { + return { + path: (await build.resolve('three/src/Three.js', { kind, resolveDir })).path, + } + }) } }, { From 07491fd72c45cea6030e888723639266ae4ea78e Mon Sep 17 00:00:00 2001 From: gguio <109200692+gguio@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:00:11 +0400 Subject: [PATCH 4/8] feat: effects + system indicators in hud! (#95) Co-authored-by: gguio --- prismarine-viewer/viewer/lib/worldrenderer.ts | 1 + src/browserfs.ts | 24 +++- src/globalState.ts | 1 + src/index.ts | 2 + src/loadSave.ts | 4 +- src/react/IndicatorEffects.css | 35 ++++++ src/react/IndicatorEffects.stories.tsx | 35 ++++++ src/react/IndicatorEffects.tsx | 111 ++++++++++++++++ src/react/IndicatorEffectsProvider.tsx | 118 ++++++++++++++++++ src/react/effectsImages.ts | 76 +++++++++++ src/reactUi.tsx | 2 + src/utils.ts | 6 + 12 files changed, 413 insertions(+), 2 deletions(-) create mode 100644 src/react/IndicatorEffects.css create mode 100644 src/react/IndicatorEffects.stories.tsx create mode 100644 src/react/IndicatorEffects.tsx create mode 100644 src/react/IndicatorEffectsProvider.tsx create mode 100644 src/react/effectsImages.ts diff --git a/prismarine-viewer/viewer/lib/worldrenderer.ts b/prismarine-viewer/viewer/lib/worldrenderer.ts index 1b67a342..a3c0f79c 100644 --- a/prismarine-viewer/viewer/lib/worldrenderer.ts +++ b/prismarine-viewer/viewer/lib/worldrenderer.ts @@ -342,6 +342,7 @@ export class WorldRenderer { } setSectionDirty (pos, value = true) { + this.renderUpdateEmitter.emit('dirty', pos, value) this.cleanChunkTextures(pos.x, pos.z) // todo don't do this! // Dispatch sections to workers based on position // This guarantees uniformity accross workers and that a given section diff --git a/src/browserfs.ts b/src/browserfs.ts index 42159b49..ebe8acfd 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -60,7 +60,18 @@ fs.promises = new Proxy(Object.fromEntries(['readFile', 'writeFile', 'stat', 'mk if (p === 'open' && fsState.isReadonly) { args[1] = 'r' // read-only, zipfs throw otherwise } - return target[p](...args) + if (p === 'readFile') { + fsState.openReadOperations++ + } else if (p === 'writeFile') { + fsState.openWriteOperations++ + } + return target[p](...args).finally(() => { + if (p === 'readFile') { + fsState.openReadOperations-- + } else if (p === 'writeFile') { + fsState.openWriteOperations-- + } + }) } } }) @@ -77,7 +88,18 @@ fs.promises.open = async (...args) => { return } + if (x === 'read') { + fsState.openReadOperations++ + } else if (x === 'write' || x === 'close') { + fsState.openWriteOperations++ + } fs[x](fd, ...args, (err, bytesRead, buffer) => { + if (x === 'read') { + fsState.openReadOperations-- + } else if (x === 'write' || x === 'close') { + // todo that's not correct + fsState.openWriteOperations-- + } if (err) throw err // todo if readonly probably there is no need to open at all (return some mocked version - check reload)? if (x === 'write' && !fsState.isReadonly) { diff --git a/src/globalState.ts b/src/globalState.ts index 749b3a8f..392d5fd9 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -129,6 +129,7 @@ export type AppConfig = { export const miscUiState = proxy({ currentDisplayQr: null as string | null, currentTouch: null as boolean | null, + hasErrors: false, singleplayer: false, flyingSquid: false, wanOpened: false, diff --git a/src/index.ts b/src/index.ts index e10f60a9..162f19a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -318,6 +318,7 @@ async function connect (connectOptions: { server?: string; singleplayer?: any; username: string; password?: any; proxy?: any; botVersion?: any; serverOverrides?; serverOverridesFlat?; peerId?: string }) { if (miscUiState.gameLoaded) return + miscUiState.hasErrors = false lastConnectOptions.value = connectOptions document.getElementById('play-screen').style = 'display: none;' removePanorama() @@ -378,6 +379,7 @@ async function connect (connectOptions: { console.error(err) errorAbortController.abort() if (isCypress()) throw err + miscUiState.hasErrors = true if (miscUiState.gameLoaded) return setLoadingScreenStatus(`Error encountered. ${err}`, true) diff --git a/src/loadSave.ts b/src/loadSave.ts index a707da4d..655fbea8 100644 --- a/src/loadSave.ts +++ b/src/loadSave.ts @@ -17,7 +17,9 @@ export const fsState = proxy({ isReadonly: false, syncFs: false, inMemorySave: false, - saveLoaded: false + saveLoaded: false, + openReadOperations: 0, + openWriteOperations: 0, }) const PROPOSE_BACKUP = true diff --git a/src/react/IndicatorEffects.css b/src/react/IndicatorEffects.css new file mode 100644 index 00000000..2902bfee --- /dev/null +++ b/src/react/IndicatorEffects.css @@ -0,0 +1,35 @@ +.effectsScreen-container { + position: fixed; + top: 6%; + left: 0px; + z-index: -1; + pointer-events: none; +} + +.indicators-container { + display: flex; + font-size: 0.7em; +} + +.effects-container { + display: flex; + flex-direction: column; +} + +.effect-box { + display: flex; + align-items: center; +} + +.effect-box__image { + width: 23px; + margin-right: 3px; +} + +.effect-box__time { + font-size: 0.65rem; +} + +.effect-box__level { + font-size: 0.45rem; +} diff --git a/src/react/IndicatorEffects.stories.tsx b/src/react/IndicatorEffects.stories.tsx new file mode 100644 index 00000000..455718fe --- /dev/null +++ b/src/react/IndicatorEffects.stories.tsx @@ -0,0 +1,35 @@ +import 'iconify-icon' + +import type { Meta, StoryObj } from '@storybook/react' + +import IndicatorEffects, { defaultIndicatorsState } from './IndicatorEffects' +import { images } from './effectsImages' + +const meta: Meta = { + component: IndicatorEffects +} + +export default meta +type Story = StoryObj; + +export const Primary: Story = { + args: { + indicators: defaultIndicatorsState, + effects: [ + { + image: images.glowing, + time: 200, + level: 255, + removeEffect (image: string) {}, + reduceTime (image: string) {} + }, + { + image: images.absorption, + time: 30, + level: 99, + removeEffect (image: string) {}, + reduceTime (image: string) {} + } + ], + } +} diff --git a/src/react/IndicatorEffects.tsx b/src/react/IndicatorEffects.tsx new file mode 100644 index 00000000..b767cc23 --- /dev/null +++ b/src/react/IndicatorEffects.tsx @@ -0,0 +1,111 @@ +import { useMemo, useEffect, useRef } from 'react' +import PixelartIcon from './PixelartIcon' +import './IndicatorEffects.css' + + + +function formatTime (seconds: number): string { + if (seconds < 0) return '' + const minutes = Math.floor(seconds / 60) + const remainingSeconds = seconds % 60 + const formattedMinutes = String(minutes).padStart(2, '0') + const formattedSeconds = String(remainingSeconds).padStart(2, '0') + return `${formattedMinutes}:${formattedSeconds}` +} + +export type EffectType = { + image: string, + time: number, + level: number, + removeEffect: (image: string) => void, + reduceTime: (image: string) => void +} + +const EffectBox = ({ image, time, level }: Pick) => { + + const formattedTime = useMemo(() => formatTime(time), [time]) + + return
+ +
+ {formattedTime ? ( + // if time is negative then effect is shown without time. + // Component should be removed manually with time = 0 +
{formattedTime}
+ ) : null } + {level > 0 && level < 256 ? ( +
{level + 1}
+ ) : null } +
+
+} + +export const defaultIndicatorsState = { + chunksLoading: false, + readingFiles: false, + readonlyFiles: false, + writingFiles: false, // saving + appHasErrors: false, +} + +const indicatorIcons: Record = { + chunksLoading: 'add-grid', + readingFiles: 'arrow-bar-down', + writingFiles: 'arrow-bar-up', + appHasErrors: 'alert', + readonlyFiles: 'file-off', +} + +export default ({ indicators, effects }: {indicators: typeof defaultIndicatorsState, effects: readonly EffectType[]}) => { + const effectsRef = useRef(effects) + useEffect(() => { + effectsRef.current = effects + }, [effects]) + + useEffect(() => { + // todo use more precise timer for each effect + const interval = setInterval(() => { + for (const [index, effect] of effectsRef.current.entries()) { + if (effect.time === 0) { + // effect.removeEffect(effect.image) + return + } + effect.reduceTime(effect.image) + } + }, 1000) + + return () => { + clearInterval(interval) + } + }, []) + + const indicatorsMapped = Object.entries(defaultIndicatorsState).map(([key, state]) => ({ + icon: indicatorIcons[key], + // preserve order + state: indicators[key], + })) + return
+
+ { + indicatorsMapped.map((indicator) =>
+ +
) + } +
+
+ { + effects.map( + (effect) => + ) + } +
+
+} diff --git a/src/react/IndicatorEffectsProvider.tsx b/src/react/IndicatorEffectsProvider.tsx new file mode 100644 index 00000000..00b78d8a --- /dev/null +++ b/src/react/IndicatorEffectsProvider.tsx @@ -0,0 +1,118 @@ +import { proxy, useSnapshot } from 'valtio' +import { useEffect, useMemo } from 'react' +import { inGameError } from '../utils' +import { fsState } from '../loadSave' +import { miscUiState } from '../globalState' +import IndicatorEffects, { EffectType, defaultIndicatorsState } from './IndicatorEffects' +import { images } from './effectsImages' + +export const state = proxy({ + indicators: { + chunksLoading: false + }, + effects: [] as EffectType[] +}) + +export const addEffect = (newEffect: Omit) => { + const effectIndex = getEffectIndex(newEffect as EffectType) + if (typeof effectIndex === 'number') { + state.effects[effectIndex].time = newEffect.time + state.effects[effectIndex].level = newEffect.level + } else { + const effect = { ...newEffect, reduceTime, removeEffect } + state.effects.push(effect) + } +} + +const removeEffect = (image: string) => { + for (const [index, effect] of (state.effects).entries()) { + if (effect.image === image) { + state.effects.splice(index, 1) + } + } +} + +const reduceTime = (image: string) => { + for (const [index, effect] of (state.effects).entries()) { + if (effect.image === image) { + effect.time -= 1 + } + } +} + +const getEffectIndex = (newEffect: EffectType) => { + for (const [index, effect] of (state.effects).entries()) { + if (effect.image === newEffect.image) { + return index + } + } + return null +} + +export default () => { + const stateIndicators = useSnapshot(state.indicators) + const { hasErrors } = useSnapshot(miscUiState) + const { isReadonly, openReadOperations, openWriteOperations } = useSnapshot(fsState) + const allIndicators: typeof defaultIndicatorsState = { + readonlyFiles: isReadonly, + writingFiles: openWriteOperations > 0, + readingFiles: openReadOperations > 0, + appHasErrors: hasErrors, + ...stateIndicators, + } + + useEffect(() => { + let alreadyWaiting = false + const listener = () => { + if (alreadyWaiting) return + state.indicators.chunksLoading = true + alreadyWaiting = true + void viewer.waitForChunksToRender().then(() => { + state.indicators.chunksLoading = false + alreadyWaiting = false + }) + } + viewer.world.renderUpdateEmitter.on('dirty', listener) + + return () => { + viewer.world.renderUpdateEmitter.off('dirty', listener) + } + }, []) + + const effects = useSnapshot(state.effects) + + useMemo(() => { + const effectsImages = Object.fromEntries(loadedData.effectsArray.map((effect) => { + const nameKebab = effect.name.replaceAll(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`).slice(1) + return [effect.id, images[nameKebab]] + })) + bot.on('entityEffect', (entity, effect) => { + if (entity.id !== bot.entity.id) return + const image = effectsImages[effect.id] ?? null + if (!image) { + inGameError(`received unknown effect id ${effect.id}}`) + return + } + const newEffect = { + image, + time: effect.duration / 20, // duration received in ticks + level: effect.amplifier, + } + addEffect(newEffect) + }) + bot.on('entityEffectEnd', (entity, effect) => { + if (entity.id !== bot.entity.id) return + const image = effectsImages[effect.id] ?? null + if (!image) { + inGameError(`received unknown effect id ${effect.id}}}`) + return + } + removeEffect(image) + }) + }, []) + + return +} diff --git a/src/react/effectsImages.ts b/src/react/effectsImages.ts new file mode 100644 index 00000000..21421e0f --- /dev/null +++ b/src/react/effectsImages.ts @@ -0,0 +1,76 @@ +import absorption from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/absorption.png' +import glowing from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/glowing.png' +import instant_health from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/instant_health.png' +import nausea from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/nausea.png' +import slow_falling from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/slow_falling.png' +import weakness from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/weakness.png' +import bad_omen from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/bad_omen.png' +import haste from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/haste.png' +import invisibility from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/invisibility.png' +import night_vision from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/night_vision.png' +import slowness from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/slowness.png' +import wither from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/wither.png' +import blindness from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/blindness.png' +import health_boost from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/health_boost.png' +import jump_boost from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/jump_boost.png' +import poison from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/poison.png' +import speed from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/speed.png' +import conduit_power from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/conduit_power.png' +import hero_of_the_village from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/hero_of_the_village.png' +import levitation from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/levitation.png' +import regeneration from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/regeneration.png' +import strength from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/strength.png' +import dolphins_grace from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/dolphins_grace.png' +import hunger from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/hunger.png' +import luck from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/luck.png' +import resistance from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/resistance.png' +import unluck from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/unluck.png' +import fire_resistance from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/fire_resistance.png' +import instant_damage from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/instant_damage.png' +import mining_fatigue from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/mining_fatigue.png' +import saturation from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/saturation.png' +import water_breathing from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/water_breathing.png' +import darkness from 'minecraft-assets/minecraft-assets/data/1.20.2/mob_effect/darkness.png' + +interface Images { + [key: string]: string; +} + +// Export an object containing image URLs +export const images: Images = { + absorption, + glowing, + instant_health, + nausea, + slow_falling, + weakness, + bad_omen, + haste, + invisibility, + night_vision, + slowness, + wither, + blindness, + health_boost, + jump_boost, + poison, + speed, + conduit_power, + hero_of_the_village, + levitation, + regeneration, + strength, + dolphins_grace, + hunger, + luck, + resistance, + unluck, + bad_luck: unluck, + good_luck: luck, + fire_resistance, + instant_damage, + mining_fatigue, + saturation, + water_breathing, + darkness +} diff --git a/src/reactUi.tsx b/src/reactUi.tsx index aa1d74dd..d56cb805 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -16,6 +16,7 @@ import ChatProvider from './react/ChatProvider' import TitleProvider from './react/TitleProvider' import ScoreboardProvider from './react/ScoreboardProvider' import SignEditorProvider from './react/SignEditorProvider' +import IndicatorEffectsProvider from './react/IndicatorEffectsProvider' import SoundMuffler from './react/SoundMuffler' import TouchControls from './react/TouchControls' import widgets from './react/widgets' @@ -65,6 +66,7 @@ const InGameUi = () => { + diff --git a/src/utils.ts b/src/utils.ts index 61ceeeb8..39bf940e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -20,6 +20,12 @@ export const toNumber = (val) => { return isNaN(num) ? undefined : num } +export const inGameError = (err) => { + console.error(err) + // todo report + miscUiState.hasErrors = true +} + export const pointerLock = { get hasPointerLock () { return document.pointerLockElement From 1c45ded8055c73e64c09c4f1cad0f7551973a451 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 28 Mar 2024 01:41:48 +0300 Subject: [PATCH 5/8] fix page loadeding indicator showing sometimes --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 162f19a2..3a8fbd5c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -919,7 +919,7 @@ downloadAndOpenFile().then((downloadAction) => { const initialLoader = document.querySelector('.initial-loader') as HTMLElement | null if (initialLoader) { initialLoader.style.opacity = '0' - window.pageLoaded = true } +window.pageLoaded = true void possiblyHandleStateVariable() From f2803b9b3ac399dc84802e6041732e59bd52fe84 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 6 Apr 2024 15:22:50 +0300 Subject: [PATCH 6/8] remove alias set --- .github/workflows/preview.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2a5f5665..02598446 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -42,10 +42,6 @@ jobs: with: run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} id: deploy - - name: Set deployment alias - # only if on branch next - if: github.ref == 'refs/heads/next' - run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ secrets.TEST_PREVIEW_DOMAIN }} --token=${{ secrets.VERCEL_TOKEN }} - uses: mshick/add-pr-comment@v2 with: message: | From b89aab72d0a92d8129024f5d3c8168b59befa633 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 9 Apr 2024 03:28:55 +0300 Subject: [PATCH 7/8] ci: text aliases --- .github/workflows/next-deploy.yml | 8 +++++++- scripts/githubActions.mjs | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 scripts/githubActions.mjs diff --git a/.github/workflows/next-deploy.yml b/.github/workflows/next-deploy.yml index 93c46aeb..d7d4d322 100644 --- a/.github/workflows/next-deploy.yml +++ b/.github/workflows/next-deploy.yml @@ -2,6 +2,7 @@ name: Vercel Deploy Next env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + ALIASES: ${{ vars.ALIASES }} on: push: branches: @@ -30,8 +31,13 @@ jobs: with: run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} id: deploy + - name: Get deployment alias + run: node scripts/githubActions.mjs getAlias + - run: echo $OUTPUT + env: + OUTPUT: ${{ steps.repos.outputs.alias }} - name: Set deployment alias - run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ secrets.TEST_PREVIEW_DOMAIN }} --token=${{ secrets.VERCEL_TOKEN }} + run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ secrets.TEST_PREVIEW_DOMAIN }} --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro # - uses: mshick/add-pr-comment@v2 # with: # message: | diff --git a/scripts/githubActions.mjs b/scripts/githubActions.mjs new file mode 100644 index 00000000..f392da55 --- /dev/null +++ b/scripts/githubActions.mjs @@ -0,0 +1,23 @@ +const fns = { + async getAlias () { + const aliasesRaw = process.env.ALIASES + if (!aliasesRaw) throw new Error('No aliases found') + const aliases = aliasesRaw.split('\n').map((x) => x.search('=')) + const githubActionsPull = process.env.GITHUB_REF.match(/refs\/pull\/(\d+)\/merge/) + if (!githubActionsPull) throw new Error(`Not a pull request, got ${process.env.GITHUB_REF}`) + const prNumber = githubActionsPull[1] + const alias = aliases.find((x) => x[0] === prNumber) + if (alias) { + console.log('Found alias', alias[1]) + // set github output + console.log(`::set-output name=alias::${alias[1]}`) + } + } +} + +const fn = fns[process.argv[2]] +if (fn) { + fn() +} else { + console.error('Function not found') +} From ac3448c74502e6e23c357c1614bc81dab3beb354 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 9 Apr 2024 03:47:03 +0300 Subject: [PATCH 8/8] ci: set custom mcraft aliases for PRs! (#99) --- .github/workflows/next-deploy.yml | 5 ----- .github/workflows/preview.yml | 7 +++++++ scripts/githubActions.mjs | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/next-deploy.yml b/.github/workflows/next-deploy.yml index d7d4d322..7ee33ac0 100644 --- a/.github/workflows/next-deploy.yml +++ b/.github/workflows/next-deploy.yml @@ -31,11 +31,6 @@ jobs: with: run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} id: deploy - - name: Get deployment alias - run: node scripts/githubActions.mjs getAlias - - run: echo $OUTPUT - env: - OUTPUT: ${{ steps.repos.outputs.alias }} - name: Set deployment alias run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ secrets.TEST_PREVIEW_DOMAIN }} --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro # - uses: mshick/add-pr-comment@v2 diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 02598446..44c55480 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -2,6 +2,7 @@ name: Vercel Deploy Preview env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + ALIASES: ${{ vars.ALIASES }} on: issue_comment: types: [created] @@ -48,3 +49,9 @@ jobs: Deployed to Vercel Preview: ${{ steps.deploy.outputs.stdout }} [Playground](${{ steps.deploy.outputs.stdout }}/playground.html) [Storybook](${{ steps.deploy.outputs.stdout }}/storybook/) + - name: Get deployment alias + run: node scripts/githubActions.mjs getAlias + id: alias + - name: Set deployment alias + if: ${{ steps.alias.outputs.alias != '' }} + run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ steps.alias.outputs.alias }} --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro diff --git a/scripts/githubActions.mjs b/scripts/githubActions.mjs index f392da55..ac218f56 100644 --- a/scripts/githubActions.mjs +++ b/scripts/githubActions.mjs @@ -1,20 +1,29 @@ +//@ts-check +import fs from 'fs' +import os from 'os' + const fns = { async getAlias () { const aliasesRaw = process.env.ALIASES if (!aliasesRaw) throw new Error('No aliases found') - const aliases = aliasesRaw.split('\n').map((x) => x.search('=')) + const aliases = aliasesRaw.split('\n').map((x) => x.split('=')) const githubActionsPull = process.env.GITHUB_REF.match(/refs\/pull\/(\d+)\/merge/) if (!githubActionsPull) throw new Error(`Not a pull request, got ${process.env.GITHUB_REF}`) const prNumber = githubActionsPull[1] const alias = aliases.find((x) => x[0] === prNumber) if (alias) { - console.log('Found alias', alias[1]) // set github output - console.log(`::set-output name=alias::${alias[1]}`) + setOutput('alias', alias[1]) } } } +function setOutput(key, value) { + // Temporary hack until core actions library catches up with github new recommendations + const output = process.env['GITHUB_OUTPUT'] + fs.appendFileSync(output, `${key}=${value}${os.EOL}`) +} + const fn = fns[process.argv[2]] if (fn) { fn()