Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ jobs:
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: '20'
node-version: '24'

- name: Run type checking
run: npm install && npx tsc

- name: Run tests
run: npm test -- --experimental-test-coverage --test-reporter lcov --test-reporter-destination lcov.info
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/vendor-node-modules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ jobs:
PR: ${{ github.event.pull_request.number || github.event.inputs.pull_request }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Vendor node_modules
run: |
npm ci --ignore-scripts
rm -r ./node_modules/esm-reload/.github/
run: npm ci --ignore-scripts --omit=dev
- name: Commit changes
id: commit
run: |
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/node_modules/.bin/
/node_modules/@tsconfig/
/node_modules/@types/
/node_modules/typescript/
/node_modules/undici-types/
/node_modules/**/.github/
/node_modules/**/tests/
4 changes: 2 additions & 2 deletions api-commit-and-push/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ inputs:
required: false
default: ${{ github.repository }}
runs:
using: node20
main: main.mjs
using: node24
main: main.mts
14 changes: 9 additions & 5 deletions api-commit-and-push/main.mjs → api-commit-and-push/main.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import github from "@actions/github"
import fs from "node:fs/promises"
import path from "node:path"

import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"

async function main() {
const commitMessage = core.getInput("message")
const branch = core.getInput("branch")
Expand All @@ -22,17 +24,19 @@ async function main() {
const headCommitSha = (await exec.getExecOutput("git", ["-C", directory, "rev-parse", "HEAD"], { silent: true })).stdout.trim()
const headTreeSha = (await exec.getExecOutput("git", ["-C", directory, "rev-parse", "HEAD:"], { silent: true })).stdout.trim()

const tree = {}
const tree: Record<string, RestEndpointMethodTypes["git"]["createTree"]["parameters"]["tree"][number]> = {}
for (const file of files) {
const absoluteFile = path.resolve(directory, file)

let content;
try {
content = await fs.readFile(absoluteFile, {encoding: "base64"})
} catch (error) {
if (error.code !== "ENOENT") throw error;

content = null;
if (Error.isError(error) && "code" in error && error.code === "ENOENT") {
content = null
} else {
throw error
}
}

if (content !== null) {
Expand All @@ -59,7 +63,7 @@ async function main() {
const blobResponse = await client.rest.git.createBlob({
owner: owner,
repo: repo,
content: await fs.readFile(absoluteFile, {encoding: "base64"}),
content: content,
encoding: "base64"
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import os from "node:os"
import path from "node:path"
import util from "node:util"

import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"

describe("api-commit-and-push", async () => {
const token = "fake-token"
const branch = "a-branch"
const message = "Some message"
let directory
let baseCommitSha
let baseTreeSha
let directory: string
let baseCommitSha: string
let baseTreeSha: string

const blobSha = "abcdef1234567890abcdef1234567890abcdef12"
const treeSha = "abcdef1234567890abcdef1234567890abcdef13"
Expand Down Expand Up @@ -70,7 +72,7 @@ describe("api-commit-and-push", async () => {
sha: blobSha,
})

const tree = []
const tree: Array<RestEndpointMethodTypes["git"]["createTree"]["parameters"]["tree"][number]> = []
addedFile.split("/").slice(0, -1).reduce((parentPath, component) => {
const treePath = path.posix.join(parentPath, component);

Expand Down
4 changes: 2 additions & 2 deletions check-commit-format/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ inputs:
required: false
default: CI-published-bottle-commits
runs:
using: node20
main: main.mjs
using: node24
main: main.mts
24 changes: 19 additions & 5 deletions check-commit-format/main.mjs → check-commit-format/main.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import assert from "node:assert/strict"
import core from "@actions/core"
import github from "@actions/github"
import fs from "fs"
import path from "path"

import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"

async function main() {
try {
const token = core.getInput("token", { required: true })
Expand All @@ -13,6 +16,7 @@ async function main() {
const client = github.getOctokit(token)

// Parse event
assert(process.env.GITHUB_EVENT_PATH)
const json = fs.readFileSync(process.env.GITHUB_EVENT_PATH, { encoding: "utf-8" })
const event = JSON.parse(json)
const pull = event.pull_request
Expand All @@ -25,9 +29,9 @@ async function main() {

let is_success = true
let autosquash = false
let commit_state = "success"
let commit_state: RestEndpointMethodTypes["repos"]["createCommitStatus"]["parameters"]["state"] = "success"
let message = "Commit format is correct."
let files_touched = []
let files_touched: Array<string> = []
let target_url = "https://docs.brew.sh/Formula-Cookbook#commit"

// For each commit...
Expand All @@ -47,6 +51,14 @@ async function main() {
break
}

// Empty commits that are not merge commits are usually a mistake or a bad rebase.
if (!commit_info.data.files || commit_info.data.files.length === 0) {
is_success = false
commit_state = "failure"
message = `${short_sha} is an empty commit.`
break
}

// Autosquash doesn't support commits that modify more than one file.
if (commit_info.data.files.length != 1) {
is_success = false
Expand All @@ -72,7 +84,7 @@ async function main() {
}

const file = commit_info.data.files[0]
const commit_subject = commit_info.data.commit.message.split("\n").shift()
const commit_subject = commit_info.data.commit.message.split("\n")[0]

if (file.filename.startsWith("Formula/")) {
const formula = path.basename(file.filename, '.rb')
Expand Down Expand Up @@ -118,11 +130,11 @@ async function main() {
})

// Get existing labels on PR
let existingLabels = await client.rest.issues.listLabelsOnIssue({
let issueLabels = await client.rest.issues.listLabelsOnIssue({
...github.context.repo,
issue_number: pull.number
})
existingLabels = existingLabels.data.map(label => label.name)
let existingLabels = issueLabels.data.map(label => label.name)

// Copy labels into new Array
const updatedLabels = existingLabels.slice()
Expand Down Expand Up @@ -167,6 +179,8 @@ async function main() {
labels: updatedLabels
})
} catch (error) {
if (!Error.isError(error)) throw error

core.setFailed(error)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ describe("check-commit-format", async () => {
})

afterEach(() => {
fs.rmSync(path.dirname(process.env.GITHUB_EVENT_PATH), { recursive: true })
if (process.env.GITHUB_EVENT_PATH) {
fs.rmSync(path.dirname(process.env.GITHUB_EVENT_PATH), { recursive: true })
}
})

describe("on a correct commit", async () => {
Expand Down Expand Up @@ -132,6 +134,177 @@ describe("check-commit-format", async () => {
})
})

describe("on a merge commit", async () => {
beforeEach(() => {
const mockPool = githubMockPool()

mockPool.intercept({
method: "GET",
path: `/repos/${GITHUB_REPOSITORY}/pulls/${pr}/commits`,
headers: {
Authorization: `token ${token}`,
},
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, [
{
sha: sha,
},
])

mockPool.intercept({
method: "GET",
path: `/repos/${GITHUB_REPOSITORY}/commits/${sha}`,
headers: {
Authorization: `token ${token}`,
},
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, {
sha: sha,
parents: [
{ sha: "abcdef1234567890abcdef1234567890abcdef11" },
{ sha: "abcdef1234567890abcdef1234567890abcdef12" }
],
files: [],
commit: {
message: "Merge commit",
}
})

mockPool.intercept({
method: "POST",
path: `/repos/${GITHUB_REPOSITORY}/statuses/${sha}`,
headers: {
Authorization: `token ${token}`,
},
body: (body) => util.isDeepStrictEqual(JSON.parse(body), {
state: "failure",
description: `${sha.substring(0, 10)} has 2 parents. Please rebase against origin/HEAD.`,
context: "Commit style",
target_url: "https://docs.brew.sh/Formula-Cookbook#commit"
}),
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, {})
})

it("fails and adds a failure label", async () => {
const mockPool = githubMockPool()

mockPool.intercept({
method: "GET",
path: `/repos/${GITHUB_REPOSITORY}/issues/${pr}/labels`,
headers: {
Authorization: `token ${token}`,
},
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, [
{ name: "other-label" },
])

mockPool.intercept({
method: "PATCH",
path: `/repos/${GITHUB_REPOSITORY}/issues/${pr}`,
headers: {
Authorization: `token ${token}`,
},
body: (body) => util.isDeepStrictEqual(JSON.parse(body), {
labels: ["other-label", failureLabel],
}),
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, {})

await loadMain()
})
})

describe("on an empty commit", async () => {
beforeEach(() => {
const mockPool = githubMockPool()

mockPool.intercept({
method: "GET",
path: `/repos/${GITHUB_REPOSITORY}/pulls/${pr}/commits`,
headers: {
Authorization: `token ${token}`,
},
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, [
{
sha: sha,
},
])

mockPool.intercept({
method: "GET",
path: `/repos/${GITHUB_REPOSITORY}/commits/${sha}`,
headers: {
Authorization: `token ${token}`,
},
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, {
sha: sha,
parents: [{ sha: "abcdef1234567890abcdef1234567890abcdef11" }],
files: [],
commit: {
message: "Empty commit",
}
})

mockPool.intercept({
method: "POST",
path: `/repos/${GITHUB_REPOSITORY}/statuses/${sha}`,
headers: {
Authorization: `token ${token}`,
},
body: (body) => util.isDeepStrictEqual(JSON.parse(body), {
state: "failure",
description: `${sha.substring(0, 10)} is an empty commit.`,
context: "Commit style",
target_url: "https://docs.brew.sh/Formula-Cookbook#commit"
}),
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, {})
})

it("fails and adds a failure label", async () => {
const mockPool = githubMockPool()

mockPool.intercept({
method: "GET",
path: `/repos/${GITHUB_REPOSITORY}/issues/${pr}/labels`,
headers: {
Authorization: `token ${token}`,
},
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, [
{ name: "other-label" },
])

mockPool.intercept({
method: "PATCH",
path: `/repos/${GITHUB_REPOSITORY}/issues/${pr}`,
headers: {
Authorization: `token ${token}`,
},
body: (body) => util.isDeepStrictEqual(JSON.parse(body), {
labels: ["other-label", failureLabel],
}),
}).defaultReplyHeaders({
"Content-Type": "application/json",
}).reply(200, {})

await loadMain()
})
})

describe("on an autosquashable incorrect commit", async () => {
beforeEach(() => {
const mockPool = githubMockPool()
Expand Down
2 changes: 1 addition & 1 deletion check-commit-format/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "check-commit-format",
"main": "main.mjs"
"main": "main.mts"
}
4 changes: 2 additions & 2 deletions create-or-update-issue/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,5 @@ outputs:
The node ID of the created or updated issue, used in GitHub GraphQL API
queries; undefined if `outcome` is `none`
runs:
using: node20
main: main.mjs
using: node24
main: main.mts
Loading
Loading