Merge branch 'master' into WebView-Focus-Issue

This commit is contained in:
creamy-corn 2025-11-03 14:58:49 -08:00 committed by GitHub
commit e2d480b0bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 334 additions and 613 deletions

View file

@ -70,7 +70,7 @@ body:
validations:
required: false
- type: textarea
id: systemetails
id: systemdetails
attributes:
label: System Details
description: Please add the output of `wails doctor`.

View file

@ -1,4 +1,3 @@
name: Nightly Release v3-alpha
on:
@ -32,33 +31,20 @@ jobs:
with:
ref: v3-alpha
fetch-depth: 0
token: ${{ github.token }}
token: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: '1.23'
go-version: '1.24'
cache: true
cache-dependency-path: 'v3/go.sum'
- name: Install Task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ github.token }}
- name: Verify Go and Task installation
run: |
echo "Go version:"
go version
echo ""
echo "Task version:"
task --version
echo ""
echo "Working directory:"
pwd
echo ""
echo "v3 directory contents:"
ls -la v3/
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Git
run: |
@ -66,7 +52,7 @@ jobs:
git config --global user.email "github-actions[bot]@users.noreply.github.com"
# Configure git to use the token for authentication
git config --global url."https://x-access-token:${{ github.token }}@github.com/".insteadOf "https://github.com/"
git config --global url."https://x-access-token:${{ secrets.WAILS_REPO_TOKEN || github.token }}@github.com/".insteadOf "https://github.com/"
- name: Check for existing release tag
id: check_tag
@ -84,24 +70,16 @@ jobs:
run: |
echo "🔍 Checking UNRELEASED_CHANGELOG.md for content..."
# Check if the file exists and has content
if [ -f "v3/UNRELEASED_CHANGELOG.md" ]; then
echo "Found v3/UNRELEASED_CHANGELOG.md"
# Run the release script in check mode to see if there's content
cd v3/tasks/release
# Use the release script itself to check for content
if go run release.go --check-only 2>/dev/null; then
echo "has_unreleased_content=true" >> $GITHUB_OUTPUT
echo "✅ Found unreleased changelog content"
else
echo "has_unreleased_content=false" >> $GITHUB_OUTPUT
echo " No unreleased changelog content found"
fi
# Run the release script in check mode to see if there's content
cd v3/tasks/release
# Use the release script itself to check for content
if go run release.go --check-only 2>/dev/null; then
echo "has_unreleased_content=true" >> $GITHUB_OUTPUT
echo "✅ Found unreleased changelog content"
else
echo "⚠️ v3/UNRELEASED_CHANGELOG.md not found"
echo "has_unreleased_content=false" >> $GITHUB_OUTPUT
echo " No unreleased changelog content found"
fi
- name: Quick change detection and early exit
@ -126,441 +104,107 @@ jobs:
CURRENT_TAG=$(git describe --tags --exact-match HEAD)
echo "Current commit has release tag: $CURRENT_TAG"
# Get the tag creation date
TAG_DATE=$(git log -1 --format=%aI "$CURRENT_TAG")
if [ -z "$TAG_DATE" ]; then
echo "Failed to obtain tag date, proceeding with conservative fallback"
TAG_DATE="1970-01-01T00:00:00Z"
fi
echo "Tag date: $TAG_DATE"
# Compare this commit date with tag date
COMMIT_DATE=$(git log -1 --format=%aI HEAD)
echo "Commit date: $COMMIT_DATE"
# If the commit is newer than the tag, then it means there are changes
if [ "$COMMIT_DATE" \> "$TAG_DATE" ]; then
echo "Commit is newer than the tag. Proceeding with release."
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
echo "reason=Commit newer than tag" >> $GITHUB_OUTPUT
else
echo "Commit is the same as the tag. No new changes."
# For tagged commits, check if there are changes since the tag
COMMIT_COUNT=$(git rev-list ${CURRENT_TAG}..HEAD --count)
if [ "$COMMIT_COUNT" -eq 0 ]; then
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "should_continue=false" >> $GITHUB_OUTPUT
echo "reason=No changes since existing tag $CURRENT_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
fi
exit 0
fi
echo "Checking against the latest existing release tag..."
# Find the latest release tag matching v3-alpha
LATEST_TAG=$(git tag --list 'v3.0.0-alpha.*' --sort=-v:refname | head -n 1)
echo "Latest tag: $LATEST_TAG"
if [ -z "$LATEST_TAG" ]; then
echo "No previous release found, proceeding with release"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
echo "reason=No previous tag found" >> $GITHUB_OUTPUT
exit 0
fi
# Compare against the latest release tag
if git diff --quiet "$LATEST_TAG"..HEAD -- v3; then
echo "No changes detected since latest release $LATEST_TAG and no unreleased changelog content"
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "should_continue=false" >> $GITHUB_OUTPUT
echo "reason=No changes since latest release $LATEST_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT
else
echo "Changes detected since latest release $LATEST_TAG"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
echo "reason=Changes detected since latest release $LATEST_TAG" >> $GITHUB_OUTPUT
# No current tag, check against latest release
LATEST_TAG=$(git tag --list "v3.0.0-alpha.*" | sort -V | tail -1)
if [ -z "$LATEST_TAG" ]; then
echo "No previous release found, proceeding with release"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
else
COMMIT_COUNT=$(git rev-list ${LATEST_TAG}..HEAD --count)
if [ "$COMMIT_COUNT" -gt 0 ]; then
echo "Found $COMMIT_COUNT commits since $LATEST_TAG"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "should_continue=false" >> $GITHUB_OUTPUT
echo "reason=No changes since latest release $LATEST_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT
fi
fi
fi
- name: Early exit if no changes and not forced
if: >-
steps.quick_check.outputs.should_continue == 'false' &&
- name: Early exit - No changes detected
if: |
steps.quick_check.outputs.should_continue == 'false' &&
github.event.inputs.force_release != 'true'
run: |
echo "🛑 EARLY EXIT: ${{ steps.quick_check.outputs.reason }}"
echo ""
echo " No changes detected since last release and force_release is not enabled."
echo " Skipping release to avoid unnecessary version churn."
echo " Workflow will exit early to save resources."
echo ""
echo " To force a release anyway, run this workflow with 'force_release=true'"
# Add summary to GITHUB_STEP_SUMMARY
echo "### Nightly Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "- Reason: ${{ steps.quick_check.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
echo "- Has unreleased changelog content: ${{ steps.changelog_check.outputs.has_unreleased_content }}" >> $GITHUB_STEP_SUMMARY
echo "- Force release: ${{ github.event.inputs.force_release || 'false' }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo ""
echo "## 🛑 Early Exit Summary" >> $GITHUB_STEP_SUMMARY
echo "**Reason:** ${{ steps.quick_check.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
echo "**Action:** Workflow exited early to save resources" >> $GITHUB_STEP_SUMMARY
echo "**Force Release:** Set 'force_release=true' to override this behavior" >> $GITHUB_STEP_SUMMARY
exit 0
- name: Continue with release process
if: >-
steps.quick_check.outputs.should_continue == 'true' ||
if: |
steps.quick_check.outputs.should_continue == 'true' ||
github.event.inputs.force_release == 'true'
run: |
echo "✅ Proceeding with release process..."
if [ "${{ github.event.inputs.force_release }}" == "true" ]; then
echo "🚨 FORCE RELEASE: Overriding change detection"
echo "🔨 FORCE RELEASE: Overriding change detection"
fi
- name: Extract changelog content before release
if: >-
steps.changelog_check.outputs.has_unreleased_content == 'true' ||
- name: Run release script
id: release
if: |
steps.quick_check.outputs.should_continue == 'true' ||
github.event.inputs.force_release == 'true'
env:
WAILS_REPO_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
run: |
echo "📝 Extracting changelog content before release..."
# Use the new --create-release-notes flag
cd v3/tasks/release
if go run release.go --create-release-notes ../../release_notes.md; then
echo "✅ Successfully created release notes"
cd ../..
# Show release notes preview
echo "Release notes preview:"
head -10 ../../release_notes.md
else
echo " Could not create release notes, continuing without them"
cd ../..
fi
- name: Process release and bump version
id: process_release
run: |
echo "🔢 Processing release: updating changelog and bumping version.txt"
cd v3/tasks/release
# Run release processing (updates internal/version/version.txt and changelog)
if go run release.go; then
echo "✅ Release processing complete"
else
echo "❌ Release processing failed"
exit 1
fi
cd ../..
# Read the new version from version.txt
NEW_VERSION=$(cat v3/internal/version/version.txt | tr -d '\r\n')
if [ -z "$NEW_VERSION" ]; then
echo "❌ Could not read new version from v3/internal/version/version.txt"
exit 1
fi
# Ensure tag starts with 'v'
case "$NEW_VERSION" in
v*) NEW_TAG="$NEW_VERSION" ;;
*) NEW_TAG="v$NEW_VERSION" ;;
esac
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "tag=$NEW_TAG" >> $GITHUB_OUTPUT
# Nightly v3-alpha are prereleases
echo "is_prerelease=true" >> $GITHUB_OUTPUT
echo "is_latest=false" >> $GITHUB_OUTPUT
echo "version_changed=true" >> $GITHUB_OUTPUT
- name: Commit updated version and changelog
env:
GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN }}
run: |
echo "📝 Committing updated version and changelog to v3-alpha"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add v3/internal/version/version.txt v3/docs/src/content/docs/changelog.mdx v3/UNRELEASED_CHANGELOG.md || true
if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit -m "chore(v3): bump to ${{ steps.process_release.outputs.version }} and update changelog [skip ci]"
git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" HEAD:v3-alpha
fi
- name: Create and push Git tag
if: >-
steps.process_release.outputs.version_changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN }}
run: |
echo "🏷️ Creating and pushing git tag: ${{ steps.process_release.outputs.tag }}"
# Try pushing the tag with debugging and error capture
PUSH_SUCCESS=false
PUSH_OUTPUT=$(cat << 'EOF'
$(
git tag -f "${{ steps.process_release.outputs.tag }}" &&
if git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" "${{ steps.process_release.outputs.tag }}" 2>&1; then
echo "SUCCESS"
else
echo "FAILURE"
fi)
EOF
)
if echo "$PUSH_OUTPUT" | grep -q "SUCCESS"; then
echo "✅ Successfully pushed git tag to origin"
PUSH_SUCCESS=true
else
echo "❌ Failed to push tag. Output:"
echo "$PUSH_OUTPUT"
# Check if tag already exists on remote
echo "Checking if tag already exists on remote..."
if git ls-remote --tags origin | grep -q "refs/tags/${{ steps.process_release.outputs.tag }}$"; then
echo " Tag already exists on remote, treating as success"
PUSH_SUCCESS=true
else
echo "❌ Tag does not exist on remote"
fi
fi
if [ "$PUSH_SUCCESS" != "true" ]; then
echo "❌ Could not push tag even after retries"
exit 1
fi
- name: Create GitHub Release
if: >-
steps.process_release.outputs.version_changed == 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
echo "🚀 Creating GitHub release for tag: ${{ steps.process_release.outputs.tag }}"
# Prepare release notes content
RELEASE_NOTES_FILE="release_notes.md"
if [ -f "$RELEASE_NOTES_FILE" ]; then
echo "Using generated release notes from $RELEASE_NOTES_FILE"
else
echo "No release notes file found, generating basic notes"
echo "# Release ${{ steps.release.outputs.version }}" > $RELEASE_NOTES_FILE
echo "Generated by nightly release workflow" >> $RELEASE_NOTES_FILE
fi
echo "Creating release via GitHub API..."
API_URL="https://api.github.com/repos/${{ github.repository }}/releases"
AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}"
ACCEPT_HEADER="Accept: application/vnd.github+json"
USER_AGENT_HEADER="User-Agent: nightly-release-script"
# Create GitHub release using gh cli if available, fallback to API
if command -v gh >/dev/null 2>&1; then
echo "Using gh CLI to create release"
if gh release create "${{ steps.process_release.outputs.tag }}" \
--title "${{ steps.process_release.outputs.version }}" \
--notes-file "$RELEASE_NOTES_FILE" \
$([ "${{ steps.process_release.outputs.is_prerelease }}" == "true" ] && echo "--prerelease") \
$([ "${{ steps.process_release.outputs.is_latest }}" == "true" ] && echo "--latest"); then
echo "✅ GitHub release created successfully using gh CLI"
else
echo "❌ Failed to create GitHub release using gh CLI"
exit 1
fi
else
echo "gh CLI not available, using GitHub API"
RELEASE_DATA=$(jq -n \
--arg tag_name "${{ steps.process_release.outputs.tag }}" \
--arg name "${{ steps.process_release.outputs.version }}" \
--arg body "$(cat "$RELEASE_NOTES_FILE" | sed 's/"/\\"/g')" \
--argjson prerelease $([ "${{ steps.process_release.outputs.is_prerelease }}" == "true" ] && echo true || echo false) \
--argjson make_latest $([ "${{ steps.process_release.outputs.is_latest }}" == "true" ] && echo true || echo false) \
'{ tag_name: $tag_name, name: $name, body: $body, prerelease: $prerelease, make_latest: $make_latest }')
echo "Release data payload:"
echo "$RELEASE_DATA" | jq '.'
RESPONSE=$(curl -sS -X POST -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -H "$USER_AGENT_HEADER" -d "$RELEASE_DATA" "$API_URL")
echo "GitHub API response:"
echo "$RESPONSE" | jq '.'
if echo "$RESPONSE" | jq -e '.id' >/dev/null; then
echo "✅ GitHub release created successfully via API"
else
echo "❌ Failed to create GitHub release via API"
exit 1
fi
fi
- name: Read generated release notes
id: read_notes
if: >-
steps.process_release.outputs.version_changed == 'true'
run: |
if [ -f "release_notes.md" ]; then
echo "Reading release notes content..."
echo "release_notes<<EOF" >> $GITHUB_OUTPUT
cat release_notes.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "No release notes file found"
echo "release_notes=" >> $GITHUB_OUTPUT
fi
- name: Handle GitHub Release Creation Result
id: release_result
if: >-
steps.process_release.outputs.version_changed == 'true'
run: |
echo "🔍 Checking release result..."
# Initialize variables
HAS_GITHUB_ERRORS=false
GITHUB_ERRORS=""
# Check if we can access the release via API
if command -v gh >/dev/null 2>&1; then
echo "Checking release via gh CLI..."
if gh release view "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then
echo "✅ Release is accessible via gh CLI"
else
echo "⚠️ Release not accessible via gh CLI"
HAS_GITHUB_ERRORS=true
GITHUB_ERRORS="$GITHUB_ERRORS\n- Release not accessible via gh CLI"
fi
else
echo "gh CLI not available, skipping CLI checks"
fi
echo "has_github_errors=$HAS_GITHUB_ERRORS" >> $GITHUB_OUTPUT
if [ -n "$GITHUB_ERRORS" ]; then
echo "github_errors=$GITHUB_ERRORS" >> $GITHUB_OUTPUT
else
echo "github_errors=" >> $GITHUB_OUTPUT
fi
- name: Error Summary and Reporting
id: error_summary
if: always()
run: |
echo "🧮 Generating comprehensive error summary..."
# Initialize error tracking
TOTAL_ERRORS=0
ERROR_SUMMARY=""
OVERALL_SUCCESS=true
# Check for changelog errors
if [ "${{ steps.changelog_check.outputs.has_errors }}" == "true" ]; then
echo "❌ Changelog processing errors detected"
ERROR_SUMMARY="$ERROR_SUMMARY\n### 📄 Changelog Processing Errors\n${{ steps.changelog_check.outputs.changelog_errors }}\n"
TOTAL_ERRORS=$((TOTAL_ERRORS + 1))
OVERALL_SUCCESS=false
fi
# Check for release script errors
if [ "${{ steps.release.outputs.has_release_errors }}" == "true" ]; then
echo "❌ Release script errors detected"
ERROR_SUMMARY="$ERROR_SUMMARY\n### 🚀 Release Script Errors\n${{ steps.release.outputs.release_errors }}\n"
TOTAL_ERRORS=$((TOTAL_ERRORS + 1))
OVERALL_SUCCESS=false
fi
# Check for GitHub release errors
if [ "${{ steps.release_result.outputs.has_github_errors }}" == "true" ]; then
echo "❌ GitHub release errors detected"
ERROR_SUMMARY="$ERROR_SUMMARY\n### 🐙 GitHub Release Errors\n${{ steps.release_result.outputs.github_errors }}\n"
TOTAL_ERRORS=$((TOTAL_ERRORS + 1))
OVERALL_SUCCESS=false
fi
# Set outputs for final summary
echo "total_errors=$TOTAL_ERRORS" >> $GITHUB_OUTPUT
echo "overall_success=$OVERALL_SUCCESS" >> $GITHUB_OUTPUT
if [ -n "$ERROR_SUMMARY" ]; then
echo "error_summary<<EOF" >> $GITHUB_OUTPUT
echo -e "$ERROR_SUMMARY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
# Log summary
if [ "$OVERALL_SUCCESS" == "true" ]; then
echo "✅ Workflow completed successfully with no errors"
else
echo "⚠️ Workflow completed with $TOTAL_ERRORS error categories"
ARGS=()
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then
ARGS+=(--dry-run)
fi
go run release.go "${ARGS[@]}"
- name: Summary
if: always()
run: |
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then
echo "## 🧪 DRY RUN Release Test Summary" >> $GITHUB_STEP_SUMMARY
echo "## 🧪 DRY RUN Release Summary" >> $GITHUB_STEP_SUMMARY
else
echo "## 🚀 Nightly Release Summary" >> $GITHUB_STEP_SUMMARY
fi
echo "================================" >> $GITHUB_STEP_SUMMARY
echo "- **Version:** ${{ steps.process_release.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag:** ${{ steps.process_release.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "- **Version Changed:** ${{ steps.process_release.outputs.version_changed }}" >> $GITHUB_STEP_SUMMARY
echo "- **Has existing tag:** ${{ steps.check_tag.outputs.has_tag }}" >> $GITHUB_STEP_SUMMARY
echo "- **Has unreleased changelog content:** ${{ steps.changelog_check.outputs.has_unreleased_content }}" >> $GITHUB_STEP_SUMMARY
echo "- **Has changes:** true" >> $GITHUB_STEP_SUMMARY
echo "- **Is prerelease:** ${{ steps.process_release.outputs.is_prerelease }}" >> $GITHUB_STEP_SUMMARY
echo "- **Is latest:** ${{ steps.process_release.outputs.is_latest }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Overall status
if [ "${{ steps.error_summary.outputs.overall_success }}" == "true" ]; then
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then
echo "- **Mode:** 🧪 DRY RUN (no actual release created)" >> $GITHUB_STEP_SUMMARY
echo "- **Status:** ✅ Test completed successfully" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.release.outputs.release_version }}" ]; then
echo "- **Version:** ${{ steps.release.outputs.release_version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag:** ${{ steps.release.outputs.release_tag }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status:** ${{ steps.release.outcome == 'success' && '✅ Success' || '⚠️ Failed' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Mode:** ${{ steps.release.outputs.release_dry_run == 'true' && '🧪 Dry Run' || '🚀 Live release' }}" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.release.outputs.release_url }}" ]; then
echo "- **Release URL:** ${{ steps.release.outputs.release_url }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Changelog" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then
echo "✅ Unreleased changelog processed and reset." >> $GITHUB_STEP_SUMMARY
else
echo "- **Mode:** 🚀 Live release" >> $GITHUB_STEP_SUMMARY
echo "- **Status:** ✅ Release created successfully" >> $GITHUB_STEP_SUMMARY
echo " No unreleased changelog content detected." >> $GITHUB_STEP_SUMMARY
fi
else
echo "- **Mode:** ${{ github.event.inputs.dry_run == 'true' && '🧪 DRY RUN' || '🚀 Live release' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status:** ⚠️ Completed with ${{ steps.error_summary.outputs.total_errors }} error(s)" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Release Processing" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.process_release.outputs.version_changed }}" == "true" ]; then
echo "✅ **Version was incremented** and release created" >> $GITHUB_STEP_SUMMARY
else
echo " **Version was not changed** - no release created" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Changelog Processing" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then
echo "✅ **UNRELEASED_CHANGELOG.md** had content and was processed" >> $GITHUB_STEP_SUMMARY
echo "- Content moved to main changelog" >> $GITHUB_STEP_SUMMARY
echo "- UNRELEASED_CHANGELOG.md reset with template" >> $GITHUB_STEP_SUMMARY
else
echo " **UNRELEASED_CHANGELOG.md** had no content to process" >> $GITHUB_STEP_SUMMARY
fi
# Error reporting section
if [ "${{ steps.error_summary.outputs.total_errors }}" -gt 0 ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "## ⚠️ Error Report" >> $GITHUB_STEP_SUMMARY
echo "**Total Error Categories:** ${{ steps.error_summary.outputs.total_errors }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${{ steps.error_summary.outputs.error_summary }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🔧 Troubleshooting Tips" >> $GITHUB_STEP_SUMMARY
echo "- Check the individual step logs above for detailed error messages" >> $GITHUB_STEP_SUMMARY
echo "- Verify GitHub token permissions (contents: write, pull-requests: read)" >> $GITHUB_STEP_SUMMARY
echo "- Ensure UNRELEASED_CHANGELOG.md follows the expected format" >> $GITHUB_STEP_SUMMARY
echo "- Check for network connectivity issues if git/GitHub operations failed" >> $GITHUB_STEP_SUMMARY
echo "- Re-run the workflow with 'force_release=true' if needed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Release Notes Preview" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.read_notes.outputs.release_notes }}" ]; then
echo "${{ steps.read_notes.outputs.release_notes }}" >> $GITHUB_STEP_SUMMARY
else
echo "No specific release notes generated" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "*Generated by automated nightly release workflow with enhanced error handling and changelog integration*" >> $GITHUB_STEP_SUMMARY
# Set final workflow status
if [ "${{ steps.error_summary.outputs.overall_success }}" != "true" ]; then
echo "⚠️ Workflow completed with errors. Check the summary above for details."
exit 1
echo "- Release script did not run (skipped or failed before execution)." >> $GITHUB_STEP_SUMMARY
fi

View file

@ -1,17 +1,26 @@
name: PR Checks (master)
# Updated to ensure "Run Go Tests" runs for pull requests as expected.
# Key fix: the test_go job previously required github.event.review.state == 'approved'
# which only exists on pull_request_review events. That prevented the job from
# running for regular pull_request events (opened / synchronize / reopened).
# New logic: run tests for pull_request events, and also allow running when a
# pull_request_review is submitted with state == 'approved'.
on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- master
pull_request_review:
types: [submitted]
branches:
- master
workflow_dispatch: {}
name: PR Checks (master)
jobs:
check_docs:
name: Check Docs
if: ${{github.repository == 'wailsapp/wails' && github.base_ref == 'master'}}
if: ${{ github.repository == 'wailsapp/wails' && github.base_ref == 'master' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -23,7 +32,6 @@ jobs:
files: |
website/**/*.mdx
website/**/*.md
- name: Run step only when files change.
if: steps.verify-changed-files.outputs.files_changed != 'true'
run: |
@ -32,11 +40,18 @@ jobs:
test_go:
name: Run Go Tests
runs-on: ${{ matrix.os }}
# Run when:
# - the event is a pull_request (opened/synchronize/reopened) OR
# - the event is a pull_request_review AND the review state is 'approved'
# plus other existing filters (not the update-sponsors branch, repo and base_ref)
if: >
github.repository == 'wailsapp/wails' &&
github.base_ref == 'master' &&
github.event.pull_request.head.ref != 'update-sponsors' &&
github.event.review.state == 'approved' &&
github.repository == 'wailsapp/wails' &&
github.base_ref == 'master'
(
github.event_name == 'pull_request' ||
(github.event_name == 'pull_request_review' && github.event.review.state == 'approved')
)
strategy:
matrix:
os: [ubuntu-22.04, windows-latest, macos-latest, ubuntu-24.04]

View file

@ -25,7 +25,7 @@ Erschaffe Desktop Anwendungen mit Go & Web Technologien.
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -27,7 +27,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -27,7 +27,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -25,7 +25,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -27,7 +27,7 @@
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/BrRSWTaxVK?style=flat"/>
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">

View file

@ -27,7 +27,7 @@ require (
github.com/tkrajina/go-reflector v0.5.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/go-webview2 v1.0.22 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect

View file

@ -72,6 +72,7 @@ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhyYyDV/w=
github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=

View file

@ -34,8 +34,11 @@ void addAccelerator(GtkWidget* menuItem, GtkAccelGroup* group, guint key, GdkMod
}
*/
import "C"
import "github.com/wailsapp/wails/v2/pkg/menu"
import "unsafe"
import (
"unsafe"
"github.com/wailsapp/wails/v2/pkg/menu"
)
var menuIdCounter int
var menuItemToId map[*menu.MenuItem]int
@ -81,8 +84,10 @@ func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
func processMenu(window *Window, menu *menu.Menu) {
for _, menuItem := range menu.Items {
submenu := processSubmenu(menuItem, window.accels)
C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(window.menubar)), submenu)
if menuItem.SubMenu != nil {
submenu := processSubmenu(menuItem, window.accels)
C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(window.menubar)), submenu)
}
}
}

View file

@ -28,11 +28,13 @@ import (
"github.com/wailsapp/wails/v2/internal/frontend/originvalidator"
wailsruntime "github.com/wailsapp/wails/v2/internal/frontend/runtime"
"github.com/wailsapp/wails/v2/internal/logger"
w32consts "github.com/wailsapp/wails/v2/internal/platform/win32"
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
"github.com/wailsapp/wails/v2/pkg/assetserver"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/windows"
w "golang.org/x/sys/windows"
)
const startURL = "http://wails.localhost/"
@ -75,6 +77,13 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
// Get Windows build number
versionInfo, _ := operatingsystem.GetWindowsVersionInfo()
// Apply DLL search path settings if specified
if appoptions.Windows != nil && appoptions.Windows.DLLSearchPaths != 0 {
w.SetDefaultDllDirectories(appoptions.Windows.DLLSearchPaths)
}
// Now initialize packages that load DLLs
w32.Init()
w32consts.Init()
result := &Frontend{
frontendOptions: appoptions,
logger: myLogger,

View file

@ -69,7 +69,7 @@ var (
setWindowTheme uintptr
)
func init() {
func Init() {
// Library
libuxtheme = MustLoadLibrary("uxtheme.dll")

View file

@ -80,7 +80,7 @@ ShouldSystemUseDarkMode = bool () // ordinal 138
SetPreferredAppMode = PreferredAppMode (PreferredAppMode appMode) // ordinal 135, since 18334
IsDarkModeAllowedForApp = bool () // ordinal 139
*/
func init() {
func Init() {
if IsWindowsVersionAtLeast(10, 0, 18334) {
// AllowDarkModeForWindow is only available on Windows 10+

View file

@ -35,6 +35,27 @@ const (
Tabbed BackdropType = 4
)
const (
// Default is 0, which means no changes to the default Windows DLL search behavior
DLLSearchDefault uint32 = 0
// LoadLibrary flags for determining from where to search for a DLL
DLLSearchDontResolveDllReferences uint32 = 0x1 // windows.DONT_RESOLVE_DLL_REFERENCES
DLLSearchAsDataFile uint32 = 0x2 // windows.LOAD_LIBRARY_AS_DATAFILE
DLLSearchWithAlteredPath uint32 = 0x8 // windows.LOAD_WITH_ALTERED_SEARCH_PATH
DLLSearchIgnoreCodeAuthzLevel uint32 = 0x10 // windows.LOAD_IGNORE_CODE_AUTHZ_LEVEL
DLLSearchAsImageResource uint32 = 0x20 // windows.LOAD_LIBRARY_AS_IMAGE_RESOURCE
DLLSearchAsDataFileExclusive uint32 = 0x40 // windows.LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
DLLSearchRequireSignedTarget uint32 = 0x80 // windows.LOAD_LIBRARY_REQUIRE_SIGNED_TARGET
DLLSearchDllLoadDir uint32 = 0x100 // windows.LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
DLLSearchApplicationDir uint32 = 0x200 // windows.LOAD_LIBRARY_SEARCH_APPLICATION_DIR
DLLSearchUserDirs uint32 = 0x400 // windows.LOAD_LIBRARY_SEARCH_USER_DIRS
DLLSearchSystem32 uint32 = 0x800 // windows.LOAD_LIBRARY_SEARCH_SYSTEM32
DLLSearchDefaultDirs uint32 = 0x1000 // windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
DLLSearchSafeCurrentDirs uint32 = 0x2000 // windows.LOAD_LIBRARY_SAFE_CURRENT_DIRS
DLLSearchSystem32NoForwarder uint32 = 0x4000 // windows.LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER
DLLSearchOsIntegrityContinuity uint32 = 0x8000 // windows.LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY
)
func RGB(r, g, b uint8) int32 {
col := int32(b)
col = col<<8 | int32(g)
@ -122,6 +143,11 @@ type Options struct {
// Class name for the window. If empty, 'wailsWindow' will be used.
WindowClassName string
// DLLSearchPaths controls which directories are searched when loading DLLs
// Set to 0 for default behavior, or combine multiple flags with bitwise OR
// Example: DLLSearchApplicationDir | DLLSearchSystem32
DLLSearchPaths uint32
}
func DefaultMessages() *Messages {

View file

@ -0,0 +1,27 @@
---
title: Clustta
description: File manager and project management tool for creative professionals.
slug: /community/showcase/clustta
image: /img/showcase/clustta.png
---
<div className="text--center">
<img src="/img/showcase/clustta.png" alt="Clustta screenshot" loading="lazy" />
</div>
[Clustta](https://clustta.com) is a file manager and project management tool designed for creative professionals. Built with Wails, it simplifies file management, collaboration, and version control for creative workflows.
## Features
- **File Management**: Track all projects and files with easy access even months after completion.
- **Version Control**: Save unlimited revisions with descriptive notes, without duplicating files.
- **Collaboration**: Share files or entire projects securely through simple user tags with fine-grained permissions.
- **Recovery**: Restore corrupted files from saved checkpoints if your software crashes.
- **Templates**: Quick start with preset project and task templates.
- **Kanban Boards**: Visual task tracking to keep tasks organized.
- **Dependencies**: Track and version project resources and dependencies.
- **Checkpoints**: Create memorable milestones and explore alternate creative directions non-destructively.
- **Search & Filters**: Powerful instant search with metadata filtering (task types, tags, status, file extensions).
- **Workspaces**: Save search and filter combinations for easy access to specific task sets.
- **Integrations**: Connect with creative software packages and production management tools like Blender and Kitsu.
- **Self-Hosting**: Host private instances for teams or studios on your own servers.

View file

@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added url validation for BrowserOpenURL by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4484)
- Fixed C compilation error in onWayland on Linux due to declaration after label [#4446](https://github.com/wailsapp/wails/pull/4446) by [@jaesung9507](https://github.com/jaesung9507)
- Use computed style when adding 'wails-drop-target-active' [PR](https://github.com/wailsapp/wails/pull/4420) by [@riannucci](https://github.com/riannucci)
- Fixed panic when adding menuroles on Linux [#4558](https://github.com/wailsapp/wails/pull/4558) by [@jaesung9507](https://github.com/jaesung9507)
- Fixed Discord badge in README by @sharkmu in [PR](https://github.com/wailsapp/wails/pull/4626)
## v2.10.2 - 2025-07-06
@ -42,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated documentation to display the correct copyright year in [#4243](https://github.com/wailsapp/wails/pull/4243) by [@nnashwin](https://github.com/nnashwin)
### Added
- Added DLLSearchPaths option to control DLL search paths on Windows in [#4207](https://github.com/wailsapp/wails/pull/4207) by @ansxuman
- Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25)
- Added `-skipembedcreate` flag to build and dev command to improve compile and recompile speed [#4143](https://github.com/wailsapp/wails/pull/4143) by @josStorer
- Added `DisablePanicRecovery` option to allow handle panics manually [#4136](https://github.com/wailsapp/wails/pull/4136) by [@APshenkin](https://github.com/APshenkin)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before After
Before After

View file

@ -28,6 +28,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for
- [wails-template-quasar-ts](https://github.com/sgosiaco/wails-template-quasar-ts) - A template using TypeScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, Prettier, Composition API with &lt;script setup&gt;)
- [wails-template-naive](https://github.com/tk103331/wails-template-naive) - Wails template based on Naive UI (A Vue 3 Component Library)
- [wails-template-tdesign-js](https://github.com/tongque0/wails-template-tdesign-js) - Wails template based on TDesign UI (a Vue 3 UI library by Tencent), using Vite, Pinia, Vue Router, ESLint, and Prettier.
- [wails-template-arcodesign-ts](https://github.com/fengfengzhidao/wails-template-arcodesign-ts) - Wails template based on ArcoDesign UI (a Vue 3 UI library by ArcoDesign), using Vite, Pinia, Vue Router.
## Angular