diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ec6c67c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI +on: + pull_request: + push: + branches: + - main + +jobs: + check: + name: CI + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-website-${{ hashFiles('**/Cargo.lock') }} + + - name: Install and Build + run: | + cd parser + yarn install + yarn ci diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml new file mode 100644 index 0000000..f3ea12b --- /dev/null +++ b/.github/workflows/site.yml @@ -0,0 +1,34 @@ +name: Site +on: + push: + branches: + - main + +jobs: + deploy: + name: Site + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-website-${{ hashFiles('**/Cargo.lock') }} + + - name: Install and Build + run: | + cd site + yarn install + yarn build + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: site/build diff --git a/README.md b/README.md index 4a7731c..2ea2a54 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ARCs +# Aleo Request for Comments (ARCs) 🚨 This repository is under active development. 🚨 diff --git a/arc-0000/README.md b/arc-0000/README.md index b8c16db..0d51f7f 100644 --- a/arc-0000/README.md +++ b/arc-0000/README.md @@ -1,3 +1,13 @@ +--- +arc: 0 +title: Template +authors: The Aleo Team +topic: Meta +status: Living +reviewers: Howard Wu +created: 2020-02-07 +--- + ## Overview This file serves as the suggested template for new ARC proposals. diff --git a/arc-0001/README.md b/arc-0001/README.md index 8f90b4c..61d6748 100644 --- a/arc-0001/README.md +++ b/arc-0001/README.md @@ -1,3 +1,13 @@ +--- +arc: 1 +title: ARC Proposal Guidelines +authors: The Aleo Team +topic: Meta +status: Living +reviewers: Howard Wu +created: 2020-02-07 +--- + ## Overview Aleo Request for Comments (ARCs) are protocol-level, network-level, and application-level standards for the Aleo ecosystem. diff --git a/parser/.gitignore b/parser/.gitignore new file mode 100644 index 0000000..8101569 --- /dev/null +++ b/parser/.gitignore @@ -0,0 +1,26 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +**/arcs.json +yarn.lock diff --git a/parser/README.md b/parser/README.md new file mode 100644 index 0000000..583e1c5 --- /dev/null +++ b/parser/README.md @@ -0,0 +1,25 @@ +# parser + +## Development + +The parser expects each `arc-xxxx/README.md` file to adhere to the following format: +``` +--- +title: This is a test +description: Once upon a time... +--- + +# Title +Lorem ipsum... +``` + +The parser will then parse it into: +```json +{ + metadata: { + title: "This is a test", + description: "Once upon a time..." + }, + content: "# Title\nLorem ipsum..." +} +``` diff --git a/parser/index.js b/parser/index.js new file mode 100644 index 0000000..b5c4a5e --- /dev/null +++ b/parser/index.js @@ -0,0 +1,240 @@ +const fs = require('fs'); +const glob = require("glob"); +const parseMD = require('parse-md').default; + +/** + * arc: 1 + * title: Template + * authors: The Aleo Team + * topic: Meta + * status: Living + * reviewers: Howard Wu + * created: 2020-02-07 + */ +const requiredMetadata = ['arc', 'title', 'authors', 'topic', 'status', 'reviewers', 'created']; + +const topics = ['Meta', 'Protocol', 'Network', 'Application']; + +const statuses = ['Idea', 'Draft', 'Active', 'Withdrawn', 'Accepted', 'Rejected', 'Final', 'Deprecated', 'Living']; + +// Executes sanity checks for CI. +const ci = () => { + getArcDirectories('..', async (err, list) => { + if (err) { + console.log('Error', err); + process.exit(1); + } else { + for (let i = 0; i < list.length; i++) { + const arcDirectory = list[i]; + + /**************************** ****************************/ + /***************** ADD NEW CI CHECKS HERE ****************/ + /**************************** ****************************/ + + await checkArcReadmeExists(arcDirectory); + await checkArcReadmeContent(arcDirectory); + + /**************************** ****************************/ + /**************************** ****************************/ + /**************************** ****************************/ + + if (i === list.length - 1) { + console.log("\nAll ARCs are up to standard!\n"); + } + } + } + }) +} + +// Builds pages for site. +const site = () => { + getArcDirectories('..', async (err, list) => { + if (err) { + console.log('Error', err); + process.exit(1); + } else { + + let arcs = {}; + + for (let i = 0; i < list.length; i++) { + const arcDirectory = list[i]; + + /**************************** ****************************/ + /**************** ADD NEW SITE LOGIC HERE ****************/ + /**************************** ****************************/ + + await checkArcReadmeExists(arcDirectory); + await checkArcReadmeContent(arcDirectory); + + const { id, metadata, content } = parseArcReadme(arcDirectory); + // Skip adding arc-0000 template. + if (id !== 0) { + arcs[id] = { metadata, content }; + } + + /**************************** ****************************/ + /**************************** ****************************/ + /**************************** ****************************/ + + if (i === list.length - 1) { + console.log("\nSuccessfully built all ARCs!"); + + // Write the processed ARCs to the `site` directory. + const path = "../site/src/arcs.json"; + fs.writeFileSync(path, JSON.stringify(arcs, null, 4)); + + console.log(`\nSuccessfully wrote all ARCs to ${path}!\n`); + } + } + } + }) +} + +/***************************************************** ****************************************************************/ +/***************************************************** ****************************************************************/ +/***************************************************** ****************************************************************/ +/*************************************** HELPER METHODS ONLY BELOW ****************************************************/ +/***************************************************** ****************************************************************/ +/***************************************************** ****************************************************************/ +/***************************************************** ****************************************************************/ + +// Returns a list of CLI arguments. +const cliArguments = process.argv.slice(2); + +// Returns a list of relative paths to every ARC directory. +const getArcDirectories = (relativePathToRoot, callback) => { + glob(relativePathToRoot + '/arc-*', callback); +}; + +// Checks that a README.md file exists in a given 'arc-xxxx' directory. +const checkArcReadmeExists = async (arcDirectory) => { + const arcReadmeFile = arcDirectory + '/README.md'; + try { + if (!fs.existsSync(arcReadmeFile)) { + console.error('Could not find', arcReadmeFile); + process.exit(1); + } + } catch(err) { + console.error('Could not find', arcReadmeFile); + process.exit(1); + } +} + +// Checks that a README.md file has the required content for a given 'arc-xxxx' directory. +const checkArcReadmeContent = async (arcDirectory) => { + const arcReadmeFile = arcDirectory + '/README.md'; + const fileContents = fs.readFileSync(arcReadmeFile, 'utf8'); + const { metadata, content } = parseMD(fileContents); + + /** + * arc: 1 + * title: Template + * authors: The Aleo Team + * topic: Meta + * status: Living + * reviewers: Howard Wu + * created: 2020-02-07 + */ + + // Check that the metadata is fully filled in. + for (let i = 0; i < requiredMetadata.length; i++) { + const metatopic = requiredMetadata[i]; + if (!metadata.hasOwnProperty(metatopic)) { + console.error('\n', arcReadmeFile, 'is missing \'', metatopic, '\'.\n'); + process.exit(1); + } + + // Check that the ARC # matches the directory #. + if (metatopic === 'arc') { + try { + const directoryNumber = parseInt(arcDirectory.split('-')[1]); + const metadataNumber = parseInt(metadata.arc); + if (directoryNumber !== metadataNumber) { + console.error('\nARC directory ID (', arcDirectory, ') does not match the ARC ID in the README (', metadataNumber, ').\n'); + process.exit(1); + } + } catch (err) { + console.error("\nFailed to check that", arcDirectory, "has a matching ARC number.\n"); + process.exit(1) + } + } + + // Check that the ARC title is nonempty. + if (metatopic === 'title') { + const title = metadata['title']; + if (title === null || title === undefined || title === "") { + console.error('\nARC title cannot be empty.\n'); + process.exit(1); + } + } + + // Check that the ARC authors is nonempty. + if (metatopic === 'authors') { + const authors = metadata['authors']; + if (authors === null || authors === undefined || authors === "") { + console.error('\nARC authors cannot be empty.\n'); + process.exit(1); + } + } + + // Check that the ARC topic matches a valid topic. + if (metatopic === 'topic') { + if (!topics.includes(metadata['topic'])) { + console.error('\nARC topic (', metadata['topic'], ') is not an accepted topic.\n'); + process.exit(1); + } + } + + // Check that the ARC status matches a valid status. + if (metatopic === 'status') { + if (!statuses.includes(metadata['status'])) { + console.error('\nARC status (', metadata['status'], ') is not an accepted status.\n'); + process.exit(1); + } + } + + // Check that the ARC created is nonempty. + if (metatopic === 'created') { + const created = metadata['created']; + if (created === null || created === undefined || created === "") { + console.error('\nARC created cannot be empty.\n'); + process.exit(1); + } + } + } + + // Check that the main body is not empty. + if (content === null || content === undefined || content === "") { + console.error('\n', arcReadmeFile, 'is empty.\n'); + process.exit(1); + } +} + +// Returns the metadata and content of the README file for a given ARC. +const parseArcReadme = (arcDirectory) => { + const arcReadmeFile = arcDirectory + '/README.md'; + const fileContents = fs.readFileSync(arcReadmeFile, 'utf8'); + const { metadata, content } = parseMD(fileContents); + const id = metadata.arc; + return { id, metadata, content } +} + +// The main program. +const main = () => { + if (cliArguments.length === 0) { + console.error('\nPlease provide one CLI argument: \'ci\', \'site\'\n'); + } else { + switch (cliArguments[0].toLowerCase()) { + case 'ci': + ci(); + break; + case 'site': + site(); + break; + default: + console.error('\nInvalid command. Please provide a valid CLI argument: \'ci\', \'site\'\n'); + } + } +} + +main() diff --git a/parser/package.json b/parser/package.json new file mode 100644 index 0000000..392106c --- /dev/null +++ b/parser/package.json @@ -0,0 +1,14 @@ +{ + "name": "parser", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "glob": "^7.1.6", + "parse-md": "^2.0.4" + }, + "scripts": { + "ci": "node index.js ci", + "site": "node index.js site" + } +} diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..8101569 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,26 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +**/arcs.json +yarn.lock diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..b2051f1 --- /dev/null +++ b/site/README.md @@ -0,0 +1,70 @@ +# ARCs Website + +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.\ +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.\ +You will also see any lint errors in the console. + +### `yarn test` + +Launches the test runner in the interactive watch mode.\ +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `yarn build` + +Builds the app for production to the `build` folder.\ +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.\ +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `yarn eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + +### Analyzing the Bundle Size + +This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) + +### Making a Progressive Web App + +This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + +### Advanced Configuration + +This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) + +### Deployment + +This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) + +### `yarn build` fails to minify + +This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/site/package.json b/site/package.json new file mode 100644 index 0000000..e1f8ad3 --- /dev/null +++ b/site/package.json @@ -0,0 +1,41 @@ +{ + "name": "site", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.1.0", + "@testing-library/user-event": "^12.1.10", + "antd": "^4.12.2", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "react-markdown": "^5.0.3", + "react-scripts": "4.0.2", + "web-vitals": "^1.0.1" + }, + "scripts": { + "prestart": "cd ../parser && yarn install && yarn site", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/site/public/favicon.ico b/site/public/favicon.ico new file mode 100644 index 0000000..3db34ec Binary files /dev/null and b/site/public/favicon.ico differ diff --git a/site/public/index.html b/site/public/index.html new file mode 100644 index 0000000..b6ba5fa --- /dev/null +++ b/site/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + ARCs + + + +
+ + + diff --git a/site/public/logo192.png b/site/public/logo192.png new file mode 100644 index 0000000..09489c8 Binary files /dev/null and b/site/public/logo192.png differ diff --git a/site/public/logo512.png b/site/public/logo512.png new file mode 100644 index 0000000..12da5d7 Binary files /dev/null and b/site/public/logo512.png differ diff --git a/site/public/manifest.json b/site/public/manifest.json new file mode 100644 index 0000000..2b491e5 --- /dev/null +++ b/site/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "ARCs", + "name": "Aleo Request for Comments (ARCs)", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/site/public/robots.txt b/site/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/site/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/site/src/App.css b/site/src/App.css new file mode 100644 index 0000000..7577b3d --- /dev/null +++ b/site/src/App.css @@ -0,0 +1,12 @@ +.logo { + float: left; + width: 100px; + height: 31px; +} + +.logo:before { + content: "ARCs"; + color: white; + font-size: 20px; + font-weight: bold; +} \ No newline at end of file diff --git a/site/src/App.js b/site/src/App.js new file mode 100644 index 0000000..ba730c6 --- /dev/null +++ b/site/src/App.js @@ -0,0 +1,107 @@ +import './App.css'; +import arcs from './arcs.json'; + +import React, { useEffect, useRef, useState } from 'react'; +import ReactDOM from 'react-dom'; +import ReactMarkdown from 'react-markdown'; + +import { Button, Card, Col, Divider, Form, Input, Layout, Menu, Row, Table, Tag } from 'antd'; +const { Header, Content, Footer } = Layout; + +const Readme = ({ readme }) => { + const ref = useRef(null); + + useEffect(() => { + if (readme && ref) { + ReactDOM.render(, ref.current) + } + }, [readme]) + + return
+} + +function App() { + const rows = Object.values(arcs).map(proposal => { + const { arc, title, authors, topic, status } = proposal.metadata; + return { key: arc, arc, title, authors, topic, status } + }); + + const columns = [ + { + title: 'ARC', + dataIndex: 'arc', + key: 'arc', + sorter: true, + render: arc => {arc}, + }, + { + title: 'Title', + dataIndex: 'title', + key: 'title', + }, + { + title: 'Authors', + dataIndex: 'authors', + key: 'authors', + }, + { + title: 'Topic', + dataIndex: 'topic', + key: 'topic', + sorter: true, + render: topic => { + let tag = topic.toLowerCase(); + let color = 'green'; + if (tag === 'protocol') { + color = 'blue'; + } else if (tag === 'network') { + color = 'purple'; + } else if (tag === 'application') { + color = 'magenta'; + } + return ( + + {tag.toUpperCase()} + + ); + }, + }, + { + title: 'Status', + dataIndex: 'status', + key: 'status', + sorter: true, + } + ]; + + const landingPage = () => ; + + const proposal = (id) => { + console.log(id); + const proposal = arcs[id].content; + setBody( +

ARC-{id}: {arcs[id].metadata.title}

+
+ +
) + } + + const [body, setBody] = useState(landingPage()); + + return ( + +
+ setBody(landingPage())}>
+ {/**/} + {/* */} + {/**/} +
+ + {body} + + +
+ ); +} + +export default App; diff --git a/site/src/App.test.js b/site/src/App.test.js new file mode 100644 index 0000000..1f03afe --- /dev/null +++ b/site/src/App.test.js @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/site/src/index.css b/site/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/site/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/site/src/index.js b/site/src/index.js new file mode 100644 index 0000000..eca2e41 --- /dev/null +++ b/site/src/index.js @@ -0,0 +1,19 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +import 'antd/dist/antd.css'; + +ReactDOM.render( + + + , + document.getElementById('root') +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/site/src/reportWebVitals.js b/site/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/site/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/site/src/setupTests.js b/site/src/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/site/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom';