Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use devcard V2 and download the png image over stream #404

Merged
merged 18 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v16
v20
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ inputs:

commit_filename:
description: Filename to save devcard to
default: devcard.svg
default: devcard.png
required: false

committer_email:
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "action-devcard",
"version": "2.3.1",
"version": "2.4.0",
kopancek marked this conversation as resolved.
Show resolved Hide resolved
"description": "GitHub Action to download the devcard from daily.dev",
"private": true,
"author": {
Expand All @@ -11,6 +11,10 @@
{
"name": "Ole-Martin Bratteng",
"email": "[email protected]"
},
{
"name": "Milan Freml",
"email": "[email protected]"
}
],
"scripts": {
Expand All @@ -24,16 +28,13 @@
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0",
"jsdom": "^22.1.0",
"node-fetch": "^3.3.2",
"sharp": "^0.32.6",
"simple-git": "^3.20.0",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/jsdom": "21.x",
"@types/node": "20.x",
"@types/sharp": "0.32.x",
"@types/uuid": "9.x",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.20.0",
Expand Down
72 changes: 21 additions & 51 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import type { GraphQlQueryResponseData } from '@octokit/graphql'
import fetch from 'node-fetch'

import { finished } from 'stream/promises'

import sgit from 'simple-git'
import fetch from 'node-fetch'
import fs from 'fs/promises'
import path from 'path'
import jsdom from 'jsdom'
import sharp from 'sharp'
import { validate } from 'uuid'

process.on('unhandledRejection', (error) => {
throw error
})

const convertImageToBase64 = async (url: string): Promise<string> => {
const resp = await fetch(url)
const contentType = resp.headers.get('content-type')

return `data:${contentType};base64,${(await resp.buffer()).toString('base64')}`
}

const fetchImagesFromSVG = async (svg: string): Promise<Record<string, string>> => {
const dom = new jsdom.JSDOM(svg)

dom.serialize()

const images: Record<string, string> = {}

dom.window.document.querySelectorAll('image').forEach((image) => {
const src = image.getAttribute('xlink:href')
src && (images[src] = src)
})

return images
}

const devcardURL = (devcard_id: string): string =>
`https://api.daily.dev/devcards/${devcard_id}.svg?r=${new Date().valueOf()}&ref=action`
`https://api.daily.dev/devcards/v2/${devcard_id}.png?r=${new Date().valueOf()}&ref=action`

const validateDevcardIdAsUUID = (devcard_id: string): boolean => {
// An UUIDv4 regex without hyphens
Expand All @@ -47,8 +25,6 @@ const validateDevcardIdAsUUID = (devcard_id: string): boolean => {

;(async function () {
try {
let devCardContent = ''

const devcard_id = core.getInput('devcard_id')
const token = core.getInput('token')
const branch = core.getInput('commit_branch')
Expand All @@ -71,30 +47,21 @@ const validateDevcardIdAsUUID = (devcard_id: string): boolean => {

// Fetch the latest devcard
try {
const res = await fetch(devcardURL(devcard_id))
devCardContent = await res.text()
const images = await fetchImagesFromSVG(devCardContent)

for (const image in images) {
if (Object.prototype.hasOwnProperty.call(images, image)) {
devCardContent = devCardContent.replace(image, await convertImageToBase64(images[image]))
}
}

await fs.mkdir(path.dirname(path.join(`/tmp`, filename)), { recursive: true })
await fs.writeFile(path.join(`/tmp`, filename), devCardContent)

if (filename.endsWith('.png')) {
await sharp(path.join(`/tmp`, filename))
.png({
quality: 100,
})
.toFile(path.join(`/tmp`, `_${filename}`))

await fs.rename(path.join(`/tmp`, `_${filename}`), path.join(`/tmp`, filename))
console.log('Converted devcard to PNG', 'ok')
const { body } = await fetch(devcardURL(devcard_id))
if (body === null) {
const message = `Empty response from devcard URL: ${devcardURL(devcard_id)}`
core.setFailed(message)
console.debug(message)
process.exit(1)
}

await fs.mkdir(path.dirname(path.join(`/tmp`, filename)), {
recursive: true,
})
const file = await fs.open(path.join(`/tmp`, filename), 'w')
const stream = file.createWriteStream()
await finished(body.pipe(stream))
await file.close()
console.log(`Saved to ${path.join(`/tmp`, filename)}`, 'ok')
} catch (error) {
console.debug(error)
Expand All @@ -121,7 +88,10 @@ const validateDevcardIdAsUUID = (devcard_id: string): boolean => {

//Create head branch if needed
try {
await octokit.rest.git.getRef({ ...github.context.repo, ref: `heads/${committer.branch}` })
await octokit.rest.git.getRef({
...github.context.repo,
ref: `heads/${committer.branch}`,
})
console.log('Committer head branch status', 'ok')
} catch (error) {
if (/not found/i.test(`${error}`)) {
Expand Down
35 changes: 35 additions & 0 deletions src/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as fs from 'fs/promises'
import path from 'path'
import fetch from 'node-fetch'

import { finished } from 'stream/promises'

const devCardURL = `https://api.daily.dev/devcards/v2/l9AwmgMssuqCHDpEZpLjQ.png`
const filename = 'devcard.png'

const doStuff = async () => {
const res = await fetch(devCardURL)

const { body } = res

if (body === null) {
const message = `Empty response from devcard URL: ${devCardURL}`
console.debug(message)
process.exit(1)
}

await fs.mkdir(path.dirname(path.join(`/tmp`, filename)), {
recursive: true,
})
const file = await fs.open(path.join(`/tmp`, filename), 'w')
// await fs.writeFile(path.join(`/tmp`, filename), buffer);

const stream = file.createWriteStream()

await finished(body.pipe(stream))
await file.close()

console.log(`Saved to ${path.join(`/tmp`, filename)}`, 'ok')
}

doStuff()
Loading
Loading