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 10 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
4 changes: 4 additions & 0 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 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
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
6 changes: 3 additions & 3 deletions dist/37.index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ exports.id = 37;
exports.ids = [37];
exports.modules = {

/***/ 94037:
/***/ 4037:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "toFormData": () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32777);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(68010);
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2777);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8010);



Expand Down
245 changes: 13 additions & 232 deletions dist/index.js

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions dist/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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();
16 changes: 8 additions & 8 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": "3.0.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": "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
Loading