-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add commands for publishing extensions #1140
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
Draft
martin-helmich
wants to merge
14
commits into
master
Choose a base branch
from
feat/contributor-commands
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 8 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
527e727
feat: add commands for publishing extensions
martin-helmich 7850884
fix: pass valid request bodies for extension data
martin-helmich c27e6d0
chore: re-generate README
martin-helmich 1b4f71f
feat: add init command
martin-helmich 5826ccd
refact: refactor init command a bit
martin-helmich f06a460
chore: re-generate README
martin-helmich b710517
refact: simplify data types
martin-helmich 40a851b
fix: wrong labels for withdrawing extension
martin-helmich a381096
feat: add confirmation (and --force flag) to withdraw command
martin-helmich 8dacee1
chore: re-generate README
martin-helmich ec1c296
chore: consistently lowercase flag descriptions
martin-helmich a2bcc75
chore: re-generate README
martin-helmich 4f67334
refact: refactor "init" command + add some tests
martin-helmich c0f50ee
refact: some slight code restructurings
martin-helmich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| `mw contributor` | ||
| ================ | ||
|
|
||
| Commands for mStudio marketplace contributors | ||
|
|
||
| * [`mw contributor extension deploy EXTENSION-MANIFEST`](#mw-contributor-extension-deploy-extension-manifest) | ||
| * [`mw contributor extension init EXTENSION-MANIFEST`](#mw-contributor-extension-init-extension-manifest) | ||
| * [`mw contributor extension publish EXTENSION-MANIFEST`](#mw-contributor-extension-publish-extension-manifest) | ||
| * [`mw contributor extension withdraw EXTENSION-MANIFEST`](#mw-contributor-extension-withdraw-extension-manifest) | ||
|
|
||
| ## `mw contributor extension deploy EXTENSION-MANIFEST` | ||
|
|
||
| Deploy an extension manifest to the marketplace | ||
|
|
||
| ``` | ||
| USAGE | ||
| $ mw contributor extension deploy EXTENSION-MANIFEST [-q] [--create] | ||
|
|
||
| ARGUMENTS | ||
| EXTENSION-MANIFEST [default: ./mstudio-extension.yaml] file path to the extension manifest (as YAML or JSON) | ||
|
|
||
| FLAGS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
| --[no-]create create the extension if it does not exist | ||
|
|
||
| DESCRIPTION | ||
| Deploy an extension manifest to the marketplace | ||
|
|
||
| FLAG DESCRIPTIONS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
|
|
||
| This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in | ||
| scripts), you can use this flag to easily get the IDs of created resources for further processing. | ||
| ``` | ||
|
|
||
| ## `mw contributor extension init EXTENSION-MANIFEST` | ||
|
|
||
| Init a new extension manifest file | ||
|
|
||
| ``` | ||
| USAGE | ||
| $ mw contributor extension init EXTENSION-MANIFEST [-q] [--overwrite] | ||
|
|
||
| ARGUMENTS | ||
| EXTENSION-MANIFEST [default: ./mstudio-extension.yaml] file path to the extension manifest (as YAML or JSON) | ||
|
|
||
| FLAGS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
| --overwrite overwrite an existing extension manifest if found | ||
|
|
||
| DESCRIPTION | ||
| Init a new extension manifest file | ||
|
|
||
| This command will create a new extension manifest file. It only operates on your local file system; afterwards, use | ||
| the 'deploy' command to upload the manifest to the marketplace. | ||
|
|
||
| FLAG DESCRIPTIONS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
|
|
||
| This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in | ||
| scripts), you can use this flag to easily get the IDs of created resources for further processing. | ||
| ``` | ||
|
|
||
| ## `mw contributor extension publish EXTENSION-MANIFEST` | ||
|
|
||
| Publish an extension on the marketplace | ||
|
|
||
| ``` | ||
| USAGE | ||
| $ mw contributor extension publish EXTENSION-MANIFEST [-q] | ||
|
|
||
| ARGUMENTS | ||
| EXTENSION-MANIFEST [default: ./mstudio-extension.yaml] file path to the extension manifest (as YAML or JSON) | ||
|
|
||
| FLAGS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
|
|
||
| DESCRIPTION | ||
| Publish an extension on the marketplace | ||
|
|
||
| FLAG DESCRIPTIONS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
|
|
||
| This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in | ||
| scripts), you can use this flag to easily get the IDs of created resources for further processing. | ||
| ``` | ||
|
|
||
| ## `mw contributor extension withdraw EXTENSION-MANIFEST` | ||
|
|
||
| Withdraw an extension from the marketplace | ||
|
|
||
| ``` | ||
| USAGE | ||
| $ mw contributor extension withdraw EXTENSION-MANIFEST --reason <value> [-q] | ||
|
|
||
| ARGUMENTS | ||
| EXTENSION-MANIFEST [default: ./mstudio-extension.yaml] file path to the extension manifest (as YAML or JSON) | ||
|
|
||
| FLAGS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
| --reason=<value> (required) Reason for withdrawal | ||
|
|
||
| DESCRIPTION | ||
| Withdraw an extension from the marketplace | ||
|
|
||
| FLAG DESCRIPTIONS | ||
| -q, --quiet suppress process output and only display a machine-readable summary. | ||
|
|
||
| This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in | ||
| scripts), you can use this flag to easily get the IDs of created resources for further processing. | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| import React from "react"; | ||
| import { ExecRenderBaseCommand } from "../../../lib/basecommands/ExecRenderBaseCommand.js"; | ||
| import { | ||
| makeProcessRenderer, | ||
| processFlags, | ||
| } from "../../../rendering/process/process_flags.js"; | ||
| import { Flags } from "@oclif/core"; | ||
| import { assertStatus } from "@mittwald/api-client"; | ||
| import { Success } from "../../../rendering/react/components/Success.js"; | ||
| import { | ||
| extensionManifestArg, | ||
| parseExtensionManifest, | ||
| } from "../../../lib/resources/extension/args_contributor.js"; | ||
|
|
||
| export default class Deploy extends ExecRenderBaseCommand<typeof Deploy, void> { | ||
| static description = "Deploy an extension manifest to the marketplace"; | ||
|
|
||
| static flags = { | ||
| ...processFlags, | ||
| create: Flags.boolean({ | ||
| description: "create the extension if it does not exist", | ||
| default: true, | ||
| allowNo: true, | ||
| }), | ||
| }; | ||
|
|
||
| static args = { | ||
| "extension-manifest": extensionManifestArg({ | ||
| required: true, | ||
| }), | ||
| }; | ||
|
|
||
| protected async exec(): Promise<void> { | ||
| const p = makeProcessRenderer(this.flags, "Updating extension manifest"); | ||
|
|
||
| const manifest = await parseExtensionManifest( | ||
| this.args["extension-manifest"], | ||
| ); | ||
| const { contributorId, id } = manifest; | ||
|
|
||
| const existing = await p.runStep( | ||
| "Retrieving current extension state", | ||
| async () => { | ||
| const response = | ||
| await this.apiClient.marketplace.extensionGetOwnExtension({ | ||
| contributorId, | ||
| extensionId: id, | ||
| }); | ||
|
|
||
| if (response.status === 404) { | ||
| return null; | ||
| } | ||
|
|
||
| assertStatus(response, 200); | ||
|
|
||
| return response.data; | ||
| }, | ||
| ); | ||
|
|
||
| if (existing === null) { | ||
| if (!this.flags.create) { | ||
| await p.error("Extension does not exist, use --create to create it"); | ||
| return; | ||
| } | ||
|
|
||
| await p.runStep("Registering extension", async () => { | ||
| if (manifest.deprecation) { | ||
| throw new Error( | ||
| '"deprecation" is not supported when creating a new extension', | ||
| ); | ||
| } | ||
|
|
||
| await this.apiClient.marketplace.extensionRegisterExtension({ | ||
| contributorId, | ||
| data: { | ||
| description: manifest.description, | ||
| detailedDescriptions: manifest.detailedDescriptions, | ||
| externalFrontends: manifest.externalFrontends, | ||
| frontendFragments: manifest.frontendFragments, | ||
| name: manifest.name, | ||
| scopes: manifest.scopes, | ||
| subTitle: manifest.subTitle, | ||
| support: manifest.support, | ||
| tags: manifest.tags, | ||
| webhookURLs: manifest.webhookUrls, | ||
| }, | ||
| }); | ||
| }); | ||
| } else { | ||
| await p.runStep("Updating extension", async () => { | ||
| await this.apiClient.marketplace.extensionPatchExtension({ | ||
| extensionId: manifest.id, | ||
| contributorId: manifest.contributorId, | ||
| data: { | ||
| deprecation: manifest.deprecation, | ||
| description: manifest.description, | ||
| detailedDescriptions: manifest.detailedDescriptions, | ||
| externalFrontends: manifest.externalFrontends, | ||
| frontendFragments: manifest.frontendFragments, | ||
| name: manifest.name, | ||
| scopes: manifest.scopes, | ||
| subTitle: manifest.subTitle, | ||
| support: manifest.support, | ||
| tags: manifest.tags, | ||
| webhookUrls: manifest.webhookUrls, | ||
| }, | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| await p.complete(<Success>Extension deployed successfully</Success>); | ||
| } | ||
|
|
||
| protected render(): React.ReactNode { | ||
| return undefined; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| import React from "react"; | ||
| import { ExecRenderBaseCommand } from "../../../lib/basecommands/ExecRenderBaseCommand.js"; | ||
| import { | ||
| makeProcessRenderer, | ||
| processFlags, | ||
| } from "../../../rendering/process/process_flags.js"; | ||
| import { Flags } from "@oclif/core"; | ||
| import { Success } from "../../../rendering/react/components/Success.js"; | ||
| import { extensionManifestArg } from "../../../lib/resources/extension/args_contributor.js"; | ||
| import { ExtensionManifest } from "../../../lib/resources/extension/manifest.js"; | ||
| import * as uuid from "uuid"; | ||
| import { writeFile } from "fs/promises"; | ||
| import yaml from "js-yaml"; | ||
| import { Value } from "../../../rendering/react/components/Value.js"; | ||
| import { pathExists } from "../../../lib/util/fs/pathExists.js"; | ||
|
|
||
| export default class Init extends ExecRenderBaseCommand<typeof Init, void> { | ||
| static summary = "Init a new extension manifest file"; | ||
| static description = | ||
| "This command will create a new extension manifest file. It only operates on your local file system; afterwards, use the 'deploy' command to upload the manifest to the marketplace."; | ||
|
|
||
| static flags = { | ||
| ...processFlags, | ||
| overwrite: Flags.boolean({ | ||
| description: "overwrite an existing extension manifest if found", | ||
| default: false, | ||
| }), | ||
| }; | ||
|
|
||
| static args = { | ||
| "extension-manifest": extensionManifestArg({ | ||
| required: true, | ||
| }), | ||
| }; | ||
|
|
||
| protected async exec(): Promise<void> { | ||
| const p = makeProcessRenderer( | ||
| this.flags, | ||
| "Initializing extension manifest", | ||
| ); | ||
|
|
||
| const { overwrite } = this.flags; | ||
|
|
||
| await p.runStep("generating extension manifest file", async () => { | ||
| const renderedConfiguration: ExtensionManifest = { | ||
| name: "my-extension", | ||
| contributorId: "TODO", | ||
| id: uuid.v4(), | ||
| description: "TODO", | ||
| detailedDescriptions: { | ||
| de: { | ||
| markdown: "TODO", | ||
| plain: "TODO", | ||
| }, | ||
| en: { | ||
| markdown: "TODO", | ||
| plain: "TODO", | ||
| }, | ||
| }, | ||
| externalFrontends: [ | ||
| { | ||
| name: "example", | ||
| url: "https://mstudio-extension.example/auth/oneclick?atrek=:accessTokenRetrievalKey&userId=:userId&instanceID=:extensionInstanceId", | ||
| }, | ||
| ], | ||
| frontendFragments: { | ||
| foo: { | ||
| url: "https://mstudio-extension.example/", | ||
| }, | ||
| }, | ||
| scopes: ["user:read"], | ||
| subTitle: { | ||
| de: "TODO", | ||
| en: "TODO", | ||
| }, | ||
| support: { | ||
| email: "todo@mstudio-extension.example", | ||
| phone: "+49 0000 000000", | ||
| }, | ||
| tags: ["TODO"], | ||
| webhookUrls: { | ||
| extensionAddedToContext: { | ||
| url: "https://mstudio-extension.example/webhooks", | ||
| }, | ||
| extensionInstanceUpdated: { | ||
| url: "https://mstudio-extension.example/webhooks", | ||
| }, | ||
| extensionInstanceSecretRotated: { | ||
| url: "https://mstudio-extension.example/webhooks", | ||
| }, | ||
| extensionInstanceRemovedFromContext: { | ||
| url: "https://mstudio-extension.example/webhooks", | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| const manifestAlreadyExists = await pathExists( | ||
| this.args["extension-manifest"], | ||
| ); | ||
|
|
||
| if (manifestAlreadyExists && !overwrite) { | ||
| throw new Error( | ||
| "File already exists. Use --overwrite to overwrite it.", | ||
| ); | ||
| } | ||
|
|
||
| await writeFile( | ||
| this.args["extension-manifest"], | ||
| yaml.dump(renderedConfiguration), | ||
| ); | ||
| }); | ||
|
|
||
| await p.complete( | ||
| <Success> | ||
| Extension manifest file created at{" "} | ||
| <Value>{this.args["extension-manifest"]}</Value> | ||
| </Success>, | ||
| ); | ||
| } | ||
|
|
||
| protected render(): React.ReactNode { | ||
| return undefined; | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.