diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..eb53e55
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,65 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: 'Existing tag to release (e.g. v1.4.0)'
+ required: true
+ type: string
+
+permissions:
+ contents: read
+
+jobs:
+ release:
+ name: Create GitHub Release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Determine tag
+ id: tag
+ env:
+ INPUT_TAG: ${{ inputs.tag }}
+ REF_NAME: ${{ github.ref_name }}
+ run: |
+ TAG="${INPUT_TAG:-$REF_NAME}"
+ case "$TAG" in
+ v[0-9]*.[0-9]*.[0-9]*) ;;
+ *) echo "Not a release tag: $TAG"; exit 1 ;;
+ esac
+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
+
+ - name: Checkout tag
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ with:
+ ref: ${{ steps.tag.outputs.tag }}
+
+ - name: Extract changelog section
+ env:
+ TAG: ${{ steps.tag.outputs.tag }}
+ run: |
+ VERSION="${TAG#v}"
+ awk -v ver="$VERSION" '
+ index($0, "## [" ver "]") == 1 { found = 1; next }
+ found && /^## \[/ { exit }
+ found { print }
+ ' CHANGELOG.md > release-notes.md
+ if [ ! -s release-notes.md ]; then
+ echo "No CHANGELOG.md section found for version $VERSION"
+ exit 1
+ fi
+
+ - name: Create GitHub Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ TAG: ${{ steps.tag.outputs.tag }}
+ run: |
+ gh release create "$TAG" \
+ --verify-tag \
+ --title "$TAG" \
+ --notes-file release-notes.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49d983f..b1c4931 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Excel exports of RTL documents open with a right-to-left sheet view, and the data preview lays out right-to-left for RTL documents
- "Include Headers" now adds an Excel autofilter to the header row, making the exported sheet filterable
- Fully bilingual status messages: progress, errors, file info, page count, and column labels now follow the selected language
+- Footer now shows the GitHub logo linking to the source repository and an Apache License 2.0 notice (bilingual)
+- Release workflow (`.github/workflows/release.yml`): pushing a `v*` tag (or a manual dispatch) creates the GitHub Release with the matching CHANGELOG section as notes
### Changed
@@ -24,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Cleanup options (Skip Empty Rows, Merge Fragments, Auto-detect Columns) are now non-destructive: unchecking an option restores the original extracted data
- Turnstile verification is required once per session instead of once per file
- "Hebrew/Arabic RTL Support" is now enabled by default (it only activates on documents detected as RTL)
+- `scripts/release.sh` reworked for the PR-based flow: it now only validates and pushes the version tag (the version bump and CHANGELOG entry come from the PR, the GitHub Release comes from the release workflow)
### Fixed
diff --git a/public/app.js b/public/app.js
index 58e94ec..7ac21b1 100755
--- a/public/app.js
+++ b/public/app.js
@@ -1135,6 +1135,7 @@ const translations = {
footerLink2: "NX1X Lab",
footerLink3: "GitHub",
footerPrivacy: "Privacy: Your PDFs are processed entirely in your browser. No files are uploaded to any server. Anonymous usage analytics via Cloudflare Analytics.",
+ footerLicense: 'Open source under the Apache License 2.0',
footerCopyright: `© ${currentYear} NX1X.`,
statusVerify: "Please complete the security verification below to proceed.",
statusLoading: "Loading PDF file...",
@@ -1194,6 +1195,7 @@ const translations = {
footerLink2: "NX1X Lab",
footerLink3: "GitHub",
footerPrivacy: "פרטיות: קבצי ה-PDF שלכם מעובדים לחלוטין בדפדפן שלכם. לא מועלים קבצים לשום שרת. אנליטיקה אנונימית בסיסית דרך Cloudflare Analytics.",
+ footerLicense: 'קוד פתוח תחת רישיון Apache 2.0',
footerCopyright: `© ${currentYear} NX1X.`,
statusVerify: "אנא השלימו את אימות האבטחה למטה כדי להמשיך.",
statusLoading: "טוען קובץ PDF...",
@@ -1289,6 +1291,7 @@ function updateLanguage() {
updateElement('footerLink2', lang.footerLink2);
updateElement('footerLink3', lang.footerLink3);
updateElement('footerPrivacy', lang.footerPrivacy, true);
+ updateElement('footerLicense', lang.footerLicense, true);
updateElement('footerCopyright', lang.footerCopyright);
const customRangeInput = document.getElementById('customRange');
diff --git a/public/index.html b/public/index.html
index 44f882a..818a2e8 100755
--- a/public/index.html
+++ b/public/index.html
@@ -339,13 +339,20 @@
Found EasyConvert helpful?
+
+
diff --git a/public/style.css b/public/style.css
index cabe51d..2bd2a93 100755
--- a/public/style.css
+++ b/public/style.css
@@ -684,6 +684,27 @@ body {
margin-bottom: 20px;
}
+.footer-github-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.footer-github-icon {
+ flex-shrink: 0;
+}
+
+.footer-license {
+ font-size: 0.85rem;
+ opacity: 0.8;
+ margin-bottom: 12px;
+}
+
+.footer-license a {
+ color: white;
+ text-decoration: underline;
+}
+
.footer-copyright {
font-size: 0.85rem;
opacity: 0.7;
diff --git a/scripts/release.sh b/scripts/release.sh
index 6edc0df..8c271a4 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -1,145 +1,49 @@
-#!/bin/bash
+#!/usr/bin/env bash
-# EasyConvert Release Script
-# Automates version bumping and release process
+# EasyConvert release script.
+#
+# The version bump and CHANGELOG entry land on main through a normal PR
+# (package.json, README badge, sw.js CACHE_NAME, CHANGELOG.md). This script
+# then tags that version and pushes the tag; the GitHub Release itself is
+# created by .github/workflows/release.yml, which uses the matching
+# CHANGELOG.md section as the release notes.
-set -e # Exit on any error
+set -euo pipefail
-# Colors for output
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-BLUE='\033[0;34m'
-YELLOW='\033[1;33m'
-NC='\033[0m' # No Color
+REPO_URL="https://github.com/NX1X/EasyConvert"
-# Function to print colored output
-print_status() {
- echo -e "${BLUE}[INFO]${NC} $1"
-}
+fail() { echo "ERROR: $1" >&2; exit 1; }
-print_success() {
- echo -e "${GREEN}[SUCCESS]${NC} $1"
-}
+git rev-parse --git-dir > /dev/null 2>&1 || fail "Not in a git repository."
-print_warning() {
- echo -e "${YELLOW}[WARNING]${NC} $1"
-}
+BRANCH=$(git rev-parse --abbrev-ref HEAD)
+[ "$BRANCH" = "main" ] || fail "Releases are tagged from main (currently on '$BRANCH')."
-print_error() {
- echo -e "${RED}[ERROR]${NC} $1"
-}
+[ -z "$(git status --porcelain)" ] || fail "Working tree is not clean. Commit or stash first."
-# Check if we're in a git repository
-if ! git rev-parse --git-dir > /dev/null 2>&1; then
- print_error "Not in a git repository!"
- exit 1
-fi
-
-# Check if there are uncommitted changes
-if [[ -n $(git status --porcelain) ]]; then
- print_warning "You have uncommitted changes. Please commit or stash them first."
- git status --short
- exit 1
-fi
-
-# Parse arguments
-if [[ $# -lt 1 ]]; then
- echo "Usage: $0 [release-notes]"
- echo ""
- echo "Examples:"
- echo " $0 patch \"Fixed table detection bug\""
- echo " $0 minor \"Added new export format\""
- echo " $0 major \"Complete UI redesign\""
- echo " $0 1.2.3 \"Custom version with release notes\""
- exit 1
-fi
+git fetch origin main --tags
+[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] || \
+ fail "Local main is not in sync with origin/main. Pull or push first."
-BUMP_TYPE=$1
-RELEASE_NOTES=${2:-"Version bump"}
+VERSION=$(node -p "require('./package.json').version")
+TAG="v$VERSION"
-# Get current version from package.json
-CURRENT_VERSION=$(node -p "require('./package.json').version")
-print_status "Current version: $CURRENT_VERSION"
+grep -q "^## \[$VERSION\]" CHANGELOG.md || \
+ fail "CHANGELOG.md has no '## [$VERSION]' section. Add it before releasing."
-# Calculate new version
-if [[ $BUMP_TYPE =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
- # Custom version provided
- NEW_VERSION=$BUMP_TYPE
-else
- # Calculate based on bump type
- IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
- MAJOR=${VERSION_PARTS[0]}
- MINOR=${VERSION_PARTS[1]}
- PATCH=${VERSION_PARTS[2]}
-
- case $BUMP_TYPE in
- "patch")
- PATCH=$((PATCH + 1))
- ;;
- "minor")
- MINOR=$((MINOR + 1))
- PATCH=0
- ;;
- "major")
- MAJOR=$((MAJOR + 1))
- MINOR=0
- PATCH=0
- ;;
- *)
- print_error "Invalid bump type: $BUMP_TYPE"
- exit 1
- ;;
- esac
-
- NEW_VERSION="$MAJOR.$MINOR.$PATCH"
+if git rev-parse "$TAG" > /dev/null 2>&1; then
+ fail "Tag $TAG already exists."
fi
-print_status "New version: $NEW_VERSION"
-
-# Confirm the release
-echo ""
-print_warning "About to release version $NEW_VERSION"
-print_status "Release notes: $RELEASE_NOTES"
-echo ""
+echo "About to tag and push $TAG (version from package.json)."
read -p "Continue? (y/N): " -n 1 -r
echo ""
-if [[ ! $REPLY =~ ^[Yy]$ ]]; then
- print_status "Release cancelled."
- exit 0
-fi
-
-# Update version using Node.js script
-print_status "Updating version files..."
-if [[ -f "scripts/update-version.js" ]]; then
- node scripts/update-version.js "$NEW_VERSION" "$RELEASE_NOTES"
-else
- print_error "Version update script not found!"
- exit 1
-fi
-
-# Commit changes
-print_status "Committing version changes..."
-git add .
-git commit -m "Release v$NEW_VERSION
-
-$RELEASE_NOTES"
-
-# Create and push tag
-print_status "Creating git tag v$NEW_VERSION..."
-git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION
-
-$RELEASE_NOTES"
-
-# Push changes and tags
-print_status "Pushing to remote repository..."
-git push origin main
-git push origin "v$NEW_VERSION"
+[[ $REPLY =~ ^[Yy]$ ]] || { echo "Cancelled."; exit 0; }
-print_success "Successfully released version $NEW_VERSION!"
-print_status "🎉 Release completed! Version $NEW_VERSION is now live."
-print_status "📝 Changelog updated with release notes"
-print_status "🏷️ Git tag v$NEW_VERSION created and pushed"
-print_status "🚀 All changes pushed to remote repository"
+git tag -a "$TAG" -m "Release $TAG"
+git push origin "$TAG"
echo ""
-print_status "View your release at: https://github.com/nx1xlab/easyconvert/releases/tag/v$NEW_VERSION"
\ No newline at end of file
+echo "Tag $TAG pushed. The release workflow is creating the GitHub Release:"
+echo " $REPO_URL/actions/workflows/release.yml"
+echo " $REPO_URL/releases/tag/$TAG"