Skip to content

Commit 238792c

Browse files
Hermsi1337claude
andcommitted
Initial release
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 parents  commit 238792c

31 files changed

Lines changed: 3022 additions & 0 deletions

.gitattributes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dist/ export-ignore
2+
tests/ export-ignore
3+
phpunit.xml.dist export-ignore
4+
phpunit.integration.xml.dist export-ignore
5+
.gitattributes export-ignore
6+
.gitignore export-ignore
7+
.github/ export-ignore

.github/workflows/ci.yml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
unit-tests:
18+
name: Unit tests (PHP ${{ matrix.php }})
19+
runs-on: ubuntu-latest
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
php: ['8.1', '8.2', '8.3', '8.4']
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Set up PHP ${{ matrix.php }}
28+
uses: shivammathur/setup-php@v2
29+
with:
30+
php-version: ${{ matrix.php }}
31+
coverage: none
32+
tools: composer:v2
33+
extensions: imap, intl, mbstring
34+
35+
- name: Install dependencies
36+
uses: ramsey/composer-install@v3
37+
38+
- name: Lint PHP sources
39+
run: |
40+
find . -path ./vendor -prune -o -path ./dist -prune -o \
41+
-name '*.php' -print0 \
42+
| xargs -0 -n1 php -l > /dev/null
43+
44+
- name: Run unit tests
45+
run: composer test:unit -- --colors=never
46+
47+
integration-tests:
48+
name: Integration tests (Dovecot via Docker)
49+
runs-on: ubuntu-latest
50+
needs: unit-tests
51+
steps:
52+
- uses: actions/checkout@v4
53+
54+
- name: Set up PHP
55+
uses: shivammathur/setup-php@v2
56+
with:
57+
php-version: '8.3'
58+
coverage: none
59+
tools: composer:v2
60+
extensions: imap, intl, mbstring
61+
62+
- name: Install dependencies
63+
uses: ramsey/composer-install@v3
64+
65+
- name: Verify Docker is available
66+
run: docker info
67+
68+
- name: Pre-pull Dovecot image
69+
# Pulling outside the test timing avoids spurious wait-strategy timeouts
70+
# if the registry is slow.
71+
run: docker pull dovecot/dovecot:latest-root
72+
73+
- name: Run integration tests
74+
run: composer test:integration
75+
76+
docs-freshness:
77+
name: Documentation freshness checks
78+
runs-on: ubuntu-latest
79+
steps:
80+
- uses: actions/checkout@v4
81+
82+
- name: Config keys are consistent across docs and dist file
83+
run: |
84+
DOCS=$(git grep --untracked -hoE '\bimapsync_[a-z_]+' -- README.md AGENTS.md | sort -u)
85+
DIST=$(git grep --untracked -hoE '\bimapsync_[a-z_]+' -- config.inc.php.dist | sort -u)
86+
if ! diff <(echo "$DOCS") <(echo "$DIST") > /tmp/keys.diff; then
87+
echo "::error::Config-key mismatch between docs and config.inc.php.dist"
88+
echo "Diff (< docs, > dist):"
89+
cat /tmp/keys.diff
90+
exit 1
91+
fi
92+
93+
- name: Locale keys exist in both en_US and de_DE, and the two locales mirror each other
94+
run: |
95+
set -euo pipefail
96+
tmp=$(mktemp -d)
97+
git grep --untracked -hoE 'gettext\([^,)]+' -- '*.php' '*.js' \
98+
| sed -E "s/gettext\(['\"]([a-z0-9_]+)['\"].*/\1/" \
99+
| grep -E '^[a-z0-9_]+$' | sort -u > "$tmp/used"
100+
grep -hoE "labels\['[a-z0-9_]+'\]" localization/en_US.inc \
101+
| sed -E "s/.*\['([^']+)'\]/\1/" | sort -u > "$tmp/en"
102+
grep -hoE "labels\['[a-z0-9_]+'\]" localization/de_DE.inc \
103+
| sed -E "s/.*\['([^']+)'\]/\1/" | sort -u > "$tmp/de"
104+
105+
failed=0
106+
report() {
107+
local label="$1"; shift
108+
local file="$1"
109+
if [ -s "$file" ]; then
110+
echo "::error::$label"
111+
sed 's/^/ /' "$file"
112+
failed=1
113+
fi
114+
}
115+
116+
comm -23 "$tmp/used" "$tmp/en" > "$tmp/used_not_en"
117+
comm -23 "$tmp/used" "$tmp/de" > "$tmp/used_not_de"
118+
comm -23 "$tmp/en" "$tmp/de" > "$tmp/en_not_de"
119+
comm -13 "$tmp/en" "$tmp/de" > "$tmp/de_not_en"
120+
121+
report "Locale keys used in code but missing from en_US.inc" "$tmp/used_not_en"
122+
report "Locale keys used in code but missing from de_DE.inc" "$tmp/used_not_de"
123+
report "Locale keys defined in en_US.inc but missing from de_DE.inc" "$tmp/en_not_de"
124+
report "Locale keys defined in de_DE.inc but missing from en_US.inc" "$tmp/de_not_en"
125+
126+
exit "$failed"

.github/workflows/release.yml

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*.*.*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
name: Build and publish release
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Determine version
21+
id: version
22+
run: |
23+
tag="${GITHUB_REF#refs/tags/}"
24+
version="${tag#v}"
25+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
26+
echo "version=$version" >> "$GITHUB_OUTPUT"
27+
case "$version" in
28+
*-*) echo "prerelease=true" >> "$GITHUB_OUTPUT" ;;
29+
*) echo "prerelease=false" >> "$GITHUB_OUTPUT" ;;
30+
esac
31+
32+
- name: Set up PHP
33+
uses: shivammathur/setup-php@v2
34+
with:
35+
php-version: '8.3'
36+
coverage: none
37+
tools: composer:v2
38+
extensions: imap, intl, mbstring
39+
40+
- name: Install dependencies
41+
uses: ramsey/composer-install@v3
42+
43+
- name: Run unit tests (release safety gate)
44+
run: composer test:unit -- --colors=never
45+
46+
- name: Build release artifacts
47+
env:
48+
VERSION: ${{ steps.version.outputs.version }}
49+
run: |
50+
set -euo pipefail
51+
mkdir -p dist-release
52+
# The .gitattributes export-ignore directives strip dist/, tests/,
53+
# .github/, phpunit*.xml.dist, .gitignore, .gitattributes from the
54+
# archive automatically. The --prefix=imapsync/ wrapper lets users
55+
# extract straight into Roundcube's plugins/ directory.
56+
git archive --format=tar.gz --prefix=imapsync/ \
57+
-o "dist-release/roundcube-imapsync-${VERSION}.tar.gz" HEAD
58+
git archive --format=zip --prefix=imapsync/ \
59+
-o "dist-release/roundcube-imapsync-${VERSION}.zip" HEAD
60+
61+
- name: Inspect archive contents
62+
env:
63+
VERSION: ${{ steps.version.outputs.version }}
64+
run: |
65+
set -euo pipefail
66+
tarball="dist-release/roundcube-imapsync-${VERSION}.tar.gz"
67+
# Materialise the listing once so subsequent greps don't trigger
68+
# SIGPIPE on `tar` under `set -o pipefail` (e.g. `grep -q` closing
69+
# the pipe after the first match).
70+
listing="$(mktemp)"
71+
trap 'rm -f "$listing"' EXIT
72+
tar -tzf "$tarball" | sort > "$listing"
73+
74+
echo "=== tarball top-level layout ==="
75+
head -40 "$listing"
76+
echo
77+
echo "=== archive sanity ==="
78+
# Must contain plugin entry; must NOT contain dev-only paths.
79+
grep -q '^imapsync/imapsync\.php$' "$listing"
80+
if grep -E '^imapsync/(tests|dist|\.github|\.gitignore|\.gitattributes|phpunit)' "$listing"; then
81+
echo "Archive leaks dev-only paths"
82+
exit 1
83+
fi
84+
echo "OK"
85+
86+
- name: Generate SHA-256 checksums
87+
env:
88+
VERSION: ${{ steps.version.outputs.version }}
89+
run: |
90+
set -euo pipefail
91+
cd dist-release
92+
sha256sum "roundcube-imapsync-${VERSION}.tar.gz" "roundcube-imapsync-${VERSION}.zip" \
93+
> "roundcube-imapsync-${VERSION}.sha256"
94+
cat "roundcube-imapsync-${VERSION}.sha256"
95+
96+
- name: Create GitHub release
97+
uses: softprops/action-gh-release@v2
98+
with:
99+
tag_name: ${{ steps.version.outputs.tag }}
100+
name: ${{ steps.version.outputs.tag }}
101+
prerelease: ${{ steps.version.outputs.prerelease }}
102+
generate_release_notes: true
103+
fail_on_unmatched_files: true
104+
body: |
105+
## Install
106+
107+
**Drop-in tarball:**
108+
109+
```bash
110+
cd /path/to/roundcubemail/plugins
111+
curl -L -o imapsync.tar.gz \
112+
https://github.com/mittwald/Roundcube-IMAPsync/releases/download/${{ steps.version.outputs.tag }}/roundcube-imapsync-${{ steps.version.outputs.version }}.tar.gz
113+
tar -xzf imapsync.tar.gz && rm imapsync.tar.gz
114+
```
115+
116+
Then enable in `config/config.inc.php`:
117+
118+
```php
119+
$config['plugins'][] = 'imapsync';
120+
```
121+
122+
**Via Composer (once published on Packagist):**
123+
124+
```bash
125+
composer require mittwald/imapsync:${{ steps.version.outputs.version }}
126+
```
127+
128+
SHA-256 checksums are attached as `roundcube-imapsync-${{ steps.version.outputs.version }}.sha256`.
129+
130+
---
131+
files: |
132+
dist-release/roundcube-imapsync-${{ steps.version.outputs.version }}.tar.gz
133+
dist-release/roundcube-imapsync-${{ steps.version.outputs.version }}.zip
134+
dist-release/roundcube-imapsync-${{ steps.version.outputs.version }}.sha256

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
dist/*
2+
!dist/.gitkeep
3+
/vendor/
4+
/composer.lock
5+
/.phpunit.cache/
6+
/.phpunit.result.cache
7+
/.claude/
8+
/.codex/
9+
/.idea/

0 commit comments

Comments
 (0)