Skip to content
This repository has been archived by the owner on Jul 3, 2024. It is now read-only.

Commit

Permalink
feat: add a check if a new build is required to open generated preview (
Browse files Browse the repository at this point in the history
#2)

Added a check if a new build is required to open generated preview.
  • Loading branch information
lukmccall authored Jun 29, 2021
1 parent 629f992 commit c7828a4
Show file tree
Hide file tree
Showing 9 changed files with 9,036 additions and 7 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ After adding this action to your workflow, it will publish your project using `e

- `EXPO_MANIFEST_URL` - A URL pointing to the expo manifest of the published version.
- `EXPO_QR_CODE_URL` - A URL pointing to the generated QR code which can be scanned using Expo Go or custom development client.
- `EXPO_NEW_BUILD_IS_REQUIRED` - Whether a new build of your application is required to open generated preview.
- `EXPO_NEW_BUILD_IS_REQUIRED_MESSAGE` - If a new build of your application is required to open generated preview, it will contain a default warning message.

You can use those variables to do whatever you want. For example, you can chain this action with [unsplash/comment-on-pr](https://github.com/unsplash/comment-on-pr) to add a comment with QR code under pull request.

Expand All @@ -57,7 +59,7 @@ This action is customizable through variables - they are defined in the [action.
| `project-root` || The path to the folder where package.json lives. Defaults to main directory of the repository. |
| `expo-cli-path` || The path to the `expo-cli`. If you're using the `expo-github-action` or `expo-cli` was installed in the `bin` folder, you should ignore this option. |
| `android-manifest-path` || The path to the `AndroidManifest.xml`. If `scheme` was provided or you're using the managed workflow, this option is ignored. |
| `ios-info-plist-path` || The path to the `Info.plist`. If `scheme` was provided or you're using the managed workflow, this option is ignored. |
| `ios-info-plist-path` || The path to the `Info.plist`. If `scheme` was provided or you're using the managed workflow, this option is ignored. |

## 📝 Example workflows

Expand Down Expand Up @@ -106,7 +108,11 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
msg: Awesome! You can [preview the PR here](${{ steps.preview.outputs.EXPO_QR_CODE_URL }}).<br><br><a href="${{ steps.publish.outputs.EXPO_QR_CODE_URL }}"><img src="${{ steps.preview.outputs.EXPO_QR_CODE_URL }}" height="512px" width="512px"></a>
msg: >
Awesome! You can [preview the PR here](${{ steps.preview.outputs.EXPO_QR_CODE_URL }}).<br><br>
<a href="${{ steps.publish.outputs.EXPO_QR_CODE_URL }}"><img src="${{ steps.preview.outputs.EXPO_QR_CODE_URL }}" height="512px" width="512px"></a>
<br><br>
${{ steps.publish.outputs.EXPO_NEW_BUILD_IS_REQUIRED_MESSAGE }}
```
### Create a preview only if app files were changed
Expand Down Expand Up @@ -152,5 +158,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
msg: Awesome! You can [preview the PR here](${{ steps.preview.outputs.EXPO_QR_CODE_URL }}).<br><br><a href="${{ steps.publish.outputs.EXPO_QR_CODE_URL }}"><img src="${{ steps.preview.outputs.EXPO_QR_CODE_URL }}" height="512px" width="512px"></a>
msg: >
Awesome! You can [preview the PR here](${{ steps.preview.outputs.EXPO_QR_CODE_URL }}).<br><br>
<a href="${{ steps.publish.outputs.EXPO_QR_CODE_URL }}"><img src="${{ steps.preview.outputs.EXPO_QR_CODE_URL }}" height="512px" width="512px"></a>
<br><br>
${{ steps.publish.outputs.EXPO_NEW_BUILD_IS_REQUIRED_MESSAGE }}
```
7 changes: 7 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ inputs:
ios-info-plist-path:
description: The path to the `Info.plist`
required: false
token:
description: >
GitHub token for GitHub API requests.
You shouldn't need to set this themselves, but we recommend using a service account with the least permissions necessary if you want to do it.
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
default: ${{ github.token }}
required: true
8,859 changes: 8,859 additions & 0 deletions build/index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"prepare": "husky install"
},
"dependencies": {
"@actions/github": "^5.0.0",
"@actions/core": "^1.4.0",
"@actions/exec": "^1.1.0",
"@expo/config": "^5.0.1",
Expand Down
6 changes: 5 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ export type PlatformSpecificProjectConfig = ProjectConfig & {
infoPlist?: string;
};

export type Config = PlatformSpecificProjectConfig & PublishConfig;
export type GithubConfig = {
token: string;
};

export type Config = PlatformSpecificProjectConfig & PublishConfig & GithubConfig;
93 changes: 93 additions & 0 deletions src/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { info } from '@actions/core';
import { GithubConfig, ProjectConfig, ProjectFlavor } from './config';
import { context, getOctokit } from '@actions/github';

function getDiffInfo(): { base: string; head: string } {
const eventName = context.eventName;

let base: string | undefined;
let head: string | undefined;

if (eventName === 'pull_request') {
const pullRequestBase = (context.payload.pull_request as { base?: any }).base;
const pullRequestHead = (context.payload.pull_request as { head?: any }).head;
base = pullRequestBase.sha;
head = pullRequestHead.sha;
} else if (eventName === 'push') {
base = context.payload.before;
head = context.payload.after;
} else {
throw new Error(
`This action only supports pull requests and pushes, ${context.eventName} events are not supported.`
);
}

if (!base || !head) {
throw new Error(`The base and head commits are missing from the payload for this ${context.eventName} event.`);
}

return { base, head };
}

async function getChangedFiles(config: GithubConfig): Promise<string[]> {
const client = getOctokit(config.token);
const diffInfo = getDiffInfo();

info(`Base commit: ${diffInfo.base}`);
info(`Head commit: ${diffInfo.head}`);

const compareResponse = await client.rest.repos.compareCommits({
...diffInfo,
owner: context.repo.owner,
repo: context.repo.repo,
});

if (compareResponse.status !== 200) {
throw new Error(
`The GitHub API for comparing the base and head commits for this ${context.eventName} event returned ${compareResponse.status}, expected 200.`
);
}

if (compareResponse.data.status !== 'ahead') {
throw new Error(`The head commit for this ${context.eventName} event is not ahead of the base commit.`);
}

return (compareResponse.data.files || []).map(file => file.filename);
}

export async function needNewDevClientBuild(config: GithubConfig & ProjectConfig): Promise<boolean> {
if (config.projectFlavor === ProjectFlavor.ExpoGo) {
return false;
}

const changedFiles = await getChangedFiles(config);

const nativeFiles = changedFiles.filter(
file =>
file.endsWith('package.json') ||
// Android files
file.endsWith('.java') ||
file.endsWith('.xml') ||
file.endsWith('.gradle') ||
file.endsWith('.properties') ||
file.endsWith('.pro') ||
file.endsWith('.kt') ||
// iOS files
file.endsWith('.h') ||
file.endsWith('.m') ||
file.endsWith('.mm') ||
file.endsWith('.swift') ||
file.endsWith('.podspec') ||
file.endsWith('Podfile') ||
file.endsWith('Podfile.lock') ||
file.endsWith('.pbxproj') ||
// C/C++ files
file.endsWith('.c') ||
file.endsWith('.cpp') ||
file.endsWith('CMakeLists.txt')
);

info(`Changed native files:\n\t${nativeFiles.join('\n\t')}`);

return nativeFiles.length > 0;
}
13 changes: 13 additions & 0 deletions src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { group, getInput, setOutput, info } from '@actions/core';
import { publish } from './publish';
import { chooseScheme } from './scheme';
import { createQRCodeURL } from './url';
import { needNewDevClientBuild } from './github';

function undefinedIfEmpty(string: string): string | undefined {
return string || undefined;
Expand Down Expand Up @@ -31,6 +32,7 @@ export async function run(): Promise<void> {
projectRoot: undefinedIfEmpty(getInput('project-root')),
manifestPath: undefinedIfEmpty(getInput('android-manifest-path')),
infoPlist: undefinedIfEmpty(getInput('ios-info-plist-path')),
token: getInput('token', { required: true }),
};

const scheme = await group('Choose scheme', async () => {
Expand All @@ -39,6 +41,10 @@ export async function run(): Promise<void> {
return scheme;
});

const needToRebuildDevClient = await group('Check if a new version of the development client is required', () =>
needNewDevClientBuild(config)
);

const manifestURL = await group('Publish application', () => publish(config));

const QRCodeURL = await group('Create QRCode', () => {
Expand All @@ -49,4 +55,11 @@ export async function run(): Promise<void> {

setOutput('EXPO_MANIFEST_URL', manifestURL);
setOutput('EXPO_QR_CODE_URL', QRCodeURL);
setOutput('EXPO_NEW_BUILD_IS_REQUIRED', needToRebuildDevClient);
setOutput(
'EXPO_NEW_BUILD_IS_REQUIRED_MESSAGE',
needToRebuildDevClient
? `<strong>⚠️ Warning</strong>: To open this preview, you may need to rebuild your native application.`
: ''
);
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "./build",
"rootDir": "./src",
"noImplicitAny": false
"noImplicitAny": false,
},
"exclude": [
"build",
Expand Down
46 changes: 44 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@
dependencies:
"@actions/io" "^1.0.1"

"@actions/github@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@actions/github/-/github-5.0.0.tgz#1754127976c50bd88b2e905f10d204d76d1472f8"
integrity sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==
dependencies:
"@actions/http-client" "^1.0.11"
"@octokit/core" "^3.4.0"
"@octokit/plugin-paginate-rest" "^2.13.3"
"@octokit/plugin-rest-endpoint-methods" "^5.1.1"

"@actions/http-client@^1.0.11":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.11.tgz#c58b12e9aa8b159ee39e7dd6cbd0e91d905633c0"
integrity sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==
dependencies:
tunnel "0.0.6"

"@actions/io@^1.0.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.1.tgz#4a157406309e212ab27ed3ae30e8c1d641686a66"
Expand Down Expand Up @@ -1378,7 +1395,7 @@
dependencies:
"@octokit/types" "^6.0.3"

"@octokit/core@^3.5.0":
"@octokit/core@^3.4.0", "@octokit/core@^3.5.0":
version "3.5.1"
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b"
integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==
Expand Down Expand Up @@ -1414,7 +1431,12 @@
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.3.4.tgz#9f075e263d1d3c1ba7a789e0085a5ed75d6daad1"
integrity sha512-binmLrMQWBG0CvUE/jS3/XXrZbX3oN/6gF7ocSsb/ZJ0xfox2isJN4ZhGeL91SDJVzFK7XuUYBm2mlIDedkxsg==

"@octokit/plugin-paginate-rest@^2.6.2":
"@octokit/openapi-types@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.4.0.tgz#07631899dc32b72a532178e27235c541f3c359f1"
integrity sha512-V2qNML1knHjrjTJcIIvhYZSTkvtSAoQpNEX8y0ykTJI8vOQPqIh0y6Jf9EU6c/y+v0c9+LeC1acwLQh1xo96MA==

"@octokit/plugin-paginate-rest@^2.13.3", "@octokit/plugin-paginate-rest@^2.6.2":
version "2.13.5"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.5.tgz#e459f9b5dccbe0a53f039a355d5b80c0a2b0dc57"
integrity sha512-3WSAKBLa1RaR/7GG+LQR/tAZ9fp9H9waE9aPXallidyci9oZsfgsLn5M836d3LuDC6Fcym+2idRTBpssHZePVg==
Expand All @@ -1434,6 +1456,14 @@
"@octokit/types" "^6.16.6"
deprecation "^2.3.1"

"@octokit/plugin-rest-endpoint-methods@^5.1.1":
version "5.3.5"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.5.tgz#2ad8dd6e0ee0e9db2a807879bf4ab7775834f27c"
integrity sha512-QL3vIDYbANwxfrWOFlgn0rkp1ojlZKccsTb0+9x9keG8XGIq4ZJhxgpyJASYIui1QqCV8JIfDiEqX2dzr7x8KA==
dependencies:
"@octokit/types" "^6.17.1"
deprecation "^2.3.1"

"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677"
Expand Down Expand Up @@ -1472,6 +1502,13 @@
dependencies:
"@octokit/openapi-types" "^7.3.4"

"@octokit/types@^6.17.1":
version "6.17.1"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.17.1.tgz#b8e3fbf22efb939e1cb428f05f2991a258996810"
integrity sha512-x1RDwjjSzGHK8hfwyNa4Gz0t5YtwUfIxEtejFpm2cjH1gMMk6XDv8La3gUAPzYpDgdzTEF8Lpz+7BGv9aZR/0w==
dependencies:
"@octokit/openapi-types" "^7.4.0"

"@semantic-release/changelog@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@semantic-release/changelog/-/changelog-5.0.1.tgz#50a84b63e5d391b7debfe021421589fa2bcdafe4"
Expand Down Expand Up @@ -6549,6 +6586,11 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"

tunnel@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==

tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
Expand Down

0 comments on commit c7828a4

Please sign in to comment.