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
191 changes: 120 additions & 71 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,78 +1,127 @@
name: Release

on:
push:
tags:
- "v*"
push:
tags:
- "v*"

permissions:
contents: write
contents: write

jobs:
create-release:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Build package distribution
run: uv build

- name: Build release notes from CHANGELOG
env:
TAG_NAME: ${{ github.ref_name }}
run: |
set -eu
VERSION="${TAG_NAME#v}"

python3 - <<'PY'
import os
import pathlib
import re

version = os.environ["TAG_NAME"].lstrip("v")
changelog = pathlib.Path("CHANGELOG.md")
out = pathlib.Path("release-notes.md")

if not changelog.exists():
out.write_text(
f"Release {version}\n\nSee repository history for details.\n",
encoding="utf-8",
)
raise SystemExit(0)

text = changelog.read_text(encoding="utf-8")

pattern = re.compile(
rf"^## \[{re.escape(version)}\].*?$\n(.*?)(?=^## \[|\Z)",
re.M | re.S,
)
match = pattern.search(text)

if not match:
out.write_text(
(
f"Release {version}\n\n"
"No matching changelog section was found. "
"Please update CHANGELOG.md.\n"
),
encoding="utf-8",
)
raise SystemExit(0)

notes = match.group(1).strip()
out.write_text(notes + "\n", encoding="utf-8")
PY

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
name: ${{ github.ref_name }}
body_path: release-notes.md
files: dist/*
draft: false
prerelease: false
create-release:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Set up Python
run: uv python install 3.11

- name: Verify tag version matches package version
env:
TAG_NAME: ${{ github.ref_name }}
run: |
python3 - <<'PY'
import os
import pathlib
import re
import sys

tag = os.environ["TAG_NAME"].lstrip("v")
pyproject = pathlib.Path("pyproject.toml").read_text(encoding="utf-8")
match = re.search(r'^version\s*=\s*"([^"]+)"', pyproject, re.M)
if not match:
print("ERROR: Could not find version in pyproject.toml", file=sys.stderr)
raise SystemExit(1)
pkg_version = match.group(1)
if tag != pkg_version:
print(
f"ERROR: Tag '{tag}' does not match package version '{pkg_version}'",
file=sys.stderr,
)
raise SystemExit(1)
print(f"Version check passed: {pkg_version}")
PY

- name: Build package distribution
run: uv build
Comment on lines +19 to +52

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release workflow builds the package without pinning/installing a Python version (unlike CI, which runs uv python install ...). For reproducible builds and to avoid ubuntu-latest Python upgrades breaking releases, add an explicit Python install/select step (e.g., 3.11) before uv build in both jobs.

Copilot uses AI. Check for mistakes.

Comment on lines +51 to 53

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that tags trigger an automated PyPI publish, add a guard that verifies the Git tag version matches the package version (e.g., from pyproject.toml). Without this, it’s possible to publish a different version than the tag/release notes indicate, leading to inconsistent GitHub Releases vs PyPI artifacts.

Copilot uses AI. Check for mistakes.
- name: Upload distribution artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

- name: Build release notes from CHANGELOG
env:
TAG_NAME: ${{ github.ref_name }}
run: |
set -eu

python3 - <<'PY'
import os
import pathlib
import re

version = os.environ["TAG_NAME"].lstrip("v")
changelog = pathlib.Path("CHANGELOG.md")
out = pathlib.Path("release-notes.md")

if not changelog.exists():
out.write_text(
f"Release {version}\n\nSee repository history for details.\n",
encoding="utf-8",
)
raise SystemExit(1)

text = changelog.read_text(encoding="utf-8")

pattern = re.compile(
rf"^## \[{re.escape(version)}\].*?$\n(.*?)(?=^## \[|\Z)",
re.M | re.S,
)
match = pattern.search(text)

if not match:
out.write_text(
(
f"Release {version}\n\n"
"No matching changelog section was found. "
"Please update CHANGELOG.md.\n"
),
encoding="utf-8",
)
raise SystemExit(1)

Comment on lines +75 to 100

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If CHANGELOG.md is missing or the tagged version section isn’t found, the workflow currently exits successfully and continues to create a GitHub Release (and will also publish to PyPI). Consider failing the job in these cases so releases aren’t published without a matching changelog entry.

Copilot uses AI. Check for mistakes.
notes = match.group(1).strip()
out.write_text(notes + "\n", encoding="utf-8")
PY

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
name: ${{ github.ref_name }}
body_path: release-notes.md
files: dist/*
draft: false
prerelease: false

publish-pypi:
needs: create-release
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Download distribution artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
Comment thread
RareBird15 marked this conversation as resolved.
uses: pypa/gh-action-pypi-publish@release/v1
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Bible Reader

[](https://github.com/RareBird15/bible-reader/actions/workflows/ci.yml)
- [![CI Status](https://github.com/RareBird15/bible-reader/actions/workflows/ci.yml/badge.svg)](https://github.com/RareBird15/bible-reader/actions/workflows/ci.yml)
- [![PyPI version](https://badge.fury.io/py/bible-reader.svg)](https://badge.fury.io/py/bible-reader)

A lightweight terminal workflow for daily Bible reading.

Expand Down Expand Up @@ -38,24 +39,17 @@ If you ever need to reset your local progress, you can edit or delete those spec

## Installation

This project is built as a modern Python package.
The easiest way to install the Bible Reader is using [uv](https://github.com/astral-sh/uv). This installs the tools globally so you can use them anywhere.

1. Clone the repository and move into the project directory:

```bash
git clone [https://github.com/RareBird15/bible-reader.git](https://github.com/RareBird15/bible-reader.git)
cd bible-reader
```

2. Install the package and its dependencies using `uv`. The `-e` flag installs it in "editable" mode so any changes you pull from GitHub apply immediately.

```bash
uv pip install -e .
```
```bash
uv tool install bible-reader
```

_Note: If you do not use `uv`, standard `pip install -e .` will also work._
If you prefer using standard `pip`:

Once installed, the tools are available as global terminal commands.
```bash
pip install bible-reader
```

## Project Status

Expand Down Expand Up @@ -91,17 +85,17 @@ uv run python3 -m unittest discover tests -v

Because this is a Python package, you no longer need to type `python3 path/to/script.py`. You can use the installed commands from anywhere in your terminal.

### 1\. One-Time Setup (From EPUB)
### 1. One-Time Setup (From EPUB)

If you are starting from a new WorldBiblePlans EPUB, run this sequence from the project root:
If you are starting from a new WorldBiblePlans EPUB, create a folder for your plan and run this sequence:

Comment on lines +88 to 91

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The headings in this section are now inconsistent: ### 1. ... is unescaped, but the subsequent headings still use escaped dots (### 2\. / ### 3\.). For consistent Markdown (and screen reader output), use the same numbering style for all three headings.

Copilot uses AI. Check for mistakes.
```bash
import-epub /path/to/plan.epub --output plan.md
split-plan
extract-scripture
```

### 2\. Daily Reading
### 2. Daily Reading

To read today's passage:

Expand All @@ -114,7 +108,7 @@ When prompted:
- Enter `y` to mark the reading complete and advance to the next day.
- Enter `n` to keep your current day.

### 3\. Shell Integration (Read Once Per Day)
### 3. Shell Integration (Read Once Per Day)

If you want your terminal to prompt you to read when you open it, but only once per day, add this command to your `.bashrc` or `.zshrc`:

Expand Down Expand Up @@ -188,3 +182,13 @@ This repository is intended to distribute tooling only.
- Do not commit or publish copyrighted plan content (for example EPUB source files, generated `plan.md`, `days/`, or `days-commentary/`) unless you have explicit rights to do so.
- The repository ignores those content paths by default so they stay local.
- Users should provide their own plan files and are responsible for ensuring they have permission to use that content.

## Request Support

If a specific Bible plan from World Bible Plans is not working with this tool, I would like to help.

Because I use a screen reader, please provide information as **plain text** or **Markdown**. Please do not send screenshots of the error or the plan.

1. **Check your file:** Ensure your plan has "Day" headings (like `Day 1:`) and section headings (like `## Scripture`).
2. **Open an Issue:** Go to the "Issues" tab on GitHub and describe what happened.
3. **Paste a sample:** If possible, paste a small section of the text from the EPUB that is causing the error.