ci: soft-fail mobile OTA when EXPO_TOKEN is missing (#124)
* ci: soft-fail mobile OTA when EXPO_TOKEN is missing OTA publish is an infra helper; missing Expo credentials should not fail the ci workflow or block evo-deploy after green Product CI. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * test(updater): narrow optional workflow jobs and format the OTA assertion Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mFCmHk4zPyWpD9yY2C3yJ --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d912712668
commit
0d1939c775
28
.github/workflows/ci.yml
vendored
28
.github/workflows/ci.yml
vendored
@ -246,6 +246,7 @@ jobs:
|
||||
publish-mobile-update:
|
||||
name: Publish compatible mobile update
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
continue-on-error: true
|
||||
needs: [lint, check, build, test, test-integration, test-e2e]
|
||||
runs-on: [self-hosted, evoo-ci, Linux]
|
||||
timeout-minutes: 20
|
||||
@ -344,23 +345,30 @@ jobs:
|
||||
fi
|
||||
echo "publish=$publish" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Require Expo automation token
|
||||
- name: Check Expo automation token
|
||||
id: expo_token
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
env:
|
||||
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
|
||||
run: |
|
||||
if [[ -z "$EXPO_TOKEN" ]]; then
|
||||
echo "Add an EXPO_TOKEN repository secret with access to the inbox-zero/rakazo Expo project." >&2
|
||||
exit 1
|
||||
{
|
||||
echo "### Mobile OTA skipped"
|
||||
echo
|
||||
echo "No \`EXPO_TOKEN\` repository secret is configured, so CI will not publish a production update. Add a token with access to the inbox-zero/rakazo Expo project when OTA from main is desired."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "ready=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "ready=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
id: pnpm-setup
|
||||
with:
|
||||
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.job }}
|
||||
- name: Isolate pnpm store and PATH
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@ -377,25 +385,25 @@ jobs:
|
||||
mkdir -p "$store"
|
||||
echo "npm_config_store_dir=${store}" >> "$GITHUB_ENV"
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
- name: Set up Expo
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
uses: expo/expo-github-action@eab7a230208c952974db8c3245cfd78402c7b385 # v9
|
||||
with:
|
||||
eas-version: latest
|
||||
packager: pnpm
|
||||
token: ${{ secrets.EXPO_TOKEN }}
|
||||
- name: Install dependencies
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Verify mobile project
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
run: pnpm --filter @rakazo/mobile check
|
||||
- name: Publish production update
|
||||
if: steps.ota.outputs.publish == 'true'
|
||||
if: steps.ota.outputs.publish == 'true' && steps.expo_token.outputs.ready == 'true'
|
||||
working-directory: apps/mobile
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
47
infra/updater/src/ci-workflow.test.ts
Normal file
47
infra/updater/src/ci-workflow.test.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
|
||||
const workflowText = readFileSync(
|
||||
path.resolve(import.meta.dirname, "../../../.github/workflows/ci.yml"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
interface WorkflowJob {
|
||||
if?: string;
|
||||
needs?: string[];
|
||||
"continue-on-error"?: boolean;
|
||||
}
|
||||
|
||||
const workflow = parse(workflowText) as {
|
||||
jobs: Record<string, WorkflowJob>;
|
||||
};
|
||||
|
||||
describe("ci workflow merge gate", () => {
|
||||
it("aggregates only product jobs in Product CI", () => {
|
||||
const productGate = workflow.jobs["product-gate"];
|
||||
expect(productGate?.needs).toEqual([
|
||||
"lint",
|
||||
"check",
|
||||
"build",
|
||||
"test",
|
||||
"test-integration",
|
||||
"test-e2e",
|
||||
]);
|
||||
expect(productGate?.if).toBe("always()");
|
||||
});
|
||||
|
||||
it("soft-fails mobile OTA so missing Expo credentials do not fail the workflow", () => {
|
||||
const mobile = workflow.jobs["publish-mobile-update"];
|
||||
expect(mobile?.["continue-on-error"]).toBe(true);
|
||||
expect(workflowText).toContain('echo "ready=false" >> "$GITHUB_OUTPUT"');
|
||||
expect(workflowText).not.toMatch(
|
||||
/Check Expo automation token[\s\S]*?exit 1[\s\S]*?Require Expo/,
|
||||
);
|
||||
expect(workflowText).not.toContain(
|
||||
"Add an EXPO_TOKEN repository secret with access to the inbox-zero/rakazo Expo project.",
|
||||
);
|
||||
expect(workflowText).toContain("steps.expo_token.outputs.ready == 'true'");
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user