Blogs
CI/CD, or Continuous Integration and Continuous Delivery, is a pivotal set of practices ingrained in modern software development workflows. The fundamental objective is to streamline and automate the entire software delivery pipeline, from source code integration to production deployment.
As seasoned developers, we comprehend the significance of seamlessly integrating code changes. CI involves the frequent and automatic integration of code modifications from diverse contributors into a shared version control repository. This triggers automated builds and tests, offering swift feedback on the integration status. The aim is to promptly identify and rectify integration issues, ensuring a perpetually stable codebase.
Moving beyond CI, Continuous Delivery concentrates on automating the comprehensive release process up to the production environment. Following successful integration and testing, the software undergoes an automated preparation phase for release. The release artifacts are meticulously assembled, ready for deployment. Notably, this phase stops short of automatic deployment to production, allowing for manual intervention to align with specific release strategies and compliance requirements.
Continuous Integration (CI):
Continuous Delivery (CD):
A version control system helps manage and track changes to your source code over time. It allows multiple developers to work collaboratively on a project, providing a structured way to organize, save, and retrieve different versions of the codebase. Git is one of the most widely used version control systems.
In the context of CI/CD, a version control system is crucial because it provides a structured and organized way to manage code changes. CI/CD systems can monitor the repository for changes and trigger automated workflows (like building and testing) based on those changes.
Integrating Continuous Integration (CI) tools, such as Jenkins, Travis CI, or GitHub Actions, is crucial for automating the build and testing processes in software development. These tools monitor version control repositories for code changes and automatically trigger predefined workflows upon detecting modifications. Automated builds ensure that the code is compiled and packaged consistently, while automated tests verify its correctness. This integration minimizes manual intervention, accelerates development cycles, and enhances code quality by swiftly identifying and addressing issues during the early stages of development.
GitHub Actions is a feature of GitHub that enables you to automate workflows directly within your repository. It allows you to define custom CI/CD processes, automate tasks, and respond to events such as code pushes, pull requests, and more.
Create a React Native project with latest version of react native.
1npx react-native@latest init article_CICD
Install required packages:
1npm install --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
Changes in babel.config.js
1module.exports = {
2 presets: ['module:@react-native/babel-preset'],
3};
Changes in package.json
1 "scripts": {
2 "android": "react-native run-android",
3 "ios": "react-native run-ios",
4 "lint": "eslint .",
5 "start": "react-native start",
6 "test": "jest"
7 },
Make a simple Login Screen for testing:
We will just do the code in our main App.tsx file.
App.tsx
1/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
2
3import React, {useState} from 'react';
4import {
5 Linking,
6 StyleSheet,
7 Text,
8 TextInput,
9 TouchableOpacity,
10 View,
11} from 'react-native';
12
13const WelcomeScreen = () => {
14 return (
15 <View style={styles.view}>
16 <Text style={styles.welcomeText}>
17 Welcome to{' '}
18 <Text
19 style={{color: 'rgb(171, 91, 85)', textDecorationLine: 'underline'}}
20 onPress={() => Linking.openURL('https://code-b.dev')}>
21 CODEB
22 </Text>
23 </Text>
24 </View>
25 );
26};
27
28const LoginScreen = (): React.JSX.Element => {
29 const [username, setUsername] = useState('');
30 const [password, setPassword] = useState('');
31 const [message, setMessage] = useState('');
32 const handleSubmitPress = () => {
33 if (username === '' || password === '') {
34 setMessage('Please provide all values');
35 } else {
36 if (username === 'codeb@gmail.com' && password === 'Password@1234') {
37 setMessage('SUCCESS');
38 } else {
39 setMessage('INCORRECT CREDENTIAL');
40 }
41 }
42 };
43 return (
44 <View>
45 <TextInput
46 placeholder="Enter username"
47 autoCapitalize="none"
48 id="username"
49 keyboardType="email-address"
50 onChangeText={setUsername}
51 />
52 <TextInput
53 id="password"
54 placeholder="Enter Password"
55 autoCapitalize="none"
56 keyboardType="default"
57 onChangeText={setPassword}
58 />
59 <TouchableOpacity activeOpacity={0.5} onPress={handleSubmitPress}>
60 <Text>LOGIN</Text>
61 </TouchableOpacity>
62 {message === 'SUCCESS' ? (
63 <Text>
64 <WelcomeScreen />{' '}
65 </Text>
66 ) : (
67 <View>
68 <Text>{message}</Text>
69 </View>
70 )}
71 </View>
72 );
73};
74
75function App(): React.JSX.Element {
76 return <LoginScreen />;
77}
78
79const styles = StyleSheet.create({
80 view: {
81 flex: 1,
82 width: 'auto',
83 display: 'flex',
84 justifyContent: 'center',
85 alignItems: 'center',
86 },
87 welcomeText: {
88 fontSize: 28,
89 },
90});
91
92export default App;
Create Test functions:
Create a folder name __tests__
, create a new App.test.js
inside this folder.
App.test.js
1// App.test.js or App.test.tsx
2
3import React from 'react';
4import {render, fireEvent} from '@testing-library/react-native';
5import App from '../App';
6
7describe('App', () => {
8 it('renders LoginScreen initially', () => {
9 const {getByPlaceholderText, getByText} = render(<App />);
10
11 // Ensure login elements are present
12 expect(getByPlaceholderText('Enter username')).toBeTruthy();
13 expect(getByPlaceholderText('Enter Password')).toBeTruthy();
14 expect(getByText('LOGIN')).toBeTruthy();
15 });
16
17 it('displays an error message for empty login', () => {
18 const {getByText} = render(<App />);
19
20 // Trigger login without entering values
21 fireEvent.press(getByText('LOGIN'));
22
23 // Ensure error message is displayed
24 expect(getByText('Please provide all values')).toBeTruthy();
25 });
26
27 it('displays a success message for correct login', () => {
28 const {getByPlaceholderText, getByText} = render(<App />);
29
30 // Enter correct username and password
31 fireEvent.changeText(
32 getByPlaceholderText('Enter username'),
33 'codeb@gmail.com',
34 );
35 fireEvent.changeText(
36 getByPlaceholderText('Enter Password'),
37 'Password@1234',
38 );
39
40 // Trigger login
41 fireEvent.press(getByText('LOGIN'));
42
43 // Ensure success message is displayed
44 expect(getByText('CODEB')).toBeTruthy();
45 });
46
47 it('displays an error message for incorrect login', () => {
48 const {getByPlaceholderText, getByText} = render(<App />);
49
50 // Enter incorrect username and password
51 fireEvent.changeText(
52 getByPlaceholderText('Enter username'),
53 'incorrect@gmail.com',
54 );
55 fireEvent.changeText(
56 getByPlaceholderText('Enter Password'),
57 'IncorrectPassword',
58 );
59
60 // Trigger login
61 fireEvent.press(getByText('LOGIN'));
62
63 // Ensure error message is displayed
64 expect(getByText('INCORRECT CREDENTIAL')).toBeTruthy();
65 });
66});
1. Initial Rendering Test:
App
component initially renders the login screen.2. Empty Login Test:
3. Successful Login Test:
fireEvent.changeText
function to update the input fields, triggers a login button press, and then checks if the expected success message is displayed.4. Incorrect Login Test:
Running a test:
Run npm test
to run jest testing of created test.
1> articleCICD@0.0.1 test
2> jest
3
4 PASS __tests__/App.test.js (9.081 s)
5 App
6 √ renders LoginScreen initially (5803 ms)
7 √ displays an error message for empty login (13 ms)
8 √ displays a success message for correct login (11 ms)
9 √ displays an error message for incorrect login (7 ms)
10
11Test Suites: 1 passed, 1 total
12Tests: 4 passed, 4 total
13Snapshots: 0 total
14Time: 15.238 s
15Ran all test suites.
We can also do the e2e testing by using detox. Detox is a gray-box end-to-end testing framework for React Native applications. It is specifically designed for mobile app testing and focuses on simulating user interactions and testing the application's behavior in a real-world environment. Detox allows developers to write and execute tests that interact with the UI components of a React Native app, performing actions such as tapping buttons, entering text, and navigating between screens. By running these tests on simulator/emulator or real devices, Detox helps ensure the reliability and functionality of the app across different platforms and devices. It also provides features for asynchronous testing and handling complex scenarios, making it a powerful tool for maintaining the quality and stability of React Native applications.
To learn more about detox testing in react native, visit https://wix.github.io/Detox/docs/introduction/getting-started
CI (Continuous Integeration) testing stage in Github Actions:
Create .github/workflows
directory at root directory of project. create main.yml
file for writing github actions command.
1name: CI
2
3on:
4 push:
5 branches:
6 - master
7
8jobs:
9 build:
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Checkout Repository
14 uses: actions/checkout@v2
15
16 - name: Setup Node.js
17 uses: actions/setup-node@v2
18 with:
19 node-version: 18.19.0
20
21 - name: Install dependencies
22 run: npm install
23
24 - name: Run tests
25 run: npm test
This is a GitHub Actions workflow named "CI" triggered on every push to the "master" branch. The workflow runs on an Ubuntu environment and consists of four steps:
actions/checkout
action.actions/setup-node
action.npm install
command.npm test
command. This workflow is designed for continuous integration, automatically building and testing the project whenever changes are pushed to the master branch. The workflow helps ensure code quality and catches potential issues early in the development process.CD (Continuous Delivery) :
Add following jobs in main.yml
1 build_android:
2 runs-on: ubuntu-latest
3
4 needs: build
5
6 steps:
7 - name: Checkout Repository
8 uses: actions/checkout@v2
9
10 - name: Setup Java 17
11 uses: actions/setup-java@v2
12 with:
13 distribution: 'temurin'
14 java-version: '17'
15
16 - name: Setup Node.js
17 uses: actions/setup-node@v2
18 with:
19 node-version: 18.19.0
20
21 - name: Navigate to Project Root
22 run: cd $GITHUB_WORKSPACE
23
24 - name: Install Dependencies
25 run: npm install
26
27 - name: Build Android App Bundle
28 run: |
29 mkdir -p android/app/build/intermediates/assets/release/
30 npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/build/intermediates/assets/release/index.android.bundle --assets-dest android/app/build/intermediates/res/merged/release
31
32 - name: Build Android Release
33 run: |
34 cd android
35 chmod +x gradlew
36 ./gradlew clean
37 ./gradlew bundleRelease -Pkeystore.password=${{ secrets.KEYSTORE_PASSWORD }}
38
39 download_bundle:
40 runs-on: ubuntu-latest
41
42 needs: build_android
43
44 steps:
45 - name: Download Android App Bundle
46 run: |
47 mkdir -p $GITHUB_WORKSPACE/dist
48 cp $GITHUB_WORKSPACE/android/app/build/outputs/bundle/release/app-release.aab $GITHUB_WORKSPACE/dist
49
50 - name: Archive Bundle
51 uses: actions/upload-artifact@v2
52 with:
53 name: android-bundle
54 path: $GITHUB_WORKSPACE/dist
1. build_android
Job:
i. Checks out the repository.
ii. Sets up Java 17 and Node.js 18.19.0.
iii. Navigates to the project root directory.
iv. Installs project dependencies.
v. Builds the Android App Bundle:
a. Creates necessary directories.
b .Uses npx react-native bundle
to bundle the JavaScript code for Android.
c. Sets up the Android project using Gradle and builds the release version, applying a keystore password from GitHub secrets.
2. download_bundle
Job:
i. Downloads the Android App Bundle from the build output and saves it to the dist
directory.
ii. Archives the downloaded bundle using the actions/upload-artifact
action, making it available for further deployment or distribution.
Before running this workflow make sure you have added secrets variable in your GitHub repository.
We are triggering above CICD pipeline on branch master whenever someone push the code. we can make it for PR (pull request) whenever someone raise the PR on master branch.
This streamlined CI/CD pipeline triggers on master branch pushes, featuring three jobs: build
, build_android
, and download_bundle
. The build
job ensures code quality through Node.js tests. The subsequent build_android
job, dependent on the build
completion, orchestrates Java 17 and Node.js setup, installs dependencies, and builds the Android App Bundle. It safeguards sensitive data using GitHub secrets. Finally, the download_bundle
job, contingent on build_android
, archives the generated bundle for potential deployments. This succinct pipeline automates end-to-end processes, enhancing the development workflow for React Native Android apps.