Skip to content

Commit

Permalink
feat: can release extension
Browse files Browse the repository at this point in the history
  • Loading branch information
0xzio committed Jul 2, 2024
1 parent 0991b82 commit 2cdb956
Show file tree
Hide file tree
Showing 26 changed files with 577 additions and 1,110 deletions.
4 changes: 2 additions & 2 deletions apps/desktop/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
VITE_API_URL=https://app.penx.io
# VITE_API_URL=http://localhost:3000
# VITE_API_URL=https://app.penx.io
VITE_API_URL=http://localhost:3000

NEXT_PUBLIC_PLATFORM=DESKTOP

Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@tauri-apps/plugin-os": "2.0.0-beta.3",
"@tauri-apps/plugin-shell": "2.0.0-beta.6",
"@trpc/client": "11.0.0-rc.421",
"@walletconnect/modal": "^2.6.2",
"add": "^2.0.6",
"clsx": "^2.1.0",
"cmdk": "^1.0.0",
Expand All @@ -65,6 +66,7 @@
"ky": "^1.1.3",
"lucide-react": "^0.344.0",
"next": "14.1.4",
"octokit": "^3.1.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.11",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ import {
isFormApp,
isListApp,
isOpenInBrowser,
isReleaseExtension,
isSubmitForm,
} from '@penxio/preset-ui'
import { open } from '@tauri-apps/plugin-shell'
import { Captions, Copy, DoorOpenIcon, Globe } from 'lucide-react'
import { writeText } from 'tauri-plugin-clipboard-api'
import { toast } from 'uikit'
import { appEmitter } from '@penx/event'
import { store } from '@penx/store'
import { workerStore } from '~/common/workerStore'
import { commandLoadingAtom } from '~/hooks/useCommandAppLoading'
import { useCommandAppUI } from '~/hooks/useCommandAppUI'
import { useValue } from '~/hooks/useValue'
import { ListItemIcon } from '../ListItemIcon'
import { releaseExtension } from './lib/releaseExtension'
import { MenuItem } from './MenuItem'

interface Props {
Expand Down Expand Up @@ -47,34 +52,49 @@ export const CommandAppActions = ({ onSelect }: Props) => {
return []
}, [ui, value])

async function selectAction(item: ActionItem, index: number) {
if (isOpenInBrowser(item)) {
await open(item.url)
}

if (isCopyToClipboard(item)) {
await writeText(item.content)
}

if (isSubmitForm(item)) {
appEmitter.emit('SUBMIT_FORM_APP', index)
}

if (isReleaseExtension(item)) {
try {
store.set(commandLoadingAtom, true)
await releaseExtension(item)
toast.success('Released successfully')
} catch (error) {
toast.warning('Failed to release extension!')
}
store.set(commandLoadingAtom, false)
}

if (isCustomAction(item)) {
workerStore.currentWorker!.postMessage({
type: 'action--custom-action',
itemIndex: itemIndexRef.current,
actionIndex: index,
})
}

onSelect?.()
}

return (
<>
{actions.map((item, index) => (
<MenuItem
key={index}
shortcut="↵"
onSelect={async () => {
if (isOpenInBrowser(item)) {
await open(item.url)
}

if (isCopyToClipboard(item)) {
await writeText(item.content)
}

if (isSubmitForm(item)) {
appEmitter.emit('SUBMIT_FORM_APP', index)
}

if (isCustomAction(item)) {
workerStore.currentWorker!.postMessage({
type: 'action--custom-action',
itemIndex: itemIndexRef.current,
actionIndex: index,
})
}

onSelect?.()
selectAction(item, index)
}}
>
<Box toCenterY gap2 inlineFlex>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { join } from '@tauri-apps/api/path'
import { exists, readDir, readTextFile } from '@tauri-apps/plugin-fs'
import { Octokit } from 'octokit'
import { escAction } from './constants'
import { getManifest } from './getManifest'
import { getReadme } from './getReadme'
import { readFileToBase64 } from './readFileToBase64'

export type TreeItem = {
path: string
// mode: '100644' | '100755' | '040000' | '160000' | '120000'
mode: '100644'
// type: 'blob' | 'tree' | 'commit'
type: 'blob'
content?: string
sha?: string | null
}

export class GithubUpload {
private app: Octokit

private params = {
owner: 'penxio',
repo: 'marketplace',
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
}

private baseBranchSha: string

constructor(
private location: string,
token: string,
) {
this.app = new Octokit({ auth: token })
}

async getReadmeContent(extensionName: string) {
const readme = await getReadme(this.location)
if (!readme) return `## ${extensionName}`
return readme
}

private async getInstallationContent() {
const manifest = await getManifest(this.location)

// add code manifest.commands
for (const command of manifest.commands) {
const codePath = await join(this.location, 'dist', `${command.name}.command.js`)
const code = await readTextFile(codePath)
command.code = code + escAction
}

// const assets = await assetsToStringMap()
const assets = {}
return JSON.stringify({ ...manifest, assets }, null, 2)
}

async createTree() {
let treeItems: TreeItem[] = []
const manifest = await getManifest(this.location)

for (const command of manifest.commands) {
const codePath = await join(this.location, 'dist', `${command.name}.command.js`)
const code = await readTextFile(codePath)

treeItems.push({
path: `extensions/${manifest.name}/dist/${command.name}.command.js`,
mode: '100644',
type: 'blob',
content: code + escAction,
})
}

/** assets */
const assets = await join(this.location, 'assets')

if (await exists(assets)) {
const files = await readDir(assets)

for (const file of files) {
if (!file.isFile || file.name.includes('.DS_Store')) continue
const filePath = await join(this.location, 'assets', file.name)
const fileItem = await this.createFileTreeItem(manifest.name, filePath, file.name, 'assets')
treeItems.push(fileItem)
}
}

/** screenshots */
const screenshots = await join(this.location, 'screenshots')

if (await exists(screenshots)) {
const files = await readDir(screenshots)
for (const file of files) {
if (!file.isFile || file.name.includes('.DS_Store')) continue
const filePath = await join(this.location, 'screenshots', file.name)
const fileItem = await this.createFileTreeItem(
manifest.name,
filePath,
file.name,
'screenshots',
)
treeItems.push(fileItem)
}
}

treeItems.push({
path: `extensions/${manifest.name}/manifest.json`,
mode: '100644',
type: 'blob',
content: JSON.stringify(manifest, null, 2),
})

treeItems.push({
path: `extensions/${manifest.name}/README.md`,
mode: '100644',
type: 'blob',
content: await this.getReadmeContent(manifest.name),
})

treeItems.push({
path: `extensions/${manifest.name}/installation.json`,
mode: '100644',
type: 'blob',
content: await this.getInstallationContent(),
})

return treeItems
}

async getBaseCommit() {
const ref = await this.app.request('GET /repos/{owner}/{repo}/git/ref/{ref}', {
...this.params,
ref: `heads/main`,
})

this.baseBranchSha = ref.data.object.sha
return this.baseBranchSha
}

async createFileTreeItem(
extensionName: string,
filePath: string,
fileName: string,
dirname: string,
) {
const content = await readFileToBase64(filePath)
const { data } = await this.app.request('POST /repos/{owner}/{repo}/git/blobs', {
...this.params,
content: content,
encoding: 'base64',
})

const item: TreeItem = {
path: `extensions/${extensionName}/${dirname}/${fileName}`,
mode: '100644',
type: 'blob',
sha: data.sha,
}

return item
}

private async commit(treeSha: string) {
const parentSha = this.baseBranchSha
const manifest = await getManifest(this.location)
const msg = `Release extension: ${manifest.name}`

const commit = await this.app.request('POST /repos/{owner}/{repo}/git/commits', {
...this.params,
message: `${msg}`,
parents: [parentSha],
tree: treeSha,
})
return commit
}

private async updateRef(commitSha: string = '') {
await this.app.request('PATCH /repos/{owner}/{repo}/git/refs/{ref}', {
...this.params,
ref: 'heads/main',
sha: commitSha,
force: true,
})
}

uploadToGitHub = async () => {
const baseCommit = await this.getBaseCommit()

// update tree to GitHub before commit
const { data } = await this.app.request('POST /repos/{owner}/{repo}/git/trees', {
...this.params,
tree: await this.createTree(),
base_tree: baseCommit,
})

// create a commit for the tree
const { data: commitData } = await this.commit(data.sha)

// update ref to GitHub server after commit
await this.updateRef(commitData.sha)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* original:
if (typeof document !== 'undefined') {
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
if (!window.$__IS_ACTION_OPEN__) {
window.parent.postMessage({ type: 'escape' }, '*')
}
}
})
}
*/
export const escAction = `
"undefined"!=typeof document&&document.addEventListener("keydown",e=>{"Escape"!==e.key||window.$__IS_ACTION_OPEN__||window.parent.postMessage({type:"escape"},"*")});
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { join } from '@tauri-apps/api/path'
import { readTextFile } from '@tauri-apps/plugin-fs'

type CommandItem = {
name: string
title: string
subtitle: string
description: string
icon?: string | Record<string, string>
code?: string
mode: 'preset-ui' | 'custom-ui' | 'no-view'
framework: 'vue' | 'react' | 'solid' | 'svelte'
}

type Manifest = {
name: string
title: string
version: string
description: string
main: string
code: string
icon: string | Record<string, string>
commands: CommandItem[]
screenshots: Record<string, string>
}

export async function getManifest(location: string) {
const manifestPath = await join(location!, 'manifest.json')
const manifestStr = await readTextFile(manifestPath)
const manifest = JSON.parse(manifestStr)
return manifest as Manifest
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { join } from '@tauri-apps/api/path'
import { readTextFile } from '@tauri-apps/plugin-fs'

export async function getReadme(location: string) {
try {
try {
const path = await join(location, 'readme.md')
return readTextFile(path) || ''
} catch (error) {
const path = await join(location, 'README.md')
return readTextFile(path) || ''
}
} catch (error) {
return ''
}
}
Loading

0 comments on commit 2cdb956

Please sign in to comment.