diff --git a/.gitattributes b/.gitattributes index 50ca329..6457f67 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -*.sh eol=lf +cliw eol=lf +cli.ts eol=lf diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index 48376ee..346eb7c 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -6,6 +6,7 @@ on: branches: "master" paths-ignore: - .gitignore + - .gitattributes - LICENSE - README.md - .github/** @@ -13,6 +14,7 @@ on: pull_request: paths-ignore: - .gitignore + - .gitattributes - LICENSE - README.md - .github/** diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bf8613 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# https://github.com/jcbhmr/deno_wrapper +.deno diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cbac569 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "deno.enable": true +} diff --git a/README.md b/README.md index ea83fff..ad9a32a 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,11 @@ is specific to GitHub wikis. not push to the remote wiki. The default is `false`. This is useful for testing. +- **`preprocess`:** If this option is true, we will preprocess the wiki to move + the `README.md` to `Home.md` as well as rewriting all `.md` links to be bare + links. This helps ensure that the Markdown works in source control as well as + the wiki. The default is true. + #### `strategy:` input There are some specific usecases where using `strategy: init` might be better @@ -133,28 +138,6 @@ than the default `strategy: clone`. tab. This is essentially the concatenation of `${{ github.server_url }}`, `${{ github.repository }}`, and the `/wiki` page. -### Preprocessing - -You may wish to strip the `[link](page.md)` `.md` suffix from your links to make -them viewable in GitHub source view (with the `.md`) _as well as_ in GitHub wiki -(without the `.md`; pretty URLs!). You can use a preprocessing action like -[Strip MarkDown extensions from links action] to remove those `.md` suffixes -before using this action. Here's an example: - -```yml -publish-wiki: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: impresscms-dev/strip-markdown-extensions-from-links-action@v1.0.0 - with: - path: wiki - - uses: Andrew-Chen-Wang/github-wiki-action@v4 -``` - -❤️ If you have an awesome preprocessor action that you want to add here, let us -know! We'd love to add an example. - ### Cross-repo wikis You _can_ use this action to deploy your octocat/mega-docs repository to the @@ -189,9 +172,24 @@ jobs: path: . ``` +## Development + +![Deno](https://img.shields.io/static/v1?style=for-the-badge&message=Deno&color=000000&logo=Deno&logoColor=FFFFFF&label=) +![GitHub Actions](https://img.shields.io/static/v1?style=for-the-badge&message=GitHub+Actions&color=2088FF&logo=GitHub+Actions&logoColor=FFFFFF&label=) + +This GitHub Action uses a self-downloaded version of Deno. See `cliw` for the +`cli.ts` wrapper script that downloads the Deno binary and runs the TypeScript +code. The main script itself is ~100 lines of code, so it's not too bad. + +ℹ Because the version of Deno is _pinned_, it's recommended to every-so-often +bump it to the latest version. + +To test the action, open a PR! The `test-action.yml` workflow will run the code +with `dry-run: true` as well as a real run! Yes, this does get tedious swapping +between your IDE and the PR, but it's the easiest way to test the action. + [Decathlon/wiki-page-creator-action#11]: https://github.com/Decathlon/wiki-page-creator-action/issues/11 [supported markup languages]: https://github.com/github/markup#markups -[Strip MarkDown extensions from links action]: https://github.com/marketplace/actions/strip-markdown-extensions-from-links-action [PAT]: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token diff --git a/action.yml b/action.yml index f1b1de7..96bef34 100644 --- a/action.yml +++ b/action.yml @@ -64,6 +64,14 @@ inputs: push to the remote wiki. The default is false. This is useful for testing. required: true default: false + preprocess: + description: >- + If this option is true, we will preprocess the wiki to move the README.md + to Home.md as well as rewriting all .md links to be bare links. This helps + ensure that the Markdown works in source control as well as the wiki. The + default is true. + required: true + default: true outputs: wiki_url: description: >- @@ -75,7 +83,7 @@ runs: using: composite steps: - id: main - run: '"${GITHUB_ACTION_PATH%/}/src/$INPUT_STRATEGY.sh"' + run: '"${GITHUB_ACTION_PATH%/}/cliw"' shell: bash env: INPUT_STRATEGY: ${{ inputs.strategy }} @@ -86,3 +94,4 @@ runs: INPUT_COMMIT_MESSAGE: ${{ inputs.commit-message }} INPUT_IGNORE: ${{ inputs.ignore }} INPUT_DRY_RUN: ${{ inputs.dry-run }} + INPUT_PREPROCESS: ${{ inputs.preprocess }} diff --git a/cli.ts b/cli.ts new file mode 100755 index 0000000..d7aeb50 --- /dev/null +++ b/cli.ts @@ -0,0 +1,101 @@ +#!/usr/bin/env -S deno run -Aq +// Copyright 2023 Jacob Hummer +// SPDX-License-Identifier: Apache-2.0 +import process from "node:process"; +import { + readFile, + writeFile, + appendFile, + readdir, + rename, +} from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { copy } from "npm:fs-extra@^11.1.1"; +import * as core from "npm:@actions/core@^1.10.0"; +import { temporaryDirectory } from "npm:tempy@^3.1.0"; +import { $ } from "npm:zx@^7.2.2"; +import { remark } from "npm:remark@^14.0.3"; +import { visit } from "npm:unist-util-visit@^5.0.0"; +import { resolve } from "node:path"; + +core.startGroup("process.env"); +console.table(process.env); +core.endGroup(); + +const serverURL = core.getInput("github_server_url"); +const repo = core.getInput("repository"); +const wikiGitURL = `${serverURL}/${repo}.wiki.git`; +const workspacePath = process.cwd(); +const d = temporaryDirectory(); +process.chdir(d); +$.cwd = d; + +process.env.GH_TOKEN = core.getInput("token"); +process.env.GH_HOST = new URL(core.getInput("github_server_url")).host; +await $`gh auth setup-git`; + +if (core.getInput("strategy") === "clone") { + await $`git config --global --add safe.directory ${process.cwd()}`; + await $`git clone ${wikiGitURL} .`; +} else if (core.getInput("strategy") === "init") { + await $`git init -b master`; + await $`git remote add origin ${wikiGitURL}`; + await $`git fetch`; +} else { + throw new DOMException("Unknown strategy", "NotSupportedError"); +} + +// https://github.com/stefanzweifel/git-auto-commit-action/blob/master/action.yml#L35-L42 +await $`git config user.name github-actions[bot]`; +await $`git config user.email 41898282+github-actions[bot]@users.noreply.github.com`; + +await appendFile(".git/info/exclude", core.getInput("ignore")); +await copy(resolve(workspacePath, core.getInput("path")), process.cwd()); + +if (core.getBooleanInput("preprocess")) { + // https://github.com/nodejs/node/issues/39960 + if (existsSync("README.md")) { + await rename("README.md", "Home.md"); + console.log("Moved README.md to Home.md"); + } + + const mdRe = /\.(?:md|markdown|mdown|mkdn|mkd|mdwn|mkdown|ron)$/; + const plugin = () => (tree: any) => + visit(tree, ["link", "linkReference"], (node: any) => { + if (!mdRe.test(node.url)) { + return; + } + if (!new URL(node.url, "file:///-/").href.startsWith("file:///-/")) { + return; + } + + const x = node.url; + node.url = node.url.replace(mdRe, ""); + if (new URL(node.url, "file:///-/").href === "file:///-/README") { + node.url = "Home"; + } + + console.log(`Rewrote ${x} to ${node.url}`); + }); + for (const file of await readdir($.cwd!)) { + if (!mdRe.test(file)) { + continue; + } + + let md = await readFile(file, "utf-8"); + md = (await remark().use(plugin).process(md)).toString(); + await writeFile(file, md); + } +} + +await $`git add -Av`; +await $`git commit --allow-empty -m ${core.getInput("commit_message")}`; + +if (core.getBooleanInput("dry_run")) { + await $`git show`; + await $`git push -f origin master --dry-run`; +} else { + await $`git push -f origin master`; +} + +core.setOutput("wiki_url", `${serverURL}/${repo}/wiki`); diff --git a/cliw b/cliw new file mode 100755 index 0000000..d842b28 --- /dev/null +++ b/cliw @@ -0,0 +1,32 @@ +#!/bin/sh +# Copyright 2023 Jacob Hummer +# SPDX-License-Identifier: Apache-2.0 +# Based on https://github.com/jcbhmr/deno_wrapper +set -e +# https://stackoverflow.com/a/29835459 +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) +deno_dir="$script_dir/.deno" + +# https://manpages.ubuntu.com/manpages/kinetic/en/man1/chronic.1.html +chronic() ( + set +e + output=$($@ 2>&1) + exit_code=$? + set -e + if [ "$exit_code" -ne 0 ]; then + echo "$output" >&2 + fi + return "$exit_code" +) + +if [ ! -d "$deno_dir" ]; then + # https://github.com/denoland/deno_install#readme + export DENO_INSTALL=$deno_dir + curl -fsSL https://deno.land/x/install/install.sh | chronic sh -s "v1.35.1" +fi + +# https://github.com/denoland/deno_install/blob/master/install.sh#L53 +export DENO_INSTALL=$deno_dir +export PATH="$DENO_INSTALL/bin:$PATH" + +exec "$script_dir/cli.ts" "$@" diff --git a/src/clone.sh b/src/clone.sh deleted file mode 100755 index e3dfd60..0000000 --- a/src/clone.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# Copyright 2023 Jacob Hummer -# SPDX-License-Identifier: Apache-2.0 -set -e -if [[ -n $RUNNER_DEBUG ]]; then - set -x -fi - -export GITHUB_TOKEN="$INPUT_TOKEN" -export GITHUB_SERVER_URL="$INPUT_GITHUB_SERVER_URL" -export GITHUB_REPOSITORY="$INPUT_REPOSITORY" -export GH_HOST="${GITHUB_SERVER_URL#*//}" - -export GIT_DIR && GIT_DIR=$(mktemp -d) -export GIT_WORK_TREE="$INPUT_PATH" -trap 'rm -rf "$GIT_DIR"' SIGINT SIGTERM ERR EXIT - -gh auth setup-git -git config --global --add safe.directory "$GIT_DIR" - -git clone "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.wiki.git" "$GIT_DIR" --bare -git config --unset core.bare - -echo "$INPUT_IGNORE" >>"$GIT_DIR/info/exclude" -git add -Av - -# https://github.com/stefanzweifel/git-auto-commit-action/blob/master/action.yml#L35-L42 -git config user.name github-actions[bot] -git config user.email 41898282+github-actions[bot]@users.noreply.github.com -git commit --allow-empty -m "$INPUT_COMMIT_MESSAGE" - -if [[ $INPUT_DRY_RUN == true ]]; then - echo 'Dry run' - git remote show origin - git show - exit 0 -fi - -git push origin master -echo "wiki_url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/wiki" >>"$GITHUB_OUTPUT" diff --git a/src/init.sh b/src/init.sh deleted file mode 100755 index e5f55d5..0000000 --- a/src/init.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# Copyright 2023 Jacob Hummer -# SPDX-License-Identifier: Apache-2.0 -set -e -if [[ -n $RUNNER_DEBUG ]]; then - set -x -fi - -export GITHUB_TOKEN="$INPUT_TOKEN" -export GITHUB_SERVER_URL="$INPUT_GITHUB_SERVER_URL" -export GITHUB_REPOSITORY="$INPUT_REPOSITORY" -export GH_HOST="${GITHUB_SERVER_URL#*//}" - -export GIT_DIR && GIT_DIR=$(mktemp -d) -export GIT_WORK_TREE="$INPUT_PATH" -trap 'rm -rf "$GIT_DIR"' SIGINT SIGTERM ERR EXIT - -gh auth setup-git -git config --global --add safe.directory "$GIT_DIR" - -git init -b master -git remote add origin "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.wiki.git" - -echo "$INPUT_IGNORE" >>"$GIT_DIR/info/exclude" -git add -Av - -git config user.name github-actions[bot] -git config user.email 41898282+github-actions[bot]@users.noreply.github.com - -git commit --allow-empty -m "$INPUT_COMMIT_MESSAGE" - -if [[ $INPUT_DRY_RUN == true ]]; then - echo 'Dry run' - git remote show origin - git show - exit 0 -fi - -git push -f origin master -echo "wiki_url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/wiki" >>"$GITHUB_OUTPUT"