Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
14 changes: 9 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Release
"on":
on:
push:
branches:
- master
Expand All @@ -11,13 +11,17 @@ jobs:
name: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v5
with:
persist-credentials: true
- uses: actions/setup-node@v6
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npx semantic-release
- name: Install dependencies
run: npm ci
- name: Release
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.PROBOTBOT_NPM_TOKEN }}
38 changes: 31 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Test
"on":
on:
push:
branches:
- master
Expand All @@ -8,13 +8,37 @@ name: Test
- opened
- synchronize
jobs:
build:
test:
name: Test on Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22, 24]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v5
with:
node-version: 16
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test

lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- run: npm install
- run: npm test
- name: Install dependencies
run: npm install
- name: Lint
run: npm run lint
3 changes: 3 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import neostandard from 'neostandard'

export default neostandard({})
36 changes: 18 additions & 18 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Context } from "probot";

type StringOrNumber = number | string;
type Key =
| { [key: string]: StringOrNumber }
| StringOrNumber[]
| StringOrNumber;
type Value = Key;

declare function metadata(
context: Context,
issue?: { body: string; [key: string]: any }
): {
get(key?: Key): Promise<any>;
set(key: Key, value: Value): Promise<any>;
};

export = metadata;
declare module "index" {
export default metadata;
export type StringOrNumber = string | number;
export type Key = Record<string, StringOrNumber> | StringOrNumber | StringOrNumber[];
export type Value = Key;
export type IssueOption = {
owner: string;
repo: string;
issue_number: number;
body?: string | undefined;
};
export type ProbotMetadata = {
get: (key?: Key) => Promise<Value | undefined>;
set: (key?: Key, value?: Value) => Promise<void>;
};
export type ProbotMetadataConstructor = (context: import("probot").Context<"issue_comment">, issue?: IssueOption) => ProbotMetadata;
export const metadata: ProbotMetadataConstructor;
}
69 changes: 49 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,82 @@
const regex = /(\n\n|\r\n)<!-- probot = (.*) -->/
const probotMetadataRegex = /(?:\n\n|\r\n)<!-- probot = (.*) -->/

module.exports = (context, issue = null) => {
const github = context.octokit || context.github
const prefix = context.payload.installation.id
const metadata = /** @type {ProbotMetadataConstructor} */ (context, issue) => {
const octokit = context.octokit
const prefix = /** @type {{ id: number; node_id: string; }} */ (context.payload.installation).id

if (!issue) issue = context.issue()

return {
async get (key = null) {
async get (key) {
let body = issue.body

if (!body) {
body = (await github.issues.get(issue)).data.body || ''
body = (await octokit.rest.issues.get(issue)).data.body || ''
}

const match = body.match(regex)
const match = body.match(probotMetadataRegex)

if (match) {
const data = JSON.parse(match[2])[prefix]
return key ? data && data[key] : data
const probotMetadata = JSON.parse(match[1])
const data = probotMetadata[prefix]
return typeof key === 'string' || typeof key === 'number'
? data && data[key]
: data
}
},

async set (key, value) {
let body = issue.body
/** @type {Record<number, Record<number|string, any>>} */
let data = {}

if (!body) body = (await github.issues.get(issue)).data.body || ''
if (!body) body = (await octokit.rest.issues.get(issue)).data.body || ''

const match = body.match(regex)
const match = body.match(probotMetadataRegex)

if (match) {
data = JSON.parse(match[2])
data = JSON.parse(match[1])
}

body = body.replace(regex, '')
body = body.replace(probotMetadataRegex, '')

if (!data[prefix]) data[prefix] = {}

if (typeof key === 'object') {
Object.assign(data[prefix], key)
} else {
if (typeof key === 'string' || typeof key === 'number') {
data[prefix][key] = value

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
library input
.
} else {
Object.assign(data[prefix], key)
}

body = `${body}\n\n<!-- probot = ${JSON.stringify(data)} -->`

const { owner, repo, issue_number } = issue
return github.issues.update({ owner, repo, issue_number, body })
await octokit.rest.issues.update({
owner: issue.owner,
repo: issue.repo,
issue_number: issue.issue_number,
body: `${body}\n\n<!-- probot = ${JSON.stringify(data)} -->`
})
}
}
}

export default metadata
export { metadata }

/** @typedef {string|number} StringOrNumber */
/** @typedef {Record<string, StringOrNumber>|StringOrNumber|StringOrNumber[]} Key */
/** @typedef {Key} Value */

/**
* @typedef {object} IssueOption
* @property {string} owner
* @property {string} repo
* @property {number} issue_number
* @property {string} [body]
*/

/**
* @typedef {object} ProbotMetadata
* @property {(key?: Key)=>Promise<Value|undefined>} get
* @property {(key?: Key, value?: Value)=>Promise<void>} set
*/

/** @typedef {(context: import('probot').Context<'issue_comment'>, issue?: IssueOption)=>ProbotMetadata} ProbotMetadataConstructor */
Loading