Skip to content

Add SafePay ZK mini app #7

Add SafePay ZK mini app

Add SafePay ZK mini app #7

name: Mini App PR Summary
on:
pull_request_target:
paths:
- 'src/data/nimiq-mini-apps.json'
permissions:
contents: read
issues: write
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Comment mini app changes
uses: actions/github-script@v7
with:
script: |
const marker = '<!-- mini-app-summary -->'
const miniAppsPath = 'src/data/nimiq-mini-apps.json'
const pr = context.payload.pull_request
if (!pr) {
core.info('No pull request found in context.')
return
}
async function readJsonFromRepo({ owner, repo, ref }) {
const response = await github.rest.repos.getContent({
owner,
repo,
path: miniAppsPath,
ref,
})
if (Array.isArray(response.data) || response.data.type !== 'file') {
throw new Error(`${miniAppsPath} is not a file at ${owner}/${repo}@${ref}`)
}
const content = Buffer.from(response.data.content, response.data.encoding).toString('utf8')
return JSON.parse(content)
}
function escapeHtml(value) {
return String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
}
function escapeTable(value) {
return escapeHtml(value ?? '')
.replaceAll('|', '&#124;')
.replaceAll('\r', ' ')
.replaceAll('\n', '<br>')
}
function formatLink(value) {
if (!value)
return ''
const escapedUrl = escapeHtml(value)
const escapedText = escapeTable(value)
return `<a href="${escapedUrl}">${escapedText}</a>`
}
function normalizeLogoPath(logo) {
return logo.replace(/^\.\//, '').replace(/^\/+/, '')
}
function formatLogo(app, source) {
if (!app.logo)
return 'No logo'
const logo = String(app.logo)
let src = logo
if (!/^https?:\/\//.test(logo)) {
const normalizedLogo = normalizeLogoPath(logo)
src = `https://raw.githubusercontent.com/${source.owner}/${source.repo}/${source.ref}/src/data/${normalizedLogo}`
}
return `<img src="${escapeHtml(src)}" width="48" alt="Logo for ${escapeHtml(app.name)}">`
}
function comparableApp(app) {
return {
name: app.name ?? '',
url: app.url ?? '',
type: app.type ?? '',
description: app.description ?? '',
logo: app.logo ?? '',
source: app.source ?? null,
developer: app.developer ?? null,
featured: app.featured ?? false,
}
}
function hasAppChanged(baseApp, headApp) {
return JSON.stringify(comparableApp(baseApp)) !== JSON.stringify(comparableApp(headApp))
}
function formatRow(change) {
const app = change.app
return [
change.status,
escapeTable(app.name),
formatLink(app.url),
escapeTable(app.type),
escapeTable(app.description),
formatLogo(app, change.source),
app.source ? formatLink(app.source) : '',
app.developer ? escapeTable(app.developer) : '',
app.featured ? 'true' : 'false',
].join(' | ')
}
const baseSource = {
owner: context.repo.owner,
repo: context.repo.repo,
ref: pr.base.sha,
}
const headSource = {
owner: pr.head.repo.owner.login,
repo: pr.head.repo.name,
ref: pr.head.sha,
}
const baseApps = await readJsonFromRepo({
owner: context.repo.owner,
repo: context.repo.repo,
ref: pr.base.sha,
})
const headApps = await readJsonFromRepo({
owner: pr.head.repo.owner.login,
repo: pr.head.repo.name,
ref: pr.head.sha,
})
const baseAppsByName = new Map(baseApps.map(app => [app.name, app]))
const headAppsByName = new Map(headApps.map(app => [app.name, app]))
const addedOrEditedChanges = headApps
.map((app) => {
const baseApp = baseAppsByName.get(app.name)
if (!baseApp)
return { status: 'Added', app, source: headSource }
if (hasAppChanged(baseApp, app))
return { status: 'Edited', app, source: headSource }
return null
})
.filter(Boolean)
const removedChanges = baseApps
.filter(app => !headAppsByName.has(app.name))
.map(app => ({ status: 'Removed', app, source: baseSource }))
const changes = [
...addedOrEditedChanges,
...removedChanges,
]
const table = changes.length > 0
? [
'| Status | Name | URL | Type | Description | Logo | Source | Developer | Featured |',
'| --- | --- | --- | --- | --- | --- | --- | --- | --- |',
...changes.map(formatRow).map(row => `| ${row} |`),
].join('\n')
: 'No added, edited, or removed mini app entries found.'
const body = [
marker,
'## Mini App Submission Summary',
'',
table,
].join('\n')
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
per_page: 100,
})
const existingComment = comments.data.find(comment => comment.body?.includes(marker))
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body,
})
}
else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body,
})
}