Skip to content

Commit

Permalink
feat: use devcard V2 and download the png image over stream (#404)
Browse files Browse the repository at this point in the history
Co-authored-by: Ole-Martin Bratteng <1681525+omBratteng@users.noreply.github.com>
  • Loading branch information
kopancek and omBratteng committed Feb 14, 2024
1 parent 94cc3c0 commit 9aa71fd
Show file tree
Hide file tree
Showing 9 changed files with 2,368 additions and 1,899 deletions.
17 changes: 6 additions & 11 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ jobs:
name: Checkout code
uses: actions/checkout@v4

-
name: Enable Corepack
run: corepack enable

-
uses: actions/setup-node@v4.0.1
with:
Expand All @@ -39,7 +43,7 @@ jobs:
name: Install dependencies
if: steps.cache-node_modules.outputs.cache-hit != 'true'
run: |
yarn install --frozen-lockfile
yarn install --immutable
-
name: Lint
Expand All @@ -51,20 +55,11 @@ jobs:
run: |
yarn build
-
name: devcard.svg
uses: ./
with:
devcard_id: 0b156485612243bfa39092f30071e276
commit_filename: devcard.svg
commit_branch: main
dryrun: true

-
name: devcard.png
uses: ./
with:
devcard_id: 0b156485612243bfa39092f30071e276
devcard_id: XDCZD-PHG
commit_filename: devcard.png
commit_branch: main
dryrun: true
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ jobs:

- uses: chainguard-dev/actions/setup-gitsign@main

-
name: Enable Corepack
run: corepack enable

-
uses: actions/setup-node@v4.0.1
with:
Expand All @@ -49,7 +53,7 @@ jobs:
name: Install dependencies
if: steps.cache-node_modules.outputs.cache-hit != 'true'
run: |
yarn install --frozen-lockfile
yarn install --immutable
- name: Setup git
run: |
Expand Down
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ jobs:
### Required
- `devcard_id`: this is the unique hash of the devcard, it can be found in the URL of the devcard.
- e.g. `https://api.daily.dev/devcards/0b156485612243bfa39092.3.171e276.png` where the devcard_id is `0b156485612243bfa39092.3.171e276`
- Can be found at [https://app.daily.dev/devcard](https://app.daily.dev/devcard)
- `devcard_id`: this is the unique id of the devcard, it can be found in the URL of the devcard or [here](https://app.daily.dev/api/id).
- e.g. `https://api.daily.dev/devcards/v2/0b156485612243bfa39092.3.171e276.png` where the devcard_id is `0b156485612243bfa39092.3.171e276`
- Can be found at [https://app.daily.dev/api/id](https://app.daily.dev/api/id)

### Optional

- `token`: GitHub Token used to commit the devcard
- `commit_branch`: The branch to commit the devcard to. Defaults to the branch of the action.
- `commit_message`: The commit message to use when committing the devcard. Defaults to `Update ${filename}`.
- You can use `${filename}` in the message to refer to the filename of the devcard.
- `commit_filename`: The filename to commit the devcard to. Defaults to `devcard.svg`.
- If you want to save the devcard as a PNG, you can use `devcard.png` instead, or any other filename ending in `.png`.
- `commit_filename`: The filename to commit the devcard to. Defaults to `devcard.png`.
- You can also use any other filename ending in `.png`.
- `committer_email`: The committer email used in commit. Defaults to `noreply@github.com`.
- `committer_name`: The committer name used in commit. Defaults to `github-actions[bot]`.
- `dryrun`: If set to `true`, the action will run as normal, but not actually commit the devcard
Expand Down
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
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
{
"name": "Ole-Martin Bratteng",
"email": "ole-martin@bratteng.com"
},
{
"name": "Milan Freml",
"email": "kopancek@gmail.com"
}
],
"scripts": {
Expand All @@ -24,17 +28,12 @@
"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"
"simple-git": "^3.20.0"
},
"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",
"@vercel/ncc": "0.38.1",
Expand All @@ -44,5 +43,6 @@
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.2.4",
"typescript": "^5.3.3"
}
},
"packageManager": "yarn@4.1.0"
}
83 changes: 21 additions & 62 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,23 @@
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`

const validateDevcardIdAsUUID = (devcard_id: string): boolean => {
// An UUIDv4 regex without hyphens
const uuid4Regex = /^([0-9A-F]{8})([0-9A-F]{4})(4[0-9A-F]{3})([89AB][0-9A-F]{3})([0-9A-F]{12})$/i
return validate(devcard_id.replace(uuid4Regex, '$1-$2-$3-$4-$5'))
}
`https://api.daily.dev/devcards/v2/${devcard_id}.png?r=${new Date().valueOf()}&ref=action`

;(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 @@ -63,38 +32,25 @@ const validateDevcardIdAsUUID = (devcard_id: string): boolean => {
throw new Error('Filename is required')
}

if (!validateDevcardIdAsUUID(devcard_id)) {
throw new Error(`Invalid devcard_id: ${devcard_id}`)
}

console.log(`Dryrun`, dryrun)

// 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 +77,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
Loading

0 comments on commit 9aa71fd

Please sign in to comment.