From 8aab77a5c0341bf3b80f04e882952e7e89bef630 Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Fri, 17 Oct 2025 18:07:15 -0400 Subject: [PATCH 1/7] Update wiki workflow --- .github/workflows/update-wiki.yml | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index 96a1e2e..a6bdda6 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -10,6 +10,11 @@ on: types: [closed] paths: - 'docs/**' + # Allow manual runs for diagnostics and one-off pushes + workflow_dispatch: {} + +permissions: + contents: write jobs: update-wiki: @@ -17,6 +22,28 @@ jobs: if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) }} runs-on: ubuntu-latest steps: + - name: Debug: print event info + run: | + echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME" + echo "GITHUB_REF=$GITHUB_REF" + echo "GITHUB_SHA=$GITHUB_SHA" + echo "Event payload path: $GITHUB_EVENT_PATH" + if [ -f "$GITHUB_EVENT_PATH" ]; then echo "--- event payload ---"; cat "$GITHUB_EVENT_PATH"; echo "--- end payload ---"; fi + + - name: Debug: list changed files (best-effort) + id: list_changed + run: | + # fetch enough history to inspect the commit + git fetch --no-tags --prune --depth=5 origin "${{ github.ref }}" || true + echo "Files changed in this commit/PR:" + git --no-pager show --name-only --pretty="" "${{ github.sha }}" || git ls-files | sed -n '1,200p' + + - name: Add changed files to job summary + if: always() + run: | + echo "### Changed files" >> "$GITHUB_STEP_SUMMARY" + git --no-pager show --name-only --pretty="" "${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY" || git ls-files | sed -n '1,200p' >> "$GITHUB_STEP_SUMMARY" + - name: Checkout repository uses: actions/checkout@v4 with: @@ -33,6 +60,7 @@ jobs: - name: Run wiki init and push env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Use a deploy PAT stored in repository secrets (create secret DEPLOY_WIKI_TOKEN) + GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} run: | node scripts/init-wiki.js --docs docs/wiki From 4c72628f99a6a8a3a036ee1ede219fd0bceec8d0 Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Fri, 17 Oct 2025 18:14:12 -0400 Subject: [PATCH 2/7] Patch update wiki workflow --- .github/workflows/update-wiki.yml | 37 ++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index a6bdda6..0039fcb 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -2,15 +2,11 @@ name: Update Wiki on: push: - branches: - - main - paths: - - 'docs/**' + branches: [ main ] + paths: [ 'docs/**' ] pull_request: - types: [closed] - paths: - - 'docs/**' - # Allow manual runs for diagnostics and one-off pushes + types: [ closed ] + paths: [ 'docs/**' ] workflow_dispatch: {} permissions: @@ -18,10 +14,26 @@ permissions: jobs: update-wiki: - # Only run for push events, or for pull_request closed events where the PR was merged. if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) }} runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fail early if DEPLOY_WIKI_TOKEN is missing + env: + DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + run: | + if [ -z "$DEPLOY_WIKI_TOKEN" ]; then + echo "ERROR: DEPLOY_WIKI_TOKEN repository secret is not set." + echo "Create it at: Settings → Secrets and variables → Actions → New repository secret" + echo "Name: DEPLOY_WIKI_TOKEN" + echo "Value: a personal access token (PAT) with 'Contents: Write' for this repository (or 'repo' scope for classic PATs)." + exit 1 + fi + - name: Debug: print event info run: | echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME" @@ -44,11 +56,6 @@ jobs: echo "### Changed files" >> "$GITHUB_STEP_SUMMARY" git --no-pager show --name-only --pretty="" "${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY" || git ls-files | sed -n '1,200p' >> "$GITHUB_STEP_SUMMARY" - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -63,4 +70,4 @@ jobs: # Use a deploy PAT stored in repository secrets (create secret DEPLOY_WIKI_TOKEN) GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} run: | - node scripts/init-wiki.js --docs docs/wiki + node scripts/init-wiki.js --docs docs From 26121e5bc56df930e14337c14d30ca6fbba7ad1a Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Fri, 17 Oct 2025 18:14:38 -0400 Subject: [PATCH 3/7] Check for CI --- scripts/init-wiki.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/init-wiki.js b/scripts/init-wiki.js index c3c250f..a057509 100644 --- a/scripts/init-wiki.js +++ b/scripts/init-wiki.js @@ -81,6 +81,21 @@ async function main() { console.log('Remote:', opts.remote); if (opts.dryRun) console.log('Dry run: no git clone/commit/push will be executed'); + const isCI = process.env.GITHUB_ACTIONS === 'true' || process.env.CI === 'true'; + + // If running in CI, ensure we have a token to authenticate pushes. Prefer DEPLOY_WIKI_TOKEN but fall back to GITHUB_TOKEN. + if (isCI && !opts.dryRun) { + if (!process.env.DEPLOY_WIKI_TOKEN && !process.env.GITHUB_TOKEN) { + console.error('ERROR: No authentication token available in CI.'); + console.error('Set repository secret DEPLOY_WIKI_TOKEN (a PAT with Contents: write) or ensure GITHUB_TOKEN is available.'); + process.exit(20); + } + // If DEPLOY_WIKI_TOKEN is provided, prefer it by setting GITHUB_TOKEN for downstream code that reads it. + if (process.env.DEPLOY_WIKI_TOKEN) { + process.env.GITHUB_TOKEN = process.env.DEPLOY_WIKI_TOKEN; + } + } + if (!fs.existsSync(docsDir)) { console.error('Docs wiki folder not found at', docsDir); process.exitCode = 2; @@ -105,7 +120,6 @@ async function main() { } // If running in CI with a GITHUB_TOKEN available, inject it into an https wiki URL so git can authenticate. - const isCI = process.env.GITHUB_ACTIONS === 'true' || process.env.CI === 'true'; let maskedWikiUrl = wikiUrl; if (wikiUrl && isCI && process.env.GITHUB_TOKEN) { try { From 57e21086cf0f363ca1e1f3121406d58ba6b10a04 Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Fri, 17 Oct 2025 18:25:33 -0400 Subject: [PATCH 4/7] Doc generation workflow patch --- .github/workflows/update-wiki.yml | 50 +++++++------------------------ .gitignore | 0 package.json | 10 +++++++ 3 files changed, 20 insertions(+), 40 deletions(-) create mode 100644 .gitignore create mode 100644 package.json diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index 0039fcb..3274c5e 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -2,19 +2,15 @@ name: Update Wiki on: push: - branches: [ main ] - paths: [ 'docs/**' ] - pull_request: - types: [ closed ] - paths: [ 'docs/**' ] - workflow_dispatch: {} + branches: + - main + paths: + - docs/** -permissions: - contents: write + workflow_dispatch: {} jobs: update-wiki: - if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) }} runs-on: ubuntu-latest steps: - name: Checkout repository @@ -22,52 +18,26 @@ jobs: with: fetch-depth: 0 + - name: Echo docs trigger + run: echo "Update Wiki workflow triggered for docs changes" + - name: Fail early if DEPLOY_WIKI_TOKEN is missing env: DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} run: | if [ -z "$DEPLOY_WIKI_TOKEN" ]; then - echo "ERROR: DEPLOY_WIKI_TOKEN repository secret is not set." - echo "Create it at: Settings → Secrets and variables → Actions → New repository secret" - echo "Name: DEPLOY_WIKI_TOKEN" - echo "Value: a personal access token (PAT) with 'Contents: Write' for this repository (or 'repo' scope for classic PATs)." + echo "WARNING: DEPLOY_WIKI_TOKEN is not set; the workflow will not be able to push to the wiki." + echo "Set the DEPLOY_WIKI_TOKEN secret or provide GITHUB_TOKEN permissions." exit 1 fi - - name: Debug: print event info - run: | - echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME" - echo "GITHUB_REF=$GITHUB_REF" - echo "GITHUB_SHA=$GITHUB_SHA" - echo "Event payload path: $GITHUB_EVENT_PATH" - if [ -f "$GITHUB_EVENT_PATH" ]; then echo "--- event payload ---"; cat "$GITHUB_EVENT_PATH"; echo "--- end payload ---"; fi - - - name: Debug: list changed files (best-effort) - id: list_changed - run: | - # fetch enough history to inspect the commit - git fetch --no-tags --prune --depth=5 origin "${{ github.ref }}" || true - echo "Files changed in this commit/PR:" - git --no-pager show --name-only --pretty="" "${{ github.sha }}" || git ls-files | sed -n '1,200p' - - - name: Add changed files to job summary - if: always() - run: | - echo "### Changed files" >> "$GITHUB_STEP_SUMMARY" - git --no-pager show --name-only --pretty="" "${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY" || git ls-files | sed -n '1,200p' >> "$GITHUB_STEP_SUMMARY" - - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '18' - - name: Install dependencies (if any) - run: | - if [ -f package.json ]; then npm ci; fi - - name: Run wiki init and push env: - # Use a deploy PAT stored in repository secrets (create secret DEPLOY_WIKI_TOKEN) GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} run: | node scripts/init-wiki.js --docs docs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..2ec2290 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "coderoot-ai", + "version": "0.0.0", + "description": "CodeRoot.ai repository", + "type": "module", + "license": "SEE LICENSE IN LICENSE.md", + "scripts": { + "init-wiki:dry": "node scripts/init-wiki.js --dry-run" + } +} From cd7c84e6526cc0fb768db96b6dba2067129fcab9 Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Fri, 17 Oct 2025 18:44:46 -0400 Subject: [PATCH 5/7] Clean up script and update list --- .github/workflows/update-wiki.yml | 103 +++++++++++++++++++----- scripts/init-wiki.js | 125 +++++++++++------------------- 2 files changed, 128 insertions(+), 100 deletions(-) diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index f7c1b86..20a968d 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -2,18 +2,23 @@ name: Update Wiki on: push: - branches: - - main - paths: - - docs/** - + branches: [ main ] + paths: [ 'docs/**' ] workflow_dispatch: {} jobs: update-wiki: runs-on: ubuntu-latest + permissions: + contents: write + issues: write steps: - - name: Debug: print event info + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Print event info run: | echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME" echo "GITHUB_REF=$GITHUB_REF" @@ -21,35 +26,39 @@ jobs: echo "Event payload path: $GITHUB_EVENT_PATH" if [ -f "$GITHUB_EVENT_PATH" ]; then echo "--- event payload ---"; cat "$GITHUB_EVENT_PATH"; echo "--- end payload ---"; fi - - name: Debug: list changed files (best-effort) + - name: List changed files (best-effort) id: list_changed run: | - # fetch enough history to inspect the commit git fetch --no-tags --prune --depth=5 origin "${{ github.ref }}" || true echo "Files changed in this commit/PR:" git --no-pager show --name-only --pretty="" "${{ github.sha }}" || git ls-files | sed -n '1,200p' + - name: Extract docs markdown files + id: docs_files + run: | + files=$(git --no-pager show --name-only --pretty="" "${{ github.sha }}" || git ls-files) + docs_md=$(echo "$files" | grep -iE '^docs/.*\.md$' || true) + if [ -z "$docs_md" ]; then + echo "docs_files=[]" >> "$GITHUB_OUTPUT" + else + # convert lines to JSON array using jq + arr=$(echo "$docs_md" | jq -R -s -c 'split("\n")[:-1]') + echo "docs_files=$arr" >> "$GITHUB_OUTPUT" + fi + - name: Add changed files to job summary if: always() run: | echo "### Changed files" >> "$GITHUB_STEP_SUMMARY" git --no-pager show --name-only --pretty="" "${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY" || git ls-files | sed -n '1,200p' >> "$GITHUB_STEP_SUMMARY" - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Echo docs trigger - run: echo "Update Wiki workflow triggered for docs changes" - - name: Fail early if DEPLOY_WIKI_TOKEN is missing env: DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} run: | if [ -z "$DEPLOY_WIKI_TOKEN" ]; then - echo "WARNING: DEPLOY_WIKI_TOKEN is not set; the workflow will not be able to push to the wiki." - echo "Set the DEPLOY_WIKI_TOKEN secret or provide GITHUB_TOKEN permissions." + echo "ERROR: DEPLOY_WIKI_TOKEN is not set; the workflow will not be able to push to the wiki." + echo "Create a repository secret named DEPLOY_WIKI_TOKEN containing a PAT with 'Contents: write' or use GITHUB_TOKEN with write permissions." exit 1 fi @@ -59,7 +68,63 @@ jobs: node-version: '18' - name: Run wiki init and push + env: + DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + run: | + node scripts/init-wiki.js + + - name: Comment on PR with changed docs files + if: github.event_name == 'pull_request' && steps.docs_files.outputs.docs_files != '[]' env: GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + DOCS_FILES: ${{ steps.docs_files.outputs.docs_files }} run: | - node scripts/init-wiki.js --docs docs + # Extract PR number and head SHA from the event payload at runtime + pr_number=$(jq -r '.pull_request.number // empty' "$GITHUB_EVENT_PATH") + head_sha=$(jq -r '.pull_request.head.sha // empty' "$GITHUB_EVENT_PATH") + if [ -z "$pr_number" ] || [ -z "$head_sha" ]; then + echo "No PR number or head SHA found in event payload; skipping comment." + exit 0 + fi + owner=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f1) + repo=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f2) + files_json="$DOCS_FILES" + body="### Docs files changed\n\n" + # Build markdown links that point to the file at the PR head SHA + body+=$(echo "$files_json" | jq -r --arg repo "$owner/$repo" --arg sha "$head_sha" '.[] | "- [\(. )](https://github.com/" + $repo + "/blob/" + $sha + "/" + .)') + payload=$(jq -n --arg body "$body" '{body: $body}') + curl -s -S -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" \ + -d "$payload" "https://api.github.com/repos/$owner/$repo/issues/$pr_number/comments" + + - name: Post final publish comment + env: + GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + DOCS_FILES: ${{ steps.docs_files.outputs.docs_files }} + run: | + # Compose the final message with links, timestamp, and run URL + owner=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f1) + repo=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f2) + branch_url="https://github.com/$owner/$repo/tree/wiki" + run_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + files_json="$DOCS_FILES" + if [ "$files_json" = "[]" ] || [ -z "$files_json" ]; then + files_section="(no markdown docs changed)" + else + # Build markdown links pointing to files on the wiki branch + files_section=$(echo "$files_json" | jq -r --arg repo "$owner/$repo" '.[] | "- [\(. )](https://github.com/" + $repo + "/blob/wiki/" + .)') + fi + + message="### Docs published to wiki branch\n\n$branch_url\n\n**Changed files:**\n$files_section\n\nPublished at: $timestamp\nRun: $run_url" + + # If this was a PR, comment on the PR; otherwise just echo + pr_number=$(jq -r '.pull_request.number // empty' "$GITHUB_EVENT_PATH") + if [ -n "$pr_number" ]; then + payload=$(jq -n --arg body "$message" '{body: $body}') + curl -s -S -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" \ + -d "$payload" "https://api.github.com/repos/$owner/$repo/issues/$pr_number/comments" + else + echo "$message" + fi diff --git a/scripts/init-wiki.js b/scripts/init-wiki.js index a057509..9031dca 100644 --- a/scripts/init-wiki.js +++ b/scripts/init-wiki.js @@ -8,22 +8,19 @@ import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); -function parseArgs() { - const args = process.argv.slice(2); - const opts = { remote: 'origin', docs: null, temp: null, dryRun: false, wikiUrl: null }; - for (let i = 0; i < args.length; i++) { - const a = args[i]; - if (a === '--remote' && args[i+1]) { opts.remote = args[++i]; } - else if (a === '--docs' && args[i+1]) { opts.docs = args[++i]; } - else if (a === '--temp' && args[i+1]) { opts.temp = args[++i]; } - else if (a === '--wiki-url' && args[i+1]) { opts.wikiUrl = args[++i]; } - else if (a === '--dry-run') { opts.dryRun = true; } - else if (a === '--help' || a === '-h') { - console.log('Usage: node scripts/init-wiki.js [--remote origin] [--docs ] [--temp ] [--dry-run]'); - process.exit(0); - } +// Simplified script: always publish markdown files from `docs/` to the `wiki` branch +// Configuration via environment variables: +// - DRY_RUN=1 -> no git clone/commit/push, just list files +// - DEPLOY_WIKI_TOKEN -> PAT used to authenticate git pushes in CI (preferred) +// The script assumes it's run from the repository root (or within scripts/) and +// will read docs/ relative to the repository root. + +async function removeAllButGit(dir) { + const entries = await fsp.readdir(dir); + for (const e of entries) { + if (e === '.git') continue; + await fsp.rm(path.join(dir, e), { recursive: true, force: true }); } - return opts; } function exec(cmd, opts = {}) { @@ -69,107 +66,73 @@ async function listMarkdownFiles(root) { } async function main() { - const opts = parseArgs(); const scriptDir = path.dirname(__filename); const repoRoot = path.resolve(scriptDir, '..'); - const docsDir = opts.docs ? (path.isAbsolute(opts.docs) ? opts.docs : path.resolve(opts.docs)) : path.resolve(repoRoot, 'docs', 'wiki'); - const tempBase = opts.temp || path.join(os.tmpdir(), 'coderoot-wiki-clone-' + Date.now()); + const docsDir = path.resolve(repoRoot, 'docs'); + const tempBase = path.join(os.tmpdir(), 'coderoot-wiki-clone-' + Date.now()); + const branch = 'wiki'; + const isDry = !!process.env.DRY_RUN || process.env.DRY_RUN === '1'; console.log('Repo root:', repoRoot); console.log('Docs dir:', docsDir); console.log('Temp dir:', tempBase); - console.log('Remote:', opts.remote); - if (opts.dryRun) console.log('Dry run: no git clone/commit/push will be executed'); + if (isDry) console.log('Dry run: listing markdown files only'); const isCI = process.env.GITHUB_ACTIONS === 'true' || process.env.CI === 'true'; - // If running in CI, ensure we have a token to authenticate pushes. Prefer DEPLOY_WIKI_TOKEN but fall back to GITHUB_TOKEN. - if (isCI && !opts.dryRun) { + if (isCI && !isDry) { if (!process.env.DEPLOY_WIKI_TOKEN && !process.env.GITHUB_TOKEN) { console.error('ERROR: No authentication token available in CI.'); console.error('Set repository secret DEPLOY_WIKI_TOKEN (a PAT with Contents: write) or ensure GITHUB_TOKEN is available.'); process.exit(20); } - // If DEPLOY_WIKI_TOKEN is provided, prefer it by setting GITHUB_TOKEN for downstream code that reads it. - if (process.env.DEPLOY_WIKI_TOKEN) { - process.env.GITHUB_TOKEN = process.env.DEPLOY_WIKI_TOKEN; - } + if (process.env.DEPLOY_WIKI_TOKEN) process.env.GITHUB_TOKEN = process.env.DEPLOY_WIKI_TOKEN; } if (!fs.existsSync(docsDir)) { - console.error('Docs wiki folder not found at', docsDir); + console.error('Docs folder not found at', docsDir); process.exitCode = 2; return; } - // Determine remote URL - let remoteUrl = null; - try { - remoteUrl = execSync(`git -C "${repoRoot}" remote get-url ${opts.remote}`).toString().trim(); - } catch (err) { - console.error(`Could not get remote URL for '${opts.remote}':`, err.message); - if (!opts.dryRun) { process.exitCode = 3; return; } - } - - let wikiUrl = null; - if (opts.wikiUrl) { - wikiUrl = opts.wikiUrl; - } else if (remoteUrl) { - if (/\.git$/.test(remoteUrl)) wikiUrl = remoteUrl.replace(/\.git$/, '.wiki.git'); - else wikiUrl = remoteUrl + '.wiki.git'; - } - - // If running in CI with a GITHUB_TOKEN available, inject it into an https wiki URL so git can authenticate. - let maskedWikiUrl = wikiUrl; - if (wikiUrl && isCI && process.env.GITHUB_TOKEN) { - try { - const token = process.env.GITHUB_TOKEN; - if (wikiUrl.startsWith('git@')) { - // convert ssh-style URL to https with token using GITHUB_REPOSITORY if available - const repo = process.env.GITHUB_REPOSITORY || (remoteUrl && remoteUrl.match(/[:\/]([^\/]+\/[^\/.]+)(?:\.git)?$/) && RegExp.$1); - if (repo) wikiUrl = `https://x-access-token:${token}@github.com/${repo}.wiki.git`; - } else if (wikiUrl.startsWith('https://')) { - wikiUrl = wikiUrl.replace(/^https:\/\//, `https://x-access-token:${token}@`); - } - // mask the token in logs - maskedWikiUrl = wikiUrl.replace(new RegExp(process.env.GITHUB_TOKEN, 'g'), '***'); - } catch (err) { - // fall back to original wikiUrl if anything goes wrong - maskedWikiUrl = wikiUrl; - } - } - - if (opts.dryRun) { - console.log('DRY RUN: would clone wiki URL:', maskedWikiUrl || ''); - console.log('DRY RUN: would copy markdown files from', docsDir, 'to temp wiki repo at', tempBase); + if (isDry) { const samples = await listMarkdownFiles(docsDir); - console.log('DRY RUN: markdown files to copy:', samples.join(', ')); - console.log('DRY RUN complete'); + console.log('DRY RUN: markdown files found under docs/:', samples.join(', ')); return; } - // real run try { if (fs.existsSync(tempBase)) { console.log('Removing existing temp dir', tempBase); await fsp.rm(tempBase, { recursive: true, force: true }); } - console.log('Cloning wiki repo:', maskedWikiUrl, 'to', tempBase); - exec(`git clone "${wikiUrl}" "${tempBase}"`, { mask: `git clone "${maskedWikiUrl}" "${tempBase}"` }); - console.log('Copying markdown files...'); - await copyMarkdownRecursive(docsDir, tempBase); + // Clone the main repository and push docs into the wiki branch + const repoUrl = execSync(`git -C "${repoRoot}" remote get-url origin`).toString().trim(); + console.log('Cloning repository:', repoUrl, 'to', tempBase); + exec(`git clone "${repoUrl}" "${tempBase}"`, { mask: `git clone "${repoUrl}" "${tempBase}"` }); + + // checkout or create wiki branch + exec(`git -C "${tempBase}" fetch origin ${branch}:${branch} || true`); + // create branch if it doesn't exist + exec(`git -C "${tempBase}" checkout -B ${branch}`); + + // remove everything except .git + console.log('Cleaning working tree (preserving .git)...'); + await removeAllButGit(tempBase); + + console.log('Copying markdown files to branch...'); + await copyMarkdownRecursive(docsDir, tempBase); - // commit and push - exec(`git -C "${tempBase}" add --all`); - // check if there is anything to commit + // commit & push + exec(`git -C "${tempBase}" add --all`); const status = execSync(`git -C "${tempBase}" status --porcelain`).toString().trim(); if (!status) { console.log('No changes to commit.'); return; } - exec(`git -C "${tempBase}" commit -m "chore(docs): update wiki pages from docs"`); - exec(`git -C "${tempBase}" push`, { mask: `git -C "${tempBase}" push` }); - console.log('Wiki updated successfully.'); + exec(`git -C "${tempBase}" commit -m "chore(docs): update wiki branch from docs/" || true`); + exec(`git -C "${tempBase}" push origin ${branch}`, { mask: `git -C "${tempBase}" push origin ${branch}` }); + console.log('Docs branch updated successfully.'); } catch (err) { - console.error('Failed to update wiki:', err && err.message ? err.message : err); + console.error('Failed to update wiki branch:', err && err.message ? err.message : err); process.exitCode = 10; } } From df908d31f17e83f9acfea6d240b26d99946e6449 Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Fri, 17 Oct 2025 20:09:43 -0400 Subject: [PATCH 6/7] ci: update wiki publish workflow and script to use workspace and docs/wiki --- .github/workflows/update-wiki.yml | 31 +++++++----- scripts/init-wiki.js | 80 ++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index 9466de3..6958f35 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -17,6 +17,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + # Allow the checked-out repository to use the workflow's token for push/fetch + persist-credentials: true - name: Print event info run: | @@ -52,14 +54,16 @@ jobs: echo "### Changed files" >> "$GITHUB_STEP_SUMMARY" git --no-pager show --name-only --pretty="" "${{ github.sha }}" >> "$GITHUB_STEP_SUMMARY" || git ls-files | sed -n '1,200p' >> "$GITHUB_STEP_SUMMARY" - - name: Fail early if DEPLOY_WIKI_TOKEN is missing - env: - DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + - name: Prepare token (optional DEPLOY_WIKI_TOKEN) + id: prepare_token run: | - if [ -z "$DEPLOY_WIKI_TOKEN" ]; then - echo "ERROR: DEPLOY_WIKI_TOKEN is not set; the workflow will not be able to push to the wiki." - echo "Create a repository secret named DEPLOY_WIKI_TOKEN containing a PAT with 'Contents: write' or use GITHUB_TOKEN with write permissions." - exit 1 + # Prefer an explicit PAT in DEPLOY_WIKI_TOKEN, otherwise fall back to the built-in GITHUB_TOKEN + if [ -n "${{ secrets.DEPLOY_WIKI_TOKEN }}" ]; then + echo "token_type=deploy" >> "$GITHUB_OUTPUT" + echo "token=${{ secrets.DEPLOY_WIKI_TOKEN }}" >> "$GITHUB_OUTPUT" + else + echo "token_type=github" >> "$GITHUB_OUTPUT" + echo "token=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_OUTPUT" fi - name: Echo docs trigger @@ -80,17 +84,22 @@ jobs: with: node-version: '18' - - name: Run wiki init and push + - name: Run wiki init and push (in workspace) env: + # Pass token to the script; prefer DEPLOY_WIKI_TOKEN if provided, else GITHUB_TOKEN DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} - GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Instruct the script to operate on the checked-out workspace instead of cloning + USE_WORKSPACE: 1 + # Source folder containing the wiki markdown files + DOCS_SRC: docs/wiki run: | node scripts/init-wiki.js - name: Comment on PR with changed docs files if: github.event_name == 'pull_request' && steps.docs_files.outputs.docs_files != '[]' env: - GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCS_FILES: ${{ steps.docs_files.outputs.docs_files }} run: | # Extract PR number and head SHA from the event payload at runtime @@ -112,7 +121,7 @@ jobs: - name: Post final publish comment env: - GITHUB_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCS_FILES: ${{ steps.docs_files.outputs.docs_files }} run: | # Compose the final message with links, timestamp, and run URL diff --git a/scripts/init-wiki.js b/scripts/init-wiki.js index 9031dca..a31699f 100644 --- a/scripts/init-wiki.js +++ b/scripts/init-wiki.js @@ -68,10 +68,13 @@ async function listMarkdownFiles(root) { async function main() { const scriptDir = path.dirname(__filename); const repoRoot = path.resolve(scriptDir, '..'); - const docsDir = path.resolve(repoRoot, 'docs'); + // Allow overriding the docs source path via DOCS_SRC (relative to repo root) + const docsSrc = process.env.DOCS_SRC || 'docs'; + const docsDir = path.resolve(repoRoot, docsSrc); const tempBase = path.join(os.tmpdir(), 'coderoot-wiki-clone-' + Date.now()); const branch = 'wiki'; const isDry = !!process.env.DRY_RUN || process.env.DRY_RUN === '1'; + const useWorkspace = !!process.env.USE_WORKSPACE || process.env.USE_WORKSPACE === '1'; console.log('Repo root:', repoRoot); console.log('Docs dir:', docsDir); @@ -107,29 +110,80 @@ async function main() { await fsp.rm(tempBase, { recursive: true, force: true }); } - // Clone the main repository and push docs into the wiki branch + // Decide whether to operate in the checked-out workspace or clone into a temp dir const repoUrl = execSync(`git -C "${repoRoot}" remote get-url origin`).toString().trim(); - console.log('Cloning repository:', repoUrl, 'to', tempBase); - exec(`git clone "${repoUrl}" "${tempBase}"`, { mask: `git clone "${repoUrl}" "${tempBase}"` }); + let workingDir = tempBase; + if (useWorkspace) { + console.log('Using checked-out workspace at', repoRoot, 'to perform branch operations (no second clone).'); + workingDir = repoRoot; + } else { + // If running in CI and a token is available, embed it into the clone URL so git push can authenticate. + let cloneUrl = repoUrl; + if (isCI && process.env.GITHUB_TOKEN && repoUrl.startsWith('https://')) { + // Use the token in the URL in the form https://x-access-token:TOKEN@github.com/owner/repo.git + const token = process.env.GITHUB_TOKEN; + cloneUrl = repoUrl.replace(/^https:\/\//, `https://x-access-token:${token}@`); + } + console.log('Cloning repository:', repoUrl, 'to', tempBase); + // Mask the token in logs when present + const maskedClone = cloneUrl.replace(/https:\/\/x-access-token:(?:[^@]+)@/, 'https://x-access-token:@'); + exec(`git clone "${cloneUrl}" "${tempBase}"`, { mask: `git clone "${maskedClone}" "${tempBase}"` }); + } // checkout or create wiki branch - exec(`git -C "${tempBase}" fetch origin ${branch}:${branch} || true`); - // create branch if it doesn't exist - exec(`git -C "${tempBase}" checkout -B ${branch}`); + // operate against the selected workingDir (either temp clone or workspace) + exec(`git -C "${workingDir}" fetch origin ${branch}:${branch} || true`); + // create branch if it doesn't exist; save current ref to restore if using workspace + let originalRef = null; + if (useWorkspace) { + try { + originalRef = execSync(`git -C "${workingDir}" rev-parse --abbrev-ref HEAD`).toString().trim(); + } catch (e) { + originalRef = null; + } + } + exec(`git -C "${workingDir}" checkout -B ${branch}`); // remove everything except .git console.log('Cleaning working tree (preserving .git)...'); - await removeAllButGit(tempBase); + await removeAllButGit(workingDir); console.log('Copying markdown files to branch...'); - await copyMarkdownRecursive(docsDir, tempBase); + await copyMarkdownRecursive(docsDir, workingDir); // commit & push exec(`git -C "${tempBase}" add --all`); - const status = execSync(`git -C "${tempBase}" status --porcelain`).toString().trim(); - if (!status) { console.log('No changes to commit.'); return; } - exec(`git -C "${tempBase}" commit -m "chore(docs): update wiki branch from docs/" || true`); - exec(`git -C "${tempBase}" push origin ${branch}`, { mask: `git -C "${tempBase}" push origin ${branch}` }); + const status = execSync(`git -C "${workingDir}" status --porcelain`).toString().trim(); + if (!status) { + console.log('No changes to commit.'); + // If we used the workspace, try to restore original ref + if (useWorkspace && originalRef) { + try { exec(`git -C "${workingDir}" checkout "${originalRef}"`); } catch (e) { /* ignore */ } + } + return; + } + // Ensure git author identity is set in the temporary clone so commits succeed in CI + try { + const gitUserName = process.env.GIT_USER_NAME || process.env.GIT_COMMITTER_NAME || process.env.GITHUB_ACTOR || 'github-actions'; + const gitUserEmail = process.env.GIT_USER_EMAIL || process.env.GIT_COMMITTER_EMAIL || `${process.env.GITHUB_ACTOR || 'github-actions'}@users.noreply.github.com`; + exec(`git -C "${workingDir}" config user.name "${gitUserName}"`); + exec(`git -C "${workingDir}" config user.email "${gitUserEmail}"`); + } catch (e) { + console.warn('Warning: failed to set git user identity in temp clone:', e && e.message ? e.message : e); + } + + exec(`git -C "${workingDir}" commit -m "chore(docs): update wiki branch from docs/" || true`); + // Mask push so token doesn't leak in logs + exec(`git -C "${workingDir}" push origin ${branch}`, { mask: `git -C "${workingDir}" push origin ${branch} (token redacted)` }); + + // If we modified the workspace, attempt to restore the original branch/ref + if (useWorkspace && originalRef) { + try { + exec(`git -C "${workingDir}" checkout "${originalRef}"`); + } catch (e) { + console.warn('Warning: failed to restore original branch/ref:', originalRef, e && e.message ? e.message : e); + } + } console.log('Docs branch updated successfully.'); } catch (err) { console.error('Failed to update wiki branch:', err && err.message ? err.message : err); From 3ce48c151bf66883db22ce86cd6be8c67d186d76 Mon Sep 17 00:00:00 2001 From: settletop-niles Date: Sat, 18 Oct 2025 11:18:13 -0400 Subject: [PATCH 7/7] Workflow patch to pull in local wiki files --- .github/workflows/update-wiki.yml | 81 ++++++++++++++++--------------- .gitignore | 2 + scripts/init-wiki.js | 2 +- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index 6958f35..d427cab 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -1,13 +1,13 @@ -name: Update Wiki +name: Update GitHub Wiki on: push: branches: [ main ] - paths: [ 'docs/**' ] + paths: [ 'docs/wiki/**' ] workflow_dispatch: {} jobs: - update-wiki: + publish-wiki: runs-on: ubuntu-latest permissions: contents: write @@ -67,34 +67,48 @@ jobs: fi - name: Echo docs trigger - run: echo "Update Wiki workflow triggered for docs changes" + run: echo "Update GitHub Wiki workflow triggered for docs/wiki changes" - - name: Fail early if DEPLOY_WIKI_TOKEN is missing - env: - DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} + - name: Select token + id: select_token run: | - if [ -z "$DEPLOY_WIKI_TOKEN" ]; then - echo "WARNING: DEPLOY_WIKI_TOKEN is not set; the workflow will not be able to push to the wiki." - echo "Set the DEPLOY_WIKI_TOKEN secret or provide GITHUB_TOKEN permissions." - exit 1 + if [ -n "${{ secrets.DEPLOY_WIKI_TOKEN }}" ]; then + echo "token=${{ secrets.DEPLOY_WIKI_TOKEN }}" >> "$GITHUB_OUTPUT" + echo "source=DEPLOY_WIKI_TOKEN" >> "$GITHUB_OUTPUT" + else + echo "token=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_OUTPUT" + echo "source=GITHUB_TOKEN" >> "$GITHUB_OUTPUT" fi + echo "Using token from: ${{ steps.select_token.outputs.source }}" - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '18' - - - name: Run wiki init and push (in workspace) + - name: Publish docs/wiki to GitHub Wiki env: - # Pass token to the script; prefer DEPLOY_WIKI_TOKEN if provided, else GITHUB_TOKEN - DEPLOY_WIKI_TOKEN: ${{ secrets.DEPLOY_WIKI_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Instruct the script to operate on the checked-out workspace instead of cloning - USE_WORKSPACE: 1 - # Source folder containing the wiki markdown files - DOCS_SRC: docs/wiki + GITHUB_TOKEN: ${{ steps.select_token.outputs.token }} + REPO: ${{ github.repository }} run: | - node scripts/init-wiki.js + set -e + if [ ! -d docs/wiki ]; then + echo "docs/wiki not found"; exit 0 + fi + TMP_DIR="$(mktemp -d)" + WIKI_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/${REPO}.wiki.git" + echo "Cloning wiki repo" + git clone "$WIKI_URL" "$TMP_DIR" + echo "Cleaning wiki repo (preserving .git)" + find "$TMP_DIR" -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} + + echo "Copying markdown from docs/wiki" + rsync -a --prune-empty-dirs --include='*/' --include='*.md' --exclude='*' docs/wiki/ "$TMP_DIR"/ + cd "$TMP_DIR" + git config user.name "${GITHUB_ACTOR:-github-actions}" + git config user.email "${GITHUB_ACTOR:-github-actions}@users.noreply.github.com" + if [ -n "$(git status --porcelain)" ]; then + git add -A + git commit -m "Publish docs/wiki to GitHub Wiki" + git push origin HEAD:master || git push origin HEAD:main || git push + echo "Published changes to GitHub Wiki" + else + echo "No changes to publish." + fi - name: Comment on PR with changed docs files if: github.event_name == 'pull_request' && steps.docs_files.outputs.docs_files != '[]' @@ -121,27 +135,14 @@ jobs: - name: Post final publish comment env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.select_token.outputs.token }} DOCS_FILES: ${{ steps.docs_files.outputs.docs_files }} run: | - # Compose the final message with links, timestamp, and run URL owner=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f1) repo=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f2) - branch_url="https://github.com/$owner/$repo/tree/wiki" run_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - - files_json="$DOCS_FILES" - if [ "$files_json" = "[]" ] || [ -z "$files_json" ]; then - files_section="(no markdown docs changed)" - else - # Build markdown links pointing to files on the wiki branch - files_section=$(echo "$files_json" | jq -r --arg repo "$owner/$repo" '.[] | "- [\(. )](https://github.com/" + $repo + "/blob/wiki/" + .)') - fi - - message="### Docs published to wiki branch\n\n$branch_url\n\n**Changed files:**\n$files_section\n\nPublished at: $timestamp\nRun: $run_url" - - # If this was a PR, comment on the PR; otherwise just echo + message="### Docs published to GitHub Wiki\n\nhttps://github.com/$owner/$repo/wiki\n\nPublished at: $timestamp\nRun: $run_url" pr_number=$(jq -r '.pull_request.number // empty' "$GITHUB_EVENT_PATH") if [ -n "$pr_number" ]; then payload=$(jq -n --arg body "$message" '{body: $body}') diff --git a/.gitignore b/.gitignore index e69de29..af9849b 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,2 @@ +.crush/ +CRUSH.md \ No newline at end of file diff --git a/scripts/init-wiki.js b/scripts/init-wiki.js index a31699f..bd829eb 100644 --- a/scripts/init-wiki.js +++ b/scripts/init-wiki.js @@ -152,7 +152,7 @@ async function main() { await copyMarkdownRecursive(docsDir, workingDir); // commit & push - exec(`git -C "${tempBase}" add --all`); + exec(`git -C "${workingDir}" add --all`); const status = execSync(`git -C "${workingDir}" status --porcelain`).toString().trim(); if (!status) { console.log('No changes to commit.');