├── assets
├── icon.png
├── favicon.png
└── splash.png
├── babel.config.js
├── README.md
├── .expo-shared
└── assets.json
├── .gitignore
├── tsconfig.json
├── jest.config.js
├── test
└── App.test.tsx
├── app.config.js
├── App.tsx
├── .eslintrc.js
├── app.json
├── .github
└── workflows
│ ├── multi_environment_test.yml
│ ├── deploy_staging.yml
│ ├── testing.yml
│ └── deploy_prod.yml
└── package.json
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justussoh/github-actions-expo-boiler-template/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justussoh/github-actions-expo-boiler-template/HEAD/assets/favicon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justussoh/github-actions-expo-boiler-template/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Expo working together with GitHub Actions starter
2 |
3 | This is the example explained in a dev.to post, more details will be available soon.
--------------------------------------------------------------------------------
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 |
12 | # macOS
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "jsx": "react-native",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "noEmit": true,
8 | "skipLibCheck": true,
9 | "resolveJsonModule": true,
10 | "strict": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | verbose: true,
3 | preset: "react-native",
4 | transformIgnorePatterns: [
5 | "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
6 | ]
7 | };
--------------------------------------------------------------------------------
/test/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Text } from "react-native";
3 | import { render } from "@testing-library/react-native";
4 |
5 | describe('example test', () => {
6 | it('get by text', () => {
7 | const { getByText } = render(Hello World);
8 | expect(getByText("Hello World")).not.toBeNull();
9 | });
10 | });
--------------------------------------------------------------------------------
/app.config.js:
--------------------------------------------------------------------------------
1 | export default ({ config }) => {
2 | return {
3 | ...config,
4 | version: process.env.APP_BINARY_VERSION || "1.0.0",
5 | android: {
6 | ...config.android,
7 | versionCode: parseInt(process.env.APP_BUILD_VERSION || 1)
8 | },
9 | ios: {
10 | ...config.ios,
11 | buildNumber: process.env.APP_BUILD_VERSION || "1"
12 | }
13 | };
14 | };
--------------------------------------------------------------------------------
/App.tsx:
--------------------------------------------------------------------------------
1 | import { StatusBar } from 'expo-status-bar';
2 | import React from 'react';
3 | import { StyleSheet, Text, View } from 'react-native';
4 |
5 | export default function App() {
6 | return (
7 |
8 | Hello World
9 |
10 |
11 | );
12 | }
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | flex: 1,
17 | backgroundColor: '#fff',
18 | alignItems: 'center',
19 | justifyContent: 'center',
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es2020": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "universe/native",
10 | "plugin:@typescript-eslint/recommended"
11 | ],
12 | "parser": "@typescript-eslint/parser",
13 | "parserOptions": {
14 | "ecmaVersion": 2018,
15 | "sourceType": "module"
16 | },
17 | "plugins": [
18 | "@typescript-eslint"
19 | ],
20 | "rules": {
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "github-actions-expo-boiler-template",
4 | "slug": "github-actions-expo-boiler-template",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "splash": {
9 | "image": "./assets/splash.png",
10 | "resizeMode": "contain",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "updates": {
14 | "fallbackToCacheTimeout": 0
15 | },
16 | "assetBundlePatterns": ["**/*"],
17 | "android": {
18 | "package": "me.jszh.expo.example"
19 | },
20 | "ios": {
21 | "supportsTablet": true
22 | },
23 | "web": {
24 | "favicon": "./assets/favicon.png"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.github/workflows/multi_environment_test.yml:
--------------------------------------------------------------------------------
1 | name: Test Different Development Environments
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | test:
7 | name: Lint & Test
8 | runs-on: ${{ matrix.os }}
9 | strategy:
10 | matrix:
11 | os: [ubuntu-latest, macOS-latest, windows-latest]
12 | node: [10, 12, 13]
13 | steps:
14 | - uses: actions/checkout@v2
15 | - uses: actions/setup-node@v1
16 | with:
17 | node-version: ${{ matrix.node }}
18 | - name: Cache Node Modules
19 | uses: actions/cache@v2
20 | env:
21 | cache-name: cache-node-modules
22 | with:
23 | # npm cache files are stored in `~/.npm` on Linux/macOS
24 | path: ~/.npm
25 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
26 | restore-keys: |
27 | ${{ runner.os }}-build-${{ env.cache-name }}-
28 | ${{ runner.os }}-build-
29 | ${{ runner.os }}-
30 | - name: Install Packages
31 | run: npm install
32 | - name: Typecheck
33 | run: npx --no-install tsc --noEmit
34 | - name: Check Lint
35 | run: npm run lint
36 | - name: Test
37 | run: npm run test
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject",
9 | "lint": "eslint --ext .js,.ts,.tsx,.mdx --max-warnings 0",
10 | "lint:fix": "npm run lint -- --fix",
11 | "test": "jest"
12 | },
13 | "dependencies": {
14 | "expo": "~38.0.8",
15 | "expo-status-bar": "^1.0.2",
16 | "react": "~16.11.0",
17 | "react-dom": "~16.11.0",
18 | "react-native": "https://github.com/expo/react-native/archive/sdk-38.0.2.tar.gz",
19 | "react-native-web": "~0.11.7"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.8.6",
23 | "@testing-library/react-native": "^7.0.2",
24 | "@types/jest": "^26.0.10",
25 | "@types/node": "^14.6.0",
26 | "@types/react": "~16.9.41",
27 | "@types/react-native": "~0.62.13",
28 | "@types/react-test-renderer": "^16.9.3",
29 | "@typescript-eslint/eslint-plugin": "^3.9.1",
30 | "@typescript-eslint/parser": "^3.9.1",
31 | "eslint": "^7.7.0",
32 | "eslint-config-universe": "^4.0.0",
33 | "jest": "^26.4.0",
34 | "react-test-renderer": "^16.13.1",
35 | "typescript": "~3.9.5"
36 | },
37 | "private": true
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/deploy_staging.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Staging
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | test:
10 | name: Lint & Test
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: 12.x
17 | - name: Cache Node Modules
18 | uses: actions/cache@v2
19 | env:
20 | cache-name: cache-node-modules
21 | with:
22 | path: ~/.npm
23 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
24 | restore-keys: |
25 | ${{ runner.os }}-build-${{ env.cache-name }}-
26 | ${{ runner.os }}-build-
27 | ${{ runner.os }}-
28 | - name: Install Packages
29 | run: npm install
30 | - name: Typecheck
31 | run: npx --no-install tsc --noEmit
32 | - name: Check Lint
33 | run: npm run lint
34 | - name: Test
35 | run: npm run test
36 | deploy_staging:
37 | name: Deploy to Staging
38 | needs: test
39 | runs-on: ubuntu-latest
40 | steps:
41 | - uses: actions/checkout@v2
42 | - uses: actions/setup-node@v1
43 | with:
44 | node-version: 12.x
45 | - uses: expo/expo-github-action@v5
46 | with:
47 | expo-packager: npm
48 | expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
49 | expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
50 | expo-cache: true
51 | - name: Install Packages
52 | run: npm install
53 | - name: Expo Publish Channel
54 | run: expo publish --non-interactive --release-channel staging
55 |
--------------------------------------------------------------------------------
/.github/workflows/testing.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Branch Preview
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | test:
7 | name: Lint & Test
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - uses: actions/setup-node@v1
12 | with:
13 | node-version: 12.x
14 | - name: Cache Node Modules
15 | uses: actions/cache@v2
16 | env:
17 | cache-name: cache-node-modules
18 | with:
19 | # npm cache files are stored in `~/.npm` on Linux/macOS
20 | path: ~/.npm
21 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
22 | restore-keys: |
23 | ${{ runner.os }}-build-${{ env.cache-name }}-
24 | ${{ runner.os }}-build-
25 | ${{ runner.os }}-
26 | - name: Install Packages
27 | run: npm install
28 | # Perform a type check if using typescript before testing
29 | - name: Typecheck
30 | run: npx --no-install tsc --noEmit
31 | - name: Check Lint
32 | run: npm run lint
33 | - name: Test
34 | run: npm run test
35 | deploy_branch_preview:
36 | name: Deploy Branch Preview
37 | needs: test
38 | runs-on: ubuntu-latest
39 | steps:
40 | - uses: actions/checkout@v2
41 | - uses: actions/setup-node@v1
42 | with:
43 | node-version: 12.x
44 | - uses: expo/expo-github-action@v5
45 | with:
46 | expo-packager: npm
47 | expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
48 | expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
49 | expo-cache: true
50 | - name: Cache Node Modules
51 | uses: actions/cache@v2
52 | env:
53 | cache-name: cache-node-modules
54 | with:
55 | path: ~/.npm
56 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
57 | restore-keys: |
58 | ${{ runner.os }}-build-${{ env.cache-name }}-
59 | ${{ runner.os }}-build-
60 | ${{ runner.os }}-
61 | - name: Install Packages
62 | run: npm install
63 | - name: Expo Publish Channel
64 | run: expo publish --non-interactive --release-channel pr${{ github.event.number }}
65 | - name: Add Comment To PR
66 | uses: mshick/add-pr-comment@v1
67 | env:
68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69 | EXPO_PROJECT: "@justussoh/github-actions-expo-boiler-template" # Put in your own Expo project name here
70 | with:
71 | message: |
72 | ## Application
73 | 
74 | Published to https://exp.host/${{ env.EXPO_PROJECT }}?release-channel=pr${{ github.event.number }}
--------------------------------------------------------------------------------
/.github/workflows/deploy_prod.yml:
--------------------------------------------------------------------------------
1 | name: Create Release
2 | on:
3 | push:
4 | tags:
5 | - "prod-[1-9]+.[0-9]+.[0-9]+" # Push events to matching prod-*, i.e.prod-20.15.10
6 |
7 | jobs:
8 | test:
9 | name: Lint & Test
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: actions/setup-node@v1
14 | with:
15 | node-version: 12.x
16 | - name: Install Packages
17 | run: npm install
18 | - name: Typecheck
19 | run: npx --no-install tsc --noEmit
20 | - name: Check Lint
21 | run: npm run lint
22 | - name: Test
23 | run: npm run test
24 | deploy_prod:
25 | name: Deploy To Production
26 | needs: test
27 | runs-on: ubuntu-latest
28 | outputs:
29 | releaseChannel: ${{ steps.releaseChannel.outputs.releaseChannel }}
30 | latestBinaryVersion: ${{ steps.latestBinaryVersion.outputs.version }}
31 | steps:
32 | - uses: actions/checkout@v2
33 | with:
34 | ref: ${{ github.head_ref }}
35 | - name: Fetch Tags
36 | run: |
37 | git fetch --prune --unshallow --tags -f
38 | - uses: actions/setup-node@v1
39 | with:
40 | node-version: 12.x
41 | - uses: expo/expo-github-action@v5
42 | with:
43 | expo-packager: npm
44 | expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
45 | expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
46 | expo-cache: true
47 | - uses: rlespinasse/github-slug-action@v2.x
48 | - name: Generate Release Channel # Release Channels are named prod-, i.e. prod-1, prod-3
49 | id: releaseChannel
50 | run: |
51 | RELEASE_CHANNEL=$(echo ${{ env.GITHUB_REF_SLUG }} | sed -r 's/\.[0-9]+\.[0-9]+$//')
52 | echo "::set-output name=releaseChannel::$RELEASE_CHANNEL"
53 | - name: Install Packages
54 | run: npm install
55 | - name: Get Latest Binary Version # Binary Version will be x.x.x based on the latest tag
56 | id: latestBinaryVersion
57 | run: |
58 | # Release tag finds the lastest tag in the tree branch - i.e. prod-x.x.x
59 | RELEASE_TAG=$(echo $(git describe --tags --abbrev=0))
60 | # Using param substitution, we output x.x.x instead
61 | echo "::set-output name=version::${RELEASE_TAG#*-}"
62 | - name: Echo Version Details
63 | run: |
64 | echo Build number is $GITHUB_RUN_NUMBER
65 | echo Latest release is ${{ steps.latestBinaryVersion.outputs.version }}
66 | - name: Expo Publish Channel
67 | run: expo publish --non-interactive --release-channel=${{ steps.releaseChannel.outputs.releaseChannel }}
68 | create_release:
69 | name: Create Release
70 | needs: deploy_prod
71 | runs-on: ubuntu-latest
72 | steps:
73 | - uses: actions/checkout@v2
74 | - uses: rlespinasse/github-slug-action@v2.x
75 | - name: Generate Changelog
76 | id: changelog
77 | uses: metcalfc/changelog-generator@v0.4.0
78 | with:
79 | myToken: ${{ secrets.GITHUB_TOKEN }}
80 | base-ref: 'prod-0'
81 | - name: Creating Release
82 | uses: ncipollo/release-action@v1
83 | with:
84 | body: |
85 | Changes in this Release:
86 | ${{ steps.changelog.outputs.changelog }}
87 | token: ${{ secrets.GITHUB_TOKEN }}
88 | name: Release ${{ env.GITHUB_REF_SLUG }}
89 | allowUpdates: true
90 | build_android:
91 | needs: [deploy_prod, create_release]
92 | runs-on: ubuntu-latest
93 | steps:
94 | - uses: actions/checkout@v2
95 | - uses: rlespinasse/github-slug-action@v2.x
96 | - uses: expo/expo-github-action@v5
97 | with:
98 | expo-packager: npm
99 | expo-username: ${{ secrets.EXPO_CLI_USERNAME }}
100 | expo-password: ${{ secrets.EXPO_CLI_PASSWORD }}
101 | expo-cache: true
102 | - name: Install Packages
103 | run: npm install
104 | - name: Build Android Release
105 | env:
106 | APP_BUILD_VERSION: ${{ github.run_number }}
107 | APP_BINARY_VERSION: ${{ needs.deploy_prod.outputs.latestBinaryVersion }}
108 | run: |
109 | expo build:android --release-channel=${{ needs.deploy_prod.outputs.releaseChannel }} > buildLogAndroid.txt
110 | cat buildLogAndroid.txt
111 | - name: Parse Asset URL
112 | id: androidUrl
113 | run: |
114 | ASSET_URL=$(cat buildLogAndroid.txt | tail | egrep -o 'https?://expo\.io/artifacts/[^ ]+')
115 | echo The android url is $ASSET_URL
116 | echo "::set-output name=assetUrl::$ASSET_URL"
117 | - name: Download APK Asset
118 | run: wget -O example-${{ env.GITHUB_REF_SLUG }}.apk ${{ steps.androidUrl.outputs.assetUrl }}
119 | - name: Upload Release Asset
120 | uses: svenstaro/upload-release-action@v2
121 | with:
122 | repo_token: ${{ secrets.GITHUB_TOKEN }}
123 | file: ./example-${{ env.GITHUB_REF_SLUG }}.apk
124 | asset_name: example-${{ env.GITHUB_REF_SLUG }}.apk
125 | tag: ${{ github.ref }}
--------------------------------------------------------------------------------