diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..458de94e --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,46 @@ +name: Publish API Docs +on: + push: + tags: ['v*'] + workflow_dispatch: +permissions: + contents: read +concurrency: + group: pages + cancel-in-progress: false +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0 + with: + bundler-cache: true + - run: ./script/docs + - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 + - name: Archive site + run: | + tar \ + --dereference --hard-dereference \ + --directory docs/_site \ + -cvf "$RUNNER_TEMP/artifact.tar" \ + --exclude=.git \ + --exclude=.github \ + . + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: github-pages + path: ${{ runner.temp }}/artifact.tar + retention-days: 1 + if-no-files-found: error + deploy: + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 + id: deployment diff --git a/.gitignore b/.gitignore index 28a094d7..c0369d5d 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ .vscode .idea/ +docs/_site/ +.yardoc/ diff --git a/.yardopts b/.yardopts new file mode 100644 index 00000000..9e582e14 --- /dev/null +++ b/.yardopts @@ -0,0 +1,6 @@ +--output-dir docs/_site +--markup markdown +lib/**/*.rb +- +README.md +CHANGELOG.md diff --git a/Gemfile b/Gemfile index be173b20..fb35914d 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,9 @@ source "https://rubygems.org" gemspec + +group :docs do + gem "yard" + gem "yard-markdown" + gem "webrick" +end diff --git a/Gemfile.lock b/Gemfile.lock index 5dd595f1..3ca8ae45 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,7 +17,10 @@ GEM crack (1.0.1) bigdecimal rexml + csv (3.3.5) + date (3.5.1) drb (2.2.3) + erb (6.0.4) hashdiff (1.2.1) json (2.19.4) jwt (3.1.2) @@ -33,10 +36,17 @@ GEM ast (~> 2.4.1) racc prism (1.9.0) + psych (5.3.1) + date + stringio public_suffix (7.0.5) racc (1.8.1) rainbow (3.1.1) rake (13.4.2) + rdoc (7.2.0) + erb + psych (>= 4.0.0) + tsort regexp_parser (2.12.0) rexml (3.4.4) rubocop (1.84.2) @@ -70,6 +80,8 @@ GEM standard-performance (1.9.0) lint_roller (~> 1.1) rubocop-performance (~> 1.26.0) + stringio (3.2.0) + tsort (0.2.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.2.0) @@ -77,6 +89,12 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) + webrick (1.9.2) + yard (0.9.43) + yard-markdown (0.7.1) + csv + rdoc + yard zeitwerk (2.7.5) PLATFORMS @@ -89,7 +107,10 @@ DEPENDENCIES rake standard (~> 1.49) webmock (~> 3.26) + webrick workos! + yard + yard-markdown CHECKSUMS addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af @@ -97,7 +118,10 @@ CHECKSUMS base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e + csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f + date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0 drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 + erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9 hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1 json (2.19.4) sha256=670a7d333fb3b18ca5b29cb255eb7bef099e40d88c02c80bd42a3f30fe5239ac jwt (3.1.2) sha256=af6991f19a6bb4060d618d9add7a66f0eeb005ac0bc017cd01f63b42e122d535 @@ -108,10 +132,12 @@ CHECKSUMS parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970 parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 + rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192 regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 rubocop (1.84.2) sha256=5692cea54168f3dc8cb79a6fe95c5424b7ea893c707ad7a4307b0585e88dbf5f @@ -121,10 +147,15 @@ CHECKSUMS standard (1.54.0) sha256=7a4b08f83d9893083c8f03bc486f0feeb6a84d48233b40829c03ef4767ea0100 standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2 + stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 + tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90 + webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131 workos (7.1.2) + yard (0.9.43) sha256=cf8733a8f0485df2a162927e9b5f182215a61f6d22de096b8f402c726a1c5821 + yard-markdown (0.7.1) sha256=06c378632dfe7ba053be9ba469eb4701aa0470e36bcf7e5546f353eb90c1bfd1 zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd BUNDLED WITH diff --git a/script/docs b/script/docs new file mode 100755 index 00000000..64f4e986 --- /dev/null +++ b/script/docs @@ -0,0 +1,16 @@ +#!/bin/sh +set -eu +cd "$(dirname "$0")/.." +bundle config set --local with 'docs' +bundle install +rm -rf docs/_site +# 1. Generate HTML docs (default YARD format). +bundle exec yard doc +# 2. Generate Markdown docs alongside the HTML, using the yard-markdown plugin. +# We point output at the same docs/_site directory so .md files land next to .html. +# --no-cache forces a re-parse so the markdown formatter sees fresh registry data. +# --plugin yard-markdown ensures the plugin is loaded without relying on ~/.yard/config. +bundle exec yard doc --plugin yard-markdown --format markdown --no-cache --output-dir docs/_site +# 3. Generate llms.txt (index) from the markdown output, so the published site +# is consumable by LLM tooling. +bundle exec ./script/llms-txt diff --git a/script/docs-serve b/script/docs-serve new file mode 100755 index 00000000..bba4cc2d --- /dev/null +++ b/script/docs-serve @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu +cd "$(dirname "$0")/.." +bundle config set --local with 'docs' +bundle install --quiet +if [ "${1:-}" = "--static" ]; then + if [ ! -d docs/_site ]; then + ./script/docs + fi + exec bundle exec ruby -run -e httpd -- docs/_site -p 4000 +fi +exec bundle exec yard server --reload --port 4000 diff --git a/script/llms-txt b/script/llms-txt new file mode 100755 index 00000000..ca82b7e8 --- /dev/null +++ b/script/llms-txt @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Generates llms.txt (curated index) from the Markdown docs produced by +# `yard --format markdown` in docs/_site. See https://llmstxt.org for the format. + +require "pathname" + +ROOT = Pathname.new(__dir__).join("..").expand_path +SITE = ROOT.join("docs/_site") +require ROOT.join("lib/workos/version").to_s + +abort "docs/_site is missing — run script/docs first" unless SITE.directory? + +md_files = Pathname.glob(SITE.join("**/*.md")).sort_by { |p| p.relative_path_from(SITE).to_s } + +title_for = lambda do |path| + first_heading = path.each_line.lazy.map(&:strip).find { |l| l.start_with?("# ") } + first_heading ? first_heading.sub(/^#\s+/, "").sub(/\s*\s*$/, "").strip : path.basename(".md").to_s +end + +index_lines = [] +index_lines << "# WorkOS Ruby SDK v#{WorkOS::VERSION}" +index_lines << "" +index_lines << "> API client for WorkOS. This file indexes the auto-generated YARD documentation for the `workos` gem so language models can locate per-class references." +index_lines << "" +index_lines << "## API Reference" +index_lines << "" +md_files.each do |path| + rel = path.relative_path_from(SITE).to_s + index_lines << "- [#{title_for.call(path)}](#{rel})" +end +index_lines << "" + +SITE.join("llms.txt").write(index_lines.join("\n")) + +puts "Wrote #{SITE.join("llms.txt")} (#{md_files.size} entries)"