From f109aa987a018ad9f10d6b96769ebea872cd9dcb Mon Sep 17 00:00:00 2001 From: Serdar Coskun Date: Mon, 1 Jun 2026 23:12:38 +0300 Subject: [PATCH 1/4] Add flutter_lints, analysis_options and CI - Add analysis_options.yaml including package:flutter_lints/flutter.yaml (excludes generated *.g.dart; disables constant_identifier_names since enum values mirror Strava's snake_case wire format). - Add flutter_lints dev dependency. - Fix the 5 lints it surfaced: const field, redundant this., collection literal, leading-underscore local. - Add GitHub Actions CI (.github/workflows/ci.yml): on push/PR to master, pub get, verify generated code is current, analyze (--fatal-infos --fatal-warnings), and test; plus a job analyzing the example app. - Fix stray "gir" typo in README title; add CI badge. Analyzer clean (fatal-infos), tests green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 60 +++++++++++++++++++ README.md | 4 +- analysis_options.yaml | 15 +++++ lib/src/common/local_storage.dart | 2 +- lib/src/common/session_manager.dart | 2 +- lib/src/data/repository/client.dart | 2 +- .../repository_authentication_impl.dart | 4 +- .../model/model_activity_type_enum.dart | 2 +- pubspec.yaml | 1 + 9 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 analysis_options.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..40628dd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + package: + name: Analyze & test (package) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Install dependencies + run: flutter pub get + + - name: Verify generated code is up to date + run: | + dart run build_runner build + if ! git diff --quiet; then + echo "::error::Generated *.g.dart files are out of date. Run: dart run build_runner build" + git --no-pager diff --stat + exit 1 + fi + + - name: Analyze + run: dart analyze --fatal-infos --fatal-warnings + + - name: Test + run: flutter test + + example: + name: Analyze (example) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Create example secret + working-directory: example + run: cp lib/secret.dart.example lib/secret.dart + + - name: Install dependencies + working-directory: example + run: flutter pub get + + - name: Analyze + working-directory: example + run: flutter analyze diff --git a/README.md b/README.md index d80a303..8682dc2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -gir# strava_flutter +# strava_flutter + +[![CI](https://github.com/dreampowder/strava_flutter/actions/workflows/ci.yml/badge.svg)](https://github.com/dreampowder/strava_flutter/actions/workflows/ci.yml) Dart/flutter package to use Strava API v3 diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..8a1f1c2 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + exclude: + # Generated by json_serializable / build_runner. + - "**/*.g.dart" + errors: + # Keep the published API tidy without failing the build on doc nits. + todo: ignore + +linter: + rules: + # The package exposes snake_case enum values that mirror Strava's API + # (e.g. AuthenticationScope.read_all); don't fight the wire format. + constant_identifier_names: false diff --git a/lib/src/common/local_storage.dart b/lib/src/common/local_storage.dart index c0f185c..925dfaf 100644 --- a/lib/src/common/local_storage.dart +++ b/lib/src/common/local_storage.dart @@ -5,7 +5,7 @@ import 'package:strava_client/src/domain/model/model_authentication_response.dar import 'package:strava_client/src/domain/model/model_authentication_scopes.dart'; class LocalStorageManager { - static String _kTokenKey = "strava_token"; + static const String _kTokenKey = "strava_token"; // static String _kScopesKey = "strava_scopes"; static Future saveToken( diff --git a/lib/src/common/session_manager.dart b/lib/src/common/session_manager.dart index f619cba..1fee934 100644 --- a/lib/src/common/session_manager.dart +++ b/lib/src/common/session_manager.dart @@ -27,7 +27,7 @@ class SessionManager { if (_currentToken != null) { completer.complete(_currentToken); } else { - LocalStorageManager.getToken(applicationName: this.applicationName) + LocalStorageManager.getToken(applicationName: applicationName) .then((storedValue) { if (storedValue != null) { _currentToken = storedValue; diff --git a/lib/src/data/repository/client.dart b/lib/src/data/repository/client.dart index 832635c..c5741ad 100644 --- a/lib/src/data/repository/client.dart +++ b/lib/src/data/repository/client.dart @@ -12,7 +12,7 @@ class ApiClient { var dio = Dio(); if (isAuthenticated) { var token = await sl().getToken(); - var headers = Map(); + var headers = {}; if (token != null) { headers.putIfAbsent( "Authorization", () => "Bearer ${token.accessToken}"); diff --git a/lib/src/data/repository/repository_authentication_impl.dart b/lib/src/data/repository/repository_authentication_impl.dart index 7d0e159..2afe244 100644 --- a/lib/src/data/repository/repository_authentication_impl.dart +++ b/lib/src/data/repository/repository_authentication_impl.dart @@ -101,7 +101,7 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { final Completer completer = Completer(); final params = '?client_id=${sl().clientId}&redirect_uri=$redirectUrl&response_type=code&approval_prompt=${forceShowingApproval ? "force" : "auto"}&scope=${AuthenticationScopeHelper.buildScopeString(scopes)}'; - final _appLinks = AppLinks(); + final appLinks = AppLinks(); var host = "https://www.strava.com/"; @@ -114,7 +114,7 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { didLaunchNativeApp = true; host = "strava://"; _uriLinkStream?.cancel(); - _uriLinkStream = _appLinks.uriLinkStream.listen((uri) { + _uriLinkStream = appLinks.uriLinkStream.listen((uri) { final error = uri.queryParameters['error']; final code = uri.queryParameters['code']; if (error != null) { diff --git a/lib/src/domain/model/model_activity_type_enum.dart b/lib/src/domain/model/model_activity_type_enum.dart index 45acfe2..1d5170f 100644 --- a/lib/src/domain/model/model_activity_type_enum.dart +++ b/lib/src/domain/model/model_activity_type_enum.dart @@ -53,6 +53,6 @@ extension ActivityTypeEnumHelper on ActivityTypeEnum { } String stringValue() { - return this.toString().split(".").last; + return toString().split(".").last; } } diff --git a/pubspec.yaml b/pubspec.yaml index ac7964e..943610b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dev_dependencies: sdk: flutter build_runner: ^2.15.0 json_serializable: ^6.14.0 + flutter_lints: ^5.0.0 # test: 1.5.3 # To launch some tests in /test # For information on the generic Dart part of this file, see the From defb690bf5d4e790857d3260bc22081ed19e1cf6 Mon Sep 17 00:00:00 2001 From: Serdar Coskun Date: Mon, 1 Jun 2026 23:20:04 +0300 Subject: [PATCH 2/4] Exclude example/ from package analysis The package's root `dart analyze` recursed into the standalone example app, failing CI on the (git-ignored) secret.dart. The example has its own analysis_options and CI job, so exclude example/** from the package analyzer. Co-Authored-By: Claude Opus 4.8 (1M context) --- analysis_options.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/analysis_options.yaml b/analysis_options.yaml index 8a1f1c2..9cc8b3f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,6 +4,8 @@ analyzer: exclude: # Generated by json_serializable / build_runner. - "**/*.g.dart" + # The example is a standalone package with its own analysis + CI job. + - "example/**" errors: # Keep the published API tidy without failing the build on doc nits. todo: ignore From 430d4227e52659b9a09097bada5ee8bb1223525d Mon Sep 17 00:00:00 2001 From: Serdar Coskun Date: Mon, 1 Jun 2026 23:21:22 +0300 Subject: [PATCH 3/4] Re-apply collection-literal lint fix lost in master merge The master merge reverted client.dart's header map back to a constructor invocation; restore the collection literal so analyze --fatal-infos passes. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/src/data/repository/client.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/data/repository/client.dart b/lib/src/data/repository/client.dart index 213357a..ce5dfa2 100644 --- a/lib/src/data/repository/client.dart +++ b/lib/src/data/repository/client.dart @@ -12,7 +12,7 @@ class ApiClient { var dio = Dio(); if (isAuthenticated) { var token = await sl().getValidToken(); - var headers = Map(); + var headers = {}; if (token != null) { headers.putIfAbsent( "Authorization", () => "Bearer ${token.accessToken}"); From 09723636a8de30a597fd417ab85ccc3859959644 Mon Sep 17 00:00:00 2001 From: Serdar Coskun Date: Mon, 1 Jun 2026 23:23:54 +0300 Subject: [PATCH 4/4] Bump actions/checkout to v5 (Node 24) actions/checkout@v4 runs on the deprecated Node 20 runtime. v5 uses Node 24. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40628dd..2b17e34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: name: Analyze & test (package) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: subosito/flutter-action@v2 with: @@ -40,7 +40,7 @@ jobs: name: Analyze (example) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: subosito/flutter-action@v2 with: