ray-project/ray
4 workflows · maturity 0% · 0 patterns · GitHub ↗
Practices
○ Matrix○ Permissions○ Security scan○ AI review○ Cache○ Concurrency○ Reusable workflows
Security dimensions
Workflows (4)
notify-slack-on-label .github/workflows/notify-slack-on-label.yml
View raw YAML
name: Notify Slack on Labeled Issue or PR
on:
issues:
types: [labeled]
pull_request:
types: [labeled]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Notify Slack Channel Based on Label
uses: actions/github-script@v7
with:
script: |
// ── Label → Slack webhook secret name mapping ──────────────────────
// Add or remove entries here as you add/remove Ray library channels.
// The key is the GitHub label name; the value is the name of the
// GitHub Actions secret that holds that channel's Incoming Webhook URL.
const LABEL_TO_SECRET = {
"serve": "SLACK_WEBHOOK_SERVE",
"train": "SLACK_WEBHOOK_TRAIN",
"tune": "SLACK_WEBHOOK_TUNE",
"rllib": "SLACK_WEBHOOK_RLLIB",
"data": "SLACK_WEBHOOK_DATA",
"air": "SLACK_WEBHOOK_AIR",
"core": "SLACK_WEBHOOK_CORE",
"workflow": "SLACK_WEBHOOK_WORKFLOW",
"cluster": "SLACK_WEBHOOK_CLUSTER",
};
// ── Resolve the webhook URLs from secrets ──────────────────────────
// We pass secrets in as env vars so the script can read them safely.
const SECRET_VALUES = {
"SLACK_WEBHOOK_SERVE": process.env.SLACK_WEBHOOK_SERVE,
"SLACK_WEBHOOK_TRAIN": process.env.SLACK_WEBHOOK_TRAIN,
"SLACK_WEBHOOK_TUNE": process.env.SLACK_WEBHOOK_TUNE,
"SLACK_WEBHOOK_RLLIB": process.env.SLACK_WEBHOOK_RLLIB,
"SLACK_WEBHOOK_DATA": process.env.SLACK_WEBHOOK_DATA,
"SLACK_WEBHOOK_AIR": process.env.SLACK_WEBHOOK_AIR,
"SLACK_WEBHOOK_CORE": process.env.SLACK_WEBHOOK_CORE,
"SLACK_WEBHOOK_WORKFLOW": process.env.SLACK_WEBHOOK_WORKFLOW,
"SLACK_WEBHOOK_CLUSTER": process.env.SLACK_WEBHOOK_CLUSTER,
};
// ── Determine event type and payload ──────────────────────────────
const isPR = !!context.payload.pull_request;
const item = isPR ? context.payload.pull_request : context.payload.issue;
const type = isPR ? "Pull Request" : "Issue";
const action = context.payload.action; // "opened" | "labeled"
const labels = (item.labels || []).map(l => l.name);
// On "labeled" events, only act on the label that was just added
// (avoids duplicate messages when multiple labels already exist).
const triggeredLabel = action === "labeled"
? context.payload.label.name
: null;
const labelsToProcess = triggeredLabel ? [triggeredLabel] : labels;
// ── Send a message for each matching label ─────────────────────────
for (const label of labelsToProcess) {
const secretName = LABEL_TO_SECRET[label];
if (!secretName) continue; // label not mapped
const webhookUrl = SECRET_VALUES[secretName];
if (!webhookUrl) {
core.warning(`Secret ${secretName} is not set — skipping #${label}`);
continue;
}
// Build a rich Slack Block Kit message
const allLabelNames = labels.map(l => `\`${l}\``).join(" ");
const body = {
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `*New ${type} in <https://github.com/${context.repo.owner}/${context.repo.repo}|${context.repo.owner}/${context.repo.repo}>*`
}
},
{
type: "section",
fields: [
{ type: "mrkdwn", text: `*Title*\n<${item.html_url}|${item.title}>` },
{ type: "mrkdwn", text: `*Author*\n<${item.user.html_url}|${item.user.login}>` },
{ type: "mrkdwn", text: `*Labels*\n${allLabelNames || "_none_"}` },
{ type: "mrkdwn", text: `*#*\n${item.number}` },
]
},
...(item.body ? [{
type: "section",
text: {
type: "mrkdwn",
// Truncate long bodies so the message stays readable
text: `*Description*\n${item.body.slice(0, 300)}${item.body.length > 300 ? "…" : ""}`
}
}] : []),
{
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: `View ${type}` },
url: item.html_url,
style: "primary"
}
]
}
]
};
const response = await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
if (!response.ok) {
core.error(`Failed to post to Slack for label "${label}": ${response.status} ${response.statusText}`);
} else {
core.info(`Posted to Slack channel for label "${label}"`);
}
}
env:
SLACK_WEBHOOK_SERVE: ${{ secrets.SLACK_WEBHOOK_SERVE }}
SLACK_WEBHOOK_TRAIN: ${{ secrets.SLACK_WEBHOOK_TRAIN }}
SLACK_WEBHOOK_TUNE: ${{ secrets.SLACK_WEBHOOK_TUNE }}
SLACK_WEBHOOK_RLLIB: ${{ secrets.SLACK_WEBHOOK_RLLIB }}
SLACK_WEBHOOK_DATA: ${{ secrets.SLACK_WEBHOOK_DATA }}
SLACK_WEBHOOK_AIR: ${{ secrets.SLACK_WEBHOOK_AIR }}
SLACK_WEBHOOK_CORE: ${{ secrets.SLACK_WEBHOOK_CORE }}
SLACK_WEBHOOK_WORKFLOW: ${{ secrets.SLACK_WEBHOOK_WORKFLOW }}
SLACK_WEBHOOK_CLUSTER: ${{ secrets.SLACK_WEBHOOK_CLUSTER }}
on_auto_merge .github/workflows/on_auto_merge.yaml
View raw YAML
name: Auto merge enabled
on:
pull_request_target:
types:
- auto_merge_enabled
jobs:
add-go-label:
runs-on: ubuntu-latest
steps:
- name: Add go label
uses: actions/github-script@v5
with:
script: |
github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['go']
})
on_pull_request_synchronized .github/workflows/on_pull_request_synchronized.yml
View raw YAML
name: Pull request synchronized
on:
pull_request_target:
types:
- synchronize
branches:
- "master"
- "releases/**"
jobs:
disable-automerge:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const prQuery = `query PullRequest($owner: String!, $repo: String!, $pullRequestNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pullRequestNumber) {
id
autoMergeRequest {
enabledAt
}
}
}
}`;
const prVariables = {
owner: context.repo.owner,
repo: context.repo.repo,
pullRequestNumber: context.issue.number
}
const prResult = await github.graphql(prQuery, prVariables)
if (!prResult.repository.pullRequest.autoMergeRequest) {
console.log('Auto merge is not enabled')
return
}
const automergeQuery = `mutation DisablePullRequestAutoMerge($pullRequestId: ID!) {
disablePullRequestAutoMerge(input: {pullRequestId: $pullRequestId}) {
pullRequest {
id
}
}
}`;
const automergeVariables = {
pullRequestId: prResult.repository.pullRequest.id
}
const result = await github.graphql(automergeQuery, automergeVariables)
console.log(result)
stale_pull_request .github/workflows/stale_pull_request.yaml
View raw YAML
name: Mark and Close Stale Pull Requests
on:
schedule:
# Runs twice a day at 15 minutes past midnight and noon UTC
- cron: '15 */12 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
pull-requests: write # Required to add labels, comment, close PRs.
steps:
- name: Mark and Close Stale Pull Requests
uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# --- Ignore Issues ---
# Set to -1 so we never mark issues as stale or closed.
days-before-issue-stale: -1
days-before-issue-close: -1
# --- Pull Request Specific Settings ---
# Number of days of inactivity before a Pull Request becomes stale (currently 2 weeks)
days-before-pr-stale: 14
# Number of days of inactivity after being marked stale before a Pull Request is closed (2 more weeks)
days-before-pr-close: 14
# Label to use when marking a PR as stale
stale-pr-label: 'stale'
# Comment to post when marking a PR as stale
stale-pr-message: |
This pull request has been automatically marked as stale because it has not had
any activity for 14 days. It will be closed in another 14 days if no further activity occurs.
Thank you for your contributions.
You can always ask for help on our [discussion forum](https://discuss.ray.io/) or [Ray's public slack channel](https://github.com/ray-project/ray#getting-involved).
If you'd like to keep this open, just leave any comment, and the stale label will be removed.
# Comment to post when closing a stale PR
close-pr-message: |
This pull request has been automatically closed because there has been no more activity in the 14 days
since being marked stale.
Please feel free to reopen or open a new pull request if you'd still like this to be addressed.
Again, you can always ask for help on our [discussion forum](https://discuss.ray.io) or [Ray's public slack channel](https://github.com/ray-project/ray#getting-involved).
Thanks again for your contribution!
# Pull Requests with these labels will never be considered stale
exempt-pr-labels: >
weekly-release-blocker,
release-blocker,
unstale
# Set to true to ignore PRs in a milestone (defaults to false)
exempt-all-pr-milestones: true
# --- Shared Settings & Other Options ---
# Limit the number of actions per run.
operations-per-run: 500
# Remove stale label from PRs on update (default is true)
remove-pr-stale-when-updated: true
# Add unstale label. Whenever a PR is marked as 'unstale' it will not be marked stale again.
labels-to-add-when-unstale: unstale
ascending: true