diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..677bdf6 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,33 @@ +name: Tests + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + test: + name: Node ${{ matrix.node }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + node: [20, 22, 24] + os: [ubuntu-latest] + include: + - node: 24 + os: windows-latest + - node: 24 + os: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: npm + - run: npm ci + - run: npm test + - run: npm pack --dry-run --ignore-scripts diff --git a/README.md b/README.md index 0be1a0b..3ff3648 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ npx pageleaf guide.md npx pageleaf ./docs --title "Project manual" --theme grove npx pageleaf ./docs --layout sidebar npx pageleaf notes.txt --out ./public --open +npx pageleaf ./docs --upload my-org/my-docs ``` Requires Node.js 20 or newer. @@ -19,6 +20,7 @@ Requires Node.js 20 or newer. | `--theme ` | Use `paper`, `midnight`, `grove`, `ocean`, or `contrast` | | `--layout ` | Use `standard` (the default horizontal section navigation plus page sidebar) or `sidebar` (a single left hierarchy with non-clickable section groups and page links) | | `--out ` | Choose the output folder | +| `--upload <[owner/]repo>` | Upload the HTML file to a GitHub repository's root on its default branch | | `--open` | Open the generated site | | `--force` | Replace an existing output file | @@ -26,6 +28,10 @@ A file named `guide.md` produces `guide.html`. A folder named `docs` produces `d Relative links and images remain unchanged, so they resolve from the generated HTML file. The site needs internet access to load its pinned Markdown-it dependency. +For uploads, install [Git](https://git-scm.com/downloads) and [GitHub CLI](https://cli.github.com/), then run `gh auth login --hostname github.com`. Use `--upload my-docs` for the signed-in user's personal account, or `--upload my-org/my-docs` for an explicit owner. The repository must already exist and permit direct pushes; an empty repository is supported. + +Uploads keep the generated filename (`docs.html` in the example), replace that remote file automatically, and leave other files unchanged. Identical content creates no commit. Commits use the signed-in account's GitHub noreply identity. `--force` controls local output replacement only. If an upload fails, the local HTML remains available; fix the reported problem and rerun with `--force`. Uploading stores the file in GitHub; it does not configure GitHub Pages. + ## License [MIT](LICENSE) diff --git a/lib/errors.js b/lib/errors.js new file mode 100644 index 0000000..194ee80 --- /dev/null +++ b/lib/errors.js @@ -0,0 +1,14 @@ +export class PageleafError extends Error { + constructor(message, exitCode = 1) { + super(message); + this.name = 'PageleafError'; + this.exitCode = exitCode; + } +} + +export class UsageError extends PageleafError { + constructor(message) { + super(message, 2); + this.name = 'UsageError'; + } +} diff --git a/lib/pageleaf.js b/lib/pageleaf.js index 5e1ec0a..92a8a26 100644 --- a/lib/pageleaf.js +++ b/lib/pageleaf.js @@ -4,6 +4,10 @@ import { createRequire } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseArgs } from 'node:util'; +import { PageleafError, UsageError } from './errors.js'; +import { parseUploadTarget, uploadHtml } from './upload.js'; + +export { PageleafError, UsageError } from './errors.js'; const require = createRequire(import.meta.url); @@ -14,21 +18,6 @@ export const LAYOUTS = ['standard', 'sidebar']; const MARKDOWN_OPEN = '