Skip to content

feat: price dotNS names on a scarcity curve #290

feat: price dotNS names on a scarcity curve

feat: price dotNS names on a scarcity curve #290

Workflow file for this run

name: Documentation
# SECURITY ATTENTION
# This workflow uses `pull_request_target` for the on-close cleanup job
# only. `pull_request_target` runs in the BASE branch's workflow context
# with repository secrets and a write-scoped GITHUB_TOKEN. The cleanup
# job is gated on `github.event.action == 'closed'` and does NOT check
# out PR-head code, so no untrusted code executes under that context.
#
# Adding other `pull_request_target` event types or any step that
# checks out PR-head code under that trigger would let a malicious PR
# exfiltrate repo secrets. Re-review carefully if widened.
on:
push:
branches: [master]
paths: ["src/**", "contracts/**", "**.sol", "foundry.toml"]
pull_request:
branches: [master]
paths: ["src/**", "contracts/**", "**.sol", "foundry.toml"]
pull_request_target:
types: [closed]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
issues: write
concurrency:
group: pages-deploy
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
if: github.event.action != 'closed'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
with:
version: stable
- name: Install mdbook
run: |
MDBOOK_VERSION="0.4.40"
curl -sSL "https://github.com/rust-lang/mdBook/releases/download/v${MDBOOK_VERSION}/mdbook-v${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz" | tar -xz -C /usr/local/bin
- run: forge install
- name: Apply OZ payable patches
run: bash scripts/shell/apply-oz-patches.sh
- run: forge build
- name: Generate docs
id: docs
env:
FOUNDRY_DISABLE_NIGHTLY_WARNING: "1"
run: |
REPO_NAME="${{ github.event.repository.name }}"
# Step 1: Generate mdBook source (without building)
if ! forge doc --out docs-output 2>&1 | tee docs.log; then
echo "success=false" >> "$GITHUB_OUTPUT"
echo "error=forge doc failed" >> "$GITHUB_OUTPUT"
exit 0
fi
# Step 2: Determine the deploy subpath and inject site-url
if [ "${{ github.event_name }}" = "pull_request" ]; then
PR_NUM="${{ github.event.pull_request.number }}"
SITE_URL="/${REPO_NAME}/docs/preview/pr-${PR_NUM}/"
else
SITE_URL="/${REPO_NAME}/docs/"
fi
# Inject site-url into book.toml so assets resolve correctly on gh-pages
sed -i '/^\[output\.html\]/a site-url = "'"${SITE_URL}"'"' docs-output/book.toml
# Step 3: Build with mdbook
if ! mdbook build docs-output 2>&1 | tee -a docs.log; then
echo "success=false" >> "$GITHUB_OUTPUT"
echo "error=mdbook build failed" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ -d "docs-output/book" ]; then
touch docs-output/book/.nojekyll
COUNT=$(find docs-output/book -name "*.html" | wc -l | tr -d ' ')
echo "success=true" >> "$GITHUB_OUTPUT"
echo "count=${COUNT}" >> "$GITHUB_OUTPUT"
else
echo "success=false" >> "$GITHUB_OUTPUT"
echo "error=No documentation generated" >> "$GITHUB_OUTPUT"
fi
- name: Copy diagrams into doc/src
if: steps.docs.outputs.success == 'true'
run: |
# Expects a repo-level ./diagrams directory.
# Copies into the mdBook source tree (docs-output/src) as requested.
# Also copies into the built site (docs-output/book) so assets are served even if build already ran.
if [ -d "diagrams" ]; then
mkdir -p docs-output/src
rm -rf docs-output/src/diagrams
cp -R diagrams docs-output/src/diagrams
mkdir -p docs-output/book/diagrams
rm -rf docs-output/book/diagrams
cp -R diagrams docs-output/book/diagrams
else
echo "No ./diagrams directory found; skipping copy."
fi
- name: Set deploy path and URL
id: path
run: |
OWNER="${{ github.repository_owner }}"
REPO_NAME="${{ github.event.repository.name }}"
if [ "${{ github.event_name }}" = "pull_request" ]; then
PR_NUM="${{ github.event.pull_request.number }}"
echo "target=docs/preview/pr-${PR_NUM}" >> "$GITHUB_OUTPUT"
echo "url=https://${OWNER}.github.io/${REPO_NAME}/docs/preview/pr-${PR_NUM}/" >> "$GITHUB_OUTPUT"
else
echo "target=docs" >> "$GITHUB_OUTPUT"
echo "url=https://${OWNER}.github.io/${REPO_NAME}/docs/" >> "$GITHUB_OUTPUT"
fi
- name: Prepare result
id: run
run: |
if [ "${{ steps.docs.outputs.success }}" = "true" ]; then
COUNT="${{ steps.docs.outputs.count }}"
URL="${{ steps.path.outputs.url }}"
echo "result=Passed - ${COUNT} pages generated - [View Docs](${URL})" >> "$GITHUB_OUTPUT"
else
ERROR="${{ steps.docs.outputs.error }}"
echo "result=Failed - ${ERROR}" >> "$GITHUB_OUTPUT"
fi
- uses: actions/upload-artifact@v4
if: always()
with:
name: docs
path: |
docs-output/
docs.log
retention-days: 7
- name: Deploy to GitHub Pages
if: steps.docs.outputs.success == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bash scripts/shell/deploy-to-gh-pages.sh ./docs-output/book ${{ steps.path.outputs.target }}
- name: Update PR comment
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v7
env:
SECTION: Documentation
RESULT: ${{ steps.run.outputs.result }}
with:
script: |
const marker = "<!-- ci-summary -->";
const detailsMarker = "<!-- details-section -->";
const section = process.env.SECTION;
const result = process.env.RESULT || 'Unknown';
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number, per_page: 100,
});
const existing = comments.find(c =>
c.user?.login === "github-actions[bot]" && c.body?.includes(marker)
);
let rows = {};
let detailsSection = '';
if (existing?.body) {
const parts = existing.body.split(detailsMarker);
const tableSection = parts[0] || '';
if (parts[1]) detailsSection = `\n${detailsMarker}${parts[1]}`;
const lines = tableSection.split('\n');
for (const line of lines) {
const match = line.match(/^\| ([^|]+) \| ([^|]+) \|$/);
if (match) {
const name = match[1].trim();
if (name && name !== 'Check' && !name.startsWith(':')) {
rows[name] = match[2].trim();
}
}
}
}
rows[section] = result;
const order = ['4naly3er Analysis', 'Slither Analysis', 'Contract Tests (Unit + Fuzz)', 'Contract Tests (Invariant)', 'Coverage', 'Documentation', 'Format & Lint', 'Deploy Contracts', 'PR Title', 'Labels'];
const sortedKeys = Object.keys(rows).sort((a, b) => {
const ai = order.indexOf(a), bi = order.indexOf(b);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
let table = `| Check | Result |\n|:------|:-------|\n`;
for (const key of sortedKeys) {
table += `| ${key} | ${rows[key]} |\n`;
}
let body = `${marker}\n## CI Summary\n\n${table}${detailsSection}`;
// GitHub issue/PR comments are capped at 65536 characters. Fall back
// to the summary table only when the cumulative body would overflow;
// per-section reports remain reachable via row `[View Report]` links.
const MAX_BODY = 65000;
if (body.length > MAX_BODY) {
body = `${marker}\n## CI Summary\n\n${table}\n\n_Per-section details omitted: combined body exceeded the ${MAX_BODY}-char comment limit. Follow the **View Report** links above for each section's full output._`;
}
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}
- if: steps.docs.outputs.success != 'true'
run: exit 1
cleanup:
runs-on: ubuntu-latest
if: github.event.action == 'closed'
steps:
- uses: actions/checkout@v4
with:
ref: gh-pages
fetch-depth: 0
- run: |
DIR="docs/preview/pr-${{ github.event.pull_request.number }}"
[ -d "$DIR" ] || exit 0
rm -rf "$DIR"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "Clean up preview for PR #${{ github.event.pull_request.number }}" || true
git push