Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,23 @@ updates:
applies-to: security-updates
patterns:
- '*'
# The docs toolchain is a separate install: docs.yml pip-installs
# docs/requirements-docs.txt to run MkDocs.
- package-ecosystem: pip
directory: /docs
schedule:
interval: weekly
day: monday
commit-message:
prefix: docs
labels:
- documentation
- automated
groups:
docs_toolchain:
patterns:
- '*'
docs_security:
applies-to: security-updates
patterns:
- '*'
96 changes: 96 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Docs

# Copy to .github/workflows/docs.yml. Before the first run, set the repository's
# Pages source to "GitHub Actions": Settings -> Pages -> Source, or
# `gh api -X POST repos/GeiserX/<repo>/pages -f build_type=workflow` for a repo
# with no Pages yet. For a repo already publishing from a branch the API is a
# PUT, and its docs list `source.branch` and `source.path` as required, so the
# bare `-f build_type=workflow` PUT may 422: try it on CashPilot first and
# record which form worked. A deploy while the source is still a branch is
# expected to fail; that is the test that the switch happened.
#
# Never for a repo whose gh-pages branch holds a Helm chart index (index.yaml,
# artifacthub-repo.yml): Pumperly, LynxPrompt, genieacs-container,
# duplicacy-container. Those use docs-pages-helm.yml instead.
#
# Pull requests that touch the docs run the strict build, so a broken link, a
# missing page or an unreachable page fails the check instead of reaching the
# site. A push to main deploys, and only after the same build has passed.
#
# The site is published as a Pages artifact, never with `mkdocs gh-deploy`:
# no gh-pages branch, no `contents: write`, and one artifact is the whole site.
on:
push:
branches:
- main
paths:
- 'docs/**'
- 'mkdocs.yml'
- '.github/workflows/docs.yml'
# Add every file a page includes with pymdownx.snippets (for example
# docker-compose.yml, .env.example): a change to it must rebuild the
# site, and a rename must fail the build.
# No paths filter on pull requests, on purpose: a check skipped by a path
# filter stays Pending and blocks the merge once it is a required check
# (repos.md step 5 makes green checks required). The build takes seconds.
pull_request:
workflow_dispatch:

# One run at a time per ref, in push order, never cancelled. Locking only the
# deploy job is not enough: an older push whose build finishes later would
# deploy last and publish a stale site. Pull requests get their own group.
concurrency:
group: pages-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

jobs:
build:
name: build (strict)
# Public repo: GitHub-hosted runners are free and unlimited here, and the
# job runs on pull requests, so it must never touch a self-hosted runner.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# This job never pushes; a persisted token is readable by pip and every MkDocs plugin.
persist-credentials: false

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.13'
cache: pip
cache-dependency-path: docs/requirements-docs.txt

- name: Install dependencies
run: pip install -r docs/requirements-docs.txt

# mkdocs.yml sets `strict: true`; the flag here keeps the build strict
# even if someone removes that line.
- name: Build
run: mkdocs build --strict

- name: Upload the site
if: github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site

deploy:
# Runs on push to main only, after the build job has passed.
if: github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

- id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ files/
output/
*.epub
.DS_Store

# MkDocs build output
site/
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ ePubLangMerger is an R/Shiny web application that merges two ePub files of the s

- Pairs the `<p>` and `<h1>` to `<h5>` elements of both ePubs and interleaves them as XML siblings.
- Names the merged file from the input filenames and their language codes.
- Serves a pair it has already merged from its cache, without merging it again.
- Within one browser session, hands back the cached book for any pair that gives the same output name, without a new merge.
- Web UI: upload two ePub files, click "Go!" and download the result.
- Command-line mode: `script.R` does the same merge without the UI, for scripts and batch runs.
- Adds a `_2` suffix to every `id` attribute from the second ePub, so XHTML IDs never collide.
Expand All @@ -36,14 +36,17 @@ curl -fsSLO https://raw.githubusercontent.com/GeiserX/ePubLangMerger/main/docker
docker compose up -d
```

Open http://localhost:3838. Manual install without Docker: [getting started](https://github.com/GeiserX/ePubLangMerger/blob/main/docs/getting-started.md).
Open http://localhost:3838. Manual install without Docker: [getting started](https://geiserx.github.io/ePubLangMerger/getting-started/#manual-installation-without-docker).

## Documentation

- [Getting started](https://github.com/GeiserX/ePubLangMerger/blob/main/docs/getting-started.md): prerequisites, Docker and manual install, first run.
- [Usage](https://github.com/GeiserX/ePubLangMerger/blob/main/docs/usage.md): the web UI, the command line and the filename convention.
- [How it works](https://github.com/GeiserX/ePubLangMerger/blob/main/docs/how-it-works.md): extract, parse, merge, reassemble.
- [Troubleshooting](https://github.com/GeiserX/ePubLangMerger/blob/main/docs/troubleshooting.md): the limits of the merge and what they look like.
The docs are at [geiserx.github.io/ePubLangMerger](https://geiserx.github.io/ePubLangMerger/).

- [Getting started](https://geiserx.github.io/ePubLangMerger/getting-started/): prerequisites, Docker and manual install, first run.
- [Usage](https://geiserx.github.io/ePubLangMerger/usage/): the web UI, the command line and the filename convention.
- [How it works](https://geiserx.github.io/ePubLangMerger/how-it-works/): extract, parse, merge, reassemble.
- [Troubleshooting](https://geiserx.github.io/ePubLangMerger/troubleshooting/): the limits of the merge and what they look like.
- [Development](https://geiserx.github.io/ePubLangMerger/development/): tests, the image build and releases.

Contributions: see [CONTRIBUTING.md](https://github.com/GeiserX/ePubLangMerger/blob/main/CONTRIBUTING.md).

Expand Down
47 changes: 47 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Development

## Run from a checkout

Clone the repository and install the R packages as in [Getting started](getting-started.md#manual-installation-without-docker), then start the app from the repository root:

```r
shiny::runApp(".", port = 8080)
```

The code is four files: `ui.R` (the page), `server.R` (upload, merge and download), `utils.R` (`makeSibling` and `deriveOutputName`, shared by the app and the script) and `script.R` (the [command line](usage.md#command-line-batch)).

## Test

```bash
Rscript -e 'install.packages(c("testthat", "XML"), repos = "https://cloud.r-project.org/")'
Rscript -e 'testthat::test_dir("tests/testthat")'
```

The tests cover `deriveOutputName` and `makeSibling` in `utils.R`. Two `makeSibling` tests call `skip_on_ci()`: the XML package's in-place node edits crash with `free(): invalid pointer` on the CI runners, so those two run only on your machine.

## Build the image

```bash
docker build -t epublangmerger .
```

The image is `rocker/shiny:4.6` with the R packages installed and `server.R`, `ui.R`, `utils.R` and `www/` copied in, built for `linux/amd64` only. `script.R` and `docs/` are not in it.

Every pull request runs `test` and `docker-build` from `ci.yml`, and the strict docs build from `docs.yml`.

## Release

1. Change the image tag in `docker-compose.yml` and in [Getting started](getting-started.md#docker) to the new version, and merge.
2. Push a tag `vX.Y.Z` on `main`. Never move an existing tag.
3. The Release workflow creates the GitHub release with generated notes, pushes `drumsergio/epublangmerger:X.Y.Z` and `latest` to Docker Hub for `linux/amd64`, and copies `README.md` to the Docker Hub description.

## Docs

The site is built by MkDocs Material from `docs/` and `mkdocs.yml`:

```bash
pip install -r docs/requirements-docs.txt
mkdocs build --strict
```

The same strict build runs on every pull request, and a push to `main` that changes the docs deploys the site. How to send a change: [CONTRIBUTING.md](https://github.com/GeiserX/ePubLangMerger/blob/main/CONTRIBUTING.md).
1 change: 1 addition & 0 deletions docs/images/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/images/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading