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
39 changes: 39 additions & 0 deletions .github/scripts/build-install-zip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
#
# Builds the full, self-hostable install package: dist/linkforge-v<version>.zip
# Files sit at the archive ROOT (app/, public/, vendor/, artisan, ...), so a buyer extracts
# straight into their web root. Run from the repo root AFTER:
# composer install --no-dev --optimize-autoloader
# npm ci && npm run build
#
set -euo pipefail

VERSION="${1:?usage: build-install-zip.sh <version>}"
ROOT="$(pwd)"
STAGE="$(mktemp -d)"

# Export every TRACKED file. .env, node_modules, vendor, public/build and runtime cruft are
# gitignored and therefore excluded automatically.
git archive --format=tar HEAD | tar -x -C "$STAGE"

# Add the production dependencies + compiled assets (both gitignored; built in CI).
cp -r vendor "$STAGE/vendor"
cp -r public/build "$STAGE/public/build"

# Drop development-only files a production server never needs.
rm -rf \
"$STAGE/.github" \
"$STAGE/tests" \
"$STAGE/phpunit.xml" \
"$STAGE/pint.json" \
"$STAGE/package.json" \
"$STAGE/package-lock.json" \
"$STAGE/vite.config.js" \
"$STAGE/.editorconfig" \
"$STAGE/.gitattributes"

mkdir -p "$ROOT/dist"
( cd "$STAGE" && zip -rqX "$ROOT/dist/linkforge-v${VERSION}.zip" . )
rm -rf "$STAGE"

echo "Built dist/linkforge-v${VERSION}.zip ($(du -h "$ROOT/dist/linkforge-v${VERSION}.zip" | cut -f1))"
56 changes: 56 additions & 0 deletions .github/scripts/build-update-zip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
#
# Builds the incremental in-app update package: dist/linkforge-update-v<version>.zip
# Layout matches what App\Services\Update\Updater expects:
# update.json – { version, requires, name, notes }
# files/... – only the files changed since the previous tag, mirrored at app-root paths
# An operator uploads this in Admin -> Updates; the Updater backs up, overlays files/, then migrates.
# Run from the repo root AFTER `npm run build` (so freshly-compiled assets are available).
#
set -euo pipefail

VERSION="${1:?usage: build-update-zip.sh <version>}"
TAG="v${VERSION}"
ROOT="$(pwd)"
STAGE="$(mktemp -d)"
mkdir -p "$STAGE/files"

# Only ship runtime paths an operator actually needs overlaid (never tests/, CI, or dev configs).
INCLUDE='^(app/|resources/|config/|routes/|database/|bootstrap/app\.php|bootstrap/providers\.php|public/docs/|public/(favicon\.(ico|png)|apple-touch-icon\.png|logo\.png)|composer\.(json|lock)|artisan)'

PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)"
if [ -n "$PREV" ]; then
echo "Diffing $PREV..$TAG for changed files"
mapfile -t CHANGED < <(git diff --name-only "$PREV".."$TAG" | grep -E "$INCLUDE" || true)
else
echo "No previous tag — shipping the full runtime tree"
mapfile -t CHANGED < <(git ls-files | grep -E "$INCLUDE" || true)
fi

for f in "${CHANGED[@]}"; do
if [ -n "$f" ] && [ -f "$f" ]; then
mkdir -p "$STAGE/files/$(dirname "$f")"
cp "$f" "$STAGE/files/$f"
fi
done

# Bundle the freshly-built assets whenever front-end sources (or the manifest) changed.
if [ -z "$PREV" ] || git diff --name-only "$PREV".."$TAG" | grep -qE '^(resources/(css|js)/|vite\.config\.js|package(-lock)?\.json)'; then
mkdir -p "$STAGE/files/public"
cp -r public/build "$STAGE/files/public/build"
fi

cat > "$STAGE/update.json" <<JSON
{
"version": "${VERSION}",
"requires": "1.0.0",
"name": "LinkForge ${VERSION}",
"notes": "Release notes: https://github.com/sanmaxdev/linkforge/releases/tag/${TAG}"
}
JSON

mkdir -p "$ROOT/dist"
( cd "$STAGE" && zip -rqX "$ROOT/dist/linkforge-update-v${VERSION}.zip" . )
rm -rf "$STAGE"

echo "Built dist/linkforge-update-v${VERSION}.zip ($(du -h "$ROOT/dist/linkforge-update-v${VERSION}.zip" | cut -f1))"
57 changes: 57 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
release:
name: Build & publish release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so the incremental update package can diff against the previous tag

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, gd, zip, sodium, bcmath, curl, intl, fileinfo
coverage: none
tools: composer:v2

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm

- name: Derive version from tag
id: ver
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Install production dependencies + build assets
run: |
composer install --no-dev --optimize-autoloader --no-interaction --no-progress
npm ci
npm run build

- name: Build install package (full)
run: bash .github/scripts/build-install-zip.sh "${{ steps.ver.outputs.version }}"

- name: Build update package (incremental)
run: bash .github/scripts/build-update-zip.sh "${{ steps.ver.outputs.version }}"

- name: Publish GitHub Release
uses: softprops/action-gh-release@v2
with:
name: LinkForge v${{ steps.ver.outputs.version }}
generate_release_notes: true
files: |
dist/linkforge-v${{ steps.ver.outputs.version }}.zip
dist/linkforge-update-v${{ steps.ver.outputs.version }}.zip
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ npm run build # assets must compile
4. CI (tests + lint + assets build) must pass, and a maintainer must approve before merge. `main` is
protected — changes land only through reviewed PRs.

## Releasing (maintainers)

Releases are fully automated by [`.github/workflows/release.yml`](.github/workflows/release.yml).
To cut a release:

1. Bump the version in `config/linkforge.php` and merge it through a PR.
2. Tag the merge commit and push the tag:
```bash
git tag v1.1.0
git push origin v1.1.0
```
3. The `Release` workflow builds two artifacts and publishes a GitHub Release with auto-generated notes:
- **`linkforge-v<version>.zip`** — the full, self-hostable install package (extract into your web root).
- **`linkforge-update-v<version>.zip`** — an incremental package (only files changed since the previous
tag) for the in-app updater under **Admin → Updates**.

## Reporting bugs / requesting features

Use the [issue templates](https://github.com/sanmaxdev/linkforge/issues/new/choose). For **security**
Expand Down
Loading