Add flex.sh script to streamline execution and update usage instructi… #27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build and Release Flex | |
on: | |
push: | |
branches: [ main, master ] | |
tags: | |
- 'v*' # Run when tag is pushed (for versioned releases) | |
jobs: | |
build: | |
name: Build Flex for ${{ matrix.os }} | |
runs-on: ${{ matrix.os }} | |
strategy: | |
fail-fast: false | |
matrix: | |
os: [ubuntu-latest, windows-latest, macos-latest] | |
include: | |
- os: ubuntu-latest | |
output_name: flex | |
asset_name: flex-linux | |
- os: windows-latest | |
output_name: flex.exe | |
asset_name: flex-windows | |
- os: macos-latest | |
output_name: flex | |
asset_name: flex-macos | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.10' | |
- name: Get version information | |
id: version | |
run: | | |
if [[ $GITHUB_REF == refs/tags/v* ]]; then | |
VERSION=${GITHUB_REF#refs/tags/v} | |
else | |
VERSION="dev-$(date +'%Y%m%d')-${GITHUB_SHA::7}" | |
fi | |
echo "VERSION=$VERSION" >> $GITHUB_ENV | |
echo "version=$VERSION" >> $GITHUB_OUTPUT | |
shell: bash | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip | |
pip install pyinstaller | |
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi | |
shell: bash | |
- name: Run tests | |
run: | | |
cd src | |
python -m unittest discover -s flex_tester | |
shell: bash | |
- name: Build executable with PyInstaller | |
run: | | |
cd src | |
pyinstaller --onefile main.py -n flex | |
shell: bash | |
# Windows NSIS Installer | |
- name: Create Windows Installer | |
if: runner.os == 'Windows' | |
run: | | |
choco install nsis -y | |
mkdir -p installer | |
copy src\dist\flex.exe installer\ | |
xcopy src\flex_tester installer\examples\ /E /I | |
copy README.md installer\ | |
REM Create EnvVarUpdate.nsh file locally instead of downloading it | |
( | |
echo !ifndef _AddToPath_nsh | |
echo !define _AddToPath_nsh | |
echo | |
echo !verbose push | |
echo !verbose 3 | |
echo !include "LogicLib.nsh" | |
echo !include "WinMessages.NSH" | |
echo !verbose pop | |
echo | |
echo !define HKLM_ENVIRON 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' | |
echo | |
echo !macro AddToPath dir | |
echo Push "${dir}" | |
echo Call AddToPath | |
echo !macroend | |
echo | |
echo Function AddToPath | |
echo Exch $0 | |
echo Push $1 | |
echo Push $2 | |
echo Push $3 | |
echo Push $4 | |
echo | |
echo ; Prevent adding empty string to PATH | |
echo ${If} $0 == "" | |
echo Goto exit | |
echo ${EndIf} | |
echo | |
echo ; Don't add if it's already in PATH | |
echo ReadRegStr $1 ${HKLM_ENVIRON} "PATH" | |
echo Push "$1;" | |
echo Push "$0;" | |
echo Call StrStr | |
echo Pop $2 | |
echo StrCmp $2 "" 0 exit | |
echo | |
echo ; Append to PATH | |
echo DetailPrint "Adding to PATH: $0" | |
echo ReadRegStr $1 ${HKLM_ENVIRON} "PATH" | |
echo StrCpy $2 "$1;$0" | |
echo WriteRegExpandStr ${HKLM_ENVIRON} "PATH" $2 | |
echo SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 | |
echo | |
echo exit: | |
echo Pop $4 | |
echo Pop $3 | |
echo Pop $2 | |
echo Pop $1 | |
echo Pop $0 | |
echo FunctionEnd | |
echo | |
echo !macro RemoveFromPath dir | |
echo Push "${dir}" | |
echo Call RemoveFromPath | |
echo !macroend | |
echo | |
echo Function RemoveFromPath | |
echo Exch $0 | |
echo Push $1 | |
echo Push $2 | |
echo Push $3 | |
echo Push $4 | |
echo Push $5 | |
echo Push $6 | |
echo | |
echo ReadRegStr $1 ${HKLM_ENVIRON} "PATH" | |
echo StrCpy $5 $1 1 -1 ; Get last character | |
echo ${If} $5 != ";" ; If last character is not a semicolon | |
echo StrCpy $1 "$1;" ; Append a semicolon | |
echo ${EndIf} | |
echo | |
echo ${If} $0 == "" | |
echo Goto exit | |
echo ${EndIf} | |
echo | |
echo ; Make sure it's a full path ending with a backslash | |
echo Push $0 | |
echo Call GetFullPath | |
echo Pop $0 | |
echo | |
echo ; Check if it's already in the PATH | |
echo Push "$1;" | |
echo Push "$0;" | |
echo Call StrStr | |
echo Pop $2 | |
echo StrCmp $2 "" exit | |
echo | |
echo ; Remove from PATH | |
echo DetailPrint "Removing from PATH: $0" | |
echo StrCpy $5 $1 ; Save PATH | |
echo StrLen $2 $0 | |
echo StrLen $3 $2 | |
echo StrCpy $4 0 | |
echo | |
echo loop: | |
echo StrCpy $6 $5 $3 $4 ; $6 = string of length $3 at position $4 | |
echo StrCmp $6 $2 found continue | |
echo StrCmp $6 "" exit | |
echo StrLen $6 $5 | |
echo IntOp $4 $4 + 1 | |
echo StrCmp $4 $6 exit loop | |
echo | |
echo found: | |
echo StrCpy $6 $5 $4 | |
echo IntOp $4 $4 + $3 | |
echo StrCpy $7 $5 "" $4 | |
echo StrCpy $5 $6$7 | |
echo Goto loop | |
echo | |
echo exit: | |
echo WriteRegExpandStr ${HKLM_ENVIRON} "PATH" $5 | |
echo SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 | |
echo | |
echo Pop $6 | |
echo Pop $5 | |
echo Pop $4 | |
echo Pop $3 | |
echo Pop $2 | |
echo Pop $1 | |
echo Pop $0 | |
echo FunctionEnd | |
echo | |
echo ; Helper functions | |
echo Function GetFullPath | |
echo Exch $0 | |
echo Push $1 | |
echo Push $2 | |
echo | |
echo StrCpy $1 $0 1 -1 | |
echo StrCmp $1 "\" Append | |
echo StrCmp $1 "/" Append | |
echo Goto NoAppend | |
echo | |
echo Append: | |
echo Goto Exit | |
echo | |
echo NoAppend: | |
echo StrCpy $0 "$0\" | |
echo | |
echo Exit: | |
echo Pop $2 | |
echo Pop $1 | |
echo Exch $0 | |
echo FunctionEnd | |
echo | |
echo Function StrStr | |
echo Exch $R1 ; string to search in | |
echo Exch | |
echo Exch $R2 ; string to find | |
echo Push $R3 | |
echo Push $R4 | |
echo Push $R5 | |
echo StrLen $R3 $R2 | |
echo StrCpy $R4 0 | |
echo | |
echo loop: | |
echo StrCpy $R5 $R1 $R3 $R4 | |
echo StrCmp $R5 $R2 done | |
echo StrCmp $R5 "" done | |
echo IntOp $R4 $R4 + 1 | |
echo Goto loop | |
echo | |
echo done: | |
echo StrCpy $R1 $R4 | |
echo Pop $R5 | |
echo Pop $R4 | |
echo Pop $R3 | |
echo Pop $R2 | |
echo Exch $R1 | |
echo FunctionEnd | |
echo | |
echo !endif | |
) > AddToPath.nsh | |
REM Create a simple NSIS script file with PATH environment variable addition | |
( | |
echo !include "MUI2.nsh" | |
echo !include "AddToPath.nsh" | |
echo Name "Flex Programming Language" | |
echo OutFile "flex-${{ env.VERSION }}-setup.exe" | |
echo InstallDir "$PROGRAMFILES\Flex" | |
echo RequestExecutionLevel admin | |
echo !insertmacro MUI_PAGE_WELCOME | |
echo !insertmacro MUI_PAGE_DIRECTORY | |
echo !insertmacro MUI_PAGE_INSTFILES | |
echo !insertmacro MUI_PAGE_FINISH | |
echo !insertmacro MUI_LANGUAGE "English" | |
echo Section | |
echo SetOutPath "$INSTDIR" | |
echo File /r "installer\*.*" | |
echo WriteUninstaller "$INSTDIR\uninstall.exe" | |
echo CreateDirectory "$SMPROGRAMS\Flex" | |
echo CreateShortcut "$SMPROGRAMS\Flex\Flex.lnk" "$INSTDIR\flex.exe" | |
echo CreateShortcut "$SMPROGRAMS\Flex\Uninstall.lnk" "$INSTDIR\uninstall.exe" | |
echo !insertmacro AddToPath "$INSTDIR" | |
echo SectionEnd | |
echo Section "Uninstall" | |
echo Delete "$INSTDIR\uninstall.exe" | |
echo !insertmacro RemoveFromPath "$INSTDIR" | |
echo RMDir /r "$INSTDIR" | |
echo RMDir /r "$SMPROGRAMS\Flex" | |
echo SectionEnd | |
) > installer.nsi | |
REM Compile the installer | |
makensis installer.nsi | |
REM Check if installer was created | |
if not exist flex-${{ env.VERSION }}-setup.exe ( | |
echo "WARNING: Installer creation failed. Creating a simple ZIP package instead." | |
mkdir -p dist\flex | |
copy src\dist\flex.exe dist\flex\ | |
xcopy src\flex_tester dist\flex\examples\ /E /I | |
copy README.md dist\flex\ | |
powershell Compress-Archive -Path dist\flex -DestinationPath flex-${{ env.VERSION }}-win.zip | |
REM Create latest.yml for auto-updater compatibility (fallback) | |
echo version: ${{ env.VERSION }} > latest-win.yml | |
echo files: >> latest-win.yml | |
echo - url: flex-${{ env.VERSION }}-win.zip >> latest-win.yml | |
FOR /F "tokens=*" %%a IN ('powershell -Command "(Get-FileHash -Algorithm SHA512 flex-${{ env.VERSION }}-win.zip).Hash.ToLower()"') DO SET FILEHASH=%%a | |
echo sha512: %FILEHASH% >> latest-win.yml | |
echo path: flex-${{ env.VERSION }}-win.zip >> latest-win.yml | |
) else ( | |
REM Create latest.yml for auto-updater compatibility | |
echo version: ${{ env.VERSION }} > latest-win.yml | |
echo files: >> latest-win.yml | |
echo - url: flex-${{ env.VERSION }}-setup.exe >> latest-win.yml | |
FOR /F "tokens=*" %%a IN ('powershell -Command "(Get-FileHash -Algorithm SHA512 flex-${{ env.VERSION }}-setup.exe).Hash.ToLower()"') DO SET FILEHASH=%%a | |
echo sha512: %FILEHASH% >> latest-win.yml | |
echo path: flex-${{ env.VERSION }}-setup.exe >> latest-win.yml | |
) | |
shell: cmd | |
# macOS DMG Package with code signing | |
- name: Create macOS DMG | |
if: runner.os == 'macOS' | |
run: | | |
# Install create-dmg | |
brew install create-dmg | |
# Create app bundle structure | |
mkdir -p Flex.app/Contents/{MacOS,Resources} | |
cp src/dist/flex Flex.app/Contents/MacOS/ | |
cp -r src/flex_tester Flex.app/Contents/Resources/examples | |
cp README.md Flex.app/Contents/Resources/ | |
# Create Info.plist with proper settings | |
cat > Flex.app/Contents/Info.plist << EOF | |
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>CFBundleExecutable</key> | |
<string>flex</string> | |
<key>CFBundleIconFile</key> | |
<string>flex.icns</string> | |
<key>CFBundleIdentifier</key> | |
<string>org.flex-lang.flex</string> | |
<key>CFBundleName</key> | |
<string>Flex</string> | |
<key>CFBundlePackageType</key> | |
<string>APPL</string> | |
<key>CFBundleVersion</key> | |
<string>${VERSION}</string> | |
<key>NSHighResolutionCapable</key> | |
<true/> | |
<key>LSMinimumSystemVersion</key> | |
<string>10.13</string> | |
<key>NSAppleEventsUsageDescription</key> | |
<string>Flex programming language requires access to run scripts and commands.</string> | |
</dict> | |
</plist> | |
EOF | |
# Create executable shell script wrapper for better macOS compatibility | |
mv Flex.app/Contents/MacOS/flex Flex.app/Contents/MacOS/flex-bin | |
cat > Flex.app/Contents/MacOS/flex << EOF | |
#!/bin/bash | |
DIR="\$( cd "\$( dirname "\${BASH_SOURCE[0]}" )" && pwd )" | |
"\$DIR/flex-bin" "\$@" | |
EOF | |
chmod +x Flex.app/Contents/MacOS/flex | |
chmod +x Flex.app/Contents/MacOS/flex-bin | |
# Create DMG with basic settings | |
create-dmg \ | |
--volname "Flex Installer" \ | |
--window-pos 200 120 \ | |
--window-size 800 400 \ | |
--icon-size 100 \ | |
--icon "Flex.app" 200 190 \ | |
--hide-extension "Flex.app" \ | |
--app-drop-link 600 185 \ | |
"flex-${VERSION}-mac.dmg" \ | |
"Flex.app" | |
# Add instructions for Gatekeeper bypass | |
echo "# Important instructions for macOS users" > macOS-README.txt | |
echo "" >> macOS-README.txt | |
echo "If you see a 'Flex.app is damaged' error, please run this command in Terminal:" >> macOS-README.txt | |
echo "" >> macOS-README.txt | |
echo " xattr -cr /Applications/Flex.app" >> macOS-README.txt | |
echo "" >> macOS-README.txt | |
echo "Then try opening the app again." >> macOS-README.txt | |
# Create latest-mac.yml for auto-updater compatibility | |
echo "version: ${VERSION}" > latest-mac.yml | |
echo "files:" >> latest-mac.yml | |
echo " - url: flex-${VERSION}-mac.dmg" >> latest-mac.yml | |
echo " sha512: $(shasum -a 512 flex-${VERSION}-mac.dmg | awk '{print $1}')" >> latest-mac.yml | |
echo " size: $(stat -f%z flex-${VERSION}-mac.dmg)" >> latest-mac.yml | |
echo "path: flex-${VERSION}-mac.dmg" >> latest-mac.yml | |
echo "sha512: $(shasum -a 512 flex-${VERSION}-mac.dmg | awk '{print $1}')" >> latest-mac.yml | |
echo "size: $(stat -f%z flex-${VERSION}-mac.dmg)" >> latest-mac.yml | |
shell: bash | |
continue-on-error: true # DMG creation might fail if dependencies are missing | |
# Linux AppImage | |
- name: Create Linux AppImage and Deb | |
if: runner.os == 'Linux' | |
run: | | |
# Install required tools | |
sudo apt-get update | |
sudo apt-get install -y wget fuse libfuse2 dpkg-dev | |
# Download AppImage tools | |
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool | |
chmod +x appimagetool | |
# Create AppDir structure | |
mkdir -p AppDir/usr/{bin,share/applications,share/icons/hicolor/256x256/apps,share/flex/examples} | |
cp src/dist/flex AppDir/usr/bin/ | |
cp -r src/flex_tester/* AppDir/usr/share/flex/examples/ | |
cp README.md AppDir/usr/share/flex/ | |
# Create desktop file | |
cat > AppDir/usr/share/applications/flex.desktop << EOF | |
[Desktop Entry] | |
Name=Flex | |
Comment=Flex Programming Language | |
Exec=flex | |
Icon=flex | |
Type=Application | |
Categories=Development; | |
Terminal=true | |
EOF | |
# Copy desktop file to root (required by AppImage) | |
cp AppDir/usr/share/applications/flex.desktop AppDir/ | |
# Create AppRun script | |
cat > AppDir/AppRun << EOF | |
#!/bin/sh | |
SELF=\$(readlink -f "\$0") | |
HERE=\${SELF%/*} | |
export PATH="\${HERE}/usr/bin:\${PATH}" | |
exec "\${HERE}/usr/bin/flex" "\$@" | |
EOF | |
chmod +x AppDir/AppRun | |
# Create AppImage | |
ARCH=x86_64 ./appimagetool AppDir flex-${VERSION}-linux.AppImage | |
# Create Debian package | |
mkdir -p flex-deb/DEBIAN | |
mkdir -p flex-deb/usr/bin | |
mkdir -p flex-deb/usr/share/flex/examples | |
mkdir -p flex-deb/usr/share/applications | |
cp src/dist/flex flex-deb/usr/bin/ | |
cp -r src/flex_tester/* flex-deb/usr/share/flex/examples/ | |
cp AppDir/usr/share/applications/flex.desktop flex-deb/usr/share/applications/ | |
cat > flex-deb/DEBIAN/control << EOF | |
Package: flex | |
Version: ${VERSION} | |
Section: development | |
Priority: optional | |
Architecture: amd64 | |
Maintainer: Flex Language Team <info@flex-lang.org> | |
Description: Flex Programming Language | |
A modern and flexible programming language. | |
EOF | |
dpkg-deb --build flex-deb flex-${VERSION}-linux.deb | |
# Create latest-linux.yml for auto-updater compatibility | |
echo "version: ${VERSION}" > latest-linux.yml | |
echo "files:" >> latest-linux.yml | |
echo " - url: flex-${VERSION}-linux.AppImage" >> latest-linux.yml | |
echo " sha512: $(sha512sum flex-${VERSION}-linux.AppImage | awk '{print $1}')" >> latest-linux.yml | |
echo " size: $(stat -c%s flex-${VERSION}-linux.AppImage)" >> latest-linux.yml | |
echo "path: flex-${VERSION}-linux.AppImage" >> latest-linux.yml | |
echo "sha512: $(sha512sum flex-${VERSION}-linux.AppImage | awk '{print $1}')" >> latest-linux.yml | |
echo "size: $(stat -c%s flex-${VERSION}-linux.AppImage)" >> latest-linux.yml | |
shell: bash | |
continue-on-error: true # Some parts might fail if dependencies are missing | |
# Upload only important artifacts (reduce junk files) | |
- name: Upload build artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: flex-${{ matrix.asset_name }} | |
path: | | |
flex-*-setup.exe | |
flex-*-mac.dmg | |
macOS-README.txt | |
flex-*-linux.AppImage | |
flex-*-linux.deb | |
latest-*.yml | |
if-no-files-found: warn | |
release: | |
name: Create Release | |
needs: build | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Set up version information | |
id: version | |
run: | | |
if [[ $GITHUB_REF == refs/tags/v* ]]; then | |
VERSION=${GITHUB_REF#refs/tags/v} | |
IS_PRERELEASE=false | |
RELEASE_NAME="Flex ${GITHUB_REF#refs/tags/v}" | |
TAG_NAME=${GITHUB_REF#refs/tags/} | |
else | |
VERSION="dev-$(date +'%Y%m%d')-${GITHUB_SHA::7}" | |
IS_PRERELEASE=true | |
RELEASE_NAME="Flex Latest Build" | |
TAG_NAME="latest" | |
fi | |
echo "VERSION=$VERSION" >> $GITHUB_ENV | |
echo "IS_PRERELEASE=$IS_PRERELEASE" >> $GITHUB_ENV | |
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV | |
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV | |
echo "version=$VERSION" >> $GITHUB_OUTPUT | |
shell: bash | |
- name: Download all artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
path: artifacts | |
- name: Prepare release assets | |
run: | | |
mkdir -p release_assets temp_source | |
# Create a clean source copy using git ls-files | |
echo "Creating source code archives..." | |
git ls-files | while read file; do | |
# Create the target directory structure | |
mkdir -p "temp_source/$(dirname "$file")" | |
# Copy each file | |
cp "$file" "temp_source/$file" | |
done | |
# Create source archives (only one format to reduce junk) | |
(cd temp_source && zip -r ../release_assets/flex-${VERSION}-source.zip .) | |
# Clean up temporary directory | |
rm -rf temp_source | |
# Find and copy only the main installer assets to release directory | |
echo "=== Copying artifacts to release directory ===" | |
find artifacts -type f \( \ | |
-name "flex-*-setup.exe" -o \ | |
-name "flex-*-mac.dmg" -o \ | |
-name "macOS-README.txt" -o \ | |
-name "flex-*-linux.AppImage" -o \ | |
-name "flex-*-linux.deb" -o \ | |
-name "latest-mac.yml" -o \ | |
-name "latest-win.yml" -o \ | |
-name "latest-linux.yml" \ | |
\) -exec cp {} release_assets/ \; | |
# Create a single latest.yml file | |
if [ -f "release_assets/latest-win.yml" ]; then | |
cp release_assets/latest-win.yml release_assets/latest.yml | |
elif [ -f "release_assets/latest-mac.yml" ]; then | |
cp release_assets/latest-mac.yml release_assets/latest.yml | |
elif [ -f "release_assets/latest-linux.yml" ]; then | |
cp release_assets/latest-linux.yml release_assets/latest.yml | |
fi | |
echo "=== Files in release_assets ===" | |
ls -la release_assets/ | |
shell: bash | |
- name: Create Release | |
id: create_release | |
uses: softprops/action-gh-release@v1 | |
with: | |
name: ${{ env.RELEASE_NAME }} | |
tag_name: ${{ env.TAG_NAME }} | |
draft: false | |
prerelease: ${{ env.IS_PRERELEASE }} | |
files: release_assets/* | |
body: | | |
# ${{ env.RELEASE_NAME }} | |
Cross-platform releases of the Flex programming language. | |
## Installation | |
### Windows | |
- Download and run `flex-${{ env.VERSION }}-setup.exe` to install with automatic PATH setup | |
### macOS | |
- Download `flex-${{ env.VERSION }}-mac.dmg`, open and drag to Applications folder | |
- **Important**: If you see a "damaged app" error, open Terminal and run: | |
``` | |
xattr -cr /Applications/Flex.app | |
``` | |
### Linux | |
- AppImage: Download `flex-${{ env.VERSION }}-linux.AppImage`, make executable with `chmod +x flex-${{ env.VERSION }}-linux.AppImage` and run | |
- Debian/Ubuntu: Download `flex-${{ env.VERSION }}-linux.deb` and install with `sudo dpkg -i flex-${{ env.VERSION }}-linux.deb` | |
## Source Code | |
Source code is available as a ZIP archive. |