From 0c8ed6fc7ff30d43bd337ff9f3e88b86ddcbbb89 Mon Sep 17 00:00:00 2001 From: asaf Date: Mon, 28 Apr 2025 11:39:41 -0400 Subject: [PATCH 1/5] Improve link checker to process all sitemaps - Add support for processing all sitemap files (not just main sitemap) - Implement section filtering capability - Enhance error handling and progress tracking - Improve reporting with a complete list of broken links --- Makefile | 13 +- scripts/link-checker/check-links.js | 273 ++++++++++++++++++++-------- scripts/link-checker/check-links.sh | 19 +- 3 files changed, 231 insertions(+), 74 deletions(-) diff --git a/Makefile b/Makefile index 23f8d5d3b93a..39638eec028e 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,18 @@ build: check_links: $(MAKE) banner $(MAKE) ensure - ./scripts/link-checker/check-links.sh "https://www.pulumi.com" + ./scripts/link-checker/check-links.sh "https://www.pulumi.com" "$(SECTION)" + +# Usage: make check_section SECTION=/docs/ +.PHONY: check_section +check_section: + @if [ -z "$(SECTION)" ]; then \ + echo "Error: SECTION variable is required. Example: make check_section SECTION=/docs/"; \ + exit 1; \ + fi + $(MAKE) banner + $(MAKE) ensure + ./scripts/link-checker/check-links.sh "https://www.pulumi.com" "$(SECTION)" .PHONY: check_search_urls check_search_urls: diff --git a/scripts/link-checker/check-links.js b/scripts/link-checker/check-links.js index 000d4d20cce8..191a4fd088e5 100644 --- a/scripts/link-checker/check-links.js +++ b/scripts/link-checker/check-links.js @@ -1,11 +1,10 @@ const { HtmlUrlChecker } = require("broken-link-checker"); const { WebClient, LogLevel } = require('@slack/web-api'); -const httpServer = require("http-server"); const Sitemapper = require("sitemapper"); const sitemap = new Sitemapper(); const path = require("path"); const fs = require("fs"); - +const axios = require("axios"); // Additional routes to check that are not included in the sitemap. const additionalRoutes = [ @@ -15,18 +14,25 @@ const additionalRoutes = [ "https://www.pulumi.com/registry/sitemap.xml", ] - /** * This script uses the programmatic API of https://github.com/stevenvachon/broken-link-checker - to check the links (including images, iframes, and client-side redirects) for either an individual page - or for a whole site. Usage: - - # Log successes as well as failures. - $ DEBUG=1 node scripts/check-links.js "https://www.pulumi.com" + * to check the links (including images, iframes, and client-side redirects) for either an individual page + * or for a whole site. Usage: + * + * # Standard check (default: no section filter) + * $ node scripts/link-checker/check-links.js "https://www.pulumi.com" 2 + * + * # Check specific section (e.g., only check /docs/ URLs) + * $ node scripts/link-checker/check-links.js "https://www.pulumi.com" 2 "/docs/" + * + * # Log successes as well as failures + * $ DEBUG=1 node scripts/link-checker/check-links.js "https://www.pulumi.com" 2 */ -let [ baseURL, maxRetries ] = process.argv.slice(2); +let [ baseURL, maxRetries, sectionFilter ] = process.argv.slice(2); let retryCount = 0; +let totalCheckedLinks = 0; +let startTime = Date.now(); if (!baseURL) { throw new Error("A baseURL (e.g., 'https://pulumi.com') is required."); @@ -61,14 +67,30 @@ checkLinks(); // Runs the checker. async function checkLinks() { - const checker = getChecker([]); - - // Load all URLs. - const urls = await getURLsToCheck(baseURL); + const brokenLinks = []; + const checker = getChecker(brokenLinks); - // Start the checker. - checker.enqueue(baseURL); - urls.forEach(url => checker.enqueue(url)); + console.log("=== Link Checker Started ==="); + console.log(`Base URL: ${baseURL}`); + if (sectionFilter) { + console.log(`Section filter: ${sectionFilter}`); + } + console.log(`Max retries: ${maxRetries}`); + + try { + // Get all URLs from the main sitemap AND section sitemaps + console.log("Fetching URLs from sitemaps..."); + const urls = await getAllUrlsToCheck(baseURL); + + console.log(`Found ${urls.length} URLs to check`); + + // Start the checker with the base URL and all URLs + checker.enqueue(baseURL); + urls.forEach(url => checker.enqueue(url)); + } catch (error) { + console.error(`Error fetching URLs: ${error.message}`); + process.exit(1); + } } // Returns an instance of either HtmlUrlChecker. @@ -101,21 +123,29 @@ function getDefaultHandlers(brokenLinks) { return { link: (result) => { try { + totalCheckedLinks++; + + // Show progress periodically + if (totalCheckedLinks % 500 === 0) { + const elapsedMinutes = ((Date.now() - startTime) / 1000 / 60).toFixed(1); + console.log(`Progress: Checked ${totalCheckedLinks} links in ${elapsedMinutes} minutes, found ${brokenLinks.length} broken links`); + } + onLink(result, brokenLinks); } catch (error) { - fail(error); + console.error(`Error in link handler: ${error.message}`); } }, error: (error) => { - fail(error); + console.error(`Checker error: ${error.message}`); }, page: (error, pageURL) => { try { onPage(error, pageURL, brokenLinks); } catch(error) { - fail(error); + console.error(`Error in page handler: ${error.message}`); } }, end: async () => { @@ -123,7 +153,8 @@ function getDefaultHandlers(brokenLinks) { await onComplete(brokenLinks); } catch (error) { - fail(error); + console.error(`Error in end handler: ${error.message}`); + process.exit(1); } }, }; @@ -143,9 +174,8 @@ function onLink(result, brokenLinks) { logLink(source, destination, reason); } else if (process.env.DEBUG) { - // Log successes when DEBUG is truthy. - logLink(source, destination, result.http.response.statusCode); + logLink(source, destination, result.http?.response?.statusCode || "SUCCESS"); } } @@ -163,26 +193,63 @@ function onPage(error, pageURL, brokenLinks) { // Handles the BLC 'complete' event, which is raised at the end of a run. async function onComplete(brokenLinks) { const filtered = excludeAcceptable(brokenLinks); + const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(1); - if (filtered.length > 0) { + console.log("=== Link Check Completed ==="); + console.log(`Total time: ${elapsedTime} seconds`); + console.log(`Total links checked: ${totalCheckedLinks}`); + console.log(`Total broken links found: ${filtered.length}`); + if (filtered.length > 0) { // If we failed and a retry count was provided, retry. Note that retry count !== // run count, so a retry count of 1 means run once, then retry once, which means a // total run count of two. if (maxRetries > 0 && retryCount < maxRetries) { retryCount += 1; console.log(`Retrying (${retryCount} of ${maxRetries})...`); + + // Reset counters + totalCheckedLinks = 0; + startTime = Date.now(); + checkLinks(); return; } + // Group broken links by reason + const groupedByReason = {}; + filtered.forEach(link => { + if (!groupedByReason[link.reason]) { + groupedByReason[link.reason] = []; + } + groupedByReason[link.reason].push(link); + }); + + // Display summary by reason + console.log(`Broken links by reason:`); + Object.keys(groupedByReason).forEach(reason => { + console.log(`${reason}: ${groupedByReason[reason].length} links`); + }); + + // List all broken links + console.log("\nList of all broken links:"); + filtered.forEach(link => { + console.log(`${link.source} -> ${link.destination} (${link.reason})`); + }); + + // Format for Slack const list = filtered .map(link => `:link: <${link.source}|${new URL(link.source).pathname}> → ${link.destination} (${link.reason})`) .join("\n"); // Post the results to Slack. - console.warn("Posting to slack: " + list); - await postToSlack("docs-ops", list); + console.log(`Posting ${filtered.length} broken links to Slack...`); + await postToSlack("docs-ops", `Found ${filtered.length} broken links:\n${list}`); + + // Exit with error code + process.exit(1); + } else { + console.log(`All links are valid!`); } } @@ -339,14 +406,18 @@ async function postToSlack(channel, text) { return; } - const client = new WebClient(token, { logLevel: LogLevel.ERROR }); - return await client.chat.postMessage({ - text, - channel: `#${channel}`, - as_user: true, - mrkdwn: true, - unfurl_links: false, - }); + try { + const client = new WebClient(token, { logLevel: LogLevel.ERROR }); + return await client.chat.postMessage({ + text, + channel: `#${channel}`, + as_user: true, + mrkdwn: true, + unfurl_links: false, + }); + } catch (error) { + console.error(`Error posting to Slack: ${error.message}`); + } } // Adds a broken link to the running list. @@ -360,46 +431,104 @@ function addLink(source, destination, reason, links) { // Logs a link result to the console. function logLink(source, destination, reason) { - console.log(source); - console.log(` -> ${destination}`); - console.log(` -> ${reason}`); - console.log(); + if (reason && (reason.toString().startsWith('4') || reason.toString().startsWith('5') || + typeof reason === 'string' && !reason.match(/^2\d\d$/))) { + console.log(`BROKEN: ${source} -> ${destination} (${reason})`); + } else if (process.env.DEBUG) { + console.log(`OK: ${source} -> ${destination} (${reason})`); + } } -// Logs and exits immediately. -function fail(error) { - console.error(error.message); - process.exit(1); +// Get all URLs to check from multiple sitemaps +async function getAllUrlsToCheck(base) { + try { + // Set of URLs to check to avoid duplicates + const allUrls = new Set(); + + // Add the known section sitemaps for Pulumi docs site + const sitemaps = [ + // Main sitemap + `${base}/sitemap.xml`, + + // Section sitemaps (based on examining the repo) + `${base}/static/sitemaps/sitemap-blog.xml`, + `${base}/static/sitemaps/sitemap-docs.xml`, + `${base}/static/sitemaps/sitemap-tutorials.xml`, + `${base}/static/sitemaps/sitemap-templates.xml`, + `${base}/static/sitemaps/sitemap-registry.xml`, + `${base}/static/sitemaps/sitemap-case-studies.xml`, + `${base}/static/sitemaps/sitemap-product.xml`, + `${base}/static/sitemaps/sitemap-compliance.xml`, + `${base}/static/sitemaps/sitemap-what-is.xml`, + `${base}/static/sitemaps/sitemap-other.xml`, + ]; + + // Try each sitemap + for (const sitemapUrl of sitemaps) { + try { + console.log(`Processing sitemap: ${sitemapUrl}`); + const urls = await processSitemap(sitemapUrl, base); + + // Add to our set + urls.forEach(url => allUrls.add(url)); + + console.log(`Found ${urls.length} URLs in ${sitemapUrl}`); + } catch (error) { + console.log(`Could not process ${sitemapUrl}: ${error.message}`); + } + } + + // Convert to array + let urls = [...allUrls]; + + // Apply section filter if provided + if (sectionFilter) { + urls = urls.filter(url => url.includes(sectionFilter)); + console.log(`Applied section filter "${sectionFilter}": ${urls.length} URLs remaining`); + } + + // Add the additional routes + urls = urls.concat(additionalRoutes); + + return urls; + } catch (error) { + console.error(`Error processing sitemaps: ${error.message}`); + throw error; + } } -// Start by fetching the sitemap from `baseURL`. -async function getURLsToCheck(base) { - return await sitemap - .fetch(`${base}/sitemap.xml`) - .then(map => { - const urls = map.sites - - // Exclude resource docs, SDK docs, and CLI download pages. - .filter(page => !page.match(/\/registry\/packages\/.+\/api-docs\//)) - .filter(page => !page.match(/\/docs\/reference\/pkg\/nodejs|python\//)) - .filter(page => !page.match(/\/docs\/install\/versions\//)) - - // Always check using the supplied baseURL. - .map(url => { - const newURL = new URL(url); - const baseURLObj = new URL(base); - newURL.hostname = baseURLObj.hostname; - newURL.protocol = baseURLObj.protocol; - return newURL.toString(); - }) - - // Tack on any additional pages we'd like to check. - .concat(additionalRoutes) - - // Sort everything alphabetically. - .sort(); - - // Return the list of URLs to be crawled. - return urls; - }); -} +// Process a single sitemap +async function processSitemap(sitemapUrl, base) { + try { + const result = await sitemap.fetch(sitemapUrl); + + if (!result || !result.sites || !Array.isArray(result.sites)) { + return []; + } + + const urls = result.sites; + + // Exclude resource docs, SDK docs, and CLI download pages. + const filtered = urls + .filter(page => !page.match(/\/registry\/packages\/.+\/api-docs\//)) + .filter(page => !page.match(/\/docs\/reference\/pkg\/nodejs|python\//)) + .filter(page => !page.match(/\/docs\/install\/versions\//)); + + // Always check using the supplied baseURL. + return filtered.map(url => { + try { + const newURL = new URL(url); + const baseURLObj = new URL(base); + newURL.hostname = baseURLObj.hostname; + newURL.protocol = baseURLObj.protocol; + return newURL.toString(); + } catch (e) { + console.warn(`Skipping invalid URL: ${url}`); + return null; + } + }).filter(url => url !== null); + } catch (error) { + console.error(`Error fetching sitemap ${sitemapUrl}: ${error.message}`); + return []; + } +} \ No newline at end of file diff --git a/scripts/link-checker/check-links.sh b/scripts/link-checker/check-links.sh index 06a31e5510e6..6e1f0f53c16a 100755 --- a/scripts/link-checker/check-links.sh +++ b/scripts/link-checker/check-links.sh @@ -5,5 +5,22 @@ source ./scripts/common.sh echo "Checking links..." +# Get required arguments base_url="$1" -node "./scripts/link-checker/check-links.js" "$base_url" 2 +max_retries=2 + +# Optional section filter (defaults to empty - check all) +section_filter="" +if [ "$#" -gt 1 ]; then + section_filter="$2" +fi + +# Print information about what we're checking +echo "Base URL: $base_url" +if [ -n "$section_filter" ]; then + echo "Section filter: $section_filter" +fi +echo "Max retries: $max_retries" + +# Run the link checker +node "./scripts/link-checker/check-links.js" "$base_url" "$max_retries" "$section_filter" From 49f3fd4c3a9b3f7f582a8d6ee0faf2e3c0ceab9e Mon Sep 17 00:00:00 2001 From: asaf Date: Mon, 28 Apr 2025 15:00:07 -0400 Subject: [PATCH 2/5] feat: Create CLAUDE.md with essential commands and guidelines - Add reference guide for Claude Code AI - Include essential commands for build, testing, and validation - Include key guidelines with references to other documentation files - Organize content into logical sections - Avoid duplication by referencing other files for detailed information --- CLAUDE.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000000..51429908a91a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,41 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +> **Related documentation:** +> - [CONTRIBUTING.md](CONTRIBUTING.md) - Guide for contributing to Pulumi documentation +> - [STYLE-GUIDE.md](STYLE-GUIDE.md) - Comprehensive style and UX standards +> - [BLOGGING.md](BLOGGING.md) - Instructions for writing blog posts +> - [CODE-EXAMPLES.md](CODE-EXAMPLES.md) - Guidelines for creating code examples +> - [SEO.md](SEO.md) - Search engine optimization checklist + +## Essential Commands + +### Build and Development +- `make ensure`: Install dependencies and build assets +- `make build`: Build the site to ./public +- `make serve`: Run Hugo server locally at http://localhost:1313 + +### Validation +- `make lint`: Check Markdown and other files for correctness +- `make format`: Format all applicable files + +### Testing +- `make test`: Run all example program tests +- `ONLY_TEST="example-name" make test-programs`: Run a specific test + +### Content Creation +- `make new-blog-post`: Generate scaffold for a new blog post +- `make new-tutorial`: Create a new tutorial page +- `make new-example-program`: Generate a new multi-language code example + +## Key Guidelines + +For comprehensive guidelines, please refer to the specific documentation files linked above. Here are the most critical points: + +- One h1 per page, use semantic line breaks +- Place tested code examples in /static/programs/ +- Use descriptive link text (no "click here" or "here") +- Create redirects with Hugo aliases when changing URLs +- Follow language-specific conventions in [STYLE-GUIDE.md](STYLE-GUIDE.md) +- Test all code examples before submitting \ No newline at end of file From a872ef5101ee0d032edaffb3f1ad470f098dc601 Mon Sep 17 00:00:00 2001 From: asaf Date: Mon, 28 Apr 2025 15:00:28 -0400 Subject: [PATCH 3/5] feat: Consolidate UX-STANDARDS.md into STYLE-GUIDE.md - Merge UX standards into the style guide for comprehensive guidance - Add section for component usage recommendations - Include link recommendations for buttons and links - Maintain all original guidance from both documents - Add cross-references to related documentation files --- STYLE-GUIDE.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/STYLE-GUIDE.md b/STYLE-GUIDE.md index 54317bf74bbd..a1c31334e00a 100644 --- a/STYLE-GUIDE.md +++ b/STYLE-GUIDE.md @@ -1,10 +1,16 @@ # Style Guide -This document defines some general styles we adhere to in the docs. +This document defines general styles and user experience (UX) standards we adhere to in the Pulumi documentation, marketing website, and registry. + +> **Related documentation:** +> - [CONTRIBUTING.md](CONTRIBUTING.md) - Guide for contributing to Pulumi documentation +> - [BLOGGING.md](BLOGGING.md) - Instructions for writing blog posts +> - [CODE-EXAMPLES.md](CODE-EXAMPLES.md) - Guidelines for creating code examples +> - [SEO.md](SEO.md) - Search engine optimization checklist ## Language -Words are important. Pulumi strives to use language that is clear, harmonious, and friendly to all readers. With these goals in mind, we use the following guidelines: +Words are important. Pulumi strives to use language that is clear, harmonious, and friendly to all readers. With these goals in mind, we use the following guidelines: * Avoid ableist language: * Instead of _crazy_ try _wild_. @@ -21,13 +27,18 @@ Words are important. Pulumi strives to use language that is clear, harmonious, a * Ensure that readers are able to scan the headings of a page and get an effective overview of the page's content. * Every page should have exactly one `h1`. -* Headings levels should only increment one level at a time. E.g., if your previous heading level was an `h2`, the next heading must be an `h2` or an `h3`, but not, e.g., an `h4` or `h5`. +* Headings levels should only increment one level at a time. E.g., if your previous heading level was an `h2`, the next heading must be an `h2` or an `h3`, but not, e.g., an `h4` or `h5`. * Docs and registry headings should use sentence case (i.e., first letter of the first word is capitalized). -## Links +## Links and Buttons * Link text should be descriptive and have meaning outside of the surrounding context: Avoid link text like _here_, _click here_, _see here_ and instead link to the title of the linked page, e.g. "see [Pulumi Pricing](https://www.pulumi.com/pricing/)". (While this practice benefits all readers, it is of particular importance for visually impaired users who use screen readers and often jump through the links of a document.) * When changing the URL for an already existing page, add a redirect by using a [Hugo alias](https://gohugo.io/content-management/urls/#yaml-front-matter). +* Use descriptive text for button labels and links, and avoid generic labels such as "Learn more." +* Use unique labels for distinct actions - having two buttons with the same label that take different actions is confusing to users. +* Links should take you to another page, and buttons should take an action on the page. +* When possible, links should look like links, and buttons should look like buttons. +* When not a special case (e.g., navigation), links should exist within the context of text. ## Notes and Warnings @@ -58,6 +69,7 @@ This bit of info is serious. If you missed it, bad things could happen. ## Paragraphs and Line Breaks * Keep paragraphs short, rarely use 4 or more sentences in a single paragraph. +* Use line lengths between 45 – 75 characters (this is in reference to the rendered page, not the markdown). * When writing paragraphs and long sentences, use one of the following: @@ -126,13 +138,18 @@ This bit of info is serious. If you missed it, bad things could happen. ## Content Design * Lead with content that excites and engages, end with exactly one call-to-action. +* Every page should have no more than one primary action. * Try and save links for the last 75% of the content. * Use headings and lists to make content scannable and consumable. * Use visuals: code, graphs, videos, architecture diagrams, etc. * Highlight important points. * Avoid emojis unless for notes or absolutely necessary. +## Component Usage + +* Use existing components, or improve existing components over creating new components unless you identify a need that is not solveable with the current components. + ## Additional Resources * Markdownlint will help enforce syntactically valid Markdown and is available as a [CLI tool](https://github.com/igorshubovych/markdownlint-cli#installation) or as a [Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint). -* [Writing inclusive documentation](https://developers.google.com/style/inclusive-documentation) (Google). +* [Writing inclusive documentation](https://developers.google.com/style/inclusive-documentation) (Google). \ No newline at end of file From 5af3d6b5344071c5c1c7b23e1c3399b223bf94a7 Mon Sep 17 00:00:00 2001 From: asaf Date: Mon, 28 Apr 2025 15:00:55 -0400 Subject: [PATCH 4/5] feat: Enhance CONTRIBUTING.md with improved navigation - Add quick navigation section at the top - Create task-oriented "What would you like to do?" section - Add testing and common commands sections - Add additional resources section with links to related docs - Preserve all existing technical content and guidance --- CONTRIBUTING.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1972c1af1d6b..35f694da5709 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,22 @@ -# Contributing Pulumi Documentation +# Contributing to Pulumi Documentation + +This guide will help you contribute to Pulumi's documentation. Use the navigation below to find what you need quickly. + +**Quick navigation:** +- [Documentation Overview](#documentation-structure) +- [Creating Links](#links-to-other-files) +- [Hugo Tips](#hugo-tips) +- [Style Guidelines](#style-guide) +- [Writing Blog Posts](BLOGGING.md) +- [Writing Code Examples](CODE-EXAMPLES.md) +- [Style Guide (Comprehensive)](STYLE-GUIDE.md) + +## What would you like to do? + +- **Edit existing content:** Follow our [style guide](#style-guide) and use [Hugo's relref shortcode](#links-to-other-files) for internal links +- **Add new content:** Understand our [documentation structure](#documentation-structure) and use our [Hugo tips](#hugo-tips) +- **Test your changes:** Run `make serve` to preview changes locally at http://localhost:1313 +- **Write a blog post:** See the [blogging guide](BLOGGING.md) ## Pulumi terminology @@ -51,6 +69,40 @@ Which, on a page inside the `./content/reference` directory, will generate: - **no_on_this_page** Specify this variable to prevent displaying an "On This Page" TOC on the right nav for the page. - **block_external_search_index** Specify this variable to prevent crawlers from indexing the page. + +## Testing your changes + +To see how your changes look locally before submitting a pull request: + +```bash +# Install dependencies and build assets +make ensure + +# Run the development server +make serve +``` + +Then visit http://localhost:1313 to preview your changes. + +## Common commands + +```bash +# Build the entire website +make build + +# Run linting on Markdown and other files +make lint + +# Format all applicable files +make format + +# Run all tests +make test + +# Test specific programs +ONLY_TEST="example-name" make test-programs +``` + ## Style guide ### Language and terminology styles @@ -84,3 +136,11 @@ If a tutorial has more following tutorials, use a **Next steps** section at the ## Blog Post Authoring For instructions on contributing to the [Pulumi blog](https://www.pulumi.com/blog/), [see BLOGGING.md](BLOGGING.md). + +## Additional Resources + +For more detailed guidance, please refer to: + +- [Style Guide](STYLE-GUIDE.md) - Comprehensive style and UX standards +- [Code Examples](CODE-EXAMPLES.md) - Guidelines for writing code examples +- [SEO Guidelines](SEO.md) - Search engine optimization guidance \ No newline at end of file From d242560ce83494662069df44512de52916cf16fa Mon Sep 17 00:00:00 2001 From: asaf Date: Mon, 28 Apr 2025 15:01:17 -0400 Subject: [PATCH 5/5] feat: Add cross-references between documentation files - Add related documentation sections to all files - Create navigation links between documents - Ensure consistent references across the documentation - Improve SEO.md with proper title and introduction - Enhance discoverability of related documentation --- BLOGGING.md | 6 ++++++ CODE-EXAMPLES.md | 5 +++++ SEO.md | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/BLOGGING.md b/BLOGGING.md index 619138c66121..cb25247e1905 100644 --- a/BLOGGING.md +++ b/BLOGGING.md @@ -3,6 +3,12 @@ So you're interested in contributing to the Pulumi blog? Great! Follow these steps to make it happen. +> **Related documentation:** +> - [CONTRIBUTING.md](CONTRIBUTING.md) - General contribution guidelines +> - [STYLE-GUIDE.md](STYLE-GUIDE.md) - Comprehensive style and UX standards +> - [CODE-EXAMPLES.md](CODE-EXAMPLES.md) - Guidelines for code examples +> - [SEO.md](SEO.md) - SEO optimization checklist + ## Set Up Your Development Environment If you haven't already, clone this repository and diff --git a/CODE-EXAMPLES.md b/CODE-EXAMPLES.md index e92fc6ed6942..9a7c3b919b96 100644 --- a/CODE-EXAMPLES.md +++ b/CODE-EXAMPLES.md @@ -2,6 +2,11 @@ Our documentation site at [pulumi.com/docs](http://pulumi.com/docs) provides a variety of code examples embedded within our doc pages to help users understand and apply Pulumi concepts. Ensuring the accuracy and reliability of these examples is essential, which is why we have automated testing available. +> **Related documentation:** +> - [CONTRIBUTING.md](CONTRIBUTING.md) - General contribution guidelines +> - [STYLE-GUIDE.md](STYLE-GUIDE.md) - Comprehensive style and UX standards +> - [BLOGGING.md](BLOGGING.md) - Guidelines for blog posts that might include code examples + In order to get automated testing of your code example, the code must be added to a specific directory, and integrated into the document with a [Hugo shortcode](https://gohugo.io/content-management/shortcodes/). Once added, code examples are tested through an [automated pipeline](https://github.com/pulumi/docs/blob/master/.github/workflows/pull-request.yml) on a [regular cadence](https://github.com/pulumi/docs/blob/master/.github/workflows/scheduled-test.yml) to maintain ongoing accuracy. ## How it works diff --git a/SEO.md b/SEO.md index 73a198829e05..6f5e5f99522e 100644 --- a/SEO.md +++ b/SEO.md @@ -1,3 +1,13 @@ +# SEO Guidelines + +This document provides a checklist for search engine optimization (SEO) of Pulumi documentation, blog posts, and other web content. + +> **Related documentation:** +> - [CONTRIBUTING.md](CONTRIBUTING.md) - General contribution guidelines +> - [STYLE-GUIDE.md](STYLE-GUIDE.md) - Comprehensive style and UX standards +> - [BLOGGING.md](BLOGGING.md) - Guidelines for blog posts +> - [CODE-EXAMPLES.md](CODE-EXAMPLES.md) - Guidelines for code examples + ## SEO Checklist - [ ] Identify whether the intent of the piece is informational, preferential, navigational, or transactional