style: improve TerminalShortcutBar UI and refactor key handling #49
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
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| name: "Build & Release" | |
| jobs: | |
| # Test job for PRs | |
| test: | |
| name: Test & Analyze | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: '3.32.5' | |
| channel: 'stable' | |
| - name: Flutter Analyze | |
| run: flutter analyze | |
| - name: Run Tests | |
| run: flutter test | |
| # Extract version information (shared by both build jobs) | |
| version: | |
| name: Extract Version | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| version_code: ${{ steps.version.outputs.version_code }} | |
| tag: ${{ steps.version.outputs.tag }} | |
| prev_tag: ${{ steps.prev_tag.outputs.prev_tag }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Extract Version from pubspec.yaml | |
| id: version | |
| run: | | |
| # Extract version from pubspec.yaml | |
| VERSION_WITH_BUILD=$(grep '^version: ' pubspec.yaml | sed 's/version: *//') | |
| VERSION=$(echo "$VERSION_WITH_BUILD" | cut -d'+' -f1) | |
| VERSION_CODE=$(echo "$VERSION_WITH_BUILD" | cut -d'+' -f2) | |
| # If version code is not specified, use default | |
| if [ "$VERSION" = "$VERSION_CODE" ]; then | |
| VERSION_CODE=1 | |
| fi | |
| TAG="v$VERSION" | |
| echo "🚀 Extracted Version: $VERSION" | |
| echo "🔢 Version Code: $VERSION_CODE" | |
| echo "🏷️ Git Tag: $TAG" | |
| # Set outputs | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "version_code=$VERSION_CODE" >> $GITHUB_OUTPUT | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| - name: Find Previous Release for Changelog | |
| id: prev_tag | |
| run: | | |
| CURRENT_TAG="v${{ steps.version.outputs.version }}" | |
| echo "🔍 Current tag: $CURRENT_TAG" | |
| # Fetch all tags and refs to ensure we have complete history | |
| git fetch --tags --force | |
| git fetch --prune --unshallow 2>/dev/null || true | |
| # Get all version tags, sorted by version number | |
| ALL_TAGS=$(git tag -l 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' | sort -V) | |
| echo "📋 All version tags found:" | |
| echo "$ALL_TAGS" | |
| # Find the previous tag (excluding current if it exists) | |
| PREV_TAG="" | |
| if [ -n "$ALL_TAGS" ]; then | |
| # Method 1: Get the tag before current in sorted list | |
| PREV_TAG=$(echo "$ALL_TAGS" | grep -v "^$CURRENT_TAG$" | tail -1) | |
| # Method 2: If Method 1 fails, try git describe | |
| if [ -z "$PREV_TAG" ] || [ "$PREV_TAG" = "$CURRENT_TAG" ]; then | |
| PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' || echo "") | |
| fi | |
| # Method 3: If still empty, get the latest tag from git log | |
| if [ -z "$PREV_TAG" ]; then | |
| PREV_TAG=$(git log --oneline --decorate --tags | grep -E 'tag: v[0-9]+\.[0-9]+\.[0-9]+' | head -1 | sed -n 's/.*tag: \(v[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' || echo "") | |
| fi | |
| # Method 4: Fallback - get second-to-last from sorted list | |
| if [ -z "$PREV_TAG" ]; then | |
| TAG_COUNT=$(echo "$ALL_TAGS" | wc -l) | |
| if [ "$TAG_COUNT" -gt 1 ]; then | |
| PREV_TAG=$(echo "$ALL_TAGS" | tail -2 | head -1) | |
| fi | |
| fi | |
| fi | |
| # Final validation and fallback | |
| if [ -z "$PREV_TAG" ] || [ "$PREV_TAG" = "$CURRENT_TAG" ]; then | |
| echo "⚠️ No valid previous tag found, using fallback" | |
| # Use first commit as fallback for changelog | |
| FIRST_COMMIT=$(git rev-list --max-parents=0 HEAD 2>/dev/null || echo "") | |
| if [ -n "$FIRST_COMMIT" ]; then | |
| PREV_TAG="$FIRST_COMMIT" | |
| else | |
| PREV_TAG="" | |
| fi | |
| fi | |
| echo "🏷️ Previous tag determined: $PREV_TAG" | |
| # Validate the tag exists (unless it's a commit hash) | |
| if [ -n "$PREV_TAG" ] && [[ "$PREV_TAG" =~ ^v[0-9] ]]; then | |
| if ! git rev-parse "$PREV_TAG" >/dev/null 2>&1; then | |
| echo "⚠️ Previous tag $PREV_TAG doesn't exist, using empty" | |
| PREV_TAG="" | |
| fi | |
| fi | |
| echo "✅ Final previous tag: $PREV_TAG" | |
| echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT | |
| # Android build job | |
| build-android: | |
| name: Build Android | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| needs: version | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: '3.32.5' | |
| channel: 'stable' | |
| architecture: x64 | |
| # Keystore setup | |
| - name: Decode Keystore | |
| run: | | |
| echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/upload-keystore.jks | |
| - name: Create key.properties | |
| run: | | |
| echo "storePassword=${{ secrets.KEY_STORE_PASSWORD }}" > android/key.properties | |
| echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties | |
| echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties | |
| echo "storeFile=../app/upload-keystore.jks" >> android/key.properties | |
| # Reproducible build settings | |
| - name: Set reproducible build parameters | |
| run: | | |
| echo "SOURCE_DATE_EPOCH=1577836800" >> $GITHUB_ENV | |
| echo "ZIPFLAGS=-X" >> $GITHUB_ENV | |
| # Quality checks | |
| - name: Flutter Analyze | |
| run: flutter analyze | |
| - name: Run Tests | |
| run: flutter test | |
| # Build Android | |
| - name: Build Android APK | |
| run: | | |
| flutter build apk --release --split-per-abi | |
| cd build/app/outputs/apk/release | |
| mv app-armeabi-v7a-release.apk SysAdmin-v${{ needs.version.outputs.version }}-armeabi-v7a.apk | |
| mv app-arm64-v8a-release.apk SysAdmin-v${{ needs.version.outputs.version }}-arm64-v8a.apk | |
| mv app-x86_64-release.apk SysAdmin-v${{ needs.version.outputs.version }}-x86_64.apk | |
| # Upload Android artifacts | |
| - name: Upload Android Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-apks | |
| path: | | |
| build/app/outputs/apk/release/SysAdmin-v${{ needs.version.outputs.version }}-armeabi-v7a.apk | |
| build/app/outputs/apk/release/SysAdmin-v${{ needs.version.outputs.version }}-arm64-v8a.apk | |
| build/app/outputs/apk/release/SysAdmin-v${{ needs.version.outputs.version }}-x86_64.apk | |
| # iOS build job | |
| build-ios: | |
| name: Build iOS | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| needs: version | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: '3.32.5' | |
| channel: 'stable' | |
| architecture: x64 | |
| # Quality checks | |
| - name: Flutter Analyze | |
| run: flutter analyze | |
| - name: Run Tests | |
| run: flutter test | |
| # Build iOS | |
| - name: Build iOS App | |
| run: | | |
| flutter build ios --release --no-codesign | |
| cd build/ios/iphoneos | |
| mkdir -p Payload | |
| cp -r Runner.app Payload/ | |
| zip -r SysAdmin-v${{ needs.version.outputs.version }}.ipa Payload | |
| # Upload iOS artifacts | |
| - name: Upload iOS Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-ipa | |
| path: build/ios/iphoneos/SysAdmin-v${{ needs.version.outputs.version }}.ipa | |
| # Release job (runs after both builds complete) | |
| release: | |
| name: Create Release | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| needs: [version, build-android, build-ios] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| token: ${{ secrets.TOKEN }} | |
| # Download all artifacts | |
| - name: Download Android Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-apks | |
| path: android-artifacts | |
| - name: Download iOS Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ios-ipa | |
| path: ios-artifacts | |
| # Create Tag if it doesn't exist | |
| - name: Create Tag | |
| run: | | |
| if ! git rev-parse ${{ needs.version.outputs.tag }} >/dev/null 2>&1; then | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| git tag ${{ needs.version.outputs.tag }} | |
| git push origin ${{ needs.version.outputs.tag }} | |
| echo "Created new tag: ${{ needs.version.outputs.tag }}" | |
| else | |
| echo "Tag ${{ needs.version.outputs.tag }} already exists, skipping tag creation" | |
| fi | |
| # Create Release | |
| - name: Create GitHub Release | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| artifacts: | | |
| android-artifacts/SysAdmin-v${{ needs.version.outputs.version }}-armeabi-v7a.apk | |
| android-artifacts/SysAdmin-v${{ needs.version.outputs.version }}-arm64-v8a.apk | |
| android-artifacts/SysAdmin-v${{ needs.version.outputs.version }}-x86_64.apk | |
| ios-artifacts/SysAdmin-v${{ needs.version.outputs.version }}.ipa | |
| token: ${{ secrets.TOKEN }} | |
| tag: ${{ needs.version.outputs.tag }} | |
| name: Release ${{ needs.version.outputs.tag }} | |
| body: | | |
| # Release ${{ needs.version.outputs.tag }} | |
| ${{ github.event.head_commit.message }} | |
| #### Full Changelog: [${{ needs.version.outputs.prev_tag }}...${{ needs.version.outputs.tag }}](https://github.com/${{ github.repository }}/compare/${{ needs.version.outputs.prev_tag }}...${{ needs.version.outputs.tag }}) | |
| draft: false | |
| prerelease: false |