How to include git ignored file in github actions with flutter?

Issue

In my project I have some JSON files that are used to get environment configurations at runtime through an entrypoint to the main dart file, they hold sensitive data so I put their containing folder in the .gitignore file.

I have written a test that passes when I run it locally, but fails when triggered by the github action because of this error:

The following assertion was thrown running a test:
Unable to load asset: assets/config/dev.json

Is there a way to somehow inject this JSON while performing the action? Any help is much appreciated, my only concern is having private data not stored in the github repository and having the action pass.

This is my code:

  • custom entry point

dev_run.dart

import 'package:gitgo/main.dart' as App;

void main() {
  App.main('dev');
}
  • The class that has access to the JSON:

EnvironmentConfig

import 'dart:convert';
import 'package:flutter/services.dart';

class EnvironmentConfig {
  final Map<String, String> environment;

  EnvironmentConfig({this.environment});

  static Future<EnvironmentConfig> forEnvironment(String env) async {
    env = env ?? 'dev';

    final contents = await rootBundle.loadString(
      'assets/config/$env.json',
    );

    final Map<String, String> json = Map.castFrom(jsonDecode(contents));
    return EnvironmentConfig(environment: json);
  }
}
  • Dependencies

pubspec.yaml

name: gitgo
description: Git on the go.

version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  http: ^0.12.1
  oauth2: ^1.6.1
  url_launcher: ^5.4.10
  flutter_config: ^1.0.8
  gql: ^0.12.3               
  gql_exec: ^0.2.4           
  gql_link: ^0.3.0           
  gql_http_link: ^0.3.2      


dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.11.1     
  gql_build: ^0.0.11         
  pedantic: ^1.9.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/config/

  • Main file

main.dart

import 'package:flutter/material.dart';
import 'package:gitgo/inbound/configuration/config_environment_widget.dart';
import 'package:gitgo/outbound/api/viewer.req.gql.dart';

import 'package:gql_exec/gql_exec.dart';
import 'package:gql_link/gql_link.dart';
import 'package:gitgo/outbound/api/viewer.data.gql.dart';
import 'package:gitgo/outbound/auth.dart';
import 'package:gql_http_link/gql_http_link.dart';

import 'inbound/configuration/environment_config.dart';

Future main(String env) async {
  WidgetsFlutterBinding.ensureInitialized();
  final config = await EnvironmentConfig.forEnvironment(env);
  print("starting app in $env mode");
  runApp(MyApp(config));
}

class MyApp extends StatelessWidget {
  final EnvironmentConfig config;

  MyApp(this.config);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'GitGo Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: EnvironmentConfigWidget(
          config: config, child: MyHomePage(title: 'GitGo Demo Home Page')),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState(title: "GitHub login");
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState({this.title});

  EnvironmentConfigWidget environmentConfigWidget;

  String title;

  @override
  Widget build(BuildContext context) {
    return GithubLoginWidget(
        builder: (context, httpClient) {
          final link = HttpLink(
            'https://api.github.com/graphql',
            httpClient: httpClient,
          );
          return FutureBuilder<$ViewerDetail$viewer>(
            future: viewerDetail(link),
            builder: (context, snapshot) {
              return Scaffold(
                appBar: AppBar(
                  title: Text(title),
                ),
                body: Center(
                  child: Text(
                    snapshot.hasData
                        ? 'Hello ${snapshot.data?.login}!'
                        : 'Retrieving viewer login details...',
                  ),
                ),
              );
            },
          );
        },
        githubClientId: EnvironmentConfigWidget.of(context)
            .config
            .environment['githubClientId'],
        githubClientSecret: EnvironmentConfigWidget.of(context)
            .config
            .environment['githubClientSecret'],
        githubScopes: EnvironmentConfigWidget.of(context)
            .config
            .environment['githubScopes']
            .split(","));
  }
}

Future<$ViewerDetail$viewer> viewerDetail(Link link) async {
  var result = await link.request(ViewerDetail((b) => b)).first;
  if (result.errors != null && result.errors.isNotEmpty) {
    throw QueryException(result.errors);
  }
  return $ViewerDetail(result.data).viewer;
}

class QueryException implements Exception {
  QueryException(this.errors);

  List<GraphQLError> errors;

  @override
  String toString() {
    return 'Query Exception: ${errors.map((err) => '$err').join(',')}';
  }
}

  • Failing test in github actions but not locally

widget_dart.test

import 'package:flutter_test/flutter_test.dart';
import 'package:gitgo/inbound/configuration/environment_config.dart';
import 'package:gitgo/main.dart';

void main() {
  testWidgets('Smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.

    var env = await EnvironmentConfig.forEnvironment('dev');
    await tester.pumpWidget(MyApp(env));

    // Verify that our counter starts at 0.
    expect(find.text('Log into Github'), findsOneWidget);
    expect(find.text('1'), findsNothing);
  });
}
  • Github action

ci.yml

name: ci

jobs:

  ci:
    name: ci
    runs-on: macos-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Flutter test
        uses: subosito/flutter-action@v1.4.0
        with:
          flutter-version: '1.26.0-17.2.pre'
          channel: dev
      - run: flutter pub get
      - run: flutter  analyze
      - run: flutter test --no-sound-null-safety

      - name : Clean merged branches
        run: |
          git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done
          echo "Finished cleaning"
        

Solution

The proposed solution didn’t work for me, but as hacker1024 pointed out:

Put your dev.json contents in a secret (for example, DEV_JSON_CONTENTS), and write it to the correct location with a command.

So what worked in my case, was adding the github secret DEV_JSON with the contents of the original file, then
I modified the workflow file by adding the secret as an env variable in the workflow:

 env:
  DEV_JSON : ${{ secrets.DEV_JSON }}

And I added this step to write the contents to a new file:

 - name: Write dev.json
    run: |
      touch assets/config/dev.json
      echo $DEV_JSON >> assets/config/dev.json
      cat  assets/config/dev.json

Answered By – samlo

Answer Checked By – David Goodson (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *