Skip to content

Commit

Permalink
continue stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
lowlighter committed Oct 29, 2023
1 parent d7cfd16 commit 9eb6237
Show file tree
Hide file tree
Showing 51 changed files with 705 additions and 619 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<!-- TODO(@lowlighter): write contributing guide -->

logs: none => false

See [#1533](https://github.com/lowlighter/metrics/discussions/1533)

## ✈️ Migration guide
Expand All @@ -22,7 +24,7 @@ For convenience, _metrics_ offers a [v3 to v4 migration script](/source/run/cli/
- [ ] GitHub Action
- [x] Implement `publish.gist`
- [x] Implement `publish.file`
- [ ] Implement `publish.git` (almost finished, needs to handle the PR merge)
- [x] Implement `publish.git`
- [x] Docker image
- [ ] Web server
- [ ] Config crafter (kind of ok, needs some polishing)
Expand Down
12 changes: 1 addition & 11 deletions deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"tasks": {
// '
"qq": "rm -rf .coverage && deno test --coverage=.coverage --no-check --unstable --trace-ops --allow-all source --filter='run()' && deno coverage .coverage --include='^file:.*/component.ts'",
"qq": "rm -rf .coverage && deno test --coverage=.coverage --no-check --unstable --trace-ops --allow-all source --filter='testing' && deno coverage .coverage --include='^file:.*/testing.ts'",
"q": "rm -rf .coverage && deno test --coverage=.coverage --unstable --trace-ops --fail-fast --allow-all source && deno coverage .coverage",
"btr": "deno run --allow-env --allow-read --allow-write=.btr.json --allow-run=deno --no-lock tasks.ts $0"
},
Expand Down Expand Up @@ -265,15 +265,5 @@
"semiColons": false,
"exclude": ["CODE_OF_CONDUCT.md", ".coverage", ".btr.json", "source/run/server/static/app.js", ".legacy"]
},
// TypeScript options
"compilerOptions": {
"lib": [
"deno.ns",
"deno.unstable",
"dom",
"dom.asynciterable",
"dom.iterable"
]
},
"deno.enable": true
}
2 changes: 2 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion source/engine/components/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { toFileUrl } from "std/path/to_file_url.ts"
import { MetricsError, throws } from "@engine/utils/errors.ts"
import { exists } from "std/fs/exists.ts"
import * as YAML from "std/yaml/parse.ts"
import { read } from "@engine/utils/io.ts"
import { read } from "@engine/utils/deno/io.ts"
import * as dir from "@engine/paths.ts"

/** Component */
Expand Down
2 changes: 1 addition & 1 deletion source/engine/components/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Imports
import { Component, is, parse, state } from "@engine/components/component.ts"
import { list, read } from "@engine/utils/io.ts"
import { list, read } from "@engine/utils/deno/io.ts"
import { _plugin_nop_removed_keys as schema_nop_removed_keys, plugin as schema, plugin_nop as schema_nop } from "@engine/config.ts"
import * as ejs from "y/ejs@3.1.9"
import { Requests } from "@engine/components/requests.ts"
Expand Down
2 changes: 1 addition & 1 deletion source/engine/components/plugin_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ for (const id of await Plugin.list()) {
const tests = await plugin.tests()
const templates = await plugin.templates()
const name = `${plugin.icon} plugins/${plugin.id}`
if (!tests.length) {
if (!tests?.length) {
Deno.test.ignore(t(name, null), () => void null)
continue
}
Expand Down
2 changes: 1 addition & 1 deletion source/engine/components/processor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Imports
import { Component, is, parse, state } from "@engine/components/component.ts"
import type { processor as schema } from "@engine/config.ts"
import { list } from "@engine/utils/io.ts"
import { list } from "@engine/utils/deno/io.ts"
import type { Plugin } from "@engine/components/plugin.ts"
import { Requests } from "@engine/components/requests.ts"
import { throws } from "@engine/utils/errors.ts"
Expand Down
2 changes: 1 addition & 1 deletion source/engine/components/processor_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ for (const id of await Processor.list()) {
const processor = await Processor.load({ id })
const tests = await processor.tests()
const name = `${processor.icon} processors/${processor.id}`
if (!tests.length) {
if (!tests?.length) {
Deno.test.ignore(t(name, null), () => void null)
continue
}
Expand Down
2 changes: 1 addition & 1 deletion source/engine/components/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Octokit } from "y/@octokit/rest@20.0.1"
import { paginateGraphql } from "y/@octokit/plugin-paginate-graphql@4.0.0"
import type { RequestInterface } from "y/@octokit/types@11.1.0"
import { Internal, is } from "@engine/components/internal.ts"
import { read } from "@engine/utils/io.ts"
import { read } from "@engine/utils/deno/io.ts"
import type { requests as schema } from "@engine/config.ts"
import { version } from "@engine/version.ts"
import { throws } from "@engine/utils/errors.ts"
Expand Down
3 changes: 2 additions & 1 deletion source/engine/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import { is } from "@engine/utils/validation.ts"
import { deepMerge } from "std/collections/deep_merge.ts"
import { Secret } from "@engine/utils/secret.ts"
import { env, read } from "@engine/utils/io.ts"
import { read } from "@engine/utils/deno/io.ts"
import { env } from "@engine/utils/deno/env.ts"
import * as YAML from "std/yaml/parse.ts"
import { Logger } from "@engine/utils/log.ts"
import { throws } from "@engine/utils/errors.ts"
Expand Down
13 changes: 3 additions & 10 deletions source/engine/utils/browser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//Imports
import { Logger } from "@engine/utils/log.ts"
import { getBinary, launch } from "x/astral@0.3.0/mod.ts"
import { env } from "@engine/utils/io.ts"
import { env } from "@engine/utils/deno/env.ts"
import * as dir from "@engine/paths.ts"
import { throws } from "@engine/utils/errors.ts"
import { delay } from "std/async/delay.ts"
Expand Down Expand Up @@ -74,15 +74,8 @@ export class Browser {
await this.close()
}
},
// Evaluate function (and launch func for coverage)
// Evaluate function
evaluate: (async (func: Parameters<typeof evaluate>[0], options?: Parameters<typeof evaluate>[1]) => {
if ((!Browser.shareable) && (typeof func === "function")) {
try {
await func()
} catch {
// Ignore
}
}
try {
return await evaluate(func, options)
} catch (error) {
Expand All @@ -109,7 +102,7 @@ export class Browser {
/** Instantiates or reuse */
static async page({ log, bin, width, height }: { log: Logger; bin?: string; width?: number; height?: number }) {
if ((Browser.shareable) && (!Browser.shared)) {
Object.assign(Browser, { shared: await new Browser({ log: new Logger(import.meta, { level: "none" }), bin, endpoint:env.get("BROWSER_ENDPOINT") }).ready })
Object.assign(Browser, { shared: await new Browser({ log: new Logger(import.meta, { level: "none" }), bin, endpoint: env.get("BROWSER_ENDPOINT") }).ready })
const close = Browser.shared!.close.bind(Browser.shared)
Browser.shared!.close = async () => {
await close()
Expand Down
1 change: 1 addition & 0 deletions source/engine/utils/browser_test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/// <reference lib="dom" />
import { Browser } from "@engine/utils/browser.ts"
import { dir, expect, MetricsError, t } from "@engine/utils/testing.ts"
import { Logger } from "@engine/utils/log.ts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@ import { Logger } from "@engine/utils/log.ts"
import { TextDelimiterStream } from "std/streams/text_delimiter_stream.ts"

/** Execute command */
export async function command(input: string, options:{return:"stdout"|"stderr", cwd?:string, env?:Record<string, string>, log?:Logger}):Promise<string>
export async function command(input: string, options?:{cwd?:string, env?:Record<string, string>, log?:Logger}):Promise<{success:boolean, code:number, stdout:string, stderr:string}>
export async function command(input: string, {return:returned, cwd, log, env}:{return?:"stdout"|"stderr", cwd?:string, env?:Record<string, string>, log?:Logger} = {}) {
export async function command(input: string, options: { return: "stdout" | "stderr"; cwd?: string; env?: Record<string, string>; log?: Logger }): Promise<string>
export async function command(input: string, options?: { cwd?: string; env?: Record<string, string>; log?: Logger }): Promise<{ success: boolean; code: number; stdout: string; stderr: string }>
export async function command(input: string, { return: returned, cwd, log, env }: { return?: "stdout" | "stderr"; cwd?: string; env?: Record<string, string>; log?: Logger } = {}) {
const stdio = { stdout: "", stderr: "" }
const [bin, ...args] = argv(input)
log = log?.with({ bin })
const command = new Deno.Command(bin, { args, stdin:"null", stdout: "piped", stderr: "piped", cwd, env })
const command = new Deno.Command(bin, { args, stdin: "null", stdout: "piped", stderr: "piped", cwd, env })
const process = command.spawn()
const streams = Promise.allSettled((["stdout", "stderr"] as const).map(async channel => {
const streams = Promise.allSettled((["stdout", "stderr"] as const).map(async (channel) => {
const stream = process[channel].pipeThrough(new TextDecoderStream()).pipeThrough(new TextDelimiterStream("\n"))
try {
for await (const line of stream) {
log?.[({stdout:"message", stderr:"warn"} as const)[channel]](line)
log?.[({ stdout: "message", stderr: "warn" } as const)[channel]](line)
stdio[channel] += line + "\n"
}
}
finally {
} finally {
stream.cancel()
}
}))
Expand Down
21 changes: 21 additions & 0 deletions source/engine/utils/deno/command_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, t } from "@engine/utils/testing.ts"
import { command } from "@engine/utils/deno/command.ts"
import { Logger } from "@engine/utils/log.ts"
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 })

Deno.test(t(import.meta, "`command()` can execute commands"), { permissions: { run: ["deno"] } }, 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"), { permissions: { run: ["deno"] } }, async () => {
await expect(command("deno --version", { return: "stdout" })).to.eventually.include("deno")
})

Deno.test(t(import.meta, "`command()` returns stdio content instead if asked"), { permissions: { run: ["deno"] } }, async () => {
stdio.flush()
await expect(command("deno --version", { log })).to.be.fulfilled
expect(stdio.messages).to.not.be.empty
})
38 changes: 38 additions & 0 deletions source/engine/utils/deno/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/** Get environment value */
function get(key: string, options: { boolean: true }): boolean
function get(key: string, options?: { boolean: false }): string
function get(key: string, { boolean = false } = {}) {
if (boolean) {
const value = env.get(key, { boolean: false })
if (!value.length) {
return false
}
return !/^(0|[Nn]o?|NO|[Oo]ff|OFF|[Ff]alse|FALSE)$/.test(value)
}
try {
return Deno.env.get(key) ?? ""
} catch {
return ""
}
}

/** Set environment value */
function set(key: string, value: string) {
try {
return Deno.env.set(key, value)
} catch {
return
}
}

/** Environment */
export const env = {
get,
set,
get deployment() {
return env.get("DENO_DEPLOYMENT_ID", { boolean: true })
},
get actions() {
return env.get("GITHUB_ACTIONS", { boolean: true })
},
}
61 changes: 61 additions & 0 deletions source/engine/utils/deno/env_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { expect, nodeno, t } from "@engine/utils/testing.ts"
import { env } from "@engine/utils/deno/env.ts"

const uuid = crypto.randomUUID().slice(-12).toUpperCase()
const test = {
env: `UNIT_TEST_${uuid}`,
value: uuid,
}

Deno.test(t(import.meta, "`env.get()` can read a env variable"), { permissions: { env: [test.env, `${test.env}_UNDEFINED`] } }, () => {
env.set(test.env, test.value)
expect(env.get(test.env)).to.equal(test.value)
expect(env.get(`${test.env}_UNDEFINED`)).to.equal("")
})

Deno.test(t(import.meta, "`env.get()` returns a env variable as a boolean if asked"), { permissions: { env: [test.env] } }, () => {
for (
const { value, boolean } of [
{ value: "1", boolean: true },
{ value: "true", boolean: true },
{ value: "0", boolean: false },
{ value: "false", boolean: false },
]
) {
env.set(test.env, value)
expect(env.get(test.env, { boolean: true })).to.equal(boolean)
}
})

Deno.test(
t(import.meta, "`env.get()` returns empty values in non-deno environments"),
{ permissions: "none" },
nodeno(() => {
expect(env.get(test.env)).to.equal("")
expect(env.get(test.env, { boolean: true })).to.equal(false)
}, { with: { test } }),
)

Deno.test(t(import.meta, "`env.set()` can registers a env variable"), { permissions: { env: [test.env] } }, () => {
expect(env.set(test.env, test.value))
expect(env.get(test.env)).to.equal(test.value)
expect(env.set(`${test.env}_FORBIDDEN`, test.value))
expect(env.get(`${test.env}_FORBIDDEN`)).to.equal("")
})

Deno.test(
t(import.meta, "`env.set()` is a noop in non-deno environments"),
{ permissions: "none" },
nodeno(() => {
expect(env.set(test.env, test.value))
expect(env.get(test.env)).to.equal("")
}, { with: { test } }),
)

Deno.test(t(import.meta, "`env.deployment` is a boolean"), { permissions: { env: [test.env, `${test.env}_UNDEFINED`] } }, () => {
expect(env.deployment).to.be.a("boolean")
})

Deno.test(t(import.meta, "`env.actions` is a boolean"), { permissions: { env: [test.env, `${test.env}_UNDEFINED`] } }, () => {
expect(env.actions).to.be.a("boolean")
})
62 changes: 62 additions & 0 deletions source/engine/utils/deno/io.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//Imports
import { ensureDir } from "std/fs/ensure_dir.ts"
import { expandGlob } from "std/fs/expand_glob.ts"
import { throws } from "@engine/utils/errors.ts"
import { dirname } from "std/path/dirname.ts"
import { toFileUrl } from "std/path/to_file_url.ts"
import { resolve } from "std/path/resolve.ts"
import * as dir from "@engine/paths.ts"

/** Read file */
export function read(path: string | URL, options: { sync: true }): string
export function read(path: string | URL, options?: { sync?: false }): Promise<string>
export function read(path: string | URL, { sync = false } = {}) {
if ((typeof path === "string") && (path.startsWith("metrics://"))) {
path = path.replace("metrics:/", dir.source)
} else if ((path instanceof URL) && (path.protocol === "metrics:")) {
path = path.href.replace("metrics:/", dir.source)
}
if (!globalThis.Deno) {
if (sync) {
throws("Unsupported action: synchronous read")
}
return fetch(toFileUrl(resolve(path as string))).then((response) => response.text())
}
if ((typeof path === "string") && (path.startsWith("data:"))) {
if (sync) {
throws("Unsupported action: synchronous read")
}
return fetch(path).then((response) => response.text())
}
return sync ? Deno.readTextFileSync(path) : Deno.readTextFile(path)
}

/** Write file */
export async function write(path: string, data: string | Uint8Array | ReadableStream<Uint8Array>) {
if (!globalThis.Deno) {
return
}
await ensureDir(dirname(path))
if (typeof data === "string") {
return Deno.writeTextFile(path, data)
}
return Deno.writeFile(path, data)
}

/** List files in globpath */
export async function list(glob: string) {
if (!globalThis.Deno) {
throws("Unsupported action: list")
}
const files = []
const base = glob.match(/(?<base>.*\/)\*/)?.groups?.base
const prefix = base ? new RegExp(`.*?${base}`) : null
for await (const { path } of expandGlob(glob, { extended: true, globstar: true })) {
let file = path.replaceAll("\\", "/")
if (prefix) {
file = file.replace(prefix, "")
}
files.push(file)
}
return files
}
Loading

0 comments on commit 9eb6237

Please sign in to comment.