Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
295 changes: 31 additions & 264 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,264 +1,31 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write

jobs:
markdown:
runs-on: ubuntu-latest
name: Markdown Lint

steps:
- uses: actions/checkout@v7

- name: Lint Markdown files
uses: DavidAnson/markdownlint-cli2-action@v19
with:
globs: |
*.md
.github/**/*.md
config: ".markdownlint.yaml"

quality:
runs-on: ubuntu-latest
name: Lint, Format & Test

steps:
- uses: actions/checkout@v7

- uses: ./.github/actions/setup-flutter-workspace

- name: Check formatting
run: dart format --output=none --set-exit-if-changed .

- name: Analyze
run: melos run 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: melos run outdated
continue-on-error: true

- name: Run tests
run: melos run test:coverage

- name: Upload coverage artifacts
uses: actions/upload-artifact@v7
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@v5
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@v7

- 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 "OK $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; do
if [ ! -f ".github/workflows/$wf" ]; then
echo "::error::Missing .github/workflows/$wf (referenced by release.yml)"
errors=$((errors + 1))
else
echo "OK .github/workflows/$wf"
fi
done
for act in setup-flutter-workspace; do
if [ ! -f ".github/actions/$act/action.yml" ]; then
echo "::error::Missing .github/actions/$act/action.yml (referenced by the build jobs)"
errors=$((errors + 1))
else
echo "OK .github/actions/$act/action.yml"
fi
done
[ $errors -eq 0 ] || exit 1

- name: Validate project structure
run: |
errors=0
for dir in app/lib app/windows app/macos app/assets listener/windows listener/macos; do
if [ ! -d "$dir" ]; then
echo "::error::Missing required directory: $dir"
errors=$((errors + 1))
else
echo "OK $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 "OK $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]}"
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Tag $tag produces invalid version: $VERSION"
exit 1
fi
echo "OK $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: Test & Build (Windows)

steps:
- uses: actions/checkout@v7

- uses: ./.github/actions/setup-flutter-workspace

- name: Run tests
shell: bash
run: dart pub global run melos:melos run test:coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: core/coverage/lcov.info,listener/coverage/lcov.info,app/coverage/lcov.info
flags: windows
fail_ci_if_error: false

- name: Build Windows release
run: cd app; flutter build windows --release

build-macos:
needs: quality
runs-on: macos-latest
name: Test & Build (macOS)

steps:
- uses: actions/checkout@v7

- uses: ./.github/actions/setup-flutter-workspace

- name: Run tests
shell: bash
run: dart pub global run melos:melos run test:coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: core/coverage/lcov.info,listener/coverage/lcov.info,app/coverage/lcov.info
flags: macos
fail_ci_if_error: false

- 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@v7
with:
fetch-depth: 0

- name: Download coverage reports
uses: actions/download-artifact@v7
with:
name: coverage-reports
path: .

- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-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
-Dsonar.qualitygate.wait=true
-Dsonar.qualitygate.timeout=300
name: CI

on:
push:
branches: [main]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Format, lints and tests
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --all -- --check
- run: cargo clippy --workspace --all-targets -- -D warnings
- run: cargo test --workspace

deny:
name: Dependency audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
24 changes: 24 additions & 0 deletions .github/workflows/commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Commits

on:
pull_request:

jobs:
conventional:
name: Conventional commits
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Every commit follows the convention
run: |
pattern='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9-]+\))?!?: .{1,72}$'
fail=0
while read -r sha; do
subject=$(git log -1 --format=%s "$sha")
if ! printf '%s' "$subject" | grep -qE "$pattern"; then
echo "no sigue la convencion: $subject"; fail=1
fi
done < <(git rev-list origin/${{ github.base_ref }}..HEAD)
exit $fail
Loading
Loading