Skip to content

Commit

Permalink
continue and implement .legacy plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
lowlighter committed Nov 15, 2023
1 parent 83af929 commit cd4c9c0
Show file tree
Hide file tree
Showing 14 changed files with 294 additions and 102 deletions.
7 changes: 6 additions & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
},
"tasks": {
// '
"qq": "rm -rf .coverage && deno test --coverage=.coverage --no-check --unstable --trace-ops --allow-all source --filter='calendar' && deno coverage .coverage --include='^file:.*/calendar/mod.ts'",
"qq": "rm -rf .coverage && deno test --coverage=.coverage --no-check --unstable --trace-ops --allow-all source --filter='base64' && deno coverage .coverage --include='^file:.*/transform.base64/mod.ts'",
"q": "rm -rf .coverage && deno test --coverage=.coverage --unstable --trace-ops --fail-fast --allow-all source && deno coverage .coverage --exclude='/dom/'",

"make": "deno run --allow-env --allow-read --allow-write=.deno-make.json --allow-run=deno https://deno.land/x/make@1.2.0/mod.ts $0"
Expand Down Expand Up @@ -79,6 +79,7 @@
"cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets"
],
"run": [
"docker",
"$CHROME_BIN"
],
"read": [
Expand All @@ -89,6 +90,7 @@
"metrics.config.yml"
],
"write": [
"$TMP",
"$HOME/.config/chromium/SingletonLock"
],
"env": true
Expand Down Expand Up @@ -131,12 +133,14 @@
"cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets",
// Testing
"example.com",
"loremflickr.com",
// Browser downloads
"googlechromelabs.github.io/chrome-for-testing",
"edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing"
],
"run": [
"deno",
"docker",
"$CHROME_BIN"
],
"read": [
Expand All @@ -146,6 +150,7 @@
"write": [
".test",
".cache",
"$TMP",
"$HOME/.config/chromium/SingletonLock"
],
"env": true
Expand Down
14 changes: 11 additions & 3 deletions source/engine/components/tests/context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//Imports
import { dir, test } from "@engine/utils/testing.ts"
import { deepMerge } from "std/collections/deep_merge.ts"
import { Browser } from "@engine/utils/browser.ts"
Expand Down Expand Up @@ -66,11 +67,15 @@ export async function getPermissions(test: Awaited<ReturnType<typeof Component["
read: [dir.source, dir.cache],
env: [...requested].filter((permission) => permission.startsWith("env:")).map((permission) => permission.replace("env:", "")),
net: [...requested].filter((permission) => permission.startsWith("net:")).map((permission) => permission.replace("net:", "")),
run: [...requested].filter((permission) => permission.startsWith("run:")).map((permission) => permission.replace("run:", "")).filter((bin) => !["chrome"].includes(bin)),
} as test
if (requested.has("net:all")) {
delete permissions.net
permissions.net = "inherit"
}
if (requested.has("run:chrome")) {
// TODO(@lowlighter): To remove when https://github.com/denoland/deno/issues/21123 fixed
if (permissions.run.length) {
permissions.run = "inherit"
} else if (requested.has("run:chrome")) {
Object.assign(
permissions,
deepMerge(permissions, {
Expand All @@ -82,7 +87,10 @@ export async function getPermissions(test: Awaited<ReturnType<typeof Component["
}),
)
}
if (requested.has("write")) {
if (requested.has("write:tmp")) {
Object.assign(permissions, deepMerge(permissions, { write: [env.get("TMP")] }))
}
if (requested.has("write:all")) {
Object.assign(permissions, deepMerge(permissions, { write: [dir.test] }))
}

Expand Down
90 changes: 0 additions & 90 deletions source/engine/components/tests/context_test.ts

This file was deleted.

9 changes: 5 additions & 4 deletions source/engine/utils/deno/command_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import { DevNull } from "@engine/utils/log_test.ts"
const stdio = new DevNull()
const log = new Logger(import.meta, { level: Logger.channels.trace, tags: { foo: "bar" }, stdio })

// TODO(@lowlighter): Use `{ permissions: { run: ["deno"] } }` when https://github.com/denoland/deno/issues/21123 fixed
// TODO(@lowlighter): Use `["deno"]` when https://github.com/denoland/deno/issues/21123 fixed
const permissions = { run: "inherit" } as const

Deno.test(t(import.meta, "`command()` can execute commands"), async () => {
Deno.test(t(import.meta, "`command()` can execute commands"), { permissions }, async () => {
await expect(command("deno --version")).to.be.eventually.containSubset({ success: true, code: 0 })
})

Deno.test(t(import.meta, "`command()` returns stdio content instead if asked"), async () => {
Deno.test(t(import.meta, "`command()` returns stdio content instead if asked"), { permissions }, async () => {
await expect(command("deno --version", { return: "stdout" })).to.eventually.include("deno")
})

Deno.test(t(import.meta, "`command()` returns stdio content instead if asked"), async () => {
Deno.test(t(import.meta, "`command()` returns stdio content instead if asked"), { permissions }, async () => {
stdio.flush()
await expect(command("deno --version", { log })).to.be.fulfilled
expect(stdio.messages).to.not.be.empty
Expand Down
124 changes: 124 additions & 0 deletions source/plugins/.legacy/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Imports
import { is, parse, Plugin } from "@engine/components/plugin.ts"
import { Logger } from "@engine/utils/log.ts"
import { command } from "@engine/utils/deno/command.ts"
import { throws } from "@engine/utils/errors.ts"
import { read } from "@engine/utils/deno/io.ts"
import { encodeBase64 } from "std/encoding/base64.ts"

/** Plugin */
export default class extends Plugin {
/** Import meta */
static readonly meta = import.meta

/** Name */
readonly name = "🏛️ Legacy plugins execution"

/** Category */
readonly category = "metrics"

/** Description */
readonly description = "Executes plugins using metrics v3 docker image"

/** Supports */
readonly supports = ["user", "organization", "repository"]

/** Inputs */
readonly inputs = is.object({
version: is.string().regex(/^v3\.[0-9]{1,2}$/).default("v3.34").describe("Metrics version (v3.x)"),
inputs: is.record(is.unknown()).default(() => ({})).describe("Plugin inputs (as described from respective `action.yml`). Some core options are not supported and will have no effect"),
})

/** Outputs */
readonly outputs = is.object({
content: is.string().describe("Rendered content (base64 encoded)"),
})

/** Permissions */
readonly permissions = ["run:docker", "write:tmp"]

/** Action */
protected async action() {
const { handle } = this.context
const { version, inputs } = await parse(this.inputs, this.context.args)

// Prepare context
const context = {
fixed: {
use_prebuilt_image: true,
config_base64: true,
output_action: "none",
filename: "metrics.legacy",
config_output: "svg",
// TODO(@lowlighter): set mime type according to config_output
// auto / svg / png / jpeg / json / markdown / markdown-pdf / insights
},
inherited: {
token: this.context.token.read(),
user: handle?.split("/")[0],
repo: handle?.split("/")[1],
template: this.context.template,
config_timezone: this.context.timezone,
retries: this.context.retries.attempts,
retries_delay: this.context.retries.delay,
plugins_errors_fatal: this.context.fatal,
debug: Logger.channels[this.context.logs] >= Logger.channels.debug,
use_mocked_data: this.context.mock,
github_api_rest: this.context.api,
github_api_graphql: this.context.api,
},
editable: [
/^base(?:_|$)/,
/^repositories(?:_|$)/,
/^users_ignored$/,
/^commits_authoring$/,
/^markdown$/,
/^optimize$/,
/^setup_community_templates$/,
/^query$/,
/^extras_(?:css|js)$/,
/^config_(?:order|twemoji|gemoji|octicon|display|animations|padding|presets)$/,
/^delay$/,
/^quota_required_(?:rest|graphql|search)$/,
/^verify$/,
/^debug_flags$/,
/^experimental_features$/,
/^plugin_\w+$/,
],
inputs: {} as Record<PropertyKey, unknown>,
}
Object.assign(context.inputs, context.fixed, context.inherited)

// Register user inputs
for (const [key, value] of Object.entries(inputs)) {
if (key in context.fixed) {
this.log.warn(`ignoring ${key}: cannot be overriden in this context`)
continue
}
if ((key in context.inherited) || (context.editable.some((regex) => regex.test(key)))) {
this.log.trace(`registering: ${key}=${value}`)
context.inputs[key] = value
continue
}
this.log.warn(`ignoring ${key}: not supported in this context`)
}

// Execute docker image
const tmp = await Deno.makeTempDir({ prefix: "metrics_legacy_" })
try {
const env = Object.fromEntries(Object.entries(context.inputs).map(([key, value]) => [`INPUT_${key.toLocaleUpperCase()}`, `${value ?? ""}`]))
this.log.trace(env)
const { success } = await command(`docker run --rm ${Object.keys(env).map((key) => `--env ${key}`).join(" ")} --volume=${tmp}:/renders ghcr.io/lowlighter/metrics:${version}`, {
log: this.log,
env,
})
if (!success) {
throws("Failed to execute metrics")
}
this.context.template = "legacy"
return { content: encodeBase64(await read(`${tmp}/metrics.legacy`)) }
} finally {
await Deno.remove(tmp, { recursive: true })
}
}
}
10 changes: 10 additions & 0 deletions source/plugins/.legacy/templates/legacy.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<section class="legacy">
<% if (result instanceof Error) { %>
<div class="error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<span><%= result.message %></span>
</div>
<% } else { %>
<img src="data:image/svg+xml;base64,<%= result.content %>">
<% } %>
</section>
32 changes: 32 additions & 0 deletions source/plugins/.legacy/tests/list.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
- name: supports `version`
plugins:
# - .legacy:
# version: v3.34
# handle: octocat
# processors:
# - assert:
# html:
# select: .legacy img
# count: 1=
- .legacy:
version: v3.99
handle: octocat
fatal: false
logs: none
processors:
- assert:
error: /failed to execute metrics/i

#- name: supports `inputs`
# plugins:
# - .legacy:
# inputs:
# use_prebuilt_image: false
# dryrun: true
# base: header
# handle: octocat
# processors:
# - assert:
# html:
# select: .legacy img
# count: 1=
1 change: 1 addition & 0 deletions source/plugins/webscraping/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default class extends Plugin {
/** Action */
protected async action() {
if (this.context.mock) {
this.log.trace("replacing url as mock mode is enabled")
this.context.args.url = new URL("tests/example.html", import.meta.url).href
}
const { url, select: selector, mode, viewport, wait, background } = await parse(this.inputs, this.context.args)
Expand Down
2 changes: 1 addition & 1 deletion source/processors/publish.file/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class extends Processor {
readonly supports = ["application/xml", "image/svg+xml", "image/png", "image/jpeg", "image/webp", "application/json", "text/html", "application/pdf", "text/plain"]

/** Permissions */
readonly permissions = ["write"]
readonly permissions = ["write:all"]

/** Action */
protected async action(state: state) {
Expand Down
5 changes: 4 additions & 1 deletion source/processors/render.twemojis/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ export default class extends Processor {
/** Permissions */
readonly permissions = ["net:cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets"]

/** Does this processor needs to perform requests ? */
protected requesting = true

/** Action */
protected async action(state: state) {
const result = await this.piped(state)
const twemojis = new Map<string, string>(parse(result.content).map(({ text: emoji, url }: { text: string; url: string }) => [emoji, url]))
for (const [emoji, url] of twemojis) {
const svg = await fetch(url).then((response) => response.text())
const svg = await this.requests.fetch(url, { type: "text" })
twemojis.set(emoji, svg.replace(/^<svg /, '<svg class="twemoji" '))
}
for (const [emoji, svg] of twemojis) {
Expand Down
Loading

0 comments on commit cd4c9c0

Please sign in to comment.