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

docs: Add docs generation #43

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
486a30f
add docs generation
onnovisser Jan 13, 2025
e8ba866
[bot] New pkg version: 0.0.0-alpha.6
actions-user Jan 13, 2025
6ca9ccf
Test docs automation
sophialittlejohn Jan 13, 2025
eadf434
Force yarn@stable in workflow
sophialittlejohn Jan 13, 2025
4a05f36
Try setting stable version manually
sophialittlejohn Jan 13, 2025
d825475
Update order
sophialittlejohn Jan 13, 2025
07bed06
Install deps before gen:docs
sophialittlejohn Jan 13, 2025
2f5fb40
Add user to github
sophialittlejohn Jan 13, 2025
ef531bd
Convert script to cjs file
sophialittlejohn Jan 13, 2025
095efe0
Update clone url
sophialittlejohn Jan 13, 2025
5b6fde1
Add org name
sophialittlejohn Jan 13, 2025
c313385
Remove docs
sophialittlejohn Jan 14, 2025
1c2f461
Checkout branch later
sophialittlejohn Jan 14, 2025
20104b1
Update docs
actions-user Jan 14, 2025
20f6f74
Login to github
sophialittlejohn Jan 14, 2025
60a8a25
Update docs
actions-user Jan 14, 2025
fef7676
Use PAT token to allow pushing to repo
sophialittlejohn Jan 14, 2025
d97b7f1
Use auth url
sophialittlejohn Jan 14, 2025
20843ed
oauth for authorization
sophialittlejohn Jan 14, 2025
c380e23
Update docs
actions-user Jan 14, 2025
8477ffd
Regular auth?
sophialittlejohn Jan 14, 2025
53d1140
Fix naming
sophialittlejohn Jan 14, 2025
f6a84e5
Update docs
actions-user Jan 14, 2025
06481dd
Exclude readme and improve copy
sophialittlejohn Jan 14, 2025
2da740f
[ci:bot] Update docs
actions-user Jan 14, 2025
5924ed5
Unique branch names and earlier auth
sophialittlejohn Jan 14, 2025
e6c77e4
[ci:bot] Update docs
actions-user Jan 14, 2025
212732e
Fix branch name
sophialittlejohn Jan 14, 2025
da56da0
[ci:bot] Update docs
actions-user Jan 14, 2025
f4a0555
Exclude readme in typedoc generation
sophialittlejohn Jan 14, 2025
7cb1f39
[ci:bot] Update docs
actions-user Jan 14, 2025
862f7f1
Exclude _globals.md
sophialittlejohn Jan 14, 2025
95301cd
[ci:bot] Update docs
actions-user Jan 14, 2025
ae12cdc
Fix typo
sophialittlejohn Jan 14, 2025
34d731a
[ci:bot] Update docs
actions-user Jan 14, 2025
79d4569
Add note in readme and switch trigger branch to main
sophialittlejohn Jan 14, 2025
63f931b
Exclude docs from test coverage
sophialittlejohn Jan 15, 2025
25fd692
Improve script to handle updating index.html.md and deleting old files
sophialittlejohn Jan 15, 2025
e8e313e
Update target for testing
sophialittlejohn Jan 15, 2025
45d9bb1
[ci:bot] Update docs
actions-user Jan 15, 2025
216f00e
Test deployment without pushing docs/ folder
sophialittlejohn Jan 15, 2025
89e29cf
Move diff check into script
sophialittlejohn Jan 15, 2025
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
4 changes: 2 additions & 2 deletions .c8rc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"all": true,
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.d.ts", "src/**/*.test.ts", "src/types/*.ts", "src/tests/**/*.ts"],
"exclude": ["src/**/*.d.ts", "src/**/*.test.ts", "src/types/*.ts", "src/tests/**/*.ts", "docs/**/*.md"],
"reporter": ["text", "lcov"],
"extension": [".ts"],
"report-dir": "./coverage"
}
}
194 changes: 194 additions & 0 deletions .github/ci-scripts/update-sdk-docs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
const fs = require('fs/promises')
const path = require('path')
const simpleGit = require('simple-git')
const { execSync } = require('child_process')

/**
* Recursively gets all files from a directory
*/
async function getFilesRecursively(dir) {
const items = await fs.readdir(dir, { withFileTypes: true })
const files = await Promise.all(
items.map(async (item) => {
const filePath = path.join(dir, item.name)
return item.isDirectory() ? getFilesRecursively(filePath) : filePath
})
)
return files.flat()
}

/**
* Cleans generated documentation folders: classes and type-aliases
*/
async function cleanGeneratedDocs(targetDir) {
const foldersToClean = ['classes', 'type-aliases']
for (const folder of foldersToClean) {
const folderPath = path.join(targetDir, folder)
try {
await fs.rm(folderPath, { recursive: true, force: true })
console.log(`✨ Cleared existing ${folder} folder`)
} catch (error) {
if (error.code !== 'ENOENT') {
console.error(`❌ Error clearing ${folder} folder:`, error)
}
}
}
}

/**
* Copies documentation files while maintaining structure
*/
async function copyDocs(sourceDir, targetDir) {
try {
await fs.mkdir(targetDir, { recursive: true })
await cleanGeneratedDocs(targetDir)

const docFiles = await getFilesRecursively(sourceDir)
for (const file of docFiles) {
const relativePath = path.relative(sourceDir, file)
if (path.basename(file) === 'README.md') continue

const targetPath = path.join(targetDir, relativePath)
await fs.mkdir(path.dirname(targetPath), { recursive: true })
await fs.copyFile(file, targetPath)
console.log(`📄 Copied: ${relativePath}`)
}
} catch (error) {
console.error('❌ Error copying docs:', error)
throw error
}
}

/**
* Processes and updates the index.html.md file
*/
async function updateIndexHtmlMd(docsDir, indexHtmlMdPath) {
try {
const content = await fs.readFile(indexHtmlMdPath, 'utf8')
const docFiles = await getFilesRecursively(docsDir)

// Transform and validate files
const actualFiles = new Set(
docFiles.map((file) => path.relative(docsDir, file).replace(/\/_/g, '/').replace(/^_/, '').replace(/\.md$/, ''))
)

// Extract existing includes
const existingIncludesMatch = content.match(/^includes:\n((?: - .*\n)*)/m)
const existingIncludes = existingIncludesMatch
? existingIncludesMatch[1]
.split('\n')
.map((line) => line.replace(' - ', ''))
.filter(Boolean)
: []

// Process new includes
const newIncludes = docFiles
.filter((file) => file.endsWith('.md'))
.filter((file) => !file.includes('README.md'))
.map((file) => {
const relativePath = path.relative(docsDir, file)
return relativePath.replace(/\/_/g, '/').replace(/^_/, '').replace(/\.md$/, '')
})

// Combine and validate includes
const allIncludes = [
...existingIncludes.filter((inc) => !inc.startsWith('classes/') && !inc.startsWith('type-aliases/')),
...newIncludes,
]
.filter((value, index, self) => self.indexOf(value) === index)
.filter((include) => {
if (include.startsWith('classes/') || include.startsWith('type-aliases/')) {
const exists = actualFiles.has(include)
if (!exists) console.log(`⚠️ Warning: File not found for include: ${include}`)
return exists
}
return true
})

// Update content
const [frontmatterStart, ...rest] = content.split(/^includes:/m)
const remainingContent = rest.join('includes:').split('---')
const postFrontmatter = remainingContent.slice(1).join('---')

const updatedContent = `${frontmatterStart}includes:
${allIncludes.map((file) => ` - ${file}`).join('\n')}
search: true
---${postFrontmatter}`

await fs.writeFile(indexHtmlMdPath, updatedContent)

console.log(`✅ Total includes: ${allIncludes.length}`)
} catch (error) {
console.error('❌ Error updating index.html.md:', error)
throw error
}
}

/**
* Creates a PR in the sdk-docs repository
*/
async function createPullRequest(git, branchName) {
const prTitle = '[sdk:ci:bot] Update SDK Documentation'
const prBody =
'[sdk:ci:bot] Automated PR to update SDK documentation: [Actions](https://github.com/centrifuge/sdk/actions/workflows/update-docs.yml)'

const prCommand = `gh pr create \
--title "${prTitle}" \
--body "${prBody}" \
--base main \
--head ${branchName} \
--repo "centrifuge/sdk-docs"`

execSync(prCommand, {
stdio: 'inherit',
env: { ...process.env, GH_TOKEN: process.env.PAT_TOKEN },
})
}

async function hasChanges(git) {
try {
await git.fetch('origin', 'main')

// Get the diff between current state and main branch
const diff = await git.diff(['origin/main'])

return diff.length > 0
} catch (error) {
console.error('❌ Error checking for changes:', error)
throw error
}
}

async function main() {
try {
const git = simpleGit()
const repoUrl = `https://${process.env.PAT_TOKEN}@github.com/centrifuge/sdk-docs.git`

await git.clone(repoUrl)
await git
.cwd('./sdk-docs')
.addConfig('user.name', 'github-actions[bot]')
.addConfig('user.email', 'github-actions[bot]@users.noreply.github.com')

await copyDocs('./docs', './sdk-docs/source/includes')
await updateIndexHtmlMd('./docs', './sdk-docs/source/index.html.md')

if (await hasChanges(git)) {
const branchName = `docs-update-${new Date().toISOString().slice(0, 19).replace(/[:-]/g, '').replace(' ', '-')}`
await git.checkoutLocalBranch(branchName)
await git.add('.').commit('Update SDK documentation')
await git.push('origin', branchName)

await createPullRequest(git, branchName)
console.log('✅ Successfully created PR with documentation updates')
} else {
console.log('ℹ️ No documentation changes detected')
}
process.exit(0)
} catch (error) {
console.error('❌ Failed to process docs:', error)
process.exit(1)
}
}

main()
43 changes: 43 additions & 0 deletions .github/workflows/update-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This workflow will update the docs in the sdk-docs repo

name: Update Docs

on:
push:
branches:
- generate-docs

jobs:
update-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup Yarn
run: |
corepack enable
corepack prepare [email protected] --activate

- name: Install dependencies
run: yarn install

- name: Run gen:docs command
run: yarn gen:docs

- name: Install dependencies
run: yarn add simple-git

- name: Setup GitHub CLI
run: |
gh auth login --with-token <<< "${{ secrets.GITHUB_TOKEN }}"

- name: Update docs
env:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
run: node ./.github/ci-scripts/update-sdk-docs.cjs
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ yarn test:single <path-to-file>
yarn test:simple:single <path-to-file> # without setup file, faster and without tenderly setup
```

## User Docs

User docs are written and maintained in the [sdk-docs](https://github.com/centrifuge/sdk-docs) repository. On push to the `main` branch, a GitHub Action will run and update the docs with the auto-generated docs from this repository using ([typedoc](https://typedoc.org/)).

### PR Naming Convention

PR naming should follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@centrifuge/sdk",
"version": "0.0.0-alpha.5",
"version": "0.0.0-alpha.6",
"description": "",
"homepage": "https://github.com/centrifuge/sdk/tree/main#readme",
"author": "",
Expand Down Expand Up @@ -33,7 +33,8 @@
"test:simple:single": "mocha --loader=ts-node/esm --exit --timeout 60000",
"test:single": "mocha --loader=ts-node/esm --require $(pwd)/src/tests/setup.ts --exit --timeout 60000",
"test:ci": "yarn test --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
"test:coverage": "c8 yarn test:ci"
"test:coverage": "c8 yarn test:ci",
"gen:docs": "typedoc"
},
"dependencies": {
"decimal.js-light": "^2.5.1",
Expand Down Expand Up @@ -61,6 +62,8 @@
"sinon-chai": "^4.0.0",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.2",
"typedoc": "^0.27.6",
"typedoc-plugin-markdown": "^4.4.1",
"typescript": "~5.6.3",
"typescript-eslint": "^8.8.1",
"viem": "^2.21.25"
Expand Down
27 changes: 5 additions & 22 deletions src/Centrifuge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
type Abi,
type Account as AccountType,
type Chain,
type PublicClient,
type WalletClient,
type WatchEventOnLogsParameter,
} from 'viem'
Expand All @@ -34,31 +33,14 @@ import { chains } from './config/chains.js'
import type { CurrencyMetadata } from './config/lp.js'
import { PERMIT_TYPEHASH } from './constants.js'
import { Pool } from './Pool.js'
import type { HexString } from './types/index.js'
import type { Client, DerivedConfig, EnvConfig, HexString, UserProvidedConfig } from './types/index.js'
import type { CentrifugeQueryOptions, Query } from './types/query.js'
import type { OperationStatus, Signer, Transaction, TransactionCallbackParams } from './types/transaction.js'
import { Currency } from './utils/BigInt.js'
import { hashKey } from './utils/query.js'
import { makeThenable, repeatOnEvents, shareReplayWithDelayedReset } from './utils/rx.js'
import { doTransaction, isLocalAccount } from './utils/transaction.js'

export type Config = {
environment: 'mainnet' | 'demo' | 'dev'
rpcUrls?: Record<number | string, string>
indexerUrl: string
ipfsUrl: string
}

export type UserProvidedConfig = Partial<Config>
type EnvConfig = {
indexerUrl: string
alchemyKey: string
infuraKey: string
defaultChain: number
ipfsUrl: string
}
type DerivedConfig = Config & EnvConfig

const envConfig = {
mainnet: {
indexerUrl: 'https://subql.embrio.tech/',
Expand Down Expand Up @@ -93,7 +75,7 @@ export class Centrifuge {
return this.#config
}

#clients = new Map<number, PublicClient<any, Chain>>()
#clients = new Map<number, Client>()
getClient(chainId?: number) {
return this.#clients.get(chainId ?? this.config.defaultChain)
}
Expand Down Expand Up @@ -134,8 +116,8 @@ export class Centrifuge {
})
}

pool(id: string, metadataHash?: string) {
return this._query(null, () => of(new Pool(this, id, metadataHash)))
pool(id: string | number, metadataHash?: string) {
return this._query(null, () => of(new Pool(this, String(id), metadataHash)))
}

account(address: string, chainId?: number) {
Expand Down Expand Up @@ -535,6 +517,7 @@ export class Centrifuge {
* // { type: 'SigningTransaction', title: 'Invest' }
* // { type: 'TransactionPending', title: 'Invest', hash: '0x123...abc' }
* // { type: 'TransactionConfirmed', title: 'Invest', hash: '0x123...abc', receipt: { ... } }
* ```
*
* @internal
*/
Expand Down
4 changes: 4 additions & 0 deletions src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import type { CentrifugeQueryOptions } from './types/query.js'

export class Entity {
#baseKeys: (string | number)[]
/** @internal */
_transact: Centrifuge['_transact']
/** @internal */
_transactSequence: Centrifuge['_transactSequence']
constructor(
/** @internal */
protected _root: Centrifuge,
queryKeys: (string | number)[]
) {
Expand All @@ -15,6 +18,7 @@ export class Entity {
this._transactSequence = this._root._transactSequence.bind(this._root)
}

/** @internal */
protected _query<T>(
keys: (string | number | undefined)[] | null,
observableCallback: () => Observable<T>,
Expand Down
5 changes: 4 additions & 1 deletion src/Pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Reports } from './Reports/index.js'
import { PoolMetadata } from './types/poolMetadata.js'

export class Pool extends Entity {
/** @internal */
constructor(
_root: Centrifuge,
public id: string,
Expand All @@ -19,7 +20,9 @@ export class Pool extends Entity {
}

metadata() {
return this.metadataHash ? this._root._queryIPFS<PoolMetadata>(this.metadataHash) : of(undefined)
return this.metadataHash
? this._root._queryIPFS<PoolMetadata>(this.metadataHash)
: this._query(null, () => of(null))
}

trancheIds() {
Expand Down
Loading
Loading