Compare commits

..

1 Commits

Author SHA1 Message Date
a905874930 start of bot
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-09 11:05:29 -07:00
35 changed files with 209 additions and 293 deletions

141
.github/workflows/find_duplicate_issues.py vendored Executable file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env python3
import os
import openai
import json
from github import Github
# Initialize GitHub and OpenAI clients
if not os.getenv("GITHUB_TOKEN"):
print("Please set the GITHUB_TOKEN environment variable.")
exit(1)
g = Github(os.getenv("GITHUB_TOKEN"))
if not os.getenv("OPENAI_API_KEY"):
print("Please set the OPENAI_API_KEY environment variable.")
exit(1)
openai.api_key = os.getenv("OPENAI_API_KEY")
# Target repository
repo_name = os.getenv("GITHUB_REPOSITORY")
if not repo_name:
print("Please set the GITHUB_REPOSITORY environment variable.")
exit(1)
repo = g.get_repo(repo_name)
# Fetch all issues and comments
issues = repo.get_issues(state="open")
all_issues = {}
for issue in issues:
comments = issue.get_comments()
all_comments = [comment.body for comment in comments]
all_issues[issue.number] = {
"title": issue.title,
"body": issue.body,
"comments": all_comments,
}
# Create the start of the prompt template with all the issues and bodies.
system_issues_prompt = ""
for issue_number, issue_data in all_issues.items():
system_issues_prompt += f"""
#{issue_number}: {issue_data['title']}
# body
{issue_data['body']}
---
"""
print(system_issues_prompt)
# Get the token count for the system_issues_prompt
print(f"Token count for system_issues_prompt: {len(system_issues_prompt.split())}")
# Analyze issues for duplicates using OpenAI's GPT-4
potential_duplicates = []
for issue_number, issue_data in all_issues.items():
print(f"Analyzing issue #{issue_number} {issue_data['title']} ...")
response = openai.chat.completions.create(
model="gpt-4o",
response_format={ "type": "json_object" },
messages=[
{
"role": "system",
"content": """You are a distinguished engineer. Your peers
keep creating duplicate GitHub issues and you have OCD. You have decided to use your
skills to find duplicates for them. You are analyzing the issues in the repository.
Your goal is to find potential duplicate GitHub issues.
Do not return the current issue as a duplicate of itself. Use the issue title, body,
and comments to find potential duplicates.
Whenever you find a potential duplicate, you need to be very very sure that it is a duplicate.
Error on the side of caution. If you are not sure, do not return it as a duplicate.
Most won't have duplicates and that is fine! You are looking for the ones that do.
If you mistakenly return a non-duplicate, you will be penalized to spend time with the
interns helping them learn to exit vim. You do not want to do that.
Check and make sure that no one else has already commented that the issue is a duplicate.
If they have commented, you should not return it as a duplicate.
Your confidence level must be over 90% to return an issue as a duplicate.
Take a deep breath and begin your analysis. Your reputation is on the line.
Your responses should be formatted as a json array.
The following are examples of valid responses:
```json
[]
```
```json
[{"issue_number": 1234, "title": "Issue title"}]
```
Below are the current open issues in the repository. They are formatted as:
#{issue number}: {issue title}
# body
{issue body}
---
The current open issues in the repository are:
"""
+ system_issues_prompt,
},
{
"role": "user",
"content": f"""Find duplicates for GitHub issue #{issue_number} titled:
{issue_data['title']}
# issue body
{issue_data['body']}
---
# issue comments:
{issue_data['comments']}""",
},
],
)
if len(response.choices) == 0:
print("No duplicate issues found.")
continue
for response in response.choices:
print(response.message.content)
# Print potential duplicates
print("Potential duplicate issues:")
for issue_number in potential_duplicates:
issue = all_issues[issue_number]
print(
f"Issue #{issue_number}: {issue['title']} - {repo.html_url}/issues/{issue_number}"
)

View File

@ -0,0 +1,31 @@
name: Identify Duplicate Issues
on:
schedule:
- cron: '0 0 * * *' # Runs at midnight every day
permissions:
issues: write
jobs:
find-duplicates:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install PyGithub openai
- name: Run script to identify duplicates
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: python .github/scripts/find_duplicate_issues.py

3
.gitignore vendored
View File

@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
venv
# testing
/coverage
@ -58,5 +59,3 @@ src/wasm-lib/grackle/stdlib_cube_partial.json
Mac_App_Distribution.provisionprofile
*.tsbuildinfo
venv

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2458,44 +2458,6 @@ test.describe('Onboarding tests', () => {
await expect(onboardingOverlayLocator).toBeVisible()
await expect(onboardingOverlayLocator).toContainText('the menu button')
})
test("Avatar text doesn't mention avatar when no avatar", async ({
page,
}) => {
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
}),
}
)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
// Test that the text in this step is correct
const avatarLocator = await page
.getByTestId('user-sidebar-toggle')
.locator('img')
const onboardingOverlayLocator = await page
.getByTestId('onboarding-content')
.locator('div')
.nth(1)
// Expect the avatar to be visible and for the text to reference it
await expect(avatarLocator).not.toBeVisible()
await expect(onboardingOverlayLocator).toBeVisible()
await expect(onboardingOverlayLocator).toContainText('the menu button')
})
})
test.describe('Testing selections', () => {
@ -3966,55 +3928,6 @@ test.describe('Sketch tests', () => {
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
})
test('Can delete most of a sketch and the line tool will still work', async ({
page,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeEnabled()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(600) // wait for animation
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('Home')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
await u.openAndClearDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Line' }).click()
await page.waitForTimeout(100)
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([0.31, 16.47], %)`
)
})
test('Can exit selection of face', async ({ page }) => {
// Load the app with the code panes
await page.addInitScript(async () => {
@ -4404,7 +4317,7 @@ test.describe('Sketch tests', () => {
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([6.44, -12.07], %)
|> line([14.72, 1.97], %)
|> line([14.72, 2.01], %)
|> tangentialArcTo([24.95, -5.38], %)
|> line([1.97, 2.06], %)
|> close(%)
@ -4601,53 +4514,6 @@ test.describe('Sketch tests', () => {
await doSnapAtDifferentScales(page, [0, 10000, 10000])
})
})
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
page,
}) => {
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([-0.45, 0.87], %)
|> line([1.32, 0.38], %)
|> line([1.02, -1.32], %, $seg01)
|> line([-1.01, -0.77], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// click "line([1.32, 0.38], %)"
await page.getByText(`line([1.32, 0.38], %)`).click()
await page.waitForTimeout(100)
// click edit sketch
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(600) // wait for animation
// exit sketch
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// expect extrude button to be enabled
await expect(
page.getByRole('button', { name: 'Extrude' })
).not.toBeDisabled()
// click extrude
await page.getByRole('button', { name: 'Extrude' }).click()
// sketch selection should already have been made. "Selection 1 face" only show up when the selection has been made already
// otherwise the cmdbar would be waiting for a selection.
await expect(
page.getByRole('button', { name: 'Selection 1 face' })
).toBeVisible()
})
test("Existing sketch with bad code delete user's code", async ({ page }) => {
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => {
@ -7640,25 +7506,17 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
await page.keyboard.press('e')
await expect(page.locator('.cm-content')).toHaveText('//slae')
await page.keyboard.press('Meta+/')
await page.waitForTimeout(1000)
await page.waitForTimeout(2000)
// Test these hotkeys perform actions when
// focus is on the canvas
await page.mouse.move(600, 250)
await page.mouse.click(600, 250)
// work-around: to stop "keyboard.press('s')" from typing in the editor even when it should be blurred
await page.getByRole('button', { name: 'Commands ⌘K' }).click()
await page.waitForTimeout(100)
await page.keyboard.press('Escape')
await page.waitForTimeout(100)
// end work-around
// Start a sketch
await page.keyboard.press('s')
await page.waitForTimeout(1000)
await page.waitForTimeout(2000)
await page.mouse.move(800, 300, { steps: 5 })
await page.mouse.click(800, 300)
await page.waitForTimeout(1000)
await page.waitForTimeout(2000)
await expect(lineButton).toHaveAttribute('aria-pressed', 'true', {
timeout: 15_000,
})

View File

@ -857,11 +857,6 @@ export class SceneEntities {
let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing'
sceneInfra.setCallbacks({
onDragEnd: async () => {
// After the user drags, code has been updated, and source ranges are
// potentially stale.
const astResult = kclManager.updateSourceRanges()
if (trap(astResult)) return
if (addingNewSegmentStatus !== 'nothing') {
await this.tearDownSketch({ removeAxis: false })
this.setupSketch({

View File

@ -35,7 +35,6 @@ import {
canExtrudeSelection,
handleSelectionBatch,
isSelectionLastLine,
isRangeInbetweenCharacters,
isSketchPipe,
updateSelections,
} from 'lib/selections'
@ -426,7 +425,6 @@ export const ModelingMachineProvider = ({
if (
selectionRanges.codeBasedSelections.length === 0 ||
isRangeInbetweenCharacters(selectionRanges) ||
isSelectionLastLine(selectionRanges, codeManager.code)
) {
// they have no selection, we should enable the button

View File

@ -154,16 +154,6 @@ export class KclManager {
this._executeCallback = callback
}
updateSourceRanges(): Error | null {
const newAst = parse(recast(this.ast))
if (err(newAst)) {
return newAst
}
this.ast = newAst
return null
}
clearAst() {
this._ast = {
body: [],

View File

@ -51,16 +51,8 @@ export function getNodeFromPath<T>(
let successfulPaths: PathToNode = []
let pathsExplored: PathToNode = []
for (const pathItem of path) {
if (typeof currentNode[pathItem[0]] !== 'object') {
if (stopAtNode) {
return {
node: stopAtNode,
shallowPath: pathsExplored,
deepPath: successfulPaths,
}
}
if (typeof currentNode[pathItem[0]] !== 'object')
return new Error('not an object')
}
currentNode = currentNode?.[pathItem[0]]
successfulPaths.push(pathItem)
if (!stopAtNode) {

View File

@ -156,20 +156,17 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
},
Creo: {
pan: {
description: 'Left click + Ctrl + drag',
callback: (e) => butName(e).left && !butName(e).right && e.ctrlKey,
description: 'Middle click + Shift + drag',
callback: (e) => butName(e).middle && e.shiftKey,
},
zoom: {
description: 'Scroll wheel or Right click + Ctrl + drag',
dragCallback: (e) => butName(e).right && !butName(e).left && e.ctrlKey,
description: 'Scroll wheel or Middle click + Ctrl + drag',
dragCallback: (e) => butName(e).middle && e.ctrlKey,
scrollCallback: () => true,
},
rotate: {
description: 'Middle (or Left + Right) click + Ctrl + drag',
callback: (e) => {
const b = butName(e)
return (b.middle || (b.left && b.right)) && e.ctrlKey
},
description: 'Middle click + drag',
callback: (e) => butName(e).middle && noModifiersPressed(e),
},
},
AutoCAD: {

View File

@ -360,14 +360,6 @@ export function isSelectionLastLine(
return selectionRanges.codeBasedSelections[i].range[1] === code.length
}
export function isRangeInbetweenCharacters(selectionRanges: Selections) {
return (
selectionRanges.codeBasedSelections.length === 1 &&
selectionRanges.codeBasedSelections[0].range[0] === 0 &&
selectionRanges.codeBasedSelections[0].range[1] === 0
)
}
export type CommonASTNode = {
selection: Selection
ast: Program

View File

@ -126,17 +126,11 @@ async function getUser(context: UserContext) {
if (!token && isTauri()) return Promise.reject(new Error('No token found'))
if (token) headers['Authorization'] = `Bearer ${context.token}`
if (SKIP_AUTH) {
// For local tests
if (localStorage.getItem('FORCE_NO_IMAGE')) {
LOCAL_USER.image = ''
}
if (SKIP_AUTH)
return {
user: LOCAL_USER,
token,
}
}
const userPromise = !isTauri()
? fetch(url, {
@ -150,11 +144,6 @@ async function getUser(context: UserContext) {
const user = await userPromise
// Necessary here because we use Kurt's API key in CI
if (localStorage.getItem('FORCE_NO_IMAGE')) {
user.image = ''
}
if ('error_code' in user) return Promise.reject(new Error(user.message))
return {

File diff suppressed because one or more lines are too long

View File

@ -2,18 +2,13 @@ import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths'
import { useEffect, useState } from 'react'
import { useModelingContext } from 'hooks/useModelingContext'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
export default function UserMenu() {
const { context } = useModelingContext()
const { auth } = useSettingsAuthContext()
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.PROJECT_MENU)
const [avatarErrored, setAvatarErrored] = useState(false)
const user = auth?.context?.user
const errorOrNoImage = !user?.image || avatarErrored
const buttonDescription = errorOrNoImage ? 'the menu button' : 'your avatar'
const buttonDescription = !avatarErrored ? 'your avatar' : 'the menu button'
// Set up error handling for the user's avatar image,
// so the onboarding text can be updated if it fails to load.

View File

@ -1385,7 +1385,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.72"
version = "0.1.71"
dependencies = [
"anyhow",
"approx",

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-lib"
description = "KittyCAD Language implementation and tools"
version = "0.1.72"
version = "0.1.71"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -77,24 +77,14 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
let sketch_groups: Vec<Box<SketchGroup>> = sketch_group_set.into();
let mut extrude_groups = Vec::new();
for sketch_group in &sketch_groups {
// Before we extrude, we need to enable the sketch mode.
// We do this here in case extrude is called out of order.
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::EnableSketchMode {
animated: false,
ortho: false,
entity_id: sketch_group.on.id(),
adjust_camera: false,
planar_normal: if let SketchSurface::Plane(plane) = &sketch_group.on {
// We pass in the normal for the plane here.
Some(plane.z_axis.clone().into())
} else {
None
},
},
)
.await?;
// Make sure we exited sketch mode if sketching on a plane.
if let SketchSurface::Plane(_) = sketch_group.on {
// Disable the sketch mode.
// This is necessary for when people don't close the sketch explicitly.
// The sketch mode will mess up the extrude direction if still active.
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
.await?;
}
args.send_modeling_cmd(
id,
@ -105,10 +95,6 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
},
)
.await?;
// Disable the sketch mode.
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
.await?;
extrude_groups.push(do_post_extrude(sketch_group.clone(), length, id, args.clone()).await?);
}
@ -121,6 +107,13 @@ pub(crate) async fn do_post_extrude(
id: Uuid,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
// We need to do this after extrude for sketch on face.
if let SketchSurface::Face(_) = sketch_group.on {
// Disable the sketch mode.
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
.await?;
}
// Bring the object to the front of the scene.
// See: https://github.com/KittyCAD/modeling-app/issues/806
args.batch_modeling_cmd(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

View File

@ -1,29 +0,0 @@
// create a sketch with name sketch000
const sketch000 = startSketchOn('XY')
|> startProfileAt([0.0, 0.0], %)
|> line([1.0, 1.0], %, $line000)
|> line([0.0, -1.0], %, $line001)
|> line([-1.0, 0.0], %, $line002)
// create an extrusion with name extrude000
const extrude000 = extrude(1.0, sketch000)
// define a plane with name plane005
const plane005 = {
plane: {
origin: [0.0, 0.0, 1.0],
x_axis: [0.707107, 0.707107, 0.0],
y_axis: [-0.0, 0.0, 1.0],
z_axis: [0.707107, -0.707107, 0.0]
}
}
// create a sketch with name sketch001
const sketch001 = startSketchOn(plane005)
|> startProfileAt([0.100000, 0.250000], %)
|> line([0.075545, 0.494260], %, $line003)
|> line([0.741390, -0.113317], %, $line004)
|> line([-0.816935, -0.380943], %, $line005)
// create an extrusion with name extrude001
const extrude001 = extrude(1.0, sketch001)

View File

@ -2501,10 +2501,3 @@ async fn serial_test_order_sketch_extrude_out_of_order() {
0.999,
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_extrude_custom_plane() {
let code = include_str!("inputs/extrude-custom-plane.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/extrude-custom-plane.png", &result, 0.999);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB