From 356fa457f15b16b134bfc7e8a2168d92cb1827fc Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 22:59:30 -0300 Subject: [PATCH 1/7] Refactor CI and release workflows for improved structure and clarity - Updated CI workflow (.github/workflows/ci.yml) to enhance readability and maintainability by standardizing indentation and formatting. - Refined release workflow for Linux (.github/workflows/release-linux.yml) to improve version handling and streamline package creation steps. - Added missing dependencies in make_config.yaml for both deb and rpm packaging configurations to ensure proper functionality. --- .github/workflows/ci.yml | 761 +++++++++++++---------- .github/workflows/release-linux.yml | 386 ++++++------ app/linux/packaging/deb/make_config.yaml | 6 + app/linux/packaging/rpm/make_config.yaml | 6 + scripts/ci/linux-package-smoke.sh | 93 +++ 5 files changed, 727 insertions(+), 525 deletions(-) create mode 100644 scripts/ci/linux-package-smoke.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ead9f33..40659bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,342 +1,439 @@ name: CI on: - push: - branches: [main] - pull_request: - branches: [main] + push: + branches: [main] + pull_request: + branches: [main] concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true + group: ci-${{ github.ref }} + cancel-in-progress: true permissions: - contents: read + contents: read jobs: - markdown: - runs-on: ubuntu-latest - name: Markdown Lint - - steps: - - uses: actions/checkout@v4 - - - name: Lint Markdown files - uses: DavidAnson/markdownlint-cli2-action@v19 - with: - globs: "**/*.md" - config: ".markdownlint.yaml" - - quality: - runs-on: ubuntu-latest - name: Lint, Format & Test - - steps: - - uses: actions/checkout@v4 - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Cache pub dependencies - uses: actions/cache@v4 - with: - path: ~/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} - restore-keys: ${{ runner.os }}-pub- - - - name: Get dependencies - run: | - cd core && flutter pub get - cd ../listener && flutter pub get - cd ../app && flutter pub get - - - name: Check formatting - run: dart format --output=none --set-exit-if-changed . - - - name: Analyze - run: | - cd core && flutter analyze - cd ../listener && flutter analyze - cd ../app && flutter analyze - - - name: Check auto-fixable issues - run: | - OUTPUT=$(dart fix --dry-run . 2>&1) - echo "$OUTPUT" - echo "$OUTPUT" | grep -q "Nothing to fix!" || { echo "::error::Auto-fixable issues found. Run 'dart fix --apply' and commit."; exit 1; } - - - name: Verify generated code (Drift) - run: | - cd core && dart run build_runner build --delete-conflicting-outputs - git diff --exit-code . || { echo "::error::Generated code is out of date. Run 'dart run build_runner build --delete-conflicting-outputs' in core/ and commit."; exit 1; } - - - name: Verify generated localizations - run: | - cd app && flutter gen-l10n - git diff --exit-code lib/l10n/ || { echo "::error::Generated l10n files are out of date. Run 'flutter gen-l10n' in app/ and commit."; exit 1; } - - - name: Check outdated dependencies - run: | - cd core && flutter pub outdated --no-dev-dependencies - cd ../listener && flutter pub outdated --no-dev-dependencies - cd ../app && flutter pub outdated --no-dev-dependencies - continue-on-error: true - - - name: Run tests - run: | - cd core && flutter test --coverage - cd ../listener && flutter test --coverage - cd ../app && flutter test --coverage - - - name: Upload coverage artifacts - uses: actions/upload-artifact@v4 - if: always() - with: - name: coverage-reports - path: | - core/coverage/lcov.info - listener/coverage/lcov.info - app/coverage/lcov.info - retention-days: 1 - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - if: always() - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: core/coverage/lcov.info,listener/coverage/lcov.info,app/coverage/lcov.info - flags: core,listener,app - fail_ci_if_error: false - - release-readiness: - runs-on: ubuntu-latest - name: Release Readiness - - steps: - - uses: actions/checkout@v4 - - - name: Validate pubspec structure - run: | - errors=0 - for pkg in app core listener; do - if [ ! -f "$pkg/pubspec.yaml" ]; then - echo "::error::Missing $pkg/pubspec.yaml" - errors=$((errors + 1)) - continue - fi - version=$(grep '^version:' "$pkg/pubspec.yaml" | head -1 | awk '{print $2}') - name=$(grep '^name:' "$pkg/pubspec.yaml" | head -1 | awk '{print $2}') - if [ -z "$version" ]; then - echo "::error::Missing version field in $pkg/pubspec.yaml" - errors=$((errors + 1)) - fi - if [ -z "$name" ]; then - echo "::error::Missing name field in $pkg/pubspec.yaml" - errors=$((errors + 1)) - fi - echo "✓ $pkg: name=$name version=$version" - done - [ $errors -eq 0 ] || exit 1 - - - name: Validate release workflow references - run: | - errors=0 - for wf in release-win.yml release-mac.yml release-linux.yml; do - if [ ! -f ".github/workflows/$wf" ]; then - echo "::error::Missing .github/workflows/$wf (referenced by release.yml)" - errors=$((errors + 1)) - else - echo "✓ .github/workflows/$wf" - fi - done - [ $errors -eq 0 ] || exit 1 - - - name: Validate project structure - run: | - errors=0 - for dir in app/lib app/windows app/macos app/linux app/assets; do - if [ ! -d "$dir" ]; then - echo "::error::Missing required directory: $dir" - errors=$((errors + 1)) - else - echo "✓ $dir/" - fi - done - for file in app/l10n.yaml app/pubspec.yaml core/pubspec.yaml listener/pubspec.yaml; do - if [ ! -f "$file" ]; then - echo "::error::Missing required file: $file" - errors=$((errors + 1)) - else - echo "✓ $file" - fi - done - [ $errors -eq 0 ] || exit 1 - - - name: Simulate release version extraction - run: | - test_tags=("v2.1.0" "v2.0.0-beta.1" "v3.0.0" "v1.0.0-rc.1") - for tag in "${test_tags[@]}"; do - ref="refs/tags/$tag" - if [[ "$ref" =~ refs/tags/v(.+) ]]; then - VERSION="${BASH_REMATCH[1]}" - # Validate version format (semver) - if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then - echo "::error::Tag $tag produces invalid version: $VERSION" - exit 1 - fi - echo "✓ $tag → $VERSION" - else - echo "::error::Tag $tag would fail version extraction in release.yml" - exit 1 - fi - done - - build: - needs: quality - runs-on: windows-latest - name: Build Verification (Windows) - - steps: - - uses: actions/checkout@v4 - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Cache pub dependencies - uses: actions/cache@v4 - with: - path: ~/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} - restore-keys: ${{ runner.os }}-pub- - - - name: Get dependencies - run: | - cd core; flutter pub get - cd ../listener; flutter pub get - cd ../app; flutter pub get - - - name: Build Windows release - run: cd app; flutter build windows --release - - build-linux: - needs: quality - runs-on: ubuntu-22.04 - name: Build Verification (Linux) - - steps: - - uses: actions/checkout@v4 - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Cache pub dependencies - uses: actions/cache@v4 - with: - path: ~/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} - restore-keys: ${{ runner.os }}-pub- - - - name: Install Linux build dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - clang \ - cmake \ - desktop-file-utils \ - libayatana-appindicator3-dev \ - libfuse2 \ - libgtk-3-dev \ - libkeybinder-3.0-dev \ - liblzma-dev \ - libx11-dev \ - libxtst-dev \ - lld-14 \ - ninja-build \ - patchelf \ - pkg-config - - - name: Verify Linux toolchain preflight - run: | - set -euo pipefail - test -x /usr/lib/llvm-14/bin/ld.lld || test -x /usr/bin/ld.lld-14 || test -x /usr/bin/ld.lld - pkg-config --modversion gtk+-3.0 - pkg-config --modversion keybinder-3.0 - pkg-config --modversion ayatana-appindicator3-0.1 - pkg-config --modversion x11 - pkg-config --modversion xtst - - - name: Get dependencies - run: | - cd core && flutter pub get - cd ../listener && flutter pub get - cd ../app && flutter pub get - - - name: Build Linux release - run: cd app && flutter build linux --release - - build-macos: - needs: quality - runs-on: macos-latest - name: Build Verification (macOS) - - steps: - - uses: actions/checkout@v4 - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Cache pub dependencies - uses: actions/cache@v4 - with: - path: ~/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} - restore-keys: ${{ runner.os }}-pub- - - - name: Get dependencies - run: | - cd core && flutter pub get - cd ../listener && flutter pub get - cd ../app && flutter pub get - - - name: Build macOS release - run: cd app && flutter build macos --release - - sonarcloud: - name: SonarCloud - needs: quality - runs-on: ubuntu-latest - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download coverage reports - uses: actions/download-artifact@v4 - with: - name: coverage-reports - path: . - - - name: SonarCloud Scan - uses: SonarSource/sonarcloud-github-action@v5 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - with: - args: > - -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} - -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} - -Dsonar.sources=core/lib,listener/lib,app/lib - -Dsonar.tests=core/test,listener/test,app/test - -Dsonar.dart.lcov.reportPaths=core/coverage/lcov.info,listener/coverage/lcov.info,app/coverage/lcov.info - -Dsonar.exclusions=**/generated/**,**/*.g.dart,**/*.freezed.dart,**/l10n/**,**/core.dart - -Dsonar.coverage.exclusions=**/main.dart,**/shell/**,**/services/auto_update_service.dart,**/windows_clipboard_listener.dart,**/l10n/**,**/*.g.dart,**/*.freezed.dart,**/core.dart,**/screens/settings_screen.dart + markdown: + runs-on: ubuntu-latest + name: Markdown Lint + + steps: + - uses: actions/checkout@v4 + + - name: Lint Markdown files + uses: DavidAnson/markdownlint-cli2-action@v19 + with: + globs: "**/*.md" + config: ".markdownlint.yaml" + + quality: + runs-on: ubuntu-latest + name: Lint, Format & Test + + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Cache pub dependencies + uses: actions/cache@v4 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Get dependencies + run: | + cd core && flutter pub get + cd ../listener && flutter pub get + cd ../app && flutter pub get + + - name: Check formatting + run: dart format --output=none --set-exit-if-changed . + + - name: Analyze + run: | + cd core && flutter analyze + cd ../listener && flutter analyze + cd ../app && flutter analyze + + - name: Check auto-fixable issues + run: | + OUTPUT=$(dart fix --dry-run . 2>&1) + echo "$OUTPUT" + echo "$OUTPUT" | grep -q "Nothing to fix!" || { echo "::error::Auto-fixable issues found. Run 'dart fix --apply' and commit."; exit 1; } + + - name: Verify generated code (Drift) + run: | + cd core && dart run build_runner build --delete-conflicting-outputs + git diff --exit-code . || { echo "::error::Generated code is out of date. Run 'dart run build_runner build --delete-conflicting-outputs' in core/ and commit."; exit 1; } + + - name: Verify generated localizations + run: | + cd app && flutter gen-l10n + git diff --exit-code lib/l10n/ || { echo "::error::Generated l10n files are out of date. Run 'flutter gen-l10n' in app/ and commit."; exit 1; } + + - name: Check outdated dependencies + run: | + cd core && flutter pub outdated --no-dev-dependencies + cd ../listener && flutter pub outdated --no-dev-dependencies + cd ../app && flutter pub outdated --no-dev-dependencies + continue-on-error: true + + - name: Run tests + run: | + cd core && flutter test --coverage + cd ../listener && flutter test --coverage + cd ../app && flutter test --coverage + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-reports + path: | + core/coverage/lcov.info + listener/coverage/lcov.info + app/coverage/lcov.info + retention-days: 1 + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + if: always() + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: core/coverage/lcov.info,listener/coverage/lcov.info,app/coverage/lcov.info + flags: core,listener,app + fail_ci_if_error: false + + release-readiness: + runs-on: ubuntu-latest + name: Release Readiness + + steps: + - uses: actions/checkout@v4 + + - name: Validate pubspec structure + run: | + errors=0 + for pkg in app core listener; do + if [ ! -f "$pkg/pubspec.yaml" ]; then + echo "::error::Missing $pkg/pubspec.yaml" + errors=$((errors + 1)) + continue + fi + version=$(grep '^version:' "$pkg/pubspec.yaml" | head -1 | awk '{print $2}') + name=$(grep '^name:' "$pkg/pubspec.yaml" | head -1 | awk '{print $2}') + if [ -z "$version" ]; then + echo "::error::Missing version field in $pkg/pubspec.yaml" + errors=$((errors + 1)) + fi + if [ -z "$name" ]; then + echo "::error::Missing name field in $pkg/pubspec.yaml" + errors=$((errors + 1)) + fi + echo "✓ $pkg: name=$name version=$version" + done + [ $errors -eq 0 ] || exit 1 + + - name: Validate release workflow references + run: | + errors=0 + for wf in release-win.yml release-mac.yml release-linux.yml; do + if [ ! -f ".github/workflows/$wf" ]; then + echo "::error::Missing .github/workflows/$wf (referenced by release.yml)" + errors=$((errors + 1)) + else + echo "✓ .github/workflows/$wf" + fi + done + [ $errors -eq 0 ] || exit 1 + + - name: Validate project structure + run: | + errors=0 + for dir in app/lib app/windows app/macos app/linux app/assets; do + if [ ! -d "$dir" ]; then + echo "::error::Missing required directory: $dir" + errors=$((errors + 1)) + else + echo "✓ $dir/" + fi + done + for file in app/l10n.yaml app/pubspec.yaml core/pubspec.yaml listener/pubspec.yaml; do + if [ ! -f "$file" ]; then + echo "::error::Missing required file: $file" + errors=$((errors + 1)) + else + echo "✓ $file" + fi + done + [ $errors -eq 0 ] || exit 1 + + - name: Simulate release version extraction + run: | + test_tags=("v2.1.0" "v2.0.0-beta.1" "v3.0.0" "v1.0.0-rc.1") + for tag in "${test_tags[@]}"; do + ref="refs/tags/$tag" + if [[ "$ref" =~ refs/tags/v(.+) ]]; then + VERSION="${BASH_REMATCH[1]}" + # Validate version format (semver) + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "::error::Tag $tag produces invalid version: $VERSION" + exit 1 + fi + echo "✓ $tag → $VERSION" + else + echo "::error::Tag $tag would fail version extraction in release.yml" + exit 1 + fi + done + + build: + needs: quality + runs-on: windows-latest + name: Build Verification (Windows) + + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Cache pub dependencies + uses: actions/cache@v4 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Get dependencies + run: | + cd core; flutter pub get + cd ../listener; flutter pub get + cd ../app; flutter pub get + + - name: Build Windows release + run: cd app; flutter build windows --release + + build-linux: + needs: quality + runs-on: ubuntu-22.04 + name: Build Verification (Linux) + + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Cache pub dependencies + uses: actions/cache@v4 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Install Linux build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + clang \ + cmake \ + desktop-file-utils \ + libayatana-appindicator3-dev \ + libfuse2 \ + libgtk-3-dev \ + libkeybinder-3.0-dev \ + liblzma-dev \ + libx11-dev \ + libxtst-dev \ + lld-14 \ + ninja-build \ + patchelf \ + pkg-config + + - name: Verify Linux toolchain preflight + run: | + set -euo pipefail + test -x /usr/lib/llvm-14/bin/ld.lld || test -x /usr/bin/ld.lld-14 || test -x /usr/bin/ld.lld + pkg-config --modversion gtk+-3.0 + pkg-config --modversion keybinder-3.0 + pkg-config --modversion ayatana-appindicator3-0.1 + pkg-config --modversion x11 + pkg-config --modversion xtst + + - name: Get dependencies + run: | + cd core && flutter pub get + cd ../listener && flutter pub get + cd ../app && flutter pub get + + - name: Build Linux release + run: cd app && flutter build linux --release + + package-linux-smoke: + needs: quality + runs-on: ubuntu-22.04 + timeout-minutes: 30 + name: Linux Package Smoke (deb/rpm) + + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Cache pub dependencies + uses: actions/cache@v4 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Install Linux packaging dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + clang \ + cmake \ + desktop-file-utils \ + libayatana-appindicator3-dev \ + libfuse2 \ + libgtk-3-dev \ + libkeybinder-3.0-dev \ + liblzma-dev \ + libx11-dev \ + libxtst-dev \ + lld-14 \ + ninja-build \ + patchelf \ + pkg-config \ + rpm + + - name: Get dependencies + run: | + cd core && flutter pub get + cd ../listener && flutter pub get + cd ../app && flutter pub get + + - name: Install Fastforge + run: dart pub global activate fastforge + + - name: Build deb package + run: | + cd app + fastforge package --platform linux --targets deb + + - name: Build rpm package + run: | + cd app + fastforge package --platform linux --targets rpm --skip-clean + + - name: Resolve package paths + run: | + set -euo pipefail + DEB_PATH=$(find app/dist app/build -type f -name "*.deb" 2>/dev/null | head -n 1) + RPM_PATH=$(find app/dist app/build -type f -name "*.rpm" 2>/dev/null | head -n 1) + test -n "$DEB_PATH" + test -n "$RPM_PATH" + echo "DEB_PATH=$DEB_PATH" >> "$GITHUB_ENV" + echo "RPM_PATH=$RPM_PATH" >> "$GITHUB_ENV" + echo "Using deb: $DEB_PATH" + echo "Using rpm: $RPM_PATH" + + - name: Validate package dependency metadata + run: | + set -euo pipefail + + echo "Checking deb Depends metadata" + DEB_INFO=$(dpkg-deb -I "$DEB_PATH") + echo "$DEB_INFO" | grep -Eq "Depends:.*libayatana-appindicator3-1" + echo "$DEB_INFO" | grep -Eq "Depends:.*libkeybinder-3.0-0" + echo "$DEB_INFO" | grep -Eq "Depends:.*libx11-6" + echo "$DEB_INFO" | grep -Eq "Depends:.*libxtst6" + + echo "Checking rpm Requires metadata" + RPM_REQUIRES=$(rpm -qpR "$RPM_PATH") + echo "$RPM_REQUIRES" | grep -Eq "^libayatana-appindicator-gtk3$" + echo "$RPM_REQUIRES" | grep -Eq "^keybinder3$" + echo "$RPM_REQUIRES" | grep -Eq "^gtk3$" + echo "$RPM_REQUIRES" | grep -Eq "^libX11$" + echo "$RPM_REQUIRES" | grep -Eq "^libXtst$" + + - name: Run package smoke tests + run: | + set -euo pipefail + bash scripts/ci/linux-package-smoke.sh deb "$DEB_PATH" + bash scripts/ci/linux-package-smoke.sh rpm "$RPM_PATH" + + build-macos: + needs: quality + runs-on: macos-latest + name: Build Verification (macOS) + + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Cache pub dependencies + uses: actions/cache@v4 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Get dependencies + run: | + cd core && flutter pub get + cd ../listener && flutter pub get + cd ../app && flutter pub get + + - name: Build macOS release + run: cd app && flutter build macos --release + + sonarcloud: + name: SonarCloud + needs: quality + runs-on: ubuntu-latest + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download coverage reports + uses: actions/download-artifact@v4 + with: + name: coverage-reports + path: . + + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@v5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} + -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} + -Dsonar.sources=core/lib,listener/lib,app/lib + -Dsonar.tests=core/test,listener/test,app/test + -Dsonar.dart.lcov.reportPaths=core/coverage/lcov.info,listener/coverage/lcov.info,app/coverage/lcov.info + -Dsonar.exclusions=**/generated/**,**/*.g.dart,**/*.freezed.dart,**/l10n/**,**/core.dart + -Dsonar.coverage.exclusions=**/main.dart,**/shell/**,**/services/auto_update_service.dart,**/windows_clipboard_listener.dart,**/l10n/**,**/*.g.dart,**/*.freezed.dart,**/core.dart,**/screens/settings_screen.dart diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index ab2a717..a2ad060 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -1,200 +1,200 @@ name: Release (Linux) on: - workflow_call: - inputs: - version: - required: true - type: string - workflow_dispatch: - inputs: - version: - description: "Version to use (e.g. 2.1.0). Defaults to 2.0.0-dev" - required: false - default: "2.0.0-dev" + workflow_call: + inputs: + version: + required: true + type: string + workflow_dispatch: + inputs: + version: + description: "Version to use (e.g. 2.1.0). Defaults to 2.0.0-dev" + required: false + default: "2.0.0-dev" permissions: - contents: read + contents: read jobs: - build-linux: - runs-on: ubuntu-22.04 - timeout-minutes: 45 - name: Build Linux (AppImage, deb, rpm) - - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ github.ref_name }} - - - name: Resolve version - id: get_version - run: | - VERSION="${{ inputs.version }}" - - IS_PRERELEASE="false" - if [[ "$VERSION" == *-* ]]; then - IS_PRERELEASE="true" - fi - - echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" - echo "IS_PRERELEASE=$IS_PRERELEASE" >> "$GITHUB_OUTPUT" - echo "Version: $VERSION PreRelease: $IS_PRERELEASE" - - - uses: subosito/flutter-action@v2 - with: - channel: stable - cache: true - - - name: Cache pub dependencies - uses: actions/cache@v5 - with: - path: ~/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} - restore-keys: ${{ runner.os }}-pub- - - - name: Install Linux build dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - clang \ - cmake \ - desktop-file-utils \ - libayatana-appindicator3-dev \ - libfuse2 \ - libgtk-3-dev \ - libkeybinder-3.0-dev \ - liblzma-dev \ - libx11-dev \ - libxtst-dev \ - lld-14 \ - ninja-build \ - patchelf \ - pkg-config \ - rpm - - - name: Verify Linux toolchain preflight - run: | - set -euo pipefail - test -x /usr/lib/llvm-14/bin/ld.lld || test -x /usr/bin/ld.lld-14 || test -x /usr/bin/ld.lld - pkg-config --modversion gtk+-3.0 - pkg-config --modversion keybinder-3.0 - pkg-config --modversion ayatana-appindicator3-0.1 - pkg-config --modversion x11 - pkg-config --modversion xtst - - - name: Install Fastforge - run: dart pub global activate fastforge - - - name: Install appimagetool - run: | - wget -qO /usr/local/bin/appimagetool \ - "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" - chmod +x /usr/local/bin/appimagetool - - - name: Get dependencies - run: | - cd core && flutter pub get - cd ../listener && flutter pub get - cd ../app && flutter pub get - - - name: Update pubspec version from tag - run: | - VERSION="${{ steps.get_version.outputs.VERSION }}" - sed -i "s/^version:.*/version: $VERSION/" app/pubspec.yaml - echo "Updated pubspec.yaml version to: $VERSION" - - - name: Package AppImage - env: - APPIMAGE_EXTRACT_AND_RUN: "1" - run: | - cd app - fastforge package \ - --platform linux \ - --targets appimage \ - --build-dart-define "APP_VERSION=${{ steps.get_version.outputs.VERSION }}" - - - name: Rename AppImage - run: | - VERSION="${{ steps.get_version.outputs.VERSION }}" - APPIMAGE=$(find app/dist app/build -type f -name "*.AppImage" 2>/dev/null | head -n 1) - if [[ -z "$APPIMAGE" ]]; then - echo "::error::No AppImage generated" - exit 1 - fi - mkdir -p app/dist - DEST="app/dist/CopyPaste_${VERSION}_x86_64.AppImage" - mv "$APPIMAGE" "$DEST" - chmod +x "$DEST" - echo "Renamed to: $(basename "$DEST")" - - - name: Package deb - run: | - cd app - fastforge package \ - --platform linux \ - --targets deb \ - --build-dart-define "APP_VERSION=${{ steps.get_version.outputs.VERSION }}" \ - --skip-clean - - - name: Rename deb - run: | - VERSION="${{ steps.get_version.outputs.VERSION }}" - DEB=$(find app/dist app/build -type f -name "*.deb" 2>/dev/null | head -n 1) - if [[ -z "$DEB" ]]; then - echo "::error::No deb package generated" - exit 1 - fi - mkdir -p app/dist - DEST="app/dist/CopyPaste_${VERSION}_amd64.deb" - mv "$DEB" "$DEST" - echo "Renamed to: $(basename "$DEST")" - - - name: Package rpm - run: | - cd app - fastforge package \ - --platform linux \ - --targets rpm \ - --build-dart-define "APP_VERSION=${{ steps.get_version.outputs.VERSION }}" \ - --skip-clean - - - name: Rename rpm - run: | - VERSION="${{ steps.get_version.outputs.VERSION }}" - RPM=$(find app/dist app/build -type f -name "*.rpm" 2>/dev/null | head -n 1) - if [[ -z "$RPM" ]]; then - echo "::error::No rpm package generated" - exit 1 - fi - mkdir -p app/dist - DEST="app/dist/CopyPaste_${VERSION}_x86_64.rpm" - mv "$RPM" "$DEST" - echo "Renamed to: $(basename "$DEST")" - - - name: Publish deb to Cloudsmith - if: github.event_name == 'push' - env: - CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} - run: | - pip install cloudsmith-cli - cloudsmith push deb rgdevment/copypaste/any-distro/any-version \ - app/dist/CopyPaste_${{ steps.get_version.outputs.VERSION }}_amd64.deb --republish - - - name: Publish rpm to Cloudsmith - if: github.event_name == 'push' - env: - CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} - run: | - cloudsmith push rpm rgdevment/copypaste/any-distro/any-version \ - app/dist/CopyPaste_${{ steps.get_version.outputs.VERSION }}_x86_64.rpm --republish - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: release-linux - path: | - app/dist/*.AppImage - app/dist/*.deb - app/dist/*.rpm - retention-days: 5 + build-linux: + runs-on: ubuntu-22.04 + timeout-minutes: 45 + name: Build Linux (AppImage, deb, rpm) + + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.ref_name }} + + - name: Resolve version + id: get_version + run: | + VERSION="${{ inputs.version }}" + + IS_PRERELEASE="false" + if [[ "$VERSION" == *-* ]]; then + IS_PRERELEASE="true" + fi + + echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" + echo "IS_PRERELEASE=$IS_PRERELEASE" >> "$GITHUB_OUTPUT" + echo "Version: $VERSION PreRelease: $IS_PRERELEASE" + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Cache pub dependencies + uses: actions/cache@v5 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Install Linux build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + clang \ + cmake \ + desktop-file-utils \ + libayatana-appindicator3-dev \ + libfuse2 \ + libgtk-3-dev \ + libkeybinder-3.0-dev \ + liblzma-dev \ + libx11-dev \ + libxtst-dev \ + lld-14 \ + ninja-build \ + patchelf \ + pkg-config \ + rpm + + - name: Verify Linux toolchain preflight + run: | + set -euo pipefail + test -x /usr/lib/llvm-14/bin/ld.lld || test -x /usr/bin/ld.lld-14 || test -x /usr/bin/ld.lld + pkg-config --modversion gtk+-3.0 + pkg-config --modversion keybinder-3.0 + pkg-config --modversion ayatana-appindicator3-0.1 + pkg-config --modversion x11 + pkg-config --modversion xtst + + - name: Install Fastforge + run: dart pub global activate fastforge + + - name: Install appimagetool + run: | + wget -qO /usr/local/bin/appimagetool \ + "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" + chmod +x /usr/local/bin/appimagetool + + - name: Get dependencies + run: | + cd core && flutter pub get + cd ../listener && flutter pub get + cd ../app && flutter pub get + + - name: Update pubspec version from tag + run: | + VERSION="${{ steps.get_version.outputs.VERSION }}" + sed -i "s/^version:.*/version: $VERSION/" app/pubspec.yaml + echo "Updated pubspec.yaml version to: $VERSION" + + - name: Package AppImage + env: + APPIMAGE_EXTRACT_AND_RUN: "1" + run: | + cd app + fastforge package \ + --platform linux \ + --targets appimage \ + --build-dart-define "APP_VERSION=${{ steps.get_version.outputs.VERSION }}" + + - name: Rename AppImage + run: | + VERSION="${{ steps.get_version.outputs.VERSION }}" + APPIMAGE=$(find app/dist app/build -type f -name "*.AppImage" 2>/dev/null | head -n 1) + if [[ -z "$APPIMAGE" ]]; then + echo "::error::No AppImage generated" + exit 1 + fi + mkdir -p app/dist + DEST="app/dist/CopyPaste_${VERSION}_x86_64.AppImage" + mv "$APPIMAGE" "$DEST" + chmod +x "$DEST" + echo "Renamed to: $(basename "$DEST")" + + - name: Package deb + run: | + cd app + fastforge package \ + --platform linux \ + --targets deb \ + --build-dart-define "APP_VERSION=${{ steps.get_version.outputs.VERSION }}" \ + --skip-clean + + - name: Rename deb + run: | + VERSION="${{ steps.get_version.outputs.VERSION }}" + DEB=$(find app/dist app/build -type f -name "*.deb" 2>/dev/null | head -n 1) + if [[ -z "$DEB" ]]; then + echo "::error::No deb package generated" + exit 1 + fi + mkdir -p app/dist + DEST="app/dist/CopyPaste_${VERSION}_amd64.deb" + mv "$DEB" "$DEST" + echo "Renamed to: $(basename "$DEST")" + + - name: Package rpm + run: | + cd app + fastforge package \ + --platform linux \ + --targets rpm \ + --build-dart-define "APP_VERSION=${{ steps.get_version.outputs.VERSION }}" \ + --skip-clean + + - name: Rename rpm + run: | + VERSION="${{ steps.get_version.outputs.VERSION }}" + RPM=$(find app/dist app/build -type f -name "*.rpm" 2>/dev/null | head -n 1) + if [[ -z "$RPM" ]]; then + echo "::error::No rpm package generated" + exit 1 + fi + mkdir -p app/dist + DEST="app/dist/CopyPaste_${VERSION}_x86_64.rpm" + mv "$RPM" "$DEST" + echo "Renamed to: $(basename "$DEST")" + + - name: Publish deb to Cloudsmith + if: github.event_name == 'push' + env: + CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} + run: | + pip install cloudsmith-cli + cloudsmith push deb rgdevment/copypaste/any-distro/any-version \ + app/dist/CopyPaste_${{ steps.get_version.outputs.VERSION }}_amd64.deb --republish + + - name: Publish rpm to Cloudsmith + if: github.event_name == 'push' + env: + CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} + run: | + cloudsmith push rpm rgdevment/copypaste/any-distro/any-version \ + app/dist/CopyPaste_${{ steps.get_version.outputs.VERSION }}_x86_64.rpm --republish + + - name: Upload artifact + uses: actions/upload-artifact@v6 + with: + name: release-linux + path: | + app/dist/*.AppImage + app/dist/*.deb + app/dist/*.rpm + retention-days: 5 diff --git a/app/linux/packaging/deb/make_config.yaml b/app/linux/packaging/deb/make_config.yaml index 4d88265..03a1d85 100644 --- a/app/linux/packaging/deb/make_config.yaml +++ b/app/linux/packaging/deb/make_config.yaml @@ -7,6 +7,12 @@ priority: optional section: x11 installed_size: 51200 essential: false +dependencies: + - libayatana-appindicator3-1 + - libkeybinder-3.0-0 + - libgtk-3-0 | libgtk-3-0t64 + - libx11-6 + - libxtst6 icon: assets/icons/icon_app_256.png generic_name: Clipboard Manager startup_notify: true diff --git a/app/linux/packaging/rpm/make_config.yaml b/app/linux/packaging/rpm/make_config.yaml index ee03f18..30e7403 100644 --- a/app/linux/packaging/rpm/make_config.yaml +++ b/app/linux/packaging/rpm/make_config.yaml @@ -8,6 +8,12 @@ packager: rgdevment packagerEmail: rgdevment@apirest.cl license: GPLv3 url: https://github.com/rgdevment/CopyPaste +requires: + - libayatana-appindicator-gtk3 + - keybinder3 + - gtk3 + - libX11 + - libXtst generic_name: Clipboard Manager startup_notify: true categories: diff --git a/scripts/ci/linux-package-smoke.sh b/scripts/ci/linux-package-smoke.sh new file mode 100644 index 0000000..7c845f8 --- /dev/null +++ b/scripts/ci/linux-package-smoke.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + echo "Usage: $0 " >&2 +} + +if [[ $# -ne 2 ]]; then + usage + exit 1 +fi + +package_type="$1" +package_path="$2" + +if [[ "$package_path" = /* ]]; then + host_package="$package_path" +else + host_package="$PWD/$package_path" +fi + +if [[ ! -f "$host_package" ]]; then + echo "Package file not found: $host_package" >&2 + exit 1 +fi + +if [[ "$package_type" == "deb" ]]; then + docker run --rm -v "$host_package:/tmp/copypaste.deb:ro" ubuntu:22.04 bash -lc ' + set -euo pipefail + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y file /tmp/copypaste.deb + + BIN=$(command -v copypaste || true) + if [[ -z "$BIN" ]]; then + BIN=$(find /usr -type f -name copypaste 2>/dev/null | head -n 1) + fi + test -n "$BIN" + + mapfile -t ELF_FILES < <( + dpkg -L copypaste | while read -r path; do + [[ -f "$path" ]] || continue + if file -b "$path" | grep -Eq "ELF .* (executable|shared object)"; then + echo "$path" + fi + done + ) + + test "${#ELF_FILES[@]}" -gt 0 + missing=0 + for elf in "${ELF_FILES[@]}"; do + echo "Checking ELF deps: $elf" + if ldd "$elf" 2>/dev/null | tee -a /tmp/ldd.out | grep -q "not found"; then + missing=1 + fi + done + + test "$missing" -eq 0 + ' +elif [[ "$package_type" == "rpm" ]]; then + docker run --rm -v "$host_package:/tmp/copypaste.rpm:ro" fedora:40 bash -lc ' + set -euo pipefail + dnf -y install file /tmp/copypaste.rpm + + BIN=$(command -v copypaste || true) + if [[ -z "$BIN" ]]; then + BIN=$(find /usr -type f -name copypaste 2>/dev/null | head -n 1) + fi + test -n "$BIN" + + mapfile -t ELF_FILES < <( + rpm -ql copypaste | while read -r path; do + [[ -f "$path" ]] || continue + if file -b "$path" | grep -Eq "ELF .* (executable|shared object)"; then + echo "$path" + fi + done + ) + + test "${#ELF_FILES[@]}" -gt 0 + missing=0 + for elf in "${ELF_FILES[@]}"; do + echo "Checking ELF deps: $elf" + if ldd "$elf" 2>/dev/null | tee -a /tmp/ldd.out | grep -q "not found"; then + missing=1 + fi + done + + test "$missing" -eq 0 + ' +else + usage + exit 1 +fi From fd41ca8407bde08100a822053b8f0026d77a9fa5 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 23:07:54 -0300 Subject: [PATCH 2/7] fix: normalize app version for Linux package smoke to ensure RPM compatibility --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40659bd..e732ea6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -330,6 +330,20 @@ jobs: - name: Install Fastforge run: dart pub global activate fastforge + - name: Normalize app version for Linux package smoke + run: | + set -euo pipefail + RAW_VERSION=$(grep '^version:' app/pubspec.yaml | awk '{print $2}') + BASE_VERSION="${RAW_VERSION%%-*}" + + if [[ "$RAW_VERSION" != "$BASE_VERSION" ]]; then + echo "Using RPM-compatible version for smoke packaging: $RAW_VERSION -> $BASE_VERSION" + sed -i "s/^version:.*/version: $BASE_VERSION/" app/pubspec.yaml + fi + + echo "Effective app version for package smoke:" + grep '^version:' app/pubspec.yaml + - name: Build deb package run: | cd app From 6e38c9dfc01aa667e75341aca944ec998d2e33eb Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 23:16:41 -0300 Subject: [PATCH 3/7] fix: improve smoke test output for deb and rpm packages with detailed library checks --- scripts/ci/linux-package-smoke.sh | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/scripts/ci/linux-package-smoke.sh b/scripts/ci/linux-package-smoke.sh index 7c845f8..798cd5e 100644 --- a/scripts/ci/linux-package-smoke.sh +++ b/scripts/ci/linux-package-smoke.sh @@ -25,6 +25,7 @@ if [[ ! -f "$host_package" ]]; then fi if [[ "$package_type" == "deb" ]]; then + echo "[smoke] Running deb smoke in ubuntu:22.04" docker run --rm -v "$host_package:/tmp/copypaste.deb:ro" ubuntu:22.04 bash -lc ' set -euo pipefail apt-get update @@ -49,14 +50,24 @@ if [[ "$package_type" == "deb" ]]; then missing=0 for elf in "${ELF_FILES[@]}"; do echo "Checking ELF deps: $elf" - if ldd "$elf" 2>/dev/null | tee -a /tmp/ldd.out | grep -q "not found"; then + ldd_output=$(ldd "$elf" 2>&1 || true) + echo "$ldd_output" >> /tmp/ldd.out + if echo "$ldd_output" | grep -q "not found"; then + echo "Missing libraries in: $elf" + echo "$ldd_output" | grep "not found" missing=1 fi done - test "$missing" -eq 0 + if [[ "$missing" -ne 0 ]]; then + echo "[smoke] deb package has unresolved shared libraries" + exit 1 + fi + + echo "[smoke] deb package passed" ' elif [[ "$package_type" == "rpm" ]]; then + echo "[smoke] Running rpm smoke in fedora:40" docker run --rm -v "$host_package:/tmp/copypaste.rpm:ro" fedora:40 bash -lc ' set -euo pipefail dnf -y install file /tmp/copypaste.rpm @@ -80,12 +91,21 @@ elif [[ "$package_type" == "rpm" ]]; then missing=0 for elf in "${ELF_FILES[@]}"; do echo "Checking ELF deps: $elf" - if ldd "$elf" 2>/dev/null | tee -a /tmp/ldd.out | grep -q "not found"; then + ldd_output=$(ldd "$elf" 2>&1 || true) + echo "$ldd_output" >> /tmp/ldd.out + if echo "$ldd_output" | grep -q "not found"; then + echo "Missing libraries in: $elf" + echo "$ldd_output" | grep "not found" missing=1 fi done - test "$missing" -eq 0 + if [[ "$missing" -ne 0 ]]; then + echo "[smoke] rpm package has unresolved shared libraries" + exit 1 + fi + + echo "[smoke] rpm package passed" ' else usage From 02c30e2c6fde1019313f8f1b058d9711a2e31941 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 23:24:22 -0300 Subject: [PATCH 4/7] fix: set LD_LIBRARY_PATH for deb and rpm smoke tests to ensure library accessibility --- scripts/ci/linux-package-smoke.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/ci/linux-package-smoke.sh b/scripts/ci/linux-package-smoke.sh index 798cd5e..32ed099 100644 --- a/scripts/ci/linux-package-smoke.sh +++ b/scripts/ci/linux-package-smoke.sh @@ -37,6 +37,13 @@ if [[ "$package_type" == "deb" ]]; then fi test -n "$BIN" + APP_DIR=$(dirname "$BIN") + APP_LIB_DIR="$APP_DIR/lib" + if [[ -d "$APP_LIB_DIR" ]]; then + export LD_LIBRARY_PATH="$APP_LIB_DIR:${LD_LIBRARY_PATH:-}" + echo "Using LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + fi + mapfile -t ELF_FILES < <( dpkg -L copypaste | while read -r path; do [[ -f "$path" ]] || continue @@ -78,6 +85,13 @@ elif [[ "$package_type" == "rpm" ]]; then fi test -n "$BIN" + APP_DIR=$(dirname "$BIN") + APP_LIB_DIR="$APP_DIR/lib" + if [[ -d "$APP_LIB_DIR" ]]; then + export LD_LIBRARY_PATH="$APP_LIB_DIR:${LD_LIBRARY_PATH:-}" + echo "Using LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + fi + mapfile -t ELF_FILES < <( rpm -ql copypaste | while read -r path; do [[ -f "$path" ]] || continue From a504361a1893cef5157884af3e7a6349d9a7fc33 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 23:40:04 -0300 Subject: [PATCH 5/7] fix: enhance LD_LIBRARY_PATH setup in smoke tests for better library resolution --- README.md | 1 + scripts/ci/linux-package-smoke.sh | 28 ++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d3be352..bca2f20 100644 --- a/README.md +++ b/README.md @@ -413,6 +413,7 @@ sudo dnf install copypaste > **Note:** Requires an **X11 session**. On Wayland, global hotkey and auto-paste are unavailable — a warning is shown at startup. > **Permissions note:** `apt`/`dnf` installation writes to system locations, so `sudo` is required. If your user cannot use `sudo`, those commands will fail with permission errors. > **No-sudo alternatives:** Use **Homebrew (Linux)** if available for your user, or run the `.AppImage` from your home directory (`chmod +x CopyPaste-*.AppImage && ./CopyPaste-*.AppImage`). +> **Runtime note:** On standard desktop installs, `apt`/`dnf` resolve required libraries automatically. Very minimal VMs/containers may need additional desktop runtime libraries. **Alternative Linux (requires Homebrew installed):** diff --git a/scripts/ci/linux-package-smoke.sh b/scripts/ci/linux-package-smoke.sh index 32ed099..4720833 100644 --- a/scripts/ci/linux-package-smoke.sh +++ b/scripts/ci/linux-package-smoke.sh @@ -37,10 +37,16 @@ if [[ "$package_type" == "deb" ]]; then fi test -n "$BIN" - APP_DIR=$(dirname "$BIN") - APP_LIB_DIR="$APP_DIR/lib" - if [[ -d "$APP_LIB_DIR" ]]; then - export LD_LIBRARY_PATH="$APP_LIB_DIR:${LD_LIBRARY_PATH:-}" + BIN_REAL=$(readlink -f "$BIN" || echo "$BIN") + APP_DIR=$(dirname "$BIN_REAL") + LIB_PATHS=() + for candidate in "$APP_DIR/lib" "/usr/share/copypaste/lib"; do + if [[ -d "$candidate" ]]; then + LIB_PATHS+=("$candidate") + fi + done + if [[ "${#LIB_PATHS[@]}" -gt 0 ]]; then + export LD_LIBRARY_PATH="$(IFS=:; echo "${LIB_PATHS[*]}"):${LD_LIBRARY_PATH:-}" echo "Using LD_LIBRARY_PATH=$LD_LIBRARY_PATH" fi @@ -85,10 +91,16 @@ elif [[ "$package_type" == "rpm" ]]; then fi test -n "$BIN" - APP_DIR=$(dirname "$BIN") - APP_LIB_DIR="$APP_DIR/lib" - if [[ -d "$APP_LIB_DIR" ]]; then - export LD_LIBRARY_PATH="$APP_LIB_DIR:${LD_LIBRARY_PATH:-}" + BIN_REAL=$(readlink -f "$BIN" || echo "$BIN") + APP_DIR=$(dirname "$BIN_REAL") + LIB_PATHS=() + for candidate in "$APP_DIR/lib" "/usr/share/copypaste/lib"; do + if [[ -d "$candidate" ]]; then + LIB_PATHS+=("$candidate") + fi + done + if [[ "${#LIB_PATHS[@]}" -gt 0 ]]; then + export LD_LIBRARY_PATH="$(IFS=:; echo "${LIB_PATHS[*]}"):${LD_LIBRARY_PATH:-}" echo "Using LD_LIBRARY_PATH=$LD_LIBRARY_PATH" fi From 2918784ccfe372e732127b41cc0499a6dc6348b4 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 23:48:54 -0300 Subject: [PATCH 6/7] fix: enhance library dependency checks for Flutter GTK in smoke tests --- scripts/ci/linux-package-smoke.sh | 50 ++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/scripts/ci/linux-package-smoke.sh b/scripts/ci/linux-package-smoke.sh index 4720833..4042209 100644 --- a/scripts/ci/linux-package-smoke.sh +++ b/scripts/ci/linux-package-smoke.sh @@ -50,6 +50,14 @@ if [[ "$package_type" == "deb" ]]; then echo "Using LD_LIBRARY_PATH=$LD_LIBRARY_PATH" fi + FLUTTER_GTK_LIB="" + for candidate in "${LIB_PATHS[@]}"; do + if [[ -f "$candidate/libflutter_linux_gtk.so" ]]; then + FLUTTER_GTK_LIB="$candidate/libflutter_linux_gtk.so" + break + fi + done + mapfile -t ELF_FILES < <( dpkg -L copypaste | while read -r path; do [[ -f "$path" ]] || continue @@ -65,10 +73,23 @@ if [[ "$package_type" == "deb" ]]; then echo "Checking ELF deps: $elf" ldd_output=$(ldd "$elf" 2>&1 || true) echo "$ldd_output" >> /tmp/ldd.out - if echo "$ldd_output" | grep -q "not found"; then + + missing_lines=$(echo "$ldd_output" | grep "not found" || true) + if [[ -n "$missing_lines" ]]; then + filtered_missing="$missing_lines" + if [[ -n "$FLUTTER_GTK_LIB" ]]; then + filtered_missing=$(echo "$filtered_missing" | grep -vE "libflutter_linux_gtk\.so[[:space:]]*=>[[:space:]]*not found" || true) + filtered_missing=$(echo "$filtered_missing" | sed "/^[[:space:]]*$/d" || true) + if [[ -n "$missing_lines" && -z "$filtered_missing" ]]; then + echo "Ignoring plugin-local unresolved libflutter_linux_gtk.so (resolved via bundled runtime at $FLUTTER_GTK_LIB)" + fi + fi + + if [[ -n "$filtered_missing" ]]; then echo "Missing libraries in: $elf" - echo "$ldd_output" | grep "not found" + echo "$filtered_missing" missing=1 + fi fi done @@ -104,6 +125,14 @@ elif [[ "$package_type" == "rpm" ]]; then echo "Using LD_LIBRARY_PATH=$LD_LIBRARY_PATH" fi + FLUTTER_GTK_LIB="" + for candidate in "${LIB_PATHS[@]}"; do + if [[ -f "$candidate/libflutter_linux_gtk.so" ]]; then + FLUTTER_GTK_LIB="$candidate/libflutter_linux_gtk.so" + break + fi + done + mapfile -t ELF_FILES < <( rpm -ql copypaste | while read -r path; do [[ -f "$path" ]] || continue @@ -119,10 +148,23 @@ elif [[ "$package_type" == "rpm" ]]; then echo "Checking ELF deps: $elf" ldd_output=$(ldd "$elf" 2>&1 || true) echo "$ldd_output" >> /tmp/ldd.out - if echo "$ldd_output" | grep -q "not found"; then + + missing_lines=$(echo "$ldd_output" | grep "not found" || true) + if [[ -n "$missing_lines" ]]; then + filtered_missing="$missing_lines" + if [[ -n "$FLUTTER_GTK_LIB" ]]; then + filtered_missing=$(echo "$filtered_missing" | grep -vE "libflutter_linux_gtk\.so[[:space:]]*=>[[:space:]]*not found" || true) + filtered_missing=$(echo "$filtered_missing" | sed "/^[[:space:]]*$/d" || true) + if [[ -n "$missing_lines" && -z "$filtered_missing" ]]; then + echo "Ignoring plugin-local unresolved libflutter_linux_gtk.so (resolved via bundled runtime at $FLUTTER_GTK_LIB)" + fi + fi + + if [[ -n "$filtered_missing" ]]; then echo "Missing libraries in: $elf" - echo "$ldd_output" | grep "not found" + echo "$filtered_missing" missing=1 + fi fi done From b19694186f13944d846a63c21cdb990df365b12f Mon Sep 17 00:00:00 2001 From: rgdevment Date: Fri, 13 Mar 2026 23:56:02 -0300 Subject: [PATCH 7/7] fix: add guardrail for ignored libflutter_linux_gtk.so entries in smoke tests --- scripts/ci/linux-package-smoke.sh | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/scripts/ci/linux-package-smoke.sh b/scripts/ci/linux-package-smoke.sh index 4042209..9783e06 100644 --- a/scripts/ci/linux-package-smoke.sh +++ b/scripts/ci/linux-package-smoke.sh @@ -68,6 +68,8 @@ if [[ "$package_type" == "deb" ]]; then ) test "${#ELF_FILES[@]}" -gt 0 + MAX_IGNORED_FLUTTER_GTK=30 + ignored_flutter_gtk=0 missing=0 for elf in "${ELF_FILES[@]}"; do echo "Checking ELF deps: $elf" @@ -78,6 +80,8 @@ if [[ "$package_type" == "deb" ]]; then if [[ -n "$missing_lines" ]]; then filtered_missing="$missing_lines" if [[ -n "$FLUTTER_GTK_LIB" ]]; then + ignored_in_block=$(echo "$filtered_missing" | grep -Ec "libflutter_linux_gtk\.so[[:space:]]*=>[[:space:]]*not found" || true) + ignored_flutter_gtk=$((ignored_flutter_gtk + ignored_in_block)) filtered_missing=$(echo "$filtered_missing" | grep -vE "libflutter_linux_gtk\.so[[:space:]]*=>[[:space:]]*not found" || true) filtered_missing=$(echo "$filtered_missing" | sed "/^[[:space:]]*$/d" || true) if [[ -n "$missing_lines" && -z "$filtered_missing" ]]; then @@ -86,13 +90,19 @@ if [[ "$package_type" == "deb" ]]; then fi if [[ -n "$filtered_missing" ]]; then - echo "Missing libraries in: $elf" + echo "Missing libraries in: $elf" echo "$filtered_missing" - missing=1 + missing=1 fi fi done + echo "[smoke] deb summary: checked=${#ELF_FILES[@]} ignored_flutter_linux_gtk=$ignored_flutter_gtk missing=$missing" + if [[ "$ignored_flutter_gtk" -gt "$MAX_IGNORED_FLUTTER_GTK" ]]; then + echo "[smoke] deb guardrail failed: too many ignored libflutter_linux_gtk.so entries ($ignored_flutter_gtk > $MAX_IGNORED_FLUTTER_GTK)" + exit 1 + fi + if [[ "$missing" -ne 0 ]]; then echo "[smoke] deb package has unresolved shared libraries" exit 1 @@ -143,6 +153,8 @@ elif [[ "$package_type" == "rpm" ]]; then ) test "${#ELF_FILES[@]}" -gt 0 + MAX_IGNORED_FLUTTER_GTK=30 + ignored_flutter_gtk=0 missing=0 for elf in "${ELF_FILES[@]}"; do echo "Checking ELF deps: $elf" @@ -153,6 +165,8 @@ elif [[ "$package_type" == "rpm" ]]; then if [[ -n "$missing_lines" ]]; then filtered_missing="$missing_lines" if [[ -n "$FLUTTER_GTK_LIB" ]]; then + ignored_in_block=$(echo "$filtered_missing" | grep -Ec "libflutter_linux_gtk\.so[[:space:]]*=>[[:space:]]*not found" || true) + ignored_flutter_gtk=$((ignored_flutter_gtk + ignored_in_block)) filtered_missing=$(echo "$filtered_missing" | grep -vE "libflutter_linux_gtk\.so[[:space:]]*=>[[:space:]]*not found" || true) filtered_missing=$(echo "$filtered_missing" | sed "/^[[:space:]]*$/d" || true) if [[ -n "$missing_lines" && -z "$filtered_missing" ]]; then @@ -161,13 +175,19 @@ elif [[ "$package_type" == "rpm" ]]; then fi if [[ -n "$filtered_missing" ]]; then - echo "Missing libraries in: $elf" + echo "Missing libraries in: $elf" echo "$filtered_missing" - missing=1 + missing=1 fi fi done + echo "[smoke] rpm summary: checked=${#ELF_FILES[@]} ignored_flutter_linux_gtk=$ignored_flutter_gtk missing=$missing" + if [[ "$ignored_flutter_gtk" -gt "$MAX_IGNORED_FLUTTER_GTK" ]]; then + echo "[smoke] rpm guardrail failed: too many ignored libflutter_linux_gtk.so entries ($ignored_flutter_gtk > $MAX_IGNORED_FLUTTER_GTK)" + exit 1 + fi + if [[ "$missing" -ne 0 ]]; then echo "[smoke] rpm package has unresolved shared libraries" exit 1