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

feat: add web page to query artifacts #105

Merged
merged 19 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions .biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
"files": {
"ignore": [
"coverage/**",
"dist/**",
],
"ignore": ["coverage/**", "dist/**"],
},
"formatter": {
// use dprint instead
Expand All @@ -17,19 +14,15 @@
"overrides": [
{
"include": ["packages/cli/src/spinner.ts"],
"linter": {
"rules": {
"suspicious": { "noExplicitAny": "off" },
},
},
"linter": { "rules": { "suspicious": { "noExplicitAny": "off" } } },
},
{
"include": ["packages/cli/src/commands/download/index.ts", "packages/cli/src/commands/generate/action.ts"],
"linter": {
"rules": {
"style": { "noParameterAssign": "off" },
},
},
"linter": { "rules": { "style": { "noParameterAssign": "off" } } },
},
{
"include": ["apps/web/src/main.tsx"],
"linter": { "rules": { "style": { "noNonNullAssertion": "off" } } },
},
],
}
56 changes: 56 additions & 0 deletions .github/workflows/deploy-web-app.yaml
cedoor marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Upload Artifacts
on:
push:
branches:
- main
paths: ['apps/web/**', '.github/workflows/deploy-web-app.yaml']

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0

- uses: actions/setup-node@v4

- uses: pnpm/action-setup@v4
with:
run_install: false

- shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- run: pnpm i --ignore-scripts

- uses: actions/cache@v4
with:
path: ${{env.STORE_PATH}}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-pnpm-store

- name: Build
run: pnpm --filter web build

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::490752553772:role/snark-artifacts-assume_role-slc
role-duration-seconds: 900
aws-region: us-west-2

- name: Upload index.html and assets to s3 bucket
run: |
aws s3 sync apps/web/dist s3://snark-artifacts/ --delete
sripwoud marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 12 additions & 0 deletions apps/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Snark Artifacts Registry</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "web",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@octokit/rest": "^21.0.0",
"jotai": "^2.8.3",
"preact": "^10.22.0",
"react-query": "^3.39.3"
},
"devDependencies": {
"@preact/preset-vite": "^2.8.2",
"typescript": "^5.2.2",
"vite": "^5.3.1"
}
}
5 changes: 5 additions & 0 deletions apps/web/src/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#app {
max-width: 1280px;
padding: 2rem;
text-align: center;
}
15 changes: 15 additions & 0 deletions apps/web/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Footer, Header } from './components'
import { DownloadButton, FileSelect, ProjectSelect, VersionSelect } from './containers'

export const App = () => (
<>
<Header />
<div className='form'>
<ProjectSelect />
<VersionSelect />
<FileSelect />
<DownloadButton />
</div>
<Footer />
</>
)
16 changes: 16 additions & 0 deletions apps/web/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { owner, repo } from '../constants'

export const Footer = () => (
<footer>
<p>
Copyright &copy;
<span>{' '}2024</span>
<span>{' '}PSE</span>
<a href={`https://github.com/${owner}/${repo}`}>
<span>
{' '}GitHub
</span>
</a>
</p>
</footer>
)
5 changes: 5 additions & 0 deletions apps/web/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const Header = () => (
<header>
<h1>Snark Artifacts Registry</h1>
</header>
)
3 changes: 3 additions & 0 deletions apps/web/src/components/Options.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const Options = ({ items }: { items: string[] }) => (
<>{items.map((item) => <option key={item} value={item}>{item}</option>)}</>
)
3 changes: 3 additions & 0 deletions apps/web/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { Footer } from './Footer'
export { Header } from './Header'
export { Options } from './Options'
5 changes: 5 additions & 0 deletions apps/web/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const artifacts = ['wasm', 'zkey']
export const owner = 'privacy-scaling-explorations'
export const repo = 'snark-artifacts'
export const projects = ['poseidon', 'semaphore', 'sempahore-identity']
export const cdnUrl = 'https://snark-artifacts.pse.dev'
13 changes: 13 additions & 0 deletions apps/web/src/containers/DownloadButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { cdnUrl } from '../constants'
import { useStore } from '../hooks'

export function DownloadButton() {
const { selectedFile, selectedProject, selectedVersion } = useStore()

const onClick = () => {
const url = `${cdnUrl}/${selectedProject}/${selectedVersion}/${selectedFile}`
window.location.href = url
}

return <button type='button' onClick={onClick} className='btn btn-primary' disabled={!selectedFile}>Download</button>
}
31 changes: 31 additions & 0 deletions apps/web/src/containers/FileSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { ChangeEvent } from 'react'
import { Options } from '../components'
import { useFiles, useStore } from '../hooks'

export function FileSelect() {
const { selectedFile, setSelectedFile } = useStore()
const { data: files, isLoading, isFetched, isSuccess, isError, error } = useFiles()
const onChange = (e: ChangeEvent<HTMLSelectElement>) => {
const file = e.target as HTMLSelectElement
setSelectedFile(file.value)
}

if (isLoading) return <div>Loading...</div>
if (!isFetched) return null
if (isError) return <div>{(error as Error).message}</div>
if (isSuccess) {
return (
<div>
<label htmlFor='file'>File</label>
<select id='file' onChange={onChange} value={selectedFile}>
<Options items={files} />
</select>
{
/* <label htmlFor='all-files' style={{ marginLeft: '1rem'}}>Download all</label>
<input id='all-files' type='checkbox' /> */
}
</div>
)
}
return null
}
24 changes: 24 additions & 0 deletions apps/web/src/containers/ProjectSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ChangeEvent } from 'react'
import { Options } from '../components'
import { projects } from '../constants'
import { useStore } from '../hooks'

export function ProjectSelect() {
const { selectedProject, setSelectedProject, resetSelectedFile, resetSelectedVersion } = useStore()

const onChange = (e: ChangeEvent<HTMLSelectElement>) => {
resetSelectedFile()
resetSelectedVersion()
const target = e.target as HTMLSelectElement
setSelectedProject(target.value)
}

return (
<div>
<label htmlFor='project'>Project</label>
<select value={selectedProject} onChange={onChange} id='project'>
<Options items={projects} />
</select>
</div>
)
}
29 changes: 29 additions & 0 deletions apps/web/src/containers/VersionSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { ChangeEvent } from 'react'
import { Options } from '../components'
import { useStore, useVersions } from '../hooks'

export function VersionSelect() {
const { selectedVersion, setSelectedVersion, resetSelectedFile } = useStore()
const { data: versions, isSuccess, isFetched, isLoading, isError, error } = useVersions()

const onChange = (e: ChangeEvent<HTMLSelectElement>) => {
resetSelectedFile()
const target = e.target as HTMLSelectElement
setSelectedVersion(target.value)
}

if (isLoading) return <div>Loading...</div>
if (!isFetched) return null
if (isError) return <div>Error: {(error as Error).message}</div>
if (isSuccess) {
return (
<div>
<label htmlFor='version'>Version</label>
<select id='version' value={selectedVersion} onChange={onChange}>
<Options items={versions} />
</select>
</div>
)
}
return null
}
4 changes: 4 additions & 0 deletions apps/web/src/containers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './DownloadButton'
export * from './FileSelect'
export * from './ProjectSelect'
export * from './VersionSelect'
3 changes: 3 additions & 0 deletions apps/web/src/gh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Octokit } from '@octokit/rest'

export const gh = new Octokit({})
3 changes: 3 additions & 0 deletions apps/web/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './useFiles'
export * from './useStore'
export * from './useVersions'
26 changes: 26 additions & 0 deletions apps/web/src/hooks/useFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useQuery } from 'react-query'
import { artifacts, owner, repo } from '../constants'
import { gh } from '../gh'
import { useStore } from '../hooks'

async function fetchFiles(version: string) {
const { data: { tree } } = await gh.git.getTree({ owner, repo, tree_sha: version, recursive: 'true' })
return tree.reduce((files, { path, type }) => {
if (type === 'blob' && path !== undefined)
files.push(path)
return files
}, [] as string[])
}

export function useFiles() {
const { selectedProject, selectedVersion } = useStore()
return useQuery({
enabled: selectedProject !== '' && selectedVersion !== '',
queryKey: ['files', selectedProject, selectedVersion],
queryFn: async () => fetchFiles(`@zk-kit/${selectedProject}-artifacts@${selectedVersion}`),
select: data => {
const regex = new RegExp(`^packages/${selectedProject}/.*\\.(${artifacts.join('|')})$`)
return data.filter(file => regex.test(file)).map(file => file.replace(`packages/${selectedProject}/`, ''))
},
})
}
22 changes: 22 additions & 0 deletions apps/web/src/hooks/useStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { atom, useAtom } from 'jotai'

const selectedFileAtom = atom('')
const selectedProjectAtom = atom('')
const selectedVersionAtom = atom('')

export const useStore = () => {
const [selectedFile, setSelectedFile] = useAtom(selectedFileAtom)
const [selectedProject, setSelectedProject] = useAtom(selectedProjectAtom)
const [selectedVersion, setSelectedVersion] = useAtom(selectedVersionAtom)

return {
selectedFile,
resetSelectedFile: () => setSelectedFile(''),
setSelectedFile,
selectedProject,
setSelectedProject,
selectedVersion,
resetSelectedVersion: () => setSelectedVersion(''),
setSelectedVersion,
}
}
21 changes: 21 additions & 0 deletions apps/web/src/hooks/useVersions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from 'react-query'
import { owner, repo } from '../constants'
import { gh } from '../gh'
import { useStore } from './useStore'

async function queryFn() {
const { data: tags } = await gh.repos.listTags({ owner, repo })
return tags.map(tag => tag.name)
}

export function useVersions() {
const { selectedProject } = useStore()

return useQuery({
enabled: selectedProject !== '',
queryKey: ['versions', selectedProject],
queryFn,
select: (data) =>
data.filter(tag => tag.includes(`@zk-kit/${selectedProject}-artifacts@`)).map(tag => tag.split('@')[2]),
})
}
Loading