Skip to content

release: promote accepted RC1 to public stable with preferred installer ZIP #22

release: promote accepted RC1 to public stable with preferred installer ZIP

release: promote accepted RC1 to public stable with preferred installer ZIP #22

name: ARStack Latest Stable Landing Browser and Mobile Acceptance
on:
push:
branches: [main]
paths: ['index.html', '.github/workflows/m3h-landing-browser-acceptance.yml']
pull_request:
paths: ['index.html', '.github/workflows/m3h-landing-browser-acceptance.yml']
workflow_dispatch:
permissions:
contents: read
jobs:
browser-acceptance:
runs-on: ubuntu-latest
timeout-minutes: 12
steps:
- uses: actions/checkout@v4
- name: Install isolated browser automation
shell: bash
run: |
set -euo pipefail
mkdir -p /tmp/m3h-browser
cd /tmp/m3h-browser
npm init -y >/dev/null
npm install --no-save --no-package-lock playwright@1.57.0 >/dev/null
npx playwright install chromium
- name: Render current candidate desktop and mobile layouts
shell: bash
run: |
set -euo pipefail
mkdir -p evidence
cat > /tmp/m3h-browser/check.cjs <<'JS'
const {chromium}=require('playwright');
(async()=>{
const browser=await chromium.launch({headless:true});
for(const width of [1440,768,390,360]){
const page=await browser.newPage({viewport:{width,height:900},deviceScaleFactor:1});
const response=await page.goto('http://127.0.0.1:4173/',{waitUntil:'domcontentloaded',timeout:30000});
if(response.status()!==200)throw new Error('LIVE_HTTP_'+response.status());
const data=await page.evaluate(()=>{
const buttons=[...document.querySelectorAll('a[data-asset="windows"],a[data-asset="portable"],a[data-asset="firmware"]')];
const releaseLink=/^https:\/\/github\.com\/masarray\/arstack61850\/releases\/(latest|download\/[^/]+\/[^/]+)$/;
return {title:document.title, width:document.documentElement.clientWidth,scroll:document.documentElement.scrollWidth,
latest:document.body.textContent.includes('M3.H.3') || document.body.textContent.includes('processbus-sim-m3h3-20261025'),benchmark:document.body.textContent.includes('4,705,090') && document.body.textContent.includes('M3.H.3') && document.body.textContent.includes('Next: multiple virtual Merging Units'), bodyPreview:document.body.textContent.slice(0,170),
buttons:buttons.length, linksValid:buttons.every(el=>releaseLink.test(el.href)), anyVisible:buttons.some(el=>{const r=el.getBoundingClientRect();return r.width>0&&r.height>0}),
linuxReady:!!document.querySelector('[data-asset="linux"]'),
og:!!document.querySelector('meta[property="og:title"]')};
});
console.log(width,JSON.stringify(data));
await page.screenshot({path:'evidence/m3h-'+width+'.png',fullPage:true});
if(!data.latest||!data.benchmark||!data.og||data.buttons!==4||!data.anyVisible||!data.linksValid||!data.linuxReady)throw new Error('MISSING_LATEST_RELEASE_LINKS_'+width);
if(data.scroll>data.width+2)throw new Error('HORIZONTAL_OVERFLOW_'+width+':'+data.scroll+'>'+data.width);
await page.close();
}
await browser.close();
console.log('PASS: latest-release UI links and desktop/tablet/mobile layouts');
})().catch(e=>{console.error(e);process.exit(1)});
JS
python3 -m http.server 4173 --bind 127.0.0.1 > /tmp/arstack-pages-preview.log 2>&1 &
server_pid=$!
trap 'kill "$server_pid" 2>/dev/null || true' EXIT
for attempt in {1..20}; do
if curl --silent --fail http://127.0.0.1:4173/ >/dev/null; then break; fi
sleep 0.25
done
node /tmp/m3h-browser/check.cjs
- name: Archive actual rendered screenshots
if: always()
uses: actions/upload-artifact@v4
with:
name: arstack-latest-responsive-screenshots
path: evidence/
if-no-files-found: ignore