Skip to content

Commit 621ab35

Browse files
committed
feat: initial commit
0 parents  commit 621ab35

24 files changed

+4492
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @Robert27

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: CI - Biome and TypeScript
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
jobs:
8+
test:
9+
name: Run biome and typescript checks
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
with:
15+
lfs: true
16+
17+
- name: Set up Bun
18+
uses: oven-sh/setup-bun@v2
19+
20+
- name: Install dependencies
21+
run: bun install --frozen-lockfile
22+
23+
- name: Run TypeScript checks
24+
run: bun tsc --noEmit
25+
26+
- name: Run Biome check
27+
run: bun biome ci .
28+
29+
- name: Run tests
30+
run: bun test

.github/workflows/codeql.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: "CodeQL"
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
schedule:
9+
- cron: "19 20 * * 3"
10+
11+
jobs:
12+
analyze:
13+
name: Analyze (${{ matrix.language }})
14+
# Runner size impacts CodeQL analysis time. To learn more, please see:
15+
# - https://gh.io/recommended-hardware-resources-for-running-codeql
16+
# - https://gh.io/supported-runners-and-hardware-resources
17+
# - https://gh.io/using-larger-runners (GitHub.com only)
18+
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
19+
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
20+
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
21+
permissions:
22+
# required for all workflows
23+
security-events: write
24+
25+
# required to fetch internal or private CodeQL packs
26+
packages: read
27+
28+
# only required for workflows in private repositories
29+
actions: read
30+
contents: read
31+
32+
strategy:
33+
fail-fast: false
34+
matrix:
35+
include:
36+
- language: javascript-typescript
37+
build-mode: none
38+
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
39+
# Use `c-cpp` to analyze code written in C, C++ or both
40+
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
41+
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
42+
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
43+
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
44+
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
45+
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
46+
steps:
47+
- name: Checkout repository
48+
uses: actions/checkout@v4
49+
50+
# Initializes the CodeQL tools for scanning.
51+
- name: Initialize CodeQL
52+
uses: github/codeql-action/init@v3
53+
with:
54+
languages: ${{ matrix.language }}
55+
build-mode: ${{ matrix.build-mode }}
56+
# If you wish to specify custom queries, you can do so here or in a config file.
57+
# By default, queries listed here will override any specified in a config file.
58+
# Prefix the list here with "+" to use these queries and those in the config file.
59+
60+
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
61+
# queries: security-extended,security-and-quality
62+
63+
# If the analyze step fails for one of the languages you are analyzing with
64+
# "We were unable to automatically build your code", modify the matrix above
65+
# to set the build mode to "manual" for that language. Then modify this step
66+
# to build your code.
67+
# ℹ️ Command-line programs to run using the OS shell.
68+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
69+
- if: matrix.build-mode == 'manual'
70+
shell: bash
71+
run: |
72+
echo 'If you are using a "manual" build mode for one or more of the' \
73+
'languages you are analyzing, replace this with the commands to build' \
74+
'your code, for example:'
75+
echo ' make bootstrap'
76+
echo ' make release'
77+
exit 1
78+
79+
- name: Perform CodeQL Analysis
80+
uses: github/codeql-action/analyze@v3
81+
with:
82+
category: "/language:${{matrix.language}}"

.github/workflows/pr-title.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: "Lint PR"
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- opened
7+
- edited
8+
- synchronize
9+
- reopened
10+
11+
permissions:
12+
pull-requests: read
13+
14+
jobs:
15+
main:
16+
name: Validate PR title
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: amannn/action-semantic-pull-request@v5
20+
with:
21+
subjectPattern: ^[^A-Z].*[^.]$
22+
env:
23+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
node_modules
2+
build
3+
logs
4+
.env
5+
.env.development.local
6+
.env.test.local
7+
.env.production.local
8+
.env.local
9+
*.tsbuildinfo
10+
.idea
11+
.vscode
12+
.DS_Store
13+

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Robert Eggl
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Expo GitHub Cache
2+
3+
A remote build cache provider plugin for Expo that uses GitHub Releases to store and retrieve cached build artifacts, dramatically accelerating your local development workflow.
4+
5+
## What is this?
6+
7+
This library implements Expo's [remote build cache provider interface](https://docs.expo.dev/guides/cache-builds-remotely/) using GitHub Releases as the storage backend. Build caching is an experimental Expo feature that speeds up `npx expo run:ios` and `npx expo run:android` commands by caching builds remotely based on your project's [fingerprint](https://docs.expo.dev/versions/latest/sdk/fingerprint/).
8+
9+
### How it works with Expo's caching system
10+
11+
When you run local development builds with `npx expo run:[android|ios]`, this plugin:
12+
13+
1. **Checks for existing builds**: Searches GitHub Releases for a cached build matching your project's current fingerprint
14+
2. **Downloads if available**: If a matching build exists, downloads and launches it instead of compiling from scratch
15+
3. **Uploads new builds**: If no cache exists, compiles normally and uploads the resulting binary to GitHub Releases for future use
16+
17+
This integration with Expo's build caching system can save significant time during development, especially for large projects or when switching between branches with similar dependencies. Instead of waiting for full compilation every time, you can reuse previously built binaries when your project's fingerprint hasn't changed.
18+
19+
## Installation
20+
21+
```bash
22+
npm install @robert27/expo-github-cache
23+
# or
24+
bun add @robert27/expo-github-cache
25+
```
26+
27+
## Configuration
28+
29+
### 1. Set up GitHub Token
30+
31+
Create a GitHub Personal Access Token with `repo` permissions and set it as an environment variable:
32+
33+
```bash
34+
export GITHUB_TOKEN=your_github_token_here
35+
```
36+
37+
### 2. Configure your Expo project
38+
39+
Add the build cache provider to your `app.json` or `app.config.js`:
40+
41+
```json
42+
{
43+
"expo": {
44+
"experiments": {
45+
"buildCacheProvider": {
46+
"plugin": "@robert27/expo-github-cache",
47+
"options": {
48+
"owner": "your-github-username",
49+
"repo": "your-repo-name"
50+
}
51+
}
52+
}
53+
}
54+
}
55+
```
56+
57+
### 3. Usage
58+
59+
Now when you run your Expo commands, the cache will automatically be used:
60+
61+
```bash
62+
npx expo run:ios
63+
npx expo run:android
64+
```
65+
66+
## How it works
67+
68+
The plugin uses your project's [fingerprint hash](https://docs.expo.dev/versions/latest/sdk/fingerprint/) to create unique tags in GitHub Releases. Each build artifact is stored as a release asset with a tag like:
69+
70+
- `fingerprint.abc123def456.ios` - iOS production build
71+
- `fingerprint.abc123def456.dev-client.android` - Android development client build
72+
73+
When you run a build command, the plugin:
74+
75+
1. Calculates your project's current fingerprint
76+
2. Searches for a GitHub Release with the matching tag
77+
3. Downloads the cached build if found, or compiles and uploads if not
78+
79+
## Requirements
80+
81+
- Node.js 18 or higher
82+
- GitHub repository with release permissions
83+
- GitHub Personal Access Token with `repo` scope
84+
- Expo project with fingerprinting enabled
85+
86+
## Environment Variables
87+
88+
| Variable | Required | Description |
89+
|----------|----------|-------------|
90+
| `GITHUB_TOKEN` | Yes | GitHub Personal Access Token with repo permissions |
91+
92+
## Contributing
93+
94+
This project is based on the [Expo build cache provider example](https://github.com/expo/examples/tree/master/with-github-remote-build-cache-provider) and implements the standard Expo build cache provider interface.
95+
96+
## License
97+
98+
MIT

__tests__/basic.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, expect, test } from "bun:test";
2+
import type { ResolveRemoteBuildCacheProps } from "@expo/config";
3+
import buildCachePlugin from "../src/index";
4+
5+
// Create dummy build props
6+
const createDummyProps = (
7+
platform: "ios" | "android",
8+
): ResolveRemoteBuildCacheProps => ({
9+
projectRoot: "/fake/project",
10+
platform,
11+
fingerprintHash: "1234567890abcdef",
12+
runOptions: {
13+
buildCache: false, // Disable cache to avoid external dependencies
14+
},
15+
});
16+
17+
describe("GitHub Cache Plugin Basic Tests", () => {
18+
test("should return null when build cache is disabled", async () => {
19+
// When buildCache is false, the function should return null immediately
20+
const props = createDummyProps("ios");
21+
22+
const result = await buildCachePlugin.resolveRemoteBuildCache(props, {
23+
owner: "owner",
24+
repo: "repo",
25+
});
26+
27+
expect(result).toBeNull();
28+
});
29+
30+
test("should include platform in tag name for iOS", async () => {
31+
const props = createDummyProps("ios");
32+
const uploadProps = {
33+
...props,
34+
buildPath: "/fake/build/path/app.zip",
35+
};
36+
37+
// Since GITHUB_TOKEN is missing, this should return null but still generate the tag name internally
38+
const result = await buildCachePlugin.uploadRemoteBuildCache(uploadProps, {
39+
owner: "owner",
40+
repo: "repo",
41+
});
42+
43+
expect(result).toBeNull();
44+
});
45+
46+
test("should include platform in tag name for Android", async () => {
47+
const props = createDummyProps("android");
48+
const uploadProps = {
49+
...props,
50+
buildPath: "/fake/build/path/app.apk",
51+
};
52+
53+
// Since GITHUB_TOKEN is missing, this should return null but still generate the tag name internally
54+
const result = await buildCachePlugin.uploadRemoteBuildCache(uploadProps, {
55+
owner: "owner",
56+
repo: "repo",
57+
});
58+
59+
expect(result).toBeNull();
60+
});
61+
});

0 commit comments

Comments
 (0)