Skip to content

Commit

Permalink
Add continuous-deploy-fingerprint action (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
wschurman authored Apr 17, 2024
1 parent f6f3acf commit ec8dc84
Show file tree
Hide file tree
Showing 13 changed files with 46,346 additions and 156 deletions.
44,762 changes: 44,762 additions & 0 deletions build/continuous-deploy-fingerprint/index.js

Large diffs are not rendered by default.

835 changes: 835 additions & 0 deletions build/continuous-deploy-fingerprint/license.txt

Large diffs are not rendered by default.

61 changes: 56 additions & 5 deletions build/preview-build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88807,6 +88807,7 @@ const github_1 = __nccwpck_require__(5438);
const assert_1 = __importDefault(__nccwpck_require__(9491));
const uuid_1 = __nccwpck_require__(5840);
const cacher_1 = __nccwpck_require__(331);
const comment_1 = __nccwpck_require__(7810);
const expo_1 = __nccwpck_require__(2489);
const fingerprint_1 = __nccwpck_require__(3111);
const github_2 = __nccwpck_require__(978);
Expand Down Expand Up @@ -88959,9 +88960,6 @@ function getVariables(config, builds) {
};
}
exports.getVariables = getVariables;
function createDetails({ summary, details, delim = '\n', }) {
return `<details><summary>${summary}</summary>${delim.repeat(2)}${details}${delim}</details>`;
}
function createMessageBodyInBuilding(builds, fingerprintDiff, input) {
const tableRows = [];
for (const build of builds) {
Expand All @@ -88976,7 +88974,7 @@ function createMessageBodyInBuilding(builds, fingerprintDiff, input) {
name = 'Unknown build';
}
const buildPageURL = (0, expo_1.getBuildLogsUrl)(build);
const details = createDetails({
const details = (0, comment_1.createDetails)({
summary: 'Details',
details: [
`Distribution: \`${build.distribution}\``,
Expand All @@ -88999,7 +88997,7 @@ function createMessageBodyInBuilding(builds, fingerprintDiff, input) {
'| :-- | :-- | :-- |',
...tableRows,
'',
createDetails({
(0, comment_1.createDetails)({
summary: 'Fingerprint diff',
details: ['```json', JSON.stringify(fingerprintDiff, null, 2), '```'].join('\n'),
}),
Expand Down Expand Up @@ -89237,6 +89235,59 @@ function handleCacheError(error) {
exports.handleCacheError = handleCacheError;


/***/ }),

/***/ 7810:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createDetails = exports.getSchemesInOrderFromConfig = exports.getQrTarget = void 0;
const core_1 = __nccwpck_require__(2186);
const expo_1 = __nccwpck_require__(2489);
function getQrTarget(input) {
if (!input.qrTarget) {
const appType = (0, expo_1.projectAppType)(input.workingDirectory);
(0, core_1.debug)(`Using inferred QR code target: "${appType}"`);
return appType;
}
switch (input.qrTarget) {
// Note, `dev-build` is prefered, but `dev-client` is supported to aovid confusion
case 'dev-client':
case 'dev-build':
(0, core_1.debug)(`Using QR code target: "dev-build"`);
return 'dev-build';
case 'expo-go':
(0, core_1.debug)(`Using QR code target: "expo-go"`);
return 'expo-go';
default:
throw new Error(`Invalid QR code target: "${input.qrTarget}", expected "expo-go" or "dev-build"`);
}
}
exports.getQrTarget = getQrTarget;
/**
* Retrieve the app schemes, in correct priority order, from project config.
* - If the scheme is a string, return `[scheme]`.
* - If the scheme is an array, return the schemes sorted by length, longest first.
* - If the scheme is empty/incorrect, return an empty array.
*/
function getSchemesInOrderFromConfig(config) {
if (typeof config.scheme === 'string') {
return [config.scheme];
}
if (Array.isArray(config.scheme)) {
return config.scheme.sort((a, b) => b.length - a.length);
}
return [];
}
exports.getSchemesInOrderFromConfig = getSchemesInOrderFromConfig;
function createDetails({ summary, details, delim = '\n', }) {
return `<details><summary>${summary}</summary>${delim.repeat(2)}${details}${delim}</details>`;
}
exports.createDetails = createDetails;


/***/ }),

/***/ 2489:
Expand Down
97 changes: 57 additions & 40 deletions build/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41838,10 +41838,10 @@ try {
"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createSummary = exports.getSchemesInOrderFromConfig = exports.getQrTarget = exports.getVariables = exports.previewAction = exports.previewInput = exports.MESSAGE_ID = void 0;
exports.createSummary = exports.getVariables = exports.previewAction = exports.previewInput = exports.MESSAGE_ID = void 0;
const core_1 = __nccwpck_require__(2186);
const comment_1 = __nccwpck_require__(7810);
const eas_1 = __nccwpck_require__(3251);
const expo_1 = __nccwpck_require__(2489);
const github_1 = __nccwpck_require__(978);
const project_1 = __nccwpck_require__(7191);
const utils_1 = __nccwpck_require__(1314);
Expand Down Expand Up @@ -41933,9 +41933,9 @@ function getVariables(config, updates, options) {
const projectId = config.extra?.eas?.projectId;
const android = updates.find(update => update.platform === 'android');
const ios = updates.find(update => update.platform === 'ios');
const appSchemes = getSchemesInOrderFromConfig(config) || [];
const appSchemes = (0, comment_1.getSchemesInOrderFromConfig)(config) || [];
const appSlug = config.slug;
const qrTarget = getQrTarget(options);
const qrTarget = (0, comment_1.getQrTarget)(options);
return {
// EAS / Expo specific
projectId,
Expand Down Expand Up @@ -41975,42 +41975,6 @@ function getVariables(config, updates, options) {
};
}
exports.getVariables = getVariables;
function getQrTarget(input) {
if (!input.qrTarget) {
const appType = (0, expo_1.projectAppType)(input.workingDirectory);
(0, core_1.debug)(`Using inferred QR code target: "${appType}"`);
return appType;
}
switch (input.qrTarget) {
// Note, `dev-build` is prefered, but `dev-client` is supported to aovid confusion
case 'dev-client':
case 'dev-build':
(0, core_1.debug)(`Using QR code target: "dev-build"`);
return 'dev-build';
case 'expo-go':
(0, core_1.debug)(`Using QR code target: "expo-go"`);
return 'expo-go';
default:
throw new Error(`Invalid QR code target: "${input.qrTarget}", expected "expo-go" or "dev-build"`);
}
}
exports.getQrTarget = getQrTarget;
/**
* Retrieve the app schemes, in correct priority order, from project config.
* - If the scheme is a string, return `[scheme]`.
* - If the scheme is an array, return the schemes sorted by length, longest first.
* - If the scheme is empty/incorrect, return an empty array.
*/
function getSchemesInOrderFromConfig(config) {
if (typeof config.scheme === 'string') {
return [config.scheme];
}
if (Array.isArray(config.scheme)) {
return config.scheme.sort((a, b) => b.length - a.length);
}
return [];
}
exports.getSchemesInOrderFromConfig = getSchemesInOrderFromConfig;
/**
* Generate the message body for a single update.
* Note, this is not configurable, but you can use the variables used to construct your own.
Expand Down Expand Up @@ -42074,6 +42038,59 @@ ${androidQr || '_not created_'} | ${iosQr || '_not created_'}
}


/***/ }),

/***/ 7810:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createDetails = exports.getSchemesInOrderFromConfig = exports.getQrTarget = void 0;
const core_1 = __nccwpck_require__(2186);
const expo_1 = __nccwpck_require__(2489);
function getQrTarget(input) {
if (!input.qrTarget) {
const appType = (0, expo_1.projectAppType)(input.workingDirectory);
(0, core_1.debug)(`Using inferred QR code target: "${appType}"`);
return appType;
}
switch (input.qrTarget) {
// Note, `dev-build` is prefered, but `dev-client` is supported to aovid confusion
case 'dev-client':
case 'dev-build':
(0, core_1.debug)(`Using QR code target: "dev-build"`);
return 'dev-build';
case 'expo-go':
(0, core_1.debug)(`Using QR code target: "expo-go"`);
return 'expo-go';
default:
throw new Error(`Invalid QR code target: "${input.qrTarget}", expected "expo-go" or "dev-build"`);
}
}
exports.getQrTarget = getQrTarget;
/**
* Retrieve the app schemes, in correct priority order, from project config.
* - If the scheme is a string, return `[scheme]`.
* - If the scheme is an array, return the schemes sorted by length, longest first.
* - If the scheme is empty/incorrect, return an empty array.
*/
function getSchemesInOrderFromConfig(config) {
if (typeof config.scheme === 'string') {
return [config.scheme];
}
if (Array.isArray(config.scheme)) {
return config.scheme.sort((a, b) => b.length - a.length);
}
return [];
}
exports.getSchemesInOrderFromConfig = getSchemesInOrderFromConfig;
function createDetails({ summary, details, delim = '\n', }) {
return `<details><summary>${summary}</summary>${delim.repeat(2)}${details}${delim}</details>`;
}
exports.createDetails = createDetails;


/***/ }),

/***/ 3251:
Expand Down
160 changes: 160 additions & 0 deletions continuous-deploy-fingerprint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<div align="center">
<h1>continuous-deploy-fingerprint</h1>
<p>Continuously deploys an Expo project using EAS Build and EAS Update in combination with fingerprint runtime versions</p>
</div>

<p align="center">
<a href="https://github.com/expo/expo-github-action/releases" title="Latest release">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/package-json/v/expo/expo-github-action?style=flat-square&color=0366D6&labelColor=49505A">
<img alt="Latest release" src="https://img.shields.io/github/package-json/v/expo/expo-github-action?style=flat-square&color=0366D6&labelColor=D1D5DA" />
</picture>
</a>
<a href="https://github.com/expo/expo-github-action/actions" title="Workflow status">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/actions/workflow/status/expo/expo-github-action/test.yml?branch=main&style=flat-square&labelColor=49505A">
<img alt="Workflow status" src="https://img.shields.io/github/actions/workflow/status/expo/expo-github-action/test.yml?branch=main&style=flat-square&labelColor=D1D5DA" />
</picture>
</a>
</p>

<p align="center">
<a href="#usage"><b>Usage</b></a>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;
<a href="#available-outputs"><b>Outputs</b></a>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;
<a href="#example-workflows"><b>Examples</b></a>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;
<a href="#caveats"><b>Caveats</b></a>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;
<a href="https://github.com/expo/expo-github-action/blob/main/CHANGELOG.md"><b>Changelog</b></a>
</p>

<br />

> **Warning**
> This sub action is experimental and might change without notice. Use it at your own risk.
## Overview

`continuous-deploy-fingerprint` is a GitHub Action continuously deploys an Expo project using the expo-updates fingerprint runtime version policy. When run, it performs the following tasks in order, once for each platform:
1. Check current fingerprint of the project.
2. Check for EAS builds with specified profile matching that fingerprint.
3. If an EAS build doesn't exist, start one.
4. Publish an update on EAS update.
5. If run on a PR, post a comment indicating what was done.

### Configuration options

This action is customizable through variables defined in the [`action.yml`](action.yml).
Here is a summary of all the input options you can use.

| variable | default | description |
| ---------------------------------- | ---------------- | ----------------------------------------------------------------------- |
| **profile** | (required) | The EAS Build profile to use |
| **branch** | (required) | The EAS Update branch on which to publish |
| **working-directory** | - | The relative directory of your Expo app |
| **github-token** | `github.token` | GitHub token to use when commenting on PR ([read more](#github-tokens)) |

And the action will generate these [outputs](#available-outputs) for other actions to do something based on what this action did.

### Available outputs

In case you want to reuse this action for other purpose, this action will set the following action outputs.

| output name | description |
| ------------------------ | ------------------------------ |
| **ios-fingerprint** | The iOS fingerprint of the current commit. |
| **android-fingerprint** | The Android fingerprint of the current commit. |
| **android-build-id** | ID for Android EAS Build if one was started. |
| **ios-build-id** | ID for iOS EAS Build if one was started. |
| **update-output** | The output (JSON) from the `eas update` command. |

## Caveats

### GitHub tokens

When using the GitHub API, you always need to be authenticated.
This action tries to auto-authenticate using the [Automatic token authentication][link-gha-token] from GitHub.
You can overwrite the token by adding the `GITHUB_TOKEN` environment variable or add the **github-token** input.

<div align="center">
<br />
with :heart:&nbsp;<strong>Expo</strong>
<br />
</div>

## Example workflows

Before diving into the workflow examples, you should know the basics of GitHub Actions.
You can read more about this in the [GitHub Actions documentation][link-actions].

### Continuously deploy after tests on main branch and pull requests

This workflow continuously deploys:
- main branch -> production EAS Build profile and EAS Update branch
- PR branches -> development EAS Build profile and EAS Update branch

This means that every commit landed to main will go out to users on production. If a new build is created, it will need to be manually submitted to the app stores.

Pull requests also do a build if necessary and display a QR code to scan to preview the latest commit on the PR.

```yml
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize]

env:
EXPO_STAGING: 1
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 🏗 Setup repo
uses: actions/checkout@v4
- name: 🏗 Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: yarn
- name: 📦 Install dependencies
run: yarn install
- name: 🧪 Run tests
run: yarn test

continuously-deploy:
needs: test
runs-on: ubuntu-latest
concurrency: continuous-deploy-fingerprint-${{ github.event_name != 'pull_request' && 'main' || github.run_id }}
permissions:
contents: read # Allow checkout
pull-requests: write # Allow comments on PRs
steps:
- name: 🏗 Setup repo
uses: actions/checkout@v4

- name: 🏗 Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x

- name: 📦 Install dependencies
run: yarn install

- name: 🏗 Setup EAS
uses: expo/expo-github-action@main
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}

- name: Continuously Deploy
uses: expo/expo-github-action/continuous-deploy-fingerprint@main
with:
profile: ${{ github.event_name != 'pull_request' && 'production' || 'development' }}
branch: ${{ github.event_name != 'pull_request' && 'production' || 'development' }}
```
Loading

0 comments on commit ec8dc84

Please sign in to comment.