Skip to content

Commit

Permalink
Merge pull request #454 from code-hike/new-ci
Browse files Browse the repository at this point in the history
New github workflow
  • Loading branch information
pomber authored Sep 12, 2024
2 parents 1f2688f + d4bd878 commit 6a4babd
Show file tree
Hide file tree
Showing 19 changed files with 1,250 additions and 125 deletions.
46 changes: 46 additions & 0 deletions .github/scripts/git-utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { exec, getExecOutput } from "@actions/exec"
import github from "@actions/github"
import fs from "fs"

export async function checkout(branch) {
let { stderr } = await getExecOutput("git", ["checkout", branch], {
ignoreReturnCode: true,
})
let isCreatingBranch = !stderr
.toString()
.includes(`Switched to a new branch '${branch}'`)
if (isCreatingBranch) {
await exec("git", ["checkout", "-b", branch])
}
}

export async function resetBranch() {
// reset current branch to the commit that triggered the workflow
await exec("git", ["reset", `--hard`, github.context.sha])
}

export async function commitAll(message) {
await exec("git", ["add", "."])
await exec("git", ["commit", "-m", message])
}

export async function forcePush(branch) {
await exec("git", ["push", "origin", `HEAD:${branch}`, "--force"])
}

export async function setupUser() {
await exec("git", ["config", "user.name", `"github-actions[bot]"`])
await exec("git", [
"config",
"user.email",
`"github-actions[bot]@users.noreply.github.com"`,
])
await fs.promises.writeFile(
`${process.env.HOME}/.netrc`,
`machine github.com\nlogin github-actions[bot]\npassword ${process.env.GITHUB_TOKEN}`,
)
}

export async function pushTags() {
await exec("git", ["push", "origin", "--tags"])
}
55 changes: 55 additions & 0 deletions .github/scripts/md-utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkStringify from "remark-stringify"
import * as mdastToString from "mdast-util-to-string"

const BumpLevels = {
dep: 0,
patch: 1,
minor: 2,
major: 3,
}

export function getChangelogEntry(changelog, version) {
let ast = unified().use(remarkParse).parse(changelog)

let highestLevel = BumpLevels.dep

let nodes = ast.children
let headingStartInfo
let endIndex

for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
if (node.type === "heading") {
let stringified = mdastToString.toString(node)
let match = stringified.toLowerCase().match(/(major|minor|patch)/)
if (match !== null) {
let level = BumpLevels[match[0]]
highestLevel = Math.max(level, highestLevel)
}
if (headingStartInfo === undefined && stringified === version) {
headingStartInfo = {
index: i,
depth: node.depth,
}
continue
}
if (
endIndex === undefined &&
headingStartInfo !== undefined &&
headingStartInfo.depth === node.depth
) {
endIndex = i
break
}
}
}
if (headingStartInfo) {
ast.children = ast.children.slice(headingStartInfo.index + 1, endIndex)
}
return {
content: unified().use(remarkStringify).stringify(ast),
highestLevel: highestLevel,
}
}
8 changes: 8 additions & 0 deletions .github/scripts/params.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const PACKAGE_NAME = "codehike"
export const VERSION_COMMAND = "pnpm version-packages"
export const PUBLISH_COMMAND = "pnpm release"
export const RELEASE_BRANCH = "release"
export const BASE_BRANCH = "next"
export const PACKAGE_DIR = `packages/${PACKAGE_NAME}`

export const IDENTIFIER = "<!-- CH_ACTION -->"
51 changes: 51 additions & 0 deletions .github/scripts/pr-merged.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Octokit } from "@octokit/action"
import { IDENTIFIER, PACKAGE_NAME } from "./params.mjs"
import github from "@actions/github"

const octokit = new Octokit({})
const prNumber = github.context.payload.pull_request.number

console.log("Querying closing issues")
const query = `query ($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
title
state
closingIssuesReferences(first: 10) {
nodes {
number
}
}
}
}
}`
const result = await octokit.graphql(query, {
...github.context.repo,
prNumber: Number(prNumber),
})

const body = `${IDENTIFIER}
This issue has been fixed but not yet released.
Try it in your project before the release with:
${"```"}
npm i https://pkg.pr.new/${PACKAGE_NAME}@${prNumber}
${"```"}
Or wait for the next [release](https://github.com/${github.context.repo.owner}/${github.context.repo.repo}/pulls?q=is%3Aopen+is%3Apr+label%3Arelease).
`

console.log("Commenting issues")
await Promise.all(
result.repository.pullRequest.closingIssuesReferences.nodes.map(
async ({ number }) => {
console.log("Commenting issue", number)
await octokit.issues.createComment({
...github.context.repo,
issue_number: number,
body,
})
},
),
)
100 changes: 100 additions & 0 deletions .github/scripts/pr-updated.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Octokit } from "@octokit/action"
import { humanId } from "human-id"
import fs from "fs"
import { IDENTIFIER, PACKAGE_NAME } from "./params.mjs"
import github from "@actions/github"

const octokit = new Octokit({})
const prNumber = github.context.payload.pull_request.number

async function createOrUpdateComment(prevComment, prNumber, body) {
if (prevComment) {
console.log("Updating comment")
await octokit.issues.updateComment({
...github.context.repo,
comment_id: prevComment.id,
body,
})
} else {
console.log("Creating comment", prNumber)
await octokit.issues.createComment({
...github.context.repo,
issue_number: prNumber,
body,
})
}
}

console.log("Listing comments", prNumber)
const { data: comments } = await octokit.issues.listComments({
...github.context.repo,
issue_number: prNumber,
})
const prevComment = comments.find((comment) =>
comment.body.startsWith(IDENTIFIER),
)
console.log("prevComment", !!prevComment)

console.log("Finding changeset")
let changedFiles = await octokit.pulls.listFiles({
...github.context.repo,
pull_number: prNumber,
})
const changeset = changedFiles.data.find(
(file) =>
file.status === "added" &&
/^\.changeset\/.+\.md$/.test(file.filename) &&
file.filename !== ".changeset/README.md",
)
const hasChangesets = !!changeset
console.log({ hasChangesets })

if (!hasChangesets) {
console.log("Getting PR")
const pr = await octokit.pulls.get({
...github.context.repo,
pull_number: prNumber,
})
const filename = humanId({
separator: "-",
capitalize: false,
})
const value = encodeURIComponent(`---
"${PACKAGE_NAME}": patch
---
${pr.data.title}
`)
const repoURL = pr.data.head.repo.html_url
const addChangesetURL = `${repoURL}/new/${pr.data.head.ref}?filename=.changeset/${filename}.md&value=${value}`
const body = `${IDENTIFIER}
No changeset detected. If you are changing ${
"`" + PACKAGE_NAME + "`"
} [click here to add a changeset](${addChangesetURL}).
`
await createOrUpdateComment(prevComment, prNumber, body)
process.exit(0)
}

// if has changesets

console.log("Adding label")
await octokit.issues.addLabels({
...github.context.repo,
issue_number: prNumber,
labels: ["changeset"],
})

console.log("Reading canary.json")
const canary = await fs.promises.readFile("canary.json", "utf8")
console.log({ canary })
const { packages } = JSON.parse(canary)
const { name, url } = packages[0]
const body = `${IDENTIFIER}
Try ${"`" + name + "`"} from this pull request in your project with:
${"```"}
npm i https://pkg.pr.new/${name}@${prNumber}
${"```"}
`
await createOrUpdateComment(prevComment, prNumber, body)
107 changes: 107 additions & 0 deletions .github/scripts/prepare-release.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { exec } from "@actions/exec"
import readChangesets from "@changesets/read"
import {
checkout,
commitAll,
forcePush,
resetBranch,
setupUser,
} from "./git-utils.mjs"
import fs from "fs"
import { getChangelogEntry } from "./md-utils.mjs"
import { Octokit } from "@octokit/action"
import github from "@actions/github"
import {
BASE_BRANCH,
PACKAGE_DIR,
PACKAGE_NAME,
RELEASE_BRANCH,
VERSION_COMMAND,
} from "./params.mjs"

const cwd = process.cwd()
const octokit = new Octokit({})

console.log("Reading changesets")
const changesets = await readChangesets(cwd)
if (changesets.length === 0) {
console.log("No changesets found")
process.exit(0)
}

console.log("Setting up user")
await setupUser()

console.log("Checking out release branch")
await checkout(RELEASE_BRANCH)
await resetBranch()

console.log("Running version command")
const [versionCommand, ...versionArgs] = VERSION_COMMAND.split(/\s+/)
await exec(versionCommand, versionArgs, { cwd })

console.log("Reading package files")
const pkg = JSON.parse(
await fs.promises.readFile(`${PACKAGE_DIR}/package.json`, "utf8"),
)
const changelog = await fs.promises.readFile(
`${PACKAGE_DIR}/CHANGELOG.md`,
"utf8",
)
const canary = JSON.parse(await fs.promises.readFile("canary.json", "utf8"))

console.log("Committing changes")
await commitAll(`${PACKAGE_NAME}@${pkg.version}`)
console.log("Pushing changes")
await forcePush(RELEASE_BRANCH)

console.log("Find existing release PR")
const { data: prs } = await octokit.pulls.list({
...github.context.repo,
state: "open",
base: BASE_BRANCH,
head: `${github.context.repo.owner}:${RELEASE_BRANCH}`,
})
console.log("Existing PRs", prs)

const entry = getChangelogEntry(changelog, pkg.version)
const title = `🚀 Release ${"`" + pkg.name}@${pkg.version + "`"} 🚀`
const canaryUrl = canary.packages[0].url
const body = `${entry.content}
---
You can try ${
"`" + PACKAGE_NAME + "@" + pkg.version + "`"
} in your project before it's released with:
${"```"}
npm i ${canaryUrl}
${"```"}
`

if (prs.length === 0) {
console.log("Creating new release PR")
const { data: pr } = await octokit.rest.pulls.create({
...github.context.repo,
base: BASE_BRANCH,
head: RELEASE_BRANCH,
title,
body,
})
console.log("Adding `release` label")
await octokit.rest.issues.addLabels({
...github.context.repo,
issue_number: pr.number,
labels: ["release"],
})
} else {
console.log("Updating existing release PR")
const { number } = prs[0]
await octokit.rest.pulls.update({
...github.context.repo,
pull_number: number,
title,
body,
})
}
Loading

0 comments on commit 6a4babd

Please sign in to comment.