Merge branch 'kubeflow:main' into issue591_ilya

This commit is contained in:
ishavkon 2025-09-18 18:09:24 +03:00 committed by GitHub
commit 8292ba1f4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 566 additions and 2 deletions

View File

@ -1,4 +1,12 @@
blank_issues_enabled: true
issue_templates:
- bug_report.yml
- feature_request.yml
- planning_epic.yml
- planning_feature.yml
- planning_task.yml
contact_links:
- name: Kubeflow Documentation
url: https://www.kubeflow.org/

View File

@ -0,0 +1,27 @@
name: "[Planning] Epic"
description: "🛑 Only intended to be used by Kubeflow project managers"
title: "[EPIC] <short description>"
labels:
- "kind/plan-epic"
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to create an epic!
- type: checkboxes
id: certification
attributes:
label: Certification
description: Please confirm your role
options:
- label: I certify I am an Epic Owner for Kubeflow Notebooks 2.0 and expected to create planning-related issues.
required: true
- type: textarea
id: user-story
attributes:
label: User Story
description: Describe the feature from the end user's perspective, including who they are, what they want to achieve, and why it's important.
placeholder: As a [type of user], I want [goal] so that [benefit]
validations:
required: true

View File

@ -0,0 +1,48 @@
name: "[Planning] Feature"
description: "🛑 Only intended to be used by Kubeflow project managers"
title: "[FEATURE] <short description>"
labels:
- "kind/plan-feature"
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to create a feature request!
- type: checkboxes
id: certification
attributes:
label: Certification
description: Please confirm your role
options:
- label: I certify I am an Epic Owner for Kubeflow Notebooks 2.0 and expected to create planning-related issues.
required: true
- type: textarea
id: motivation
attributes:
label: Motivation
description: Explain the reasoning behind this feature. What problem does it solve or what value does it add?
placeholder: This feature will help users by...
validations:
required: true
- type: textarea
id: design
attributes:
label: High Level Design / Mock-ups
description: Provide a top-level overview of the proposed solution. Include diagrams, rough mockups, or architectural notes if helpful.
placeholder: |
[Add your design details here]
- Consider including diagrams
- Add mockups if available
- Describe the architecture
- type: textarea
id: acceptance-criteria
attributes:
label: Acceptance Criteria
description: Define what needs to be true for this feature to be considered complete. Be as clear and measurable as possible.
placeholder: |
- [ ] Criterion 1
- [ ] Criterion 2
- [ ] Criterion 3
validations:
required: true

View File

@ -0,0 +1,38 @@
name: "[Planning] Task"
description: "🛑 Only intended to be used by Kubeflow project managers"
title: "[TASK] <short description>"
labels:
- "kind/plan-task"
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to create a task!
- type: checkboxes
id: certification
attributes:
label: Certification
description: Please confirm your role
options:
- label: I certify I am an Epic Owner for Kubeflow Notebooks 2.0 and expected to create planning-related issues.
required: true
- type: textarea
id: description
attributes:
label: Description
description: Provide a brief explanation of what this task involves and why it's needed. Focus on the scope and intent of the work.
placeholder: This task will...
validations:
required: true
- type: textarea
id: acceptance-criteria
attributes:
label: Acceptance Criteria
description: List the specific, measurable conditions that must be met for the task to be considered complete.
placeholder: |
- [ ] Criterion 1
- [ ] Criterion 2
- [ ] Criterion 3
validations:
required: true

243
.github/workflows/slash-commands.yaml vendored Normal file
View File

@ -0,0 +1,243 @@
name: Slash Command Handler
on:
issue_comment:
types: [created]
permissions:
issues: write
jobs:
handle-slash-command:
if: |
github.event.issue.pull_request == null
&& contains('["thesuperzapper", "ederign", "andyatmiami", "paulovmr", "jenny-s51", "harshad16", "thaorell", "kimwnasptd", "liavweiss"]', github.event.comment.user.login)
&& (
contains(github.event.comment.body, '/add-sub-issue')
|| contains(github.event.comment.body, '/remove-sub-issue')
)
runs-on: ubuntu-latest
steps:
- name: Handle slash commands
id: handle-commands
uses: actions/github-script@v7
with:
script: |
const parseIssueNumber = (input) => {
if (!input) return null;
// Handle plain number
if (/^\d+$/.test(input)) {
return input;
}
// Handle #number format
const hashMatch = input.match(/^#(\d+)$/);
if (hashMatch) {
return hashMatch[1];
}
// Handle URL format
const urlMatch = input.match(/\/issues\/(\d+)$/);
if (urlMatch) {
return urlMatch[1];
}
throw new Error(`Could not parse issue number from input: '${input}'`);
};
const getIssueNodeId = async (owner, repo, issueNumber) => {
const response = await github.graphql(`
query {
repository(owner: "${owner}", name: "${repo}") {
issue(number: ${issueNumber}) {
id
title
}
}
}
`);
return {
id: response.repository.issue.id,
title: response.repository.issue.title
};
};
const performSubIssueMutation = async (action, parentIssueNodeId, childIssueNodeId) => {
const mutationField = `${action}SubIssue`;
const mutation = `
mutation {
${mutationField}(input: {
issueId: "${parentIssueNodeId}",
subIssueId: "${childIssueNodeId}"
}) {
clientMutationId
issue {
id
title
}
subIssue {
id
title
}
}
}
`;
try {
const response = await github.graphql(mutation);
return response;
} catch (error) {
throw new Error(error.message);
}
};
const collectSubIssueOperations = async (line, action, owner, repo) => {
const commandPrefix = `/${action}-sub-issue`;
if (!line.startsWith(commandPrefix)) return [];
const args = line.replace(commandPrefix, '').trim().split(/\s+/);
const operations = [];
for (const issue of args) {
const childIssueNumber = parseIssueNumber(issue);
const childIssue = await getIssueNodeId(owner, repo, childIssueNumber);
operations.push({
action,
issueNumber: childIssueNumber,
title: childIssue.title,
nodeId: childIssue.id
});
}
return operations;
};
const formatOperationsList = (operations, action) => {
if (operations.length === 0) return [];
return [
`### ${action} Sub-issues:`,
...operations.map(op => `- #${op.issueNumber}`),
''
];
};
try {
const { owner, repo } = context.repo;
const parentIssueNumber = context.payload.issue.number;
const commentBody = context.payload.comment.body;
// Get parent issue node ID and title
const parentIssue = await getIssueNodeId(owner, repo, parentIssueNumber);
// Collect all operations first
const lines = commentBody.split('\n');
const operations = [];
for (const line of lines) {
operations.push(...await collectSubIssueOperations(line, 'add', owner, repo));
operations.push(...await collectSubIssueOperations(line, 'remove', owner, repo));
}
if (operations.length === 0) {
return; // No valid operations found
}
// Create preview comment
const previewBodyParts = [
':mag: **Sub-issue Operation Preview**',
'',
`The following operations will be performed on issue #${parentIssueNumber} (${parentIssue.title}) at the request of @${context.payload.comment.user.login}:`,
''
];
// Group operations by action for display
const addOperations = operations.filter(op => op.action === 'add');
const removeOperations = operations.filter(op => op.action === 'remove');
previewBodyParts.push(
...formatOperationsList(addOperations, 'Adding'),
...formatOperationsList(removeOperations, 'Removing')
);
previewBodyParts.push('_This is a preview of the changes. The actual operations will be executed in the background._');
// Post preview comment
const previewComment = await github.rest.issues.createComment({
owner,
repo,
issue_number: parentIssueNumber,
body: previewBodyParts.join('\n')
});
// Execute operations in original order
for (const op of operations) {
await performSubIssueMutation(op.action, parentIssue.id, op.nodeId);
}
// Post success comment
await github.rest.issues.createComment({
owner,
repo,
issue_number: parentIssueNumber,
body: [
':white_check_mark: **GitHub Action Succeeded**',
'',
`All [sub-issue operations](${previewComment.data.html_url}) requested by @${context.payload.comment.user.login} have been completed successfully.`,
''
].join('\n')
});
} catch (error) {
core.setOutput('error_message', error.message);
core.setFailed(error.message);
}
- name: Post error comment if failure
if: failure()
uses: actions/github-script@v7
with:
script: |
try {
const commentUrl = context.payload.comment.html_url;
const runId = context.runId;
const { owner, repo } = context.repo;
const errorMessage = `${{ steps.handle-commands.outputs.error_message }}`;
const errorBodyParts = [
':x: **GitHub Action Failed**',
'',
`The workflow encountered an error while processing [your comment](${commentUrl}) to manage sub-issues.`,
'',
`:point_right: [View the run](https://github.com/${owner}/${repo}/actions/runs/${runId})`,
''
];
if (errorMessage && errorMessage !== '') {
errorBodyParts.push(
'<details>',
'<summary>Error details</summary>',
'',
'```',
errorMessage,
'```',
'',
'</details>',
''
);
}
errorBodyParts.push('Please check the logs and try again, or open a bug report if the issue persists.');
await github.rest.issues.createComment({
owner,
repo,
issue_number: context.payload.issue.number,
body: errorBodyParts.join('\n')
});
} catch (error) {
core.setFailed(`Failed to post error comment: ${error.message}`);
}

View File

@ -0,0 +1,126 @@
name: Validate Planning Issue
on:
issues:
types: [opened, labeled]
env:
AUTHORIZED_USERS: '["thesuperzapper", "ederign", "andyatmiami", "paulovmr", "jenny-s51", "harshad16", "thaorell", "kimwnasptd", "liavweiss"]'
permissions:
issues: write
jobs:
validate-issue:
if: |
(github.event.action == 'labeled' && (github.event.label.name == 'kind/plan-epic' || github.event.label.name == 'kind/plan-feature' || github.event.label.name == 'kind/plan-task')) ||
(github.event.action == 'opened' && (contains(github.event.issue.labels.*.name, 'kind/plan-epic') || contains(github.event.issue.labels.*.name, 'kind/plan-feature') || contains(github.event.issue.labels.*.name, 'kind/plan-task')))
runs-on: ubuntu-latest
steps:
- name: Log trigger
uses: actions/github-script@v7
with:
script: |
console.log(`Action triggered by: ${context.eventName} event with action: ${context.payload.action}`);
if (context.payload.action === 'labeled') {
console.log(`Label added: ${context.payload.label.name}`);
} else if (context.payload.action === 'opened') {
console.log(`Issue opened with labels: ${context.payload.issue.labels.map(l => l.name).join(', ')}`);
}
- name: Handle labeled action
if: github.event.action == 'labeled'
uses: actions/github-script@v7
with:
script: |
const AUTHORIZED_USERS = JSON.parse(process.env.AUTHORIZED_USERS);
const actor = context.actor;
const issueNumber = context.issue.number;
const addedLabel = context.payload.label.name;
// First check user authorization
if (!AUTHORIZED_USERS.includes(actor)) {
// Remove the planning label
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: addedLabel
});
// Add a comment explaining why the label was removed
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: `@${actor} You are not authorized to add planning labels. Only authorized users can add planning labels to issues.`
});
core.setFailed(`User ${actor} is not authorized to add planning labels`);
}
console.log(`User ${actor} is authorized to add planning labels`);
// Then check planning label requirements
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber
});
const currentLabels = issue.labels.map(label => label.name);
const planningLabels = ['kind/plan-epic', 'kind/plan-feature', 'kind/plan-task'];
const presentPlanningLabels = currentLabels.filter(label => planningLabels.includes(label));
if (presentPlanningLabels.length !== 1) {
// Remove the planning label
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: addedLabel
});
// Add a comment explaining why the label was removed
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: `@${actor} Planning labels require exactly one planning label to be present: ${planningLabels.join(', ')}. Please ensure only one planning label is applied.`
});
core.setFailed(`Planning labels require exactly one of: ${planningLabels.join(', ')}`);
}
console.log(`Issue has valid planning label: ${presentPlanningLabels[0]}`);
- name: Handle opened action
if: github.event.action == 'opened'
uses: actions/github-script@v7
with:
script: |
const AUTHORIZED_USERS = JSON.parse(process.env.AUTHORIZED_USERS);
const actor = context.actor;
const issueNumber = context.issue.number;
if (!AUTHORIZED_USERS.includes(actor)) {
// Add a comment explaining why the issue will be closed
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: `@${actor} You are not authorized to create planning issues. Only authorized users can create planning issues.`
});
// Close the issue
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
state: 'closed'
});
core.setFailed(`User ${actor} is not authorized to create planning issues`);
}
console.log(`User ${actor} is authorized to create planning issues`);

9
ADOPTERS.md Normal file
View File

@ -0,0 +1,9 @@
# Adopters of Kubeflow Notebooks
Below are the adopters of the Notebooks project. If you are using Notebooks
please add yourself into the following list by a pull request.
Please keep the list in alphabetical order.
| Organization | Contact | Description of Use |
|--------------------------------------------------|-------------------------------------------------------------------|--------------------------------------------------------|
| [Red Hat](https://www.redhat.com/) | [@franciscojavierarceo](https://github.com/franciscojavierarceo) | Kubeflow Notebooks is part of Red Hat OpenShift AI. |

View File

@ -1,12 +1,13 @@
# Kubeflow Notebooks
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9942/badge)](https://www.bestpractices.dev/projects/9942)
[Kubeflow Notebooks](https://www.kubeflow.org/docs/components/notebooks/overview/) lets you run web-based development environments on your Kubernetes cluster by running them inside Pods.
> ⚠️ __Note__ ⚠️
>
>
> We are currently moving the _Kubeflow Notebooks 1.0_ codebase from [`kubeflow/kubeflow`](https://github.com/kubeflow/kubeflow) to this repository ([`kubeflow/notebooks`](https://github.com/kubeflow/notebooks)).
> Please see [`kubeflow/kubeflow#7549`](https://github.com/kubeflow/kubeflow/issues/7549) for more information.
>
>
> We are currently developing _Kubeflow Notebooks 2.0_ in this repository under the [`notebooks-v2`](https://github.com/kubeflow/notebooks/tree/notebooks-v2) branch.
> Please see [`kubeflow/notebooks#85`](https://github.com/kubeflow/notebooks/issues/85) for more information.

64
SECURITY.md Normal file
View File

@ -0,0 +1,64 @@
# Security Policy
## Supported Versions
Kubeflow Notebooks versions are expressed as `vX.Y.Z`, where X is the major version,
Y is the minor version, and Z is the patch version, following the
[Semantic Versioning](https://semver.org/) terminology.
The Kubeflow Notebooks project maintains release branches for the most recent two minor releases.
Applicable fixes, including security fixes, may be backported to those two release branches,
depending on severity and feasibility.
Users are encouraged to stay updated with the latest releases to benefit from security patches and
improvements.
## Reporting a Vulnerability
We're extremely grateful for security researchers and users that report vulnerabilities to the
Kubeflow Open Source Community. All reports are thoroughly investigated by Kubeflow projects owners.
You can use the following ways to report security vulnerabilities privately:
- Using the Kubeflow Notebooks repository [GitHub Security Advisory](https://github.com/kubeflow/notebooks/security/advisories/new).
- Using our private Kubeflow Steering Committee mailing list: ksc@kubeflow.org.
Please provide detailed information to help us understand and address the issue promptly.
## Disclosure Process
**Acknowledgment**: We will acknowledge receipt of your report within 10 business days.
**Assessment**: The Kubeflow projects owners will investigate the reported issue to determine its
validity and severity.
**Resolution**: If the issue is confirmed, we will work on a fix and prepare a release.
**Notification**: Once a fix is available, we will notify the reporter and coordinate a public
disclosure.
**Public Disclosure**: Details of the vulnerability and the fix will be published in the project's
release notes and communicated through appropriate channels.
## Prevention Mechanisms
Kubeflow Notebooks employs several measures to prevent security issues:
**Code Reviews**: All code changes are reviewed by maintainers to ensure code quality and security.
**Dependency Management**: Regular updates and monitoring of dependencies (e.g. Dependabot) to
address known vulnerabilities.
**Continuous Integration**: Automated testing and security checks are integrated into the CI/CD pipeline.
**Image Scanning**: Container images are scanned for vulnerabilities.
## Communication Channels
For the general questions please join the following resources:
- Kubeflow [Slack channels](https://www.kubeflow.org/docs/about/community/#kubeflow-slack-channels).
- Kubeflow discuss [mailing list](https://www.kubeflow.org/docs/about/community/#kubeflow-mailing-list).
Please **do not report** security vulnerabilities through public channels.