samlo
samlo

Reputation: 113

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

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:

dev_run.dart

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

void main() {
  App.main('dev');
}

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);
  }
}

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.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(',')}';
  }
}

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);
  });
}

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/[email protected]
        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"
        

Upvotes: 1

Views: 1388

Answers (2)

samlo
samlo

Reputation: 113

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

Upvotes: 2

hacker1024
hacker1024

Reputation: 3668

You can use GitHub Action secrets.

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

- name: Write dev.json
  run: echo '{{ secrets.DEV_JSON_CONTENTS }}' > assets/config/dev.json

Upvotes: 3

Related Questions