Skip to content

Commit 199ad43

Browse files
committed
Release 1.2.2: FrankenPHP banner, release-check gates, LibreOffice locator PHPStan.
1 parent 5a2c989 commit 199ad43

25 files changed

Lines changed: 434 additions & 135 deletions

.github/ISSUE_TEMPLATE/---bug-report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ assignees: ''
99

1010
| Q | A |
1111
| ----------------------------| ----------------------- |
12-
| `word-template-bundle` version | x.y.z |
12+
| `word-to-pdf-bundle` version | x.y.z |
1313
| PHP version | x.y.z |
1414
| Symfony version | x.y |
1515

.github/ISSUE_TEMPLATE/--support-question.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ assignees: ''
99

1010
### Question
1111

12-
### Versions (`composer info nowo-tech/word-template-bundle`, PHP, Symfony)
12+
### Versions (`composer info nowo-tech/word-to-pdf-bundle`, PHP, Symfony)

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ jobs:
7171
composer require --dev symfony/framework-bundle:^${{ matrix.symfony }} symfony/yaml:^${{ matrix.symfony }} --no-interaction --prefer-dist --no-progress
7272
7373
- name: PHPUnit (Symfony ${{ matrix.symfony }})
74+
env:
75+
SYMFONY_DEPRECATIONS_HELPER: max[direct]=0
7476
run: composer test
7577

7678
tests:
@@ -102,9 +104,13 @@ jobs:
102104
run: composer phpstan
103105

104106
- name: PHPUnit
107+
env:
108+
SYMFONY_DEPRECATIONS_HELPER: max[direct]=0
105109
run: composer test
106110

107111
- name: PHPUnit coverage gate (min 100% lines)
112+
env:
113+
SYMFONY_DEPRECATIONS_HELPER: max[direct]=0
108114
run: composer coverage-check
109115

110116
- name: Upload coverage to Codecov

.scripts/check-open-prs.sh

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env bash
2+
# Fail if the GitHub repo has unresolved open pull requests (REQ-REL-003).
3+
# Allowed temporary exceptions: label hold|do-not-merge AND a future review-by ISO date
4+
# in the PR body (body must carry the date).
5+
set -euo pipefail
6+
7+
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
8+
cd "${ROOT}"
9+
10+
if ! command -v gh >/dev/null 2>&1; then
11+
echo "ERROR: gh CLI is required (REQ-REL-003)." >&2
12+
exit 1
13+
fi
14+
15+
if ! gh auth status >/dev/null 2>&1; then
16+
echo "ERROR: gh is not authenticated — log in for nowo-tech (REQ-REL-003)." >&2
17+
exit 1
18+
fi
19+
20+
if ! command -v python3 >/dev/null 2>&1; then
21+
echo "ERROR: python3 is required to evaluate open PR exceptions (REQ-REL-003)." >&2
22+
exit 1
23+
fi
24+
25+
# Resolve owner/name for -R (works when SSH remotes confuse `gh` default repo detection).
26+
resolve_repo() {
27+
local url owner_repo
28+
if url="$(git remote get-url origin 2>/dev/null)"; then
29+
# git@github.com:org/repo.git | https://github.com/org/repo.git
30+
owner_repo="$(printf '%s\n' "${url}" | sed -E 's#^(git@github\.com:|https://github\.com/)##; s#\.git$##')"
31+
if [[ "${owner_repo}" == */* ]]; then
32+
printf '%s\n' "${owner_repo}"
33+
return 0
34+
fi
35+
fi
36+
return 1
37+
}
38+
39+
REPO_ARGS=()
40+
if REPO="$(resolve_repo)"; then
41+
REPO_ARGS=(-R "${REPO}")
42+
fi
43+
44+
PRS_JSON="$(gh pr list "${REPO_ARGS[@]}" --state open --limit 100 --json number,title,labels,body,url)"
45+
46+
export PRS_JSON
47+
python3 <<'PY'
48+
import json
49+
import os
50+
import re
51+
import sys
52+
from datetime import date
53+
54+
prs = json.loads(os.environ.get("PRS_JSON") or "[]")
55+
if not prs:
56+
print("OK: no open pull requests (REQ-REL-003)")
57+
sys.exit(0)
58+
59+
HOLD_LABELS = {"hold", "do-not-merge"}
60+
REVIEW_BY_RE = re.compile(
61+
r"(?i)\breview[-_ ]?by\b\s*[:=]?\s*(\d{4}-\d{2}-\d{2})"
62+
)
63+
64+
today = date.today()
65+
unresolved = []
66+
held_ok = []
67+
68+
for pr in prs:
69+
labels = {str(l.get("name", "")).lower() for l in (pr.get("labels") or [])}
70+
body = pr.get("body") or ""
71+
m = REVIEW_BY_RE.search(body)
72+
review_by = None
73+
if m:
74+
try:
75+
review_by = date.fromisoformat(m.group(1))
76+
except ValueError:
77+
review_by = None
78+
79+
has_hold = bool(labels & HOLD_LABELS)
80+
hold_valid = has_hold and review_by is not None and review_by >= today
81+
82+
entry = f"#{pr['number']} {pr.get('title') or ''} ({pr.get('url') or ''})"
83+
if hold_valid:
84+
held_ok.append(f"{entry} [hold until {review_by.isoformat()}]")
85+
else:
86+
reasons = []
87+
if not has_hold:
88+
reasons.append("missing hold/do-not-merge label")
89+
if review_by is None:
90+
reasons.append("missing review-by YYYY-MM-DD in body")
91+
elif review_by < today:
92+
reasons.append(f"review-by {review_by.isoformat()} expired")
93+
unresolved.append(f"{entry} — {', '.join(reasons)}")
94+
95+
if held_ok:
96+
print("Allowed hold exceptions:")
97+
for line in held_ok:
98+
print(f" {line}")
99+
100+
if unresolved:
101+
print("ERROR: unresolved open pull requests (REQ-REL-003):", file=sys.stderr)
102+
for line in unresolved:
103+
print(f" {line}", file=sys.stderr)
104+
print(
105+
"Merge, close with reason, or label hold/do-not-merge with a future "
106+
"review-by: YYYY-MM-DD in the PR body.",
107+
file=sys.stderr,
108+
)
109+
sys.exit(1)
110+
111+
print(f"OK: {len(held_ok)} open PR(s) covered by valid hold exceptions (REQ-REL-003)")
112+
sys.exit(0)
113+
PY

Makefile

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
# WordToPdfBundle — Docker-driven development (REQ-MAKE-001 / REQ-MAKE-002)
2-
.PHONY: help up down build shell ensure-up install test test-coverage coverage-check cs-check cs-fix qa clean composer-sync release-check release-check-demos phpstan rector rector-dry update validate setup-hooks check-no-cursor-coauthor strip-cursor-coauthor-from-history assets update-deps
2+
.PHONY: help up down build shell ensure-up install test test-coverage coverage-check cs-check cs-fix qa clean composer-sync release-check release-check-demos demo-smoke phpstan rector rector-dry update validate setup-hooks check-no-cursor-coauthor check-open-prs strip-cursor-coauthor-from-history assets update-deps
33

44
COMPOSE_FILE ?= docker-compose.yml
5-
COMPOSE ?= docker-compose -f $(COMPOSE_FILE)
5+
# Prefer Compose V2; absolute docker path avoids shadowing by local docker/ (REQ-MAKE-010).
6+
DOCKER_BIN := $(shell PATH="/usr/local/bin:/usr/bin:/bin:$$PATH" command -v docker 2>/dev/null)
7+
ifeq ($(DOCKER_BIN),)
8+
COMPOSE_BIN ?= docker-compose
9+
else
10+
COMPOSE_BIN ?= $(shell $(DOCKER_BIN) compose version >/dev/null 2>&1 && echo "$(DOCKER_BIN) compose" || echo "docker-compose")
11+
endif
12+
COMPOSE ?= $(COMPOSE_BIN) -f $(COMPOSE_FILE)
613
SERVICE_PHP ?= php
714
COMPOSER_INSTALL = $(COMPOSE) exec -T $(SERVICE_PHP) sh -c 'composer install --no-interaction || { rm -rf vendor; composer clear-cache; composer install --no-interaction; }'
815
DEMO_PRESENT := $(wildcard demo)
@@ -14,7 +21,8 @@ help:
1421
@echo " Dependencies: install, update, update-deps, composer-sync, validate"
1522
@echo " Tests: test, test-coverage, coverage-check"
1623
@echo " Quality: cs-check, cs-fix, rector, rector-dry, phpstan, qa"
17-
@echo " Release: release-check, release-check-demos"
24+
@echo " Release: release-check, release-check-demos, check-open-prs"
25+
@echo " demo-smoke REQ-TEST-011: boot demo and assert HTTP 200"
1826
@echo " Demos: cd demo && make up (or make -C demo/symfony8 up)"
1927
@echo " Assets: assets (no frontend assets in this bundle)"
2028
@echo " Git hooks: setup-hooks, check-no-cursor-coauthor"
@@ -85,6 +93,7 @@ assets:
8593

8694
release-check: ensure-up
8795
@$(MAKE) check-no-cursor-coauthor
96+
@$(MAKE) check-open-prs
8897
@$(MAKE) composer-sync
8998
@$(MAKE) cs-fix
9099
@$(MAKE) cs-check
@@ -98,6 +107,14 @@ endif
98107
release-check-demos:
99108
@$(MAKE) -C demo release-check
100109

110+
# REQ-TEST-011
111+
demo-smoke:
112+
@$(MAKE) -C demo demo-smoke
113+
114+
check-open-prs:
115+
@chmod +x .scripts/check-open-prs.sh
116+
@bash .scripts/check-open-prs.sh
117+
101118
clean:
102119
rm -rf vendor .phpunit.cache coverage .php-cs-fixer.cache coverage-php.txt coverage-output.txt
103120

@@ -126,7 +143,8 @@ setup-hooks:
126143

127144
# REQ-MAKE-008: update-deps
128145
BUNDLE_ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
129-
include $(BUNDLE_ROOT)/../.scripts/Makefile.update-deps.mk
146+
# Optional: monorepo helper absent on standalone GitHub Actions checkout (REQ-MAKE-009).
147+
-include $(BUNDLE_ROOT)/../.scripts/Makefile.update-deps.mk
130148

131149
strip-cursor-coauthor-from-history:
132150
@chmod +x .scripts/strip-cursor-coauthor-from-history.sh

README.md

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,21 @@
11
# WordToPdfBundle
22

3-
[![CI](https://github.com/nowo-tech/WordToPdfBundle/actions/workflows/ci.yml/badge.svg)](https://github.com/nowo-tech/WordToPdfBundle/actions/workflows/ci.yml)
4-
[![Packagist Version](https://img.shields.io/packagist/v/nowo-tech/word-to-pdf-bundle.svg?style=flat)](https://packagist.org/packages/nowo-tech/word-to-pdf-bundle)
5-
[![Packagist Downloads](https://img.shields.io/packagist/dt/nowo-tech/word-to-pdf-bundle.svg)](https://packagist.org/packages/nowo-tech/word-to-pdf-bundle)
6-
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7-
[![PHP](https://img.shields.io/badge/PHP-8.2%2B-777BB4?logo=php)](https://php.net)
8-
[![Symfony](https://img.shields.io/badge/Symfony-7.4%20%7C%208.0%20%7C%208.1%2B-000000?logo=symfony)](https://symfony.com)
9-
[![GitHub stars](https://img.shields.io/github/stars/nowo-tech/word-to-pdf-bundle.svg?style=social&label=Star)](https://github.com/nowo-tech/WordToPdfBundle)
10-
[![Coverage](https://img.shields.io/badge/Coverage-100%25-brightgreen)](#tests-and-coverage)
3+
[![CI](https://github.com/nowo-tech/WordToPdfBundle/actions/workflows/ci.yml/badge.svg)](https://github.com/nowo-tech/WordToPdfBundle/actions/workflows/ci.yml) [![Packagist Version](https://img.shields.io/packagist/v/nowo-tech/word-to-pdf-bundle.svg?style=flat)](https://packagist.org/packages/nowo-tech/word-to-pdf-bundle) [![Packagist Downloads](https://img.shields.io/packagist/dt/nowo-tech/word-to-pdf-bundle.svg)](https://packagist.org/packages/nowo-tech/word-to-pdf-bundle) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![PHP](https://img.shields.io/badge/PHP-8.2%2B-777BB4?logo=php)](https://php.net) [![Symfony](https://img.shields.io/badge/Symfony-7.4%20%7C%208.0%20%7C%208.1%2B-000000?logo=symfony)](https://symfony.com) [![GitHub stars](https://img.shields.io/github/stars/nowo-tech/word-to-pdf-bundle.svg?style=social&label=Star)](https://github.com/nowo-tech/WordToPdfBundle) [![Coverage](https://img.shields.io/badge/Coverage-100%25-brightgreen)](#tests-and-coverage)
114

125
> **Found this useful?** Install from Packagist (`composer require nowo-tech/word-to-pdf-bundle`) and consider starring [WordToPdfBundle on GitHub](https://github.com/nowo-tech/WordToPdfBundle).
136
7+
**FrankenPHP demos:** runtime is selected with **`FRANKENPHP_MODE`** (`worker` default, or `classic` for per-request PHP / hot-reload). See [docs/DEMO-FRANKENPHP.md](docs/DEMO-FRANKENPHP.md).
148
Symfony bundle that converts **Microsoft Word** (`.docx` / `.doc`) to **PDF** using **LibreOffice Writer** (`soffice` headless) for print-quality layout fidelity:
15-
169
- **named YAML profiles** + **default profile** + deep merge with per-call options, or **`convertWithInlineProfile()`**;
1710
- **batch conversion** via **`convertMany()`** with **`PdfNaming`** (keep / prefix / suffix / surround / fixed / callback, or path ⇒ filename map);
1811
- **runtime check** that **LibreOffice Writer** is installed (`nowo:word-to-pdf:check`); fails with install hints if missing;
1912
- works under **PHP-FPM** and **FrankenPHP** (Symfony Process / `proc_open`);
2013
- Symfony-friendly export: streamed/binary responses, local path, optional **Flysystem**.
21-
2214
This bundle does **not** fill Word templates (see [WordTemplateBundle](https://github.com/nowo-tech/WordTemplateBundle)), convert HTML to Word (see [HtmlToWordBundle](https://github.com/nowo-tech/HtmlToWordBundle)), or use DomPDF (DomPDF cannot preserve Word styles).
2315

24-
## Documentation
25-
26-
- [Installation](docs/INSTALLATION.md)
27-
- [Configuration](docs/CONFIGURATION.md)
28-
- [Usage](docs/USAGE.md)
29-
- [Contributing](docs/CONTRIBUTING.md)
30-
- [Code of Conduct](CODE_OF_CONDUCT.md)
31-
- [Changelog](docs/CHANGELOG.md)
32-
- [Upgrading](docs/UPGRADING.md)
33-
- [Release](docs/RELEASE.md)
34-
- [Security](docs/SECURITY.md)
35-
- [Engram](docs/ENGRAM.md)
36-
- [Spec-driven development](docs/SPEC-DRIVEN-DEVELOPMENT.md)
37-
- [GitHub Spec Kit](docs/SPEC-KIT.md)
38-
39-
### Additional documentation
40-
41-
- [GitHub Actions CI requirements](docs/GITHUB_CI.md)
42-
- [FrankenPHP / Docker demo](docs/DEMO-FRANKENPHP.md)`demo/symfony8` (see [`demo/README.md`](demo/README.md))
43-
44-
## System requirement
45-
46-
**LibreOffice Writer must be installed on the host / container** (Composer cannot install it):
47-
48-
```bash
49-
# Debian / Ubuntu
50-
sudo apt-get install -y libreoffice-writer
51-
52-
# Alpine
53-
apk add libreoffice
54-
55-
# Fedora / RHEL
56-
sudo dnf install -y libreoffice-writer
57-
```
16+
![FrankenPHP Friendly Worker Mode](docs/images/frankenphp-friendly.png)
5817

59-
Verify:
60-
61-
```bash
62-
php bin/console nowo:word-to-pdf:check
63-
```
18+
This bundle is **FrankenPHP worker mode friendly**.
6419

6520
## Quick start
6621

@@ -102,6 +57,34 @@ public function download(WordToPdfConverterInterface $converter, ExporterInterfa
10257
}
10358
```
10459

60+
## System requirement
61+
62+
**LibreOffice Writer must be installed on the host / container** (Composer cannot install it):
63+
64+
```bash
65+
# Debian / Ubuntu
66+
sudo apt-get install -y libreoffice-writer
67+
68+
# Alpine
69+
apk add libreoffice
70+
71+
# Fedora / RHEL
72+
sudo dnf install -y libreoffice-writer
73+
```
74+
75+
Verify:
76+
77+
```bash
78+
php bin/console nowo:word-to-pdf:check
79+
```
80+
81+
## Development
82+
83+
```bash
84+
make up
85+
make qa
86+
make release-check
87+
```
10588
## FrankenPHP worker mode
10689

10790
FrankenPHP worker mode: Supported (tested with LibreOffice conversion under FrankenPHP).
@@ -114,6 +97,26 @@ Demos use **`FRANKENPHP_MODE`** (`worker` by default, or `classic`) on PHP **8.5
11497
cd demo/symfony8 && cp .env.example .env && make up # Symfony 8, port 8022
11598
```
11699

100+
## Documentation
101+
102+
- [Installation](docs/INSTALLATION.md)
103+
- [Configuration](docs/CONFIGURATION.md)
104+
- [Usage](docs/USAGE.md)
105+
- [Contributing](docs/CONTRIBUTING.md)
106+
- [Code of Conduct](CODE_OF_CONDUCT.md)
107+
- [Changelog](docs/CHANGELOG.md)
108+
- [Upgrading](docs/UPGRADING.md)
109+
- [Release](docs/RELEASE.md)
110+
- [Security](docs/SECURITY.md)
111+
- [Engram](docs/ENGRAM.md)
112+
- [Spec-driven development](docs/SPEC-DRIVEN-DEVELOPMENT.md)
113+
- [GitHub Spec Kit](docs/SPEC-KIT.md)
114+
115+
### Additional documentation
116+
117+
- [GitHub Actions CI requirements](docs/GITHUB_CI.md)
118+
- [FrankenPHP / Docker demo](docs/DEMO-FRANKENPHP.md)`demo/symfony8` (see [`demo/README.md`](demo/README.md))
119+
117120
## Tests and coverage
118121

119122
| Scope | Detail |
@@ -127,10 +130,3 @@ composer test
127130
composer coverage-check
128131
```
129132

130-
## Development
131-
132-
```bash
133-
make up
134-
make qa
135-
make release-check
136-
```

composer.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"keywords": [
77
"symfony",
88
"symfony-bundle",
9+
"php",
10+
"frankenphp",
911
"word",
1012
"docx",
1113
"pdf",
@@ -16,7 +18,7 @@
1618
"homepage": "https://github.com/nowo-tech/WordToPdfBundle",
1719
"authors": [
1820
{
19-
"name": "Héctor Franco Aceituno",
21+
"name": "H\u00e9ctor Franco Aceituno",
2022
"email": "hectorfranco@nowo.tech"
2123
},
2224
{
@@ -37,6 +39,7 @@
3739
},
3840
"require-dev": {
3941
"friendsofphp/php-cs-fixer": "^3.0",
42+
"nowo-tech/phpstan-frankenphp": "^1.0",
4043
"phpstan/extension-installer": "^1.0",
4144
"phpstan/phpstan": "^2.0",
4245
"phpstan/phpstan-phpunit": "^2.0",
@@ -89,5 +92,9 @@
8992
],
9093
"rector": "@php vendor/bin/rector --no-progress-bar",
9194
"rector-dry": "@php vendor/bin/rector --dry-run --no-progress-bar"
95+
},
96+
"support": {
97+
"issues": "https://github.com/nowo-tech/WordToPdfBundle/issues",
98+
"source": "https://github.com/nowo-tech/WordToPdfBundle"
9299
}
93100
}

0 commit comments

Comments
 (0)