Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7f8de78
ci: add NBC docker publish workflow (#650)
noalimoy Oct 31, 2025
da56293
ci: migrate pvcviewer controller publish action (#622)
noalimoy Nov 6, 2025
3090125
ci: add example notebooks server images publish workflows (#640)
liavweiss Nov 6, 2025
4da10f0
refactor: move common component into each controller (#702)
yehudit1987 Nov 6, 2025
5d8f29c
chore: rename go package paths to kubeflow/notebooks (#708)
asaadbalum Nov 27, 2025
8aa503f
chore: upgrade to python 3.12 for crud-web-apps (#757)
asaadbalum Nov 27, 2025
71c3846
chore: upgrade to go 1.24 for pvcviewer-controller (#734)
henschwartz Dec 4, 2025
40a0ecd
chore: upgrade to go 1.24 for notebook-controller (#748)
abdallahsamabd Dec 5, 2025
ef46e96
chore: update node to 24 for crud-web-apps (#749)
yehudit1987 Dec 5, 2025
44c97f1
chore: upgrade to go 1.24 for tensorboard-controller (#755)
mkoushni Dec 5, 2025
a531826
chore: update v1 readme + disallow semantic scopes (#773)
thesuperzapper Dec 5, 2025
ec9ecc4
fix: update jsonpath-plus to 10.3.0 across CRUD web apps (Security v1…
utruong309 Dec 8, 2025
53216bb
fix: update form-data to 4.0.4 across CRUD web apps (Security v1.11)
utruong309 Dec 8, 2025
fbea090
chore: Adding ADOPTERS.md file (#330)
franciscojavierarceo Jun 3, 2025
9da2bbf
chore: create issue templates for project planning (#361)
andyatmiami Jun 26, 2025
b2c1896
feat(nb): Guide to report security vulnerabilities (#485)
andreyvelich Jul 17, 2025
f046fcc
chore: enforce planning label rules for issues (#421)
andyatmiami Jul 24, 2025
617734d
chore: create `/add-sub-issue` and `/remove-sub-issue` commands (#369)
andyatmiami Jul 24, 2025
8e3b300
fix: typo in planning gha configs (#515)
jenny-s51 Aug 4, 2025
8fa2670
chore: add liavweiss as epic owner to relevant GHAs (#550)
andyatmiami Aug 28, 2025
8a541a5
ci: add trivy security scanning workflow (#571)
andyatmiami Sep 25, 2025
8aaf457
Merge branch 'notebooks-v1' into pr2-update-form-data
utruong309 Dec 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/planning_epic.yml
Original file line number Diff line number Diff line change
@@ -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
48 changes: 48 additions & 0 deletions .github/ISSUE_TEMPLATE/planning_feature.yml
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions .github/ISSUE_TEMPLATE/planning_task.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 243 additions & 0 deletions .github/workflows/slash-commands.yaml
Original file line number Diff line number Diff line change
@@ -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}`);
}
Loading