Skip to content

Commit 2626fa4

Browse files
committed
feat: add EasyLibrary internal package build
1 parent 4131286 commit 2626fa4

6 files changed

Lines changed: 226 additions & 5 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
$root = dirname(__DIR__, 2);
6+
$pluginFile = $root . '/plugin.yml';
7+
$packageFile = $root . '/package.yml';
8+
9+
foreach ([$pluginFile, $packageFile] as $requiredFile) {
10+
if (!is_file($requiredFile)) {
11+
fwrite(STDERR, basename($requiredFile) . " was not found.\n");
12+
exit(1);
13+
}
14+
}
15+
16+
$pluginYml = (string) file_get_contents($pluginFile);
17+
$packageYml = (string) file_get_contents($packageFile);
18+
$name = readYamlScalar($pluginYml, 'name');
19+
$version = readYamlScalar($pluginYml, 'version');
20+
$packageId = readYamlScalar($packageYml, 'id');
21+
$packageName = readYamlScalar($packageYml, 'name');
22+
$packageVersion = readYamlScalar($packageYml, 'version');
23+
24+
if ($name !== $packageName) {
25+
fwrite(STDERR, "package.yml name {$packageName} does not match plugin.yml name {$name}.\n");
26+
exit(1);
27+
}
28+
if ($version !== $packageVersion) {
29+
fwrite(STDERR, "package.yml version {$packageVersion} does not match plugin.yml version {$version}.\n");
30+
exit(1);
31+
}
32+
33+
$safeName = sanitizeAssetPart($name);
34+
$safeVersion = sanitizeAssetPart($version);
35+
$dist = $root . '/dist';
36+
if (!is_dir($dist) && !mkdir($dist, 0775, true) && !is_dir($dist)) {
37+
fwrite(STDERR, "Cannot create dist directory.\n");
38+
exit(1);
39+
}
40+
41+
$asset = $safeName . '-' . $safeVersion . '.easylib.zip';
42+
$zipPath = $dist . '/' . $asset;
43+
if (is_file($zipPath) && !unlink($zipPath)) {
44+
fwrite(STDERR, "Cannot remove previous package: {$zipPath}\n");
45+
exit(1);
46+
}
47+
48+
$zip = new PharData($zipPath);
49+
foreach ([
50+
'package.yml',
51+
'src',
52+
'resources',
53+
'README.md',
54+
'LICENSE',
55+
] as $entry) {
56+
addPath($zip, $root, $root . '/' . $entry);
57+
}
58+
59+
$manifestAsset = $dist . '/package.yml';
60+
if (!copy($packageFile, $manifestAsset)) {
61+
fwrite(STDERR, "Cannot copy package.yml into dist.\n");
62+
exit(1);
63+
}
64+
65+
$checksums = [];
66+
foreach (glob($dist . '/*') ?: [] as $file) {
67+
if (!is_file($file) || basename($file) === 'checksums.txt' || basename($file) === 'release.env') {
68+
continue;
69+
}
70+
$hash = hash_file('sha256', $file);
71+
if (!is_string($hash)) {
72+
fwrite(STDERR, "Cannot calculate checksum for {$file}.\n");
73+
exit(1);
74+
}
75+
$checksums[] = $hash . ' ' . basename($file);
76+
}
77+
sort($checksums);
78+
file_put_contents($dist . '/checksums.txt', implode(PHP_EOL, $checksums) . PHP_EOL);
79+
80+
$phar = $dist . '/' . $safeName . '-' . $safeVersion . '.phar';
81+
$releaseEnv = [
82+
'NAME=' . $safeName,
83+
'VERSION=' . $safeVersion,
84+
'TAG=v' . $safeVersion,
85+
'PHAR=' . (is_file($phar) ? 'dist/' . basename($phar) : ''),
86+
'EASYLIB=dist/' . $asset,
87+
'PACKAGE_MANIFEST=dist/package.yml',
88+
'PACKAGE_ID=' . sanitizeAssetPart($packageId),
89+
];
90+
file_put_contents($dist . '/release.env', implode("\n", $releaseEnv) . "\n");
91+
92+
echo "Built {$asset}\n";
93+
echo "Package manifest dist/package.yml\n";
94+
95+
function readYamlScalar(string $yaml, string $key): string {
96+
if (preg_match('/^\s*' . preg_quote($key, '/') . '\s*:\s*["\']?([^"\'#\r\n]+)["\']?/m', $yaml, $matches) !== 1) {
97+
fwrite(STDERR, "YAML is missing {$key}.\n");
98+
exit(1);
99+
}
100+
101+
return trim($matches[1]);
102+
}
103+
104+
function sanitizeAssetPart(string $value): string {
105+
$value = trim($value);
106+
if ($value === '' || preg_match('/^[A-Za-z0-9_.-]+$/', $value) !== 1) {
107+
fwrite(STDERR, "Invalid release asset value: {$value}\n");
108+
exit(1);
109+
}
110+
111+
return $value;
112+
}
113+
114+
function addPath(PharData $zip, string $root, string $path): void {
115+
if (!file_exists($path)) {
116+
return;
117+
}
118+
if (is_file($path)) {
119+
$zip->addFile($path, relativePath($root, $path));
120+
return;
121+
}
122+
if (!is_dir($path)) {
123+
return;
124+
}
125+
126+
$iterator = new RecursiveIteratorIterator(
127+
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
128+
);
129+
foreach ($iterator as $file) {
130+
if ($file instanceof SplFileInfo && $file->isFile()) {
131+
$zip->addFile($file->getPathname(), relativePath($root, $file->getPathname()));
132+
}
133+
}
134+
}
135+
136+
function relativePath(string $root, string $path): string {
137+
return str_replace('\\', '/', substr($path, strlen($root) + 1));
138+
}

.github/workflows/release.yml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ concurrency:
1515

1616
jobs:
1717
release:
18-
name: Build and publish PHAR
18+
name: Build and publish PHAR/package
1919
runs-on: ubuntu-latest
2020

2121
steps:
@@ -45,6 +45,9 @@ jobs:
4545
- name: Build PHAR
4646
run: php -d phar.readonly=0 .github/scripts/build-phar.php
4747

48+
- name: Build EasyLibrary package
49+
run: php .github/scripts/build-easylib-package.php
50+
4851
- name: Read release metadata
4952
id: metadata
5053
shell: bash
@@ -58,6 +61,8 @@ jobs:
5861
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
5962
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
6063
echo "phar=${PHAR}" >> "$GITHUB_OUTPUT"
64+
echo "easylib=${EASYLIB}" >> "$GITHUB_OUTPUT"
65+
echo "package_manifest=${PACKAGE_MANIFEST}" >> "$GITHUB_OUTPUT"
6166
6267
- name: Publish GitHub Release assets
6368
env:
@@ -67,22 +72,24 @@ jobs:
6772
TAG="${{ steps.metadata.outputs.tag }}"
6873
TITLE="${{ steps.metadata.outputs.name }} ${{ steps.metadata.outputs.version }}"
6974
PHAR="${{ steps.metadata.outputs.phar }}"
75+
EASYLIB="${{ steps.metadata.outputs.easylib }}"
76+
PACKAGE_MANIFEST="${{ steps.metadata.outputs.package_manifest }}"
7077
7178
if gh release view "$TAG" >/dev/null 2>&1; then
72-
gh release upload "$TAG" "$PHAR" dist/checksums.txt --clobber
79+
gh release upload "$TAG" "$PHAR" "$EASYLIB" "$PACKAGE_MANIFEST" dist/checksums.txt --clobber
7380
elif [[ "${{ github.event_name }}" == "push" ]]; then
74-
gh release create "$TAG" "$PHAR" dist/checksums.txt \
81+
gh release create "$TAG" "$PHAR" "$EASYLIB" "$PACKAGE_MANIFEST" dist/checksums.txt \
7582
--verify-tag \
7683
--title "$TITLE" \
7784
--generate-notes
7885
else
79-
gh release create "$TAG" "$PHAR" dist/checksums.txt \
86+
gh release create "$TAG" "$PHAR" "$EASYLIB" "$PACKAGE_MANIFEST" dist/checksums.txt \
8087
--target "$GITHUB_SHA" \
8188
--title "$TITLE" \
8289
--generate-notes
8390
fi
8491
85-
- name: Verify published PHAR
92+
- name: Verify published PHAR and package
8693
shell: bash
8794
run: |
8895
TAG="${{ steps.metadata.outputs.tag }}"
@@ -91,3 +98,9 @@ jobs:
9198
URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${ASSET}"
9299
curl --fail --location --retry 10 --retry-delay 3 --retry-all-errors --output dist/published.phar "$URL"
93100
test "$(sha256sum dist/published.phar | cut -d' ' -f1)" = "$(sha256sum "$PHAR" | cut -d' ' -f1)"
101+
102+
EASYLIB="${{ steps.metadata.outputs.easylib }}"
103+
EASYLIB_ASSET="$(basename "$EASYLIB")"
104+
EASYLIB_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${EASYLIB_ASSET}"
105+
curl --fail --location --retry 10 --retry-delay 3 --retry-all-errors --output dist/published.easylib.zip "$EASYLIB_URL"
106+
test "$(sha256sum dist/published.easylib.zip | cut -d' ' -f1)" = "$(sha256sum "$EASYLIB" | cut -d' ' -f1)"

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to `LibForm` should be documented in this file.
44

55
## [2.0.0] - 2026-06-16
66

7+
### EasyLibrary 3.x Package Asset
8+
9+
- Added `package.yml` for the EasyLibrary 3.x internal package manager.
10+
- Added `LibFormPackage` as the package entrypoint metadata class.
11+
- Added `.github/scripts/build-easylib-package.php` to generate `LibForm-2.0.0.easylib.zip`, `dist/package.yml` and checksum metadata.
12+
- Updated release workflow to publish PHAR + `.easylib.zip` + `package.yml` + `checksums.txt`.
13+
- Standalone LibForm continues to exist; EasyLibrary mixed mode keeps the standalone provider active and shadows the internal package when both are present.
14+
15+
716
**For PocketMine-MP API 5.0.0+ and PHP 8.2+**
817

918
2.0.0 is a complete rewrite of LibForm, the form/UI framework for PocketMine-MP plugins. The entire library has been rebuilt with a new architecture centered on type-safe elements, immutable collections, structured responses, and composable form patterns.

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@
1414
## Description
1515
_A comprehensive form library for PocketMine-MP that simplifies the creation and management of Minecraft Bedrock Edition forms, supporting class-based definitions, fluent dynamic builders, pagination, form chaining, conditional elements, async data loading, timeouts, and ready-made presets._
1616

17+
18+
## EasyLibrary 3.x Internal Package
19+
20+
LibForm remains available as a standalone PocketMine-MP plugin/library, but it can also be published as an official EasyLibrary 3.x internal package.
21+
22+
Release assets for the internal package manager:
23+
24+
```text
25+
LibForm-2.0.0.phar
26+
LibForm-2.0.0.easylib.zip
27+
package.yml
28+
checksums.txt
29+
```
30+
31+
The `.easylib.zip` package is installed by EasyLibrary under `plugin_data/EasyLibrary/packages/libform/2.0.0/`. Install/update prepares files and requires a server restart; EasyLibrary does not hot-load duplicate classes. During the 3.0-dev mixed-mode migration, the standalone `LibForm` plugin wins when it is installed, and the internal package stays shadowed/protected.
32+
1733
## Features
1834
- **Three form types:** Long forms (button lists), modal dialogs (yes/no), and custom forms (inputs, toggles, sliders, dropdowns).
1935
- **Class-based and dynamic approaches:** Extend abstract classes for reusable form definitions, or use fluent method chaining for quick one-off forms.

package.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
id: libform
2+
name: LibForm
3+
version: 2.0.0
4+
type: classpath
5+
api: 1
6+
namespace: imperazim\form
7+
entry: imperazim\form\LibFormPackage
8+
9+
dependencies:
10+
required: []
11+
optional: []
12+
13+
provides:
14+
- form
15+
16+
autoload:
17+
psr-4:
18+
imperazim\form\: src/imperazim/form
19+
20+
resources: []
21+
22+
permissions: []
23+
24+
lifecycle:
25+
boot: false
26+
enable: false
27+
disable: false
28+
reload: false
29+
stateful: false
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace imperazim\form;
6+
7+
final class LibFormPackage {
8+
9+
public function boot(): void {}
10+
11+
public function enable(): void {}
12+
13+
public function disable(): void {}
14+
15+
public function reload(): void {}
16+
}

0 commit comments

Comments
 (0)