Skip to content

Commit

Permalink
feat: ✨ support two depots: stable and beta
Browse files Browse the repository at this point in the history
  • Loading branch information
meisZWFLZ authored Feb 1, 2024
1 parent f5a4ec8 commit e72dedb
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 60 deletions.
30 changes: 24 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,46 @@ inputs:
description: 'The string represetation of the repo name (eg: lemlib/lemlib)'
required: false

github_token:
token:
description: 'Your GitHub Access Token. Necessary to not get rate limited.'
required: true
default: ${{ github.token }}

branch:
description: 'The branch you want to put the depot json on.'
description: 'The branch where the stable depot json will be placed.'
required: true
default: 'depot'

pre-release-branch:
description: |
The branch of the depot where pre release versions should be placed.
If omitted, then the pre-release versions will not placed in a depot.
required: false
default: 'depot'

path:
description: 'The path to the depot json.'
description: 'The path to the stable depot json.'
required: true
default: 'depot.json'
default: 'stable.json'

pre-release-path:
description: |
The path to the depot where pre release versions should be placed.
If omitted, then the pre-release versions will not placed in a depot.
If pre-release-branch == branch AND pre-release-path == path,
then the pre-release versions will be placed in the same depot as the stable versions.
If pre-release-branch is not defined, then this input will be ignored.
required: false
default: 'beta.json'

readable-json:
description: 'Whether the depot json should be formatted to be human readable (true/false).'
required: true
default: false

messsage:
description: 'The commit message for the new commit depot json.'
message:
description: 'The commit message that will be used when updating the depot'
required: false

# Define your outputs here.
Expand Down
12 changes: 5 additions & 7 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0",
"@octokit/rest": "^20.0.2",
"adm-zip": "^0.5.10"
"adm-zip": "^0.5.10",
"semver": "^7.5.4"
},
"devDependencies": {
"@types/adm-zip": "^0.5.5",
Expand Down
124 changes: 93 additions & 31 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,116 @@
import * as core from '@actions/core'
import * as github from '@actions/github'

Check failure on line 2 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

'github' is defined but never used
import { populateDepotJsonFromGithub } from './populate'
import { populateDepotJsonsFromGithub } from './populate'
import { Octokit } from '@octokit/rest'
import { pushDepotJsonToGithub } from './pushDepot'
import { createCommitMessage } from './message'
import { DepotLocation, DepotType, RepositoryIdentifier } from './types'

const repoInputRegex = /[^\/\n\s\t]+\/[^\/\n\s\t]+/

Check failure on line 9 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Unnecessary escape character: \/

Check failure on line 9 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Unnecessary escape character: \/

function getRepositoryIdentifier(): RepositoryIdentifier {
const repo: { owner: string; repo: string } = { owner: '', repo: '' }
const repoInput = core.getInput('repo')

if (repoInput.match(repoInputRegex)) {
const parsedRepoInput = repoInput.split('/')
repo.owner = parsedRepoInput[0]
repo.repo = parsedRepoInput[1]
} else throw new Error('Invalid repository input: ' + repoInput)

Check failure on line 19 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Unexpected string concatenation
return repo
}

function getDepotLocations(
repo: RepositoryIdentifier
): Record<DepotType, RepositoryIdentifier & Partial<DepotLocation>> {
const stableBranch = core.getInput('branch')
const stablePath = core.getInput('path')
const betaBranch = core.getInput('pre-release-branch')
const betaPath = core.getInput('pre-release-path')

return {
stable: {
...repo,
branch: stableBranch,
path: stablePath
},
beta: {
...repo,
branch: betaBranch,
path: betaPath
}
}
}

function filterDepots(
locations: ReturnType<typeof getDepotLocations>,
jsons: Awaited<ReturnType<typeof populateDepotJsonsFromGithub>>
): Array<DepotLocation & { json: string }> {

Check failure on line 48 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Array type using 'Array<T>' is forbidden. Use 'T[]' instead
const depots: Array<DepotLocation & { json: string }> = []

Check failure on line 49 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Array type using 'Array<T>' is forbidden. Use 'T[]' instead
for (const rawType in locations) {
const type = rawType as DepotType
const location = locations[type]
const json = jsons[type]

if (location?.path != null && location?.branch != null && json != null) {
depots.push({
...location,
path: location.path,
branch: location.branch,
json
})
}
}

return depots
}

async function updateDepotJsons(

Check failure on line 68 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Missing return type on function
locations: ReturnType<typeof getDepotLocations>,
jsons: Awaited<ReturnType<typeof populateDepotJsonsFromGithub>>,
client: Octokit
) {
const depots = filterDepots(locations, jsons)
const messageInput = core.getInput('message')
for (const depot of depots) {
const message = createCommitMessage(depot.json, depot, client)
if (message === undefined) continue
await pushDepotJsonToGithub(
depot.json,
depot,
messageInput ?? message,
client
)
}
return depots
}

/**
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
export async function run(): Promise<void> {
try {
const repo: { owner: string; repo: string } = { owner: '', repo: '' }
const repoInput = core.getInput('repo')

if (repoInput.match(repoInputRegex)) {
const parsedRepoInput = repoInput.split('/')
repo.owner = parsedRepoInput[0]
repo.repo = parsedRepoInput[1]
} else throw new Error('Invalid repository input: ' + repoInput)
const repo = getRepositoryIdentifier()
const locations = getDepotLocations(repo)

const ghToken = core.getInput('token')
const readableFlag = core.getInput('readable') === 'true'
const ghToken = core.getInput('token')

const client = new Octokit({ auth: ghToken })

Check failure on line 101 in src/main.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Delete `···`
const unified =
locations.beta.branch === locations.stable.branch &&
locations.beta.path === locations.stable.path

const json = await populateDepotJsonFromGithub(repo, client, readableFlag)

const dest = {
...repo,
branch: core.getInput('branch'),
path: core.getInput('path')
}

const message =
core.getInput('message') ?? createCommitMessage(json, dest, client)

await pushDepotJsonToGithub(
json,
{
owner: dest.owner,
repo: dest.repo,
path: dest.path,
branch: dest.branch
},
message,
client
const jsons = await populateDepotJsonsFromGithub(
repo,
client,
readableFlag,
unified
)

updateDepotJsons(locations, jsons, client)
} catch (error) {
// Fail the workflow run if an error occurs
if (error instanceof Error) core.setFailed(error.message)
Expand Down
35 changes: 26 additions & 9 deletions src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ function jsonToDepot(json: string): Depot {
return JSON.parse(json)
}

async function getOldDepot(
async function getOldJson(
location: DepotLocation,
client: Octokit
): Promise<Depot> {
): Promise<string> {
const res = await client.repos.getContent({
...location,
ref: location.branch
})
const json = res.data.toString()
return jsonToDepot(json)
return res.data.toString()
}

function depotEntryIsEqual(temp1: DepotEntry, temp2: DepotEntry): boolean {
Expand All @@ -34,23 +33,41 @@ function depotEntryIsEqual(temp1: DepotEntry, temp2: DepotEntry): boolean {
function formatCommitMessage(message: string): string {
const gitmoji = ':bookmark:'
const automationMessage = `This commit was generated by an automated workflow: ${actionURL}`
return `${gitmoji} message \n\n${automationMessage}`
let msg = message
if (!msg.trim().startsWith(':')) msg = gitmoji + ' ' + msg

Check failure on line 37 in src/message.ts

View workflow job for this annotation

GitHub Actions / Lint Codebase

Unexpected string concatenation
msg += '\n\n'
msg += automationMessage
return msg
}

/**
*
* @param newJson
* @param location
* @param client
* @returns formatted commit message unless the json's are the same, in which case it returns undefined, indicating that no commit is necessary
*/
export async function createCommitMessage(
newJson: string,
location: DepotLocation,
client: Octokit
): Promise<string> {
return formatCommitMessage(await createRawMessage(newJson, location, client))
): Promise<string | undefined> {
const raw = await createRawMessage(newJson, location, client)
if (raw === undefined) return raw
else return formatCommitMessage(raw)
}

async function createRawMessage(
newJson: string,
location: DepotLocation,
client: Octokit
): Promise<string> {
const oldDepot = await getOldDepot(location, client)
): Promise<string | undefined> {
const oldJson = await getOldJson(location, client)

if (oldJson === newJson) return undefined
if (oldJson === '') return `:tada: Create Depot: ${location.path}`

const oldDepot = jsonToDepot(oldJson)
const newDepot = jsonToDepot(newJson)

const changedTemplates: DepotEntry[] = []
Expand Down
36 changes: 30 additions & 6 deletions src/populate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'
import AdmZip from 'adm-zip'
import { DepotEntry } from './types'
import { Depot, DepotEntry, DepotType } from './types'
import semver from 'semver'

interface TemplateDetails {
name: string
Expand Down Expand Up @@ -77,21 +78,27 @@ function createDepotEntry({
version
}
}
function stringifyDepot(depot: Depot, readable: boolean): string {
return JSON.stringify(depot, null, readable ? 2 : undefined)
}

/**
* Creates a JSON string by populating the depot with templates from a GitHub repository.
* @param repoId The repository to populate the depot from.
* @param client The client to use for GitHub API requests.
* @param readable Whether to format the JSON string for human readability.
* @param unified Whether the beta and stable versions should be contained in a single depot.
* @returns
*/
export async function populateDepotJsonFromGithub(
export async function populateDepotJsonsFromGithub(
repoId: {
owner: string
repo: string
},
client: Octokit = new Octokit(),
readable: boolean = true
): Promise<string> {
readable: boolean = true,
unified: boolean = false
): Promise<Record<'stable', string> & Partial<Record<DepotType, string>>> {
const rawReleases = await client.repos.listReleases(repoId)

const templatePromises: Promise<TemplateDetails | null>[] =
Expand All @@ -111,6 +118,23 @@ export async function populateDepotJsonFromGithub(
)

const depotEntries = templates.map(createDepotEntry)
const depotJson = JSON.stringify(depotEntries, null, readable ? 2 : undefined)
return depotJson
if (unified) return { stable: stringifyDepot(depotEntries, readable) }

const stableEntries = []
const betaEntries = []

for (const entry of depotEntries) {
if (semver.parse(entry.version)?.prerelease.length ?? 0 > 0) {
betaEntries.push(entry)
} else {
stableEntries.push(entry)
}
}

const stableJson = stringifyDepot(stableEntries, readable)
const betaJson = stringifyDepot(betaEntries, readable)
return {
stable: stableJson,
beta: betaJson
}
}
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ export interface DepotEntry {
}

export type Depot = DepotEntry[]

export type DepotType = "stable" | "beta";

0 comments on commit e72dedb

Please sign in to comment.