-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into henrymorales/c#
- Loading branch information
Showing
192 changed files
with
13,837 additions
and
3,800 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Add Label Artifact | ||
description: Uploads an empty artifact named `label-${name}=${value}`, that's consumed by action "update-labels" | ||
|
||
inputs: | ||
name: | ||
description: Name | ||
required: true | ||
value: | ||
description: Value ("true" or "false") | ||
required: true | ||
|
||
runs: | ||
using: composite | ||
|
||
steps: | ||
- name: Create empty file to upload artifact | ||
run: "> $RUNNER_TEMP/empty.txt" | ||
shell: bash | ||
|
||
# The maximum length is reported to be 260 characters. A full list of invalid artifact name characters is documented here: | ||
# https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/upload/path-and-artifact-name-validation.ts | ||
- uses: actions/upload-artifact@v4 | ||
with: | ||
name: label-${{ inputs.name }}=${{ inputs.value }} | ||
path: ${{ runner.temp }}/empty.txt | ||
if-no-files-found: error | ||
overwrite: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// @ts-check | ||
|
||
/** | ||
* Extracts inputs from context based on event name and properties. | ||
* run_id is only defined for "workflow_run:completed" events. | ||
* | ||
* @param {import('github-script').AsyncFunctionArguments['github']} github | ||
* @param {import('github-script').AsyncFunctionArguments['context']} context | ||
* @param {import('github-script').AsyncFunctionArguments['core']} core | ||
* @returns {Promise<{owner: string, repo: string, head_sha: string, issue_number: number, run_id: number }>} | ||
*/ | ||
async function extractInputs(github, context, core) { | ||
core.info(`extractInputs(${context.eventName}, ${context.payload.action})`); | ||
|
||
// Add support for more event types as needed | ||
if (context.eventName === "pull_request") { | ||
const payload = | ||
/** @type {import("@octokit/webhooks-types").PullRequestEvent} */ ( | ||
context.payload | ||
); | ||
|
||
const inputs = { | ||
owner: payload.repository.owner.login, | ||
repo: payload.repository.name, | ||
head_sha: payload.pull_request.head.sha, | ||
issue_number: payload.number, | ||
run_id: NaN | ||
}; | ||
|
||
core.info(`inputs: ${JSON.stringify(inputs)}`); | ||
|
||
return inputs; | ||
} else if ( | ||
context.eventName === "workflow_run" && | ||
context.payload.action === "completed" | ||
) { | ||
const payload = | ||
/** @type {import("@octokit/webhooks-types").WorkflowRunCompletedEvent} */ ( | ||
context.payload | ||
); | ||
|
||
let issue_number; | ||
|
||
const pull_requests = payload.workflow_run.pull_requests; | ||
if (pull_requests && pull_requests.length > 0) { | ||
// For non-fork PRs, we should be able to extract the PR number from the payload, which avoids an | ||
// unnecessary API call. The listPullRequestsAssociatedWithCommit() API also seems to return | ||
// empty for non-fork PRs. | ||
issue_number = pull_requests[0].number; | ||
} else { | ||
// For fork PRs, we must call an API in the head repository to get the PR number in the target repository | ||
|
||
// Owner and repo for the PR head (may differ from target for fork PRs) | ||
const head_owner = payload.workflow_run.head_repository.owner.login; | ||
const head_repo = payload.workflow_run.head_repository.name; | ||
const head_sha = payload.workflow_run.head_sha; | ||
|
||
core.info( | ||
`listPullRequestsAssociatedWithCommit(${head_owner}, ${head_repo}, ${head_sha})`, | ||
); | ||
const { data: pullRequests } = | ||
await github.rest.repos.listPullRequestsAssociatedWithCommit({ | ||
owner: head_owner, | ||
repo: head_repo, | ||
commit_sha: head_sha, | ||
}); | ||
|
||
if (pullRequests.length === 1) { | ||
issue_number = pullRequests[0].number; | ||
} else { | ||
throw new Error( | ||
`Unexpected number of pull requests associated with commit '${head_sha}'. Expected: '1'. Actual '${pullRequests.length}'.`, | ||
); | ||
} | ||
} | ||
|
||
const inputs = { | ||
owner: payload.workflow_run.repository.owner.login, | ||
repo: payload.workflow_run.repository.name, | ||
head_sha: payload.workflow_run.head_sha, | ||
issue_number: issue_number, | ||
run_id: payload.workflow_run.id, | ||
}; | ||
|
||
core.info(`inputs: ${JSON.stringify(inputs)}`); | ||
|
||
return inputs; | ||
} else { | ||
throw new Error( | ||
`Invalid context: '${context.eventName}:${context.payload.action}'. Expected 'workflow_run:completed'.`, | ||
); | ||
} | ||
} | ||
|
||
module.exports = { extractInputs }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// @ts-check | ||
|
||
const { extractInputs } = require("../context"); | ||
|
||
/** | ||
* @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments | ||
*/ | ||
module.exports = async ({ github, context, core }) => { | ||
let owner = process.env.OWNER; | ||
let repo = process.env.REPO; | ||
let issue_number = parseInt(process.env.ISSUE_NUMBER || ""); | ||
let run_id = parseInt(process.env.RUN_ID || ""); | ||
|
||
if (!owner || !repo || !(issue_number || run_id)) { | ||
let inputs = await extractInputs(github, context, core); | ||
owner = owner || inputs.owner; | ||
repo = repo || inputs.repo; | ||
issue_number = issue_number || inputs.issue_number; | ||
run_id = run_id || inputs.run_id; | ||
} | ||
|
||
/** @type {string[]} */ | ||
let artifactNames = []; | ||
|
||
if (run_id) { | ||
// List artifacts from a single run_id | ||
core.info(`listWorkflowRunArtifacts(${owner}, ${repo}, ${run_id})`); | ||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | ||
owner: owner, | ||
repo: repo, | ||
run_id: run_id, | ||
}); | ||
|
||
artifactNames = artifacts.data.artifacts.map((a) => a.name); | ||
} else { | ||
// TODO: List all artifacts of all workflows associated with issue_number | ||
throw new Error("Required input 'run_id' not found in env or context"); | ||
} | ||
|
||
core.info(`artifactNames: ${JSON.stringify(artifactNames)}`); | ||
|
||
/** @type {string[]} */ | ||
const labelsToAdd = []; | ||
|
||
/** @type {string[]} */ | ||
const labelsToRemove = []; | ||
|
||
for (const artifactName of artifactNames) { | ||
// If artifactName has format "label-name=true|false", add or remove the label | ||
// Else, if artifactName has format "label-name=other-string", throw an error | ||
// Else, if artifactName does not start with "label-", ignore it | ||
const firstEquals = artifactName.indexOf("="); | ||
if (firstEquals !== -1) { | ||
const key = artifactName.substring(0, firstEquals); | ||
const value = artifactName.substring(firstEquals + 1); | ||
|
||
if (key.startsWith("label-")) { | ||
const name = key.substring("label-".length); | ||
if (value === "true") { | ||
labelsToAdd.push(name); | ||
} else if (value === "false") { | ||
labelsToRemove.push(name); | ||
} else { | ||
throw new Error( | ||
`Invalid value for label '${name}': ${value}. Expected "true" or "false".`, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
core.info(`labelsToAdd: ${JSON.stringify(labelsToAdd)}`); | ||
core.info(`labelsToRemove: ${JSON.stringify(labelsToRemove)}`); | ||
|
||
if (labelsToAdd.length > 0) { | ||
await github.rest.issues.addLabels({ | ||
owner: owner, | ||
repo: repo, | ||
issue_number: issue_number, | ||
labels: labelsToAdd, | ||
}); | ||
} | ||
|
||
if (labelsToRemove.length > 0) { | ||
// Must loop over labelsToRemove ourselves, since GitHub doesn't expose a REST API to remove in bulk. | ||
for (const name of labelsToRemove) { | ||
try { | ||
await github.rest.issues.removeLabel({ | ||
owner: owner, | ||
repo: repo, | ||
issue_number: issue_number, | ||
name: name, | ||
}); | ||
} catch (error) { | ||
if (error.status === 404) { | ||
core.info(`Ignoring error: ${error.status} - ${error.message}`); | ||
} else { | ||
throw error; | ||
} | ||
} | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
name: Update Labels | ||
description: Adds or removes labels to set state matching artifact names | ||
|
||
# If any inputs are not set, we will attempt to extract them from the event context | ||
inputs: | ||
owner: | ||
description: The account owner of the repository. The name is not case sensitive. | ||
required: false | ||
repo: | ||
description: The name of the repository without the .git extension. The name is not case sensitive. | ||
required: false | ||
issue_number: | ||
description: The issue that should have its labels updated. | ||
required: false | ||
run_id: | ||
description: Updates labels from a single completed workflow. | ||
required: false | ||
|
||
runs: | ||
using: composite | ||
|
||
steps: | ||
- name: Set Label | ||
uses: actions/github-script@v7 | ||
env: | ||
OWNER: ${{ inputs.owner }} | ||
REPO: ${{ inputs.repo }} | ||
ISSUE_NUMBER: ${{ inputs.issue_number }} | ||
RUN_ID: ${{ inputs.run_id }} | ||
with: | ||
script: | | ||
const action = require('./.github/actions/update-labels/action.js') | ||
await action({ github, context, core }); |
Oops, something went wrong.