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
90 changes: 75 additions & 15 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,61 @@ jobs:
name: Build installable ZIP
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
version: ${{ steps.release.outputs.version }}
archive_name: ${{ steps.release.outputs.archive_name }}

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

- name: Build and validate release
run: scripts/build-release.sh
id: release
run: |
set -Eeuo pipefail

scripts/build-release.sh

version=$(awk -F"'" '/\$this->version[[:space:]]*=/{print $2; exit}' prettyblocks.php)
archive_name="prettyblocks-${version}.zip"
test -s "dist/${archive_name}"

echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "archive_name=${archive_name}" >> "$GITHUB_OUTPUT"

- name: Upload release packages
- name: Upload release package
uses: actions/upload-artifact@v7
with:
name: prettyblocks-${{ github.sha }}
path: dist/*.zip
name: prettyblocks-${{ steps.release.outputs.version }}
path: dist/${{ steps.release.outputs.archive_name }}
if-no-files-found: error
retention-days: 14

test-release:
name: Install ZIP on PrestaShop
needs: build-release
runs-on: ubuntu-latest
timeout-minutes: 20

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

- name: Download release package
uses: actions/download-artifact@v8
with:
name: prettyblocks-${{ needs.build-release.outputs.version }}
path: dist

- name: Test downloaded release package
run: scripts/test-release.sh "dist/${{ needs.build-release.outputs.archive_name }}"

publish-release:
name: Publish GitHub release
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: build-release
needs:
- build-release
- test-release
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
Expand All @@ -49,24 +84,31 @@ jobs:
- name: Download release package
uses: actions/download-artifact@v8
with:
name: prettyblocks-${{ github.sha }}
name: prettyblocks-${{ needs.build-release.outputs.version }}
path: dist

- name: Prepare release metadata
id: release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.build-release.outputs.version }}
ARCHIVE_NAME: ${{ needs.build-release.outputs.archive_name }}
run: |
set -Eeuo pipefail

version=$(awk -F"'" '/\$this->version[[:space:]]*=/{print $2; exit}' prettyblocks.php)
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][0-9A-Za-z.-]+)?$ ]]; then
if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][0-9A-Za-z.-]+)?$ ]]; then
echo "Unable to read a valid semantic version from prettyblocks.php." >&2
exit 1
fi

test -s dist/prettyblocks.zip
sha256sum dist/prettyblocks.zip > dist/prettyblocks.zip.sha256
archive_path="dist/${ARCHIVE_NAME}"
checksum_path="${archive_path}.sha256"
test -s "$archive_path"
(
cd dist
sha256sum "$ARCHIVE_NAME" > "${ARCHIVE_NAME}.sha256"
sha256sum --check "${ARCHIVE_NAME}.sha256"
)

previous_tag=$(gh release list \
--exclude-drafts \
Expand All @@ -82,6 +124,10 @@ jobs:
fi

{
echo '## Installation'
echo
printf 'Download `%s` from this release assets. Do not use the automatically generated **Source code (zip)** archive, which is not an installable PrestaShop module.\n' "$ARCHIVE_NAME"
echo
echo '## Contributors'
echo
git log --format='%aN%x09%aE' "$commit_range" \
Expand All @@ -92,23 +138,37 @@ jobs:
done
} > release-notes-prefix.md

echo "version=$version" >> "$GITHUB_OUTPUT"
echo "archive_path=${archive_path}" >> "$GITHUB_OUTPUT"
echo "checksum_path=${checksum_path}" >> "$GITHUB_OUTPUT"

- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.release.outputs.version }}
VERSION: ${{ needs.build-release.outputs.version }}
ARCHIVE_PATH: ${{ steps.release.outputs.archive_path }}
CHECKSUM_PATH: ${{ steps.release.outputs.checksum_path }}
run: |
set -Eeuo pipefail

if gh release view "$VERSION" >/dev/null 2>&1; then
echo "Release $VERSION already exists; nothing to publish."
tag_commit=$(git rev-list -n 1 "$VERSION")
if [ "$tag_commit" != "$GITHUB_SHA" ]; then
echo "Release $VERSION points to $tag_commit instead of $GITHUB_SHA." >&2
echo "Bump the module version before publishing a different commit." >&2
exit 1
fi

gh release upload "$VERSION" \
"$ARCHIVE_PATH#PrettyBlocks installable module ZIP" \
"$CHECKSUM_PATH#SHA-256 checksum" \
--clobber
echo "Release $VERSION already exists; its assets were validated and refreshed."
exit 0
fi

gh release create "$VERSION" \
'dist/prettyblocks.zip#PrettyBlocks module ZIP' \
'dist/prettyblocks.zip.sha256#SHA-256 checksum' \
"$ARCHIVE_PATH#PrettyBlocks installable module ZIP" \
"$CHECKSUM_PATH#SHA-256 checksum" \
--target "$GITHUB_SHA" \
--title "$VERSION" \
--generate-notes \
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ cd ../../../ && php bin/console prestashop:module install prettyblocks

### Release

Téléchargez la [dernière release](https://github.com/PrestaSafe/prettyblocks/releases/latest), puis installez le module directement dans votre PrestaShop :)
Téléchargez `prettyblocks-{version}.zip` dans les assets de la
[dernière release](https://github.com/PrestaSafe/prettyblocks/releases/latest),
puis installez ce fichier directement dans votre PrestaShop. N'utilisez pas
l'archive automatique **Source code (zip)** de GitHub : elle ne contient pas le
module compilé et n'est pas installable.

Vous pouvez également utiliser notre module ClassicBlocks, afin d'avoir 4 blocks à utiliser. Disponible ici: [Télécharger ClassicBlocks](https://github.com/PrestaSafe/classicblocks)

Expand Down Expand Up @@ -64,7 +68,11 @@ cd ../../../ && php bin/console prestashop:module install prettyblocks
```

### From Release
For installing PrettyBlocks, you can download our [latest release](https://github.com/PrestaSafe/prettyblocks/releases/latest) and install the module directly in your PrestaShop.
Download `prettyblocks-{version}.zip` from the assets of our
[latest release](https://github.com/PrestaSafe/prettyblocks/releases/latest)
and install that file directly in PrestaShop. Do not use GitHub's automatically
generated **Source code (zip)** archive: it does not contain the compiled module
and cannot be installed.

You can also add our module ClassicBlocks for having 4 blocks to use [Download ClassicBlocks](https://github.com/PrestaSafe/classicblocks)

Expand Down
2 changes: 1 addition & 1 deletion _dev/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build:{
manifest: true,
manifest: 'manifest.json',
outDir: "../build/",
emptyOutDir: true,
},
Expand Down
15 changes: 11 additions & 4 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,22 @@ with a top-level `prettyblocks/` directory, and validates its contents:
scripts/build-release.sh
```

The versioned archive and its stable `prettyblocks.zip` alias are written to
`dist/`. To prove that the ZIP is self-contained, run it against two fresh,
temporary shops. This test disables both the source asset build and the runtime
Composer install, so only files present in the archive can be used:
The installable `prettyblocks-{version}.zip` archive is written to `dist/`. Its
top-level directory is always `prettyblocks/`, without the version suffix. To
prove that the ZIP is self-contained, run it against two fresh, temporary
shops. This test disables both the source asset build and the runtime Composer
install, so only files present in the archive can be used:

```bash
scripts/test-release.sh
```

Pass an existing archive to test the exact package without rebuilding it:

```bash
scripts/test-release.sh dist/prettyblocks-3.2.2.zip
```

The release test validates and extracts the ZIP with PrestaShop's native ZIP
handler, then installs that extracted artifact with the PrestaShop CLI. It uses
ports `8182` and `8191` by default and deletes its shops, databases, and
Expand Down
2 changes: 1 addition & 1 deletion prettyblocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function __construct()
{
$this->name = 'prettyblocks';
$this->tab = 'administration';
$this->version = '3.2.1';
$this->version = '3.2.2';
$this->author = 'PrestaSafe';
$this->need_instance = 1;
$this->js_path = $this->_path . 'views/js/';
Expand Down
10 changes: 2 additions & 8 deletions scripts/build-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ required_paths=(
prettyblocks/prettyblocks.php
prettyblocks/vendor/autoload.php
prettyblocks/vendor/composer/installed.php
prettyblocks/build/.vite/manifest.json
prettyblocks/build/manifest.json
prettyblocks/views/js/build/build.js
prettyblocks/views/css/iframe.css
prettyblocks/views/css/dist/main.css
Expand Down Expand Up @@ -116,15 +116,9 @@ fi

mkdir -p "$output_dir"
cp "$built_archive" "$output_dir/$archive_name"
cp "$built_archive" "$output_dir/prettyblocks.zip"
rm -f "$output_dir/prettyblocks.zip" "$output_dir/prettyblocks.zip.sha256"

versioned_checksum=$(sha256_file "$output_dir/$archive_name")
stable_checksum=$(sha256_file "$output_dir/prettyblocks.zip")
if [[ $versioned_checksum != "$stable_checksum" ]]; then
echo "Versioned and stable release archives differ." >&2
exit 1
fi

echo "Release ready: $output_dir/$archive_name"
echo "Stable alias: $output_dir/prettyblocks.zip"
echo "SHA-256: $versioned_checksum"
61 changes: 53 additions & 8 deletions scripts/test-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,34 @@ compose_files=(
-f "$project_dir/docker/compose.release-test.yml"
)

"$script_dir/build-release.sh"

version=$(awk -F"'" '/\$this->version[[:space:]]*=/{print $2; exit}' "$project_dir/prettyblocks.php")
export PRETTYBLOCKS_RELEASE_ARCHIVE_PATH="$project_dir/dist/prettyblocks-${version}.zip"
expected_archive_name="prettyblocks-${version}.zip"

if [ "$#" -gt 1 ]; then
echo "Usage: $0 [archive]" >&2
exit 1
fi

if [ "$#" -eq 1 ]; then
archive_path=$1
if [ ! -f "$archive_path" ]; then
echo "Release archive not found: $archive_path" >&2
exit 1
fi

archive_dir=$(CDPATH='' cd -- "$(dirname -- "$archive_path")" && pwd)
archive_path="$archive_dir/$(basename -- "$archive_path")"
else
"$script_dir/build-release.sh"
archive_path="$project_dir/dist/$expected_archive_name"
fi

if [ "$(basename -- "$archive_path")" != "$expected_archive_name" ]; then
echo "Expected release archive name: $expected_archive_name" >&2
exit 1
fi

export PRETTYBLOCKS_RELEASE_ARCHIVE_PATH="$archive_path"
export COMPOSE_PROJECT_NAME="prettyblocks-release-test-${$}"
export PS82_PORT=${PS82_RELEASE_PORT:-8182}
export PS91_PORT=${PS91_RELEASE_PORT:-8191}
Expand Down Expand Up @@ -60,18 +84,39 @@ for service_and_port in "ps82:$PS82_PORT" "ps91:$PS91_PORT"; do
port=${service_and_port##*:}

asset_paths=$(docker compose "${compose_files[@]}" exec -T "$service" php -r '
$manifestPath = "modules/prettyblocks/build/manifest.json";
$manifest = json_decode(
file_get_contents("modules/prettyblocks/build/.vite/manifest.json"),
file_get_contents($manifestPath),
true,
512,
JSON_THROW_ON_ERROR
);

foreach ($manifest as $entry) {
echo $entry["file"], "\n";
foreach ($entry["css"] ?? [] as $css) {
echo $css, "\n";
$entry = $manifest["index.html"] ?? null;
if (!is_array($entry) || true !== ($entry["isEntry"] ?? false)) {
throw new RuntimeException("The Vite manifest has no index.html entry.");
}

$javascript = $entry["file"] ?? null;
$stylesheets = $entry["css"] ?? null;
if (!is_string($javascript) || !str_ends_with($javascript, ".js")) {
throw new RuntimeException("The Vite manifest has no entry JavaScript.");
}
if (!is_array($stylesheets) || [] === $stylesheets) {
throw new RuntimeException("The Vite manifest has no entry stylesheet.");
}

foreach (array_merge([$javascript], $stylesheets) as $asset) {
if (
!is_string($asset)
|| str_starts_with($asset, "/")
|| str_contains($asset, "..")
|| str_contains($asset, "\\")
) {
throw new RuntimeException("The Vite manifest contains an unsafe asset path.");
}

echo $asset, "\n";
}
' | tr -d '\r')

Expand Down
10 changes: 8 additions & 2 deletions src/Controller/AdminThemeManagerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,16 @@ public function indexAction()
$js_entry = '';
if ($filesystem->exists($build_dir)) {
// load manifest.json
$manifest = $build_dir . '.vite/manifest.json';
$manifest = $build_dir . 'manifest.json';

if (!$filesystem->exists($manifest)) {
throw new \Exception('manifest.json not exist');
// Keep existing development builds working after moving the
// release manifest out of Vite's hidden .vite directory.
$manifest = $build_dir . '.vite/manifest.json';
}

if (!$filesystem->exists($manifest)) {
throw new \Exception('manifest.json does not exist');
}
$json = \Tools::file_get_contents($manifest);
$json = json_decode($json, true);
Expand Down
Loading