Skip to content

Demo Extension API (Swift) and Extension Store #416

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

Merged
merged 17 commits into from
Apr 10, 2022

Conversation

pkasila
Copy link
Member

@pkasila pkasila commented Apr 8, 2022

Description

Demoing the Extension API and basic Extension Store

  • Basic Extension Store API server is deployed on my personal server at the moment
  • Basic Extension API package for Swift-based API (it just a class conforming to protocol, at the moment no APIs are implemented)
  • Loading Swift extensions as Bundles
  • Add alert that workspace should be reopened for extensions to start working

Someone else should work on the UI for Extension Store 😄

Related Issue

Checklist

  • I read and understood the contributing guide as well as the code of conduct
  • My changes generate no new warnings
  • My code builds and runs on my machine
  • Review requested

Screenshots

As far it's just PoC, UI is not implemented according to the concept

2022-04-09.23.57.45.mov

How to work with Extensions?

First of all, right now Extension Store API adds plugin releases by receiving web-hooks from GitHub. So, you need to set up a GitHub repository. Everything in this, let's say, tutorial is based on pkasila/helloworld-extension - demo extension.

About Extension's Bundle

Any extension is a Bundle. This bundle should have ceext extension. Also, this bundle should reference CodeEditKit as its dependency.

You should declare a subclass of ExtensionBuilder which has build(withAPI:) call which returns ExtensionInterface conforming class.

import Foundation
import CodeEditKit

public class HelloWorldExtension: ExtensionInterface {
    var api: ExtensionAPI

    init(api: ExtensionAPI) {
        self.api = api
        print("Hello from HelloWorldExtension: \(api)!")
    }
}

@objc(HelloWorldBuilder)
public class HelloWorldBuilder: ExtensionBuilder {
    public override func build(withAPI api: ExtensionAPI) -> ExtensionInterface {
        return HelloWorldExtension(api: api)
    }
}

The subclass of ExtensionBuilder should be set as the NSPrincipalClass in Info.plist

Authorizing requests

In this PR, Extension Store API server is set to https://codeedit.pkasila.net/api/ (my personal server) which is secured using my Keycloak server (register, login).

NOTE: We need to deploy this Extension Store API server somewhere else.

Then you send request to receive a token to access API:

curl -XPOST -d 'client_id=codeedit-app&username=[USERNAME]&password=[PASSWORD]&grant_type=password' 'https://keycloak.pkasila.net/auth/realms/CodeEdit/protocol/openid-connect/token'

Then, you need provide this token in Authorization header:

Authorization: Bearer [TOKEN_GOES_HERE]

Publishing a plugin

You need to make a POST-request:

Authorization: Bearer ...
Content-Type: application/json

{
    "manifest": {
        "name":"helloworld",
        "displayName":"Hello World", 
        "homepage":"https://codeedit.app/", 
        "repository":"https://github.com/pkasila/codeedit-helloworld-extension", 
        "issues":"https://github.com/pkasila/codeedit-helloworld-extension"
    },
    "management":"gh_releases",
    "sdk": "swift"
}

Server will return something like this (remember, you will receive secret only once):

{
    "secret": "[YOUR_SECRET]",
    "plugin": {
        "ban":null,
        "manifest": {
            "repository":"https:\/\/github.com\/pkasila\/codeedit-helloworld-extension",
            "homepage":"https:\/\/codeedit.app\/",
            "name":"helloworld",
            "displayName":"Hello World",
            "issues":"https:\/\/github.com\/pkasila\/codeedit-helloworld-extension"
        },
        "author":"5810845E-3D12-4535-8590-5DD356173E40",
        "management":"gh_releases",
        "id":"163E6B08-3485-49B0-AC01-0626BCFCD0BC",
        "sdk":"swift"
    }
}

Then, you need to set up web-hooks on GitHub, so Extension Store API could receive information about new releases

Setting up web-hooks with GitHub Releases

Go to your repository settings and to Webhooks tab. Here you need to:

  • Set URL to: https://codeedit.pkasila.net/api/webhooks/[YOUR_PLUGIN_ID: root.plugin.id]/github
  • Set Content type to: application/json
  • Set secret to one you received in the server response
  • In "Which events would you like to trigger this webhook?" select: Let me select individual events.
  • Leave Releases only in Individual events

You will have something like this:

image

image

First delivery may be unsuccessful, but when you will publish new releases everything should work fine.

Publishing releases

You publish releases to GitHub Releases as you would usually do. But you need to provide an additional asset to the release, so CodeEdit would be able to download, install and load the extension.

The final version of the extension should be archived as TAR-archive:

tar cvhf extension.tar HelloWorldExtension.ceext

Then, extension.tar should be attached to your release (the name of the file should be exactly extension.tar).

When Extension Store API will receive a web-hook, it will automatically add release to the database. If you delete release from GitHub, then it will automatically delete it from the database after a web-hook.

@pkasila pkasila added the enhancement New feature or request label Apr 8, 2022
@nanashili
Copy link
Contributor

@pkasila look at #414 the right side of the Package Manager maybe we can follow that UI?

@nanashili
Copy link
Contributor

Or ultimately we can follow VS Code design and open an editor screen which shows the extensions it does give the extensions developers more space to work with.

@pkasila
Copy link
Member Author

pkasila commented Apr 8, 2022

@pkasila look at #414 the right side of the Package Manager maybe we can follow that UI?

As I said this is just a PoC of Extensions Store and Extension API, so UI should be handled by someone else. And also I think it's better to discuss UI with @austincondiff.

But I think that it is a good option to have UI similar to #414.

@nanashili
Copy link
Contributor

@pkasila look at #414 the right side of the Package Manager maybe we can follow that UI?

As I said this is just a PoC of Extensions Store and Extension API, so UI should be handled by someone else. And also I think it's better to discuss UI with @austincondiff.

But I think that it is a good option to have UI similar to #414.

Okay, I'll build the UI for it will just need to look into MD support but the nice thing about the Text view is it automatically supports MD

@pkasila pkasila marked this pull request as ready for review April 9, 2022 18:13
@pkasila pkasila requested a review from lukepistrol April 9, 2022 18:14
@pkasila pkasila changed the title [WIP] Demoing Extension API (Swift) and Extension Store Demoing Extension API (Swift) and Extension Store Apr 9, 2022
@pkasila pkasila changed the title Demoing Extension API (Swift) and Extension Store Demo Extension API (Swift) and Extension Store Apr 9, 2022
@pkasila pkasila marked this pull request as draft April 9, 2022 19:47
@Angelk90
Copy link
Contributor

Angelk90 commented Apr 9, 2022

@pkasila : You could put a sample video of how everything happens, from installation to use.

@pkasila
Copy link
Member Author

pkasila commented Apr 9, 2022

@Angelk90 There are no APIs for extensions implemented yet (except sample one), so video below just shows how app loads extensions on workspace opening (and extension prints "Hello from HelloWorldExtension: CodeEdit.CodeEditAPI!" on load).

codeedit_extensions_demo.mp4

@pkasila pkasila requested a review from jasonplatts April 9, 2022 20:31
@samymih
Copy link

samymih commented Apr 9, 2022

great work, keep it up! 👍🏻

@Angelk90
Copy link
Contributor

Angelk90 commented Apr 9, 2022

@pkasila : Some suggestions:

  • In the release field, it would be possible to previously assign the latest version of the extension as a value.
  • After installation, I believe that you must always restart the program if you want to try the extension.
    It would be possible instead of having that modal telling you, that the installation was successful and if you want to try, then reboot.
    Having a modal that tells you this, with a button that allows you to restart the program by yourself, close the project and reopen it.

…d (doesn't persist window's state, because it's automatically centered), remove all references to plugin after uninstall
@pkasila
Copy link
Member Author

pkasila commented Apr 9, 2022

@Angelk90 On the first suggestion I'll work a little bit later (it'll require some changes on the server-side). But with the second one here we go:

2022-04-09.23.57.45.mov

@Angelk90
Copy link
Contributor

Angelk90 commented Apr 9, 2022

@pkasila : Great, how about the loading is in the center?

Registrazione.schermo.2022-04-09.alle.23.02.48.mov

Then I think after installing it would be useful too, probably show what version is installed.

@Angelk90
Copy link
Contributor

@pkasila : If the newly installed "Hello world" extension had a settings window, how can I access it?

@pkasila
Copy link
Member Author

pkasila commented Apr 10, 2022

@pkasila : If the newly installed "Hello world" extension had a settings window, how can I access it?

It's a great question. Right now I can suggest 3 options:

  1. (code approach) We can add static func settings() -> ... function to ExtensionBuilder class and this function should return (one of):
    1.1. a set of settings (so CodeEdit handles settings in some unified way)
    1.2. a function for window to be created
    1.3. a NSViewController with extension's settings (extension handles its settings)
  2. (bundle resource approach) We can make extension developers add their settings fields to Info.plist or any other file in the extension's bundle, so CodeEdit will parse this file and handle settings in some unified way.
  3. Actually we can make it work with XIBs in the extension's bundle 😄

Again, this PR is just a PoC of how we can distribute, install, load and let extension interact with CodeEdit via ExtensionAPI protocol conforming class.

@Angelk90
Copy link
Contributor

@pkasila : Better to ask the problems now, than later.

@pkasila pkasila added the extensions Issues related to the extension architecture in CodeEdit label Apr 10, 2022
@jasonplatts
Copy link
Collaborator

jasonplatts commented Apr 10, 2022

@pkasila : If the newly installed "Hello world" extension had a settings window, how can I access it?

It's a great question. Right now I can suggest 3 options:

  1. (code approach) We can add static func settings() -> ... function to ExtensionBuilder class and this function should return (one of):
    1.1. a set of settings (so CodeEdit handles settings in some unified way)
    1.2. a function for window to be created
    1.3. a NSViewController with extension's settings (extension handles its settings)
  2. (bundle resource approach) We can make extension developers add their settings fields to Info.plist or any other file in the extension's bundle, so CodeEdit will parse this file and handle settings in some unified way.
  3. Actually we can make it work with XIBs in the extension's bundle 😄

Again, this PR is just a PoC of how we can distribute, install, load and let extension interact with CodeEdit via ExtensionAPI protocol conforming class.

Great question @Angelk90. We will need to consider that many extensions will want to store both global and project-specific settings. @austincondiff, this is something that is really confusing for users in Nova. The first white window is where global settings are shown and the second darker project window is where project-specific settings are found.

nova extensions

@jasonplatts
Copy link
Collaborator

Really nice work @pkasila! It's great progress.

Copy link
Collaborator

@jasonplatts jasonplatts left a comment

Choose a reason for hiding this comment

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

Looks great @pkasila!

@pkasila pkasila marked this pull request as ready for review April 10, 2022 17:29
@pkasila pkasila merged commit d6dd5ca into CodeEditApp:main Apr 10, 2022
@pkasila pkasila deleted the extensions-api branch April 10, 2022 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request extensions Issues related to the extension architecture in CodeEdit
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants