Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run cloud project locally #12198

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open

Run cloud project locally #12198

wants to merge 25 commits into from

Conversation

4e6
Copy link
Contributor

@4e6 4e6 commented Jan 30, 2025

Pull Request Description

implements https://github.com/enso-org/cloud-v2/issues/1651

This is a POC of the Hybrid execution feature https://github.com/enso-org/cloud-v2/issues/1650

Changelog:

  • update: add Run Locally button for Cloud projects that downloads the project into a temporary directory and starts it using the local backend
  • update: add Hybrid Execution feature that enables Run Locally button for Cloud projects. The feature is enabled for dev builds
  • update: recreate missing .enso/project.json metadata when listing projects using the project-manager-shim-middleware

Important Notes

Uploading logic is implemented but blocked by https://github.com/enso-org/cloud-v2/issues/1715

enso-12198-run-locally.mp4

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Scala,
    Java,
    TypeScript,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • Unit tests have been written where possible.

@4e6 4e6 added the CI: No changelog needed Do not require a changelog entry for this PR. label Jan 30, 2025
@4e6 4e6 self-assigned this Jan 30, 2025
Copy link

github-actions bot commented Jan 30, 2025

🧪 Storybook is successfully deployed!

📊 Dashboard:

@4e6 4e6 force-pushed the wip/db/1651-cloud-sync branch from 5b95795 to aebbad9 Compare February 4, 2025 18:14
@4e6 4e6 force-pushed the wip/db/1651-cloud-sync branch from aebbad9 to 3697a86 Compare February 5, 2025 13:49
@4e6 4e6 marked this pull request as ready for review February 10, 2025 16:42

/** Unpack a .tar.gz enso-project bundle into a temporary directory */
export async function unpackBundle(bundle: stream.Readable): Promise<string> {
const ensoProjectsDirectory =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it correct that in shim we don't respect the root directory setting ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the logic. Right now we create a {projects-root}/cloud-{projectId} directory for the cloud project. In the future, we will make it hidden. I suppose it requires using a native library for Windows.

if (projectsStore.getState().launchedProjects.length > 0) {
closeAllProjects()
return eventCallbacks.useEventCallback(
(project: LaunchedProject & { cloudProjectId?: backendModule.ProjectId }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of keeping cloudProjectId property separately from the LaunchedProject prop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extra parameter I need to pass when starting the project in the Hybrid mode. We need an original id of the cloud project to upload it after it is closed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, why we won't add it to LaunchedProject interface, or introduce another interface that extends the LaunchedProject ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I updated the schema

await client.resetQueries({ queryKey: createGetProjectDetailsQuery.getQueryKey(id) })
setProjectAsset(type, id, parentId, (asset) => ({
...asset,
projectState: { ...asset.projectState, type: backendModule.ProjectState.closed },
}))

// If the project runs in hybrid execution mode
if (cloudProjectId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I case if user closes local project in offline mode, the mutation will fail because remoteBackend is not available. This might lead to unexpected behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to handle the failure properly. I'll leave a TODO comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think typical for dashboard isa FIXME with a link to a new issue in cloud-v2

doAction={async () => {
invariant(localBackend != null, 'Local Backend is null')
const parentId = await remoteBackend.downloadProject(asset.id)
const assets = await localBackend.listDirectory({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer ensureQueryData over calling methods manually: https://tanstack.com/query/latest/docs/reference/QueryClient#queryclientensurequerydata

Prefer mutations over direct calls here. Also, consider moving this out of the function for better reusability

async downloadProject(id: backend.ProjectId): Promise<DirectoryId> {
const details = await this.getProjectDetails(id, true)

invariant(details.url != null, 'The download URL of the project must be present.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer passing id directly using parameters instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invariant method only silences the compiler. getPropertyDetails should always return url property when called with the parameter true. It is used this way in the download method above, for example

const response = await this.client.get(`./api/cloud/download-project?${queryString}`)
const path = await response.text()

invariant(response.ok, 'The download-project response must have status OK.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned above, the invariant is supposed for uh-oh situations. Prefer throwing an error here instead

directory: extractIdFromDirectoryId(directoryId),
})

await this.client.get(`./api/cloud/upload-project?${queryString}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to have / instead of ./ here. Also we prefer url constructor functions instead of bare bone strings. Please take a look at similar methods and do it similarly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this module uses strings. I wasn't able to find any examples of URL usages

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was inspired by

const projectResponse = await fetch(
`./api/project-manager/projects/${localBackendModule.extractTypeAndId(asset.id).id}/enso-project`,
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah fwiw:

@4e6 4e6 requested a review from MrFlashAccount February 14, 2025 09:04
@PabloBuchu
Copy link
Contributor

Can you add mocked support for calling endpoints:

  1. POST /projects/{PROJECT_ID}//open
  2. POST /projects/{PROJECT_ID}/set_open
  3. POST /projects/{PROJECT_ID}/close

@4e6
Copy link
Contributor Author

4e6 commented Feb 20, 2025

@PabloBuchu what's the difference between open and set_open endpoints? When should each of them be called?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: No changelog needed Do not require a changelog entry for this PR.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants